summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/CMakeLists.txt140
-rw-r--r--sql/authors.h2
-rw-r--r--sql/bounded_queue.h3
-rw-r--r--sql/compat56.cc8
-rw-r--r--sql/create_options.cc25
-rw-r--r--sql/create_options.h20
-rw-r--r--sql/datadict.cc80
-rw-r--r--sql/datadict.h19
-rw-r--r--sql/debug_sync.cc12
-rw-r--r--sql/debug_sync.h2
-rw-r--r--sql/derror.cc16
-rw-r--r--sql/derror.h2
-rw-r--r--sql/des_key_file.cc4
-rw-r--r--sql/discover.cc6
-rw-r--r--sql/discover.h2
-rw-r--r--sql/encryption.cc2
-rw-r--r--sql/event_data_objects.cc107
-rw-r--r--sql/event_data_objects.h35
-rw-r--r--sql/event_db_repository.cc217
-rw-r--r--sql/event_db_repository.h20
-rw-r--r--sql/event_parse_data.cc16
-rw-r--r--sql/event_parse_data.h10
-rw-r--r--sql/event_queue.cc34
-rw-r--r--sql/event_queue.h14
-rw-r--r--sql/event_scheduler.cc19
-rw-r--r--sql/events.cc87
-rw-r--r--sql/events.h15
-rw-r--r--sql/field.cc2680
-rw-r--r--sql/field.h1696
-rw-r--r--sql/field_comp.cc154
-rw-r--r--sql/field_comp.h33
-rw-r--r--sql/field_conv.cc31
-rw-r--r--sql/filesort.cc214
-rw-r--r--sql/filesort.h2
-rw-r--r--sql/filesort_utils.cc2
-rw-r--r--sql/filesort_utils.h1
-rw-r--r--sql/gcalc_slicescan.cc8
-rw-r--r--sql/gcalc_tools.cc2
-rw-r--r--sql/gen_lex_hash.cc11
-rw-r--r--sql/gen_lex_token.cc18
-rw-r--r--sql/group_by_handler.cc8
-rw-r--r--sql/gstream.cc2
-rw-r--r--sql/gstream.h3
-rw-r--r--sql/ha_partition.cc3775
-rw-r--r--sql/ha_partition.h481
-rw-r--r--sql/ha_sequence.cc448
-rw-r--r--sql/ha_sequence.h165
-rw-r--r--sql/handler.cc1272
-rw-r--r--sql/handler.h973
-rw-r--r--sql/hash_filo.cc2
-rw-r--r--sql/hostname.cc57
-rw-r--r--sql/hostname.h1
-rw-r--r--sql/init.cc3
-rw-r--r--sql/init.h2
-rw-r--r--sql/innodb_priv.h6
-rw-r--r--sql/item.cc3546
-rw-r--r--sql/item.h2446
-rw-r--r--sql/item_buff.cc2
-rw-r--r--sql/item_cmpfunc.cc1983
-rw-r--r--sql/item_cmpfunc.h1260
-rw-r--r--sql/item_create.cc1828
-rw-r--r--sql/item_create.h72
-rw-r--r--sql/item_func.cc2545
-rw-r--r--sql/item_func.h1445
-rw-r--r--sql/item_geofunc.cc15
-rw-r--r--sql/item_geofunc.h510
-rw-r--r--sql/item_inetfunc.cc38
-rw-r--r--sql/item_inetfunc.h38
-rw-r--r--sql/item_jsonfunc.cc61
-rw-r--r--sql/item_jsonfunc.h131
-rw-r--r--sql/item_row.cc22
-rw-r--r--sql/item_row.h28
-rw-r--r--sql/item_strfunc.cc537
-rw-r--r--sql/item_strfunc.h569
-rw-r--r--sql/item_subselect.cc265
-rw-r--r--sql/item_subselect.h42
-rw-r--r--sql/item_sum.cc1173
-rw-r--r--sql/item_sum.h502
-rw-r--r--sql/item_timefunc.cc354
-rw-r--r--sql/item_timefunc.h567
-rw-r--r--sql/item_vers.cc179
-rw-r--r--sql/item_vers.h116
-rw-r--r--sql/item_windowfunc.cc210
-rw-r--r--sql/item_windowfunc.h556
-rw-r--r--sql/item_xmlfunc.cc127
-rw-r--r--sql/item_xmlfunc.h8
-rw-r--r--sql/key.cc22
-rw-r--r--sql/key.h6
-rw-r--r--sql/keycaches.cc37
-rw-r--r--sql/keycaches.h18
-rw-r--r--sql/lex.h67
-rw-r--r--sql/lex_string.h65
-rw-r--r--sql/lock.cc58
-rw-r--r--sql/lock.h3
-rw-r--r--sql/log.cc814
-rw-r--r--sql/log.h157
-rw-r--r--sql/log_event.cc2342
-rw-r--r--sql/log_event.h324
-rw-r--r--sql/log_event_old.cc187
-rw-r--r--sql/log_event_old.h10
-rw-r--r--sql/log_slow.h30
-rw-r--r--sql/mariadb.h30
-rw-r--r--sql/mdl.cc34
-rw-r--r--sql/mdl.h7
-rw-r--r--sql/mf_iocache.cc6
-rw-r--r--sql/mf_iocache_encr.cc8
-rw-r--r--sql/multi_range_read.cc48
-rw-r--r--sql/multi_range_read.h7
-rw-r--r--sql/my_apc.cc25
-rw-r--r--sql/my_apc.h6
-rw-r--r--sql/my_decimal.cc8
-rw-r--r--sql/my_decimal.h12
-rw-r--r--sql/my_json_writer.cc6
-rw-r--r--sql/my_json_writer.h5
-rw-r--r--sql/mysql_install_db.cc44
-rw-r--r--sql/mysql_upgrade_service.cc201
-rw-r--r--sql/mysqld.cc741
-rw-r--r--sql/mysqld.h108
-rw-r--r--sql/net_serv.cc128
-rw-r--r--sql/nt_servc.cc22
-rw-r--r--sql/nt_servc.h8
-rw-r--r--sql/opt_index_cond_pushdown.cc16
-rw-r--r--sql/opt_range.cc432
-rw-r--r--sql/opt_range.h60
-rw-r--r--sql/opt_range_mrr.cc4
-rw-r--r--sql/opt_split.cc1167
-rw-r--r--sql/opt_subselect.cc253
-rw-r--r--sql/opt_subselect.h12
-rw-r--r--sql/opt_sum.cc16
-rw-r--r--sql/opt_table_elimination.cc6
-rw-r--r--sql/parse_file.cc25
-rw-r--r--sql/parse_file.h20
-rw-r--r--sql/partition_element.h59
-rw-r--r--sql/partition_info.cc785
-rw-r--r--sql/partition_info.h124
-rw-r--r--sql/password.c4
-rw-r--r--sql/procedure.cc6
-rw-r--r--sql/procedure.h27
-rw-r--r--sql/protocol.cc64
-rw-r--r--sql/proxy_protocol.cc583
-rw-r--r--sql/proxy_protocol.h19
-rw-r--r--sql/records.cc123
-rw-r--r--sql/records.h17
-rw-r--r--sql/repl_failsafe.cc4
-rw-r--r--sql/repl_failsafe.h4
-rw-r--r--sql/replication.h14
-rw-r--r--sql/rpl_filter.cc6
-rw-r--r--sql/rpl_gtid.cc364
-rw-r--r--sql/rpl_gtid.h78
-rw-r--r--sql/rpl_handler.cc553
-rw-r--r--sql/rpl_handler.h216
-rw-r--r--sql/rpl_injector.cc13
-rw-r--r--sql/rpl_injector.h3
-rw-r--r--sql/rpl_mi.cc85
-rw-r--r--sql/rpl_mi.h58
-rw-r--r--sql/rpl_parallel.cc47
-rw-r--r--sql/rpl_parallel.h38
-rw-r--r--sql/rpl_record.cc23
-rw-r--r--sql/rpl_record.h1
-rw-r--r--sql/rpl_record_old.cc6
-rw-r--r--sql/rpl_reporting.cc2
-rw-r--r--sql/rpl_reporting.h2
-rw-r--r--sql/rpl_rli.cc516
-rw-r--r--sql/rpl_rli.h11
-rw-r--r--sql/rpl_tblmap.cc5
-rw-r--r--sql/rpl_utility.cc88
-rw-r--r--sql/scheduler.cc1
-rw-r--r--sql/scheduler.h2
-rw-r--r--sql/semisync.cc32
-rw-r--r--sql/semisync.h73
-rw-r--r--sql/semisync_master.cc1352
-rw-r--r--sql/semisync_master.h673
-rw-r--r--sql/semisync_master_ack_receiver.cc291
-rw-r--r--sql/semisync_master_ack_receiver.h240
-rw-r--r--sql/semisync_slave.cc250
-rw-r--r--sql/semisync_slave.h115
-rw-r--r--sql/session_tracker.cc799
-rw-r--r--sql/session_tracker.h287
-rw-r--r--sql/set_var.cc286
-rw-r--r--sql/set_var.h41
-rw-r--r--sql/share/errmsg-utf8.txt321
-rw-r--r--sql/signal_handler.cc6
-rw-r--r--sql/slave.cc824
-rw-r--r--sql/slave.h12
-rw-r--r--sql/sp.cc1755
-rw-r--r--sql/sp.h562
-rw-r--r--sql/sp_cache.cc15
-rw-r--r--sql/sp_cache.h6
-rw-r--r--sql/sp_head.cc2076
-rw-r--r--sql/sp_head.h832
-rw-r--r--sql/sp_pcontext.cc437
-rw-r--r--sql/sp_pcontext.h328
-rw-r--r--sql/sp_rcontext.cc513
-rw-r--r--sql/sp_rcontext.h190
-rw-r--r--sql/spatial.cc10
-rw-r--r--sql/spatial.h2
-rw-r--r--sql/sql_acl.cc1106
-rw-r--r--sql/sql_acl.h69
-rw-r--r--sql/sql_admin.cc87
-rw-r--r--sql/sql_admin.h2
-rw-r--r--sql/sql_alloc.h53
-rw-r--r--sql/sql_alter.cc283
-rw-r--r--sql/sql_alter.h246
-rw-r--r--sql/sql_analyse.cc32
-rw-r--r--sql/sql_analyse.h4
-rw-r--r--sql/sql_analyze_stmt.cc2
-rw-r--r--sql/sql_array.h13
-rw-r--r--sql/sql_audit.cc3
-rw-r--r--sql/sql_audit.h80
-rw-r--r--sql/sql_base.cc1146
-rw-r--r--sql/sql_base.h46
-rw-r--r--sql/sql_binlog.cc10
-rw-r--r--sql/sql_bitmap.h15
-rw-r--r--sql/sql_bootstrap.cc2
-rw-r--r--sql/sql_builtin.cc.in8
-rw-r--r--sql/sql_cache.cc378
-rw-r--r--sql/sql_cache.h106
-rw-r--r--sql/sql_class.cc954
-rw-r--r--sql/sql_class.h1400
-rw-r--r--sql/sql_client.cc2
-rw-r--r--sql/sql_cmd.h60
-rw-r--r--sql/sql_connect.cc203
-rw-r--r--sql/sql_connect.h7
-rw-r--r--sql/sql_const.h11
-rw-r--r--sql/sql_crypt.cc2
-rw-r--r--sql/sql_crypt.h2
-rw-r--r--sql/sql_cte.cc90
-rw-r--r--sql/sql_cte.h36
-rw-r--r--sql/sql_cursor.cc21
-rw-r--r--sql/sql_cursor.h6
-rw-r--r--sql/sql_db.cc246
-rw-r--r--sql/sql_db.h15
-rw-r--r--sql/sql_delete.cc376
-rw-r--r--sql/sql_delete.h3
-rw-r--r--sql/sql_derived.cc197
-rw-r--r--sql/sql_derived.h14
-rw-r--r--sql/sql_digest.cc13
-rw-r--r--sql/sql_digest.h4
-rw-r--r--sql/sql_do.cc6
-rw-r--r--sql/sql_error.cc186
-rw-r--r--sql/sql_error.h564
-rw-r--r--sql/sql_explain.cc95
-rw-r--r--sql/sql_explain.h15
-rw-r--r--sql/sql_expression_cache.cc10
-rw-r--r--sql/sql_get_diagnostics.cc11
-rw-r--r--sql/sql_handler.cc262
-rw-r--r--sql/sql_handler.h14
-rw-r--r--sql/sql_help.cc56
-rw-r--r--sql/sql_hset.h1
-rw-r--r--sql/sql_insert.cc446
-rw-r--r--sql/sql_insert.h1
-rw-r--r--sql/sql_join_cache.cc73
-rw-r--r--sql/sql_join_cache.h26
-rw-r--r--sql/sql_lex.cc4842
-rw-r--r--sql/sql_lex.h1440
-rw-r--r--sql/sql_lifo_buffer.h4
-rw-r--r--sql/sql_list.cc1
-rw-r--r--sql/sql_list.h72
-rw-r--r--sql/sql_load.cc72
-rw-r--r--sql/sql_load.h2
-rw-r--r--sql/sql_locale.cc3
-rw-r--r--sql/sql_locale.h1
-rw-r--r--sql/sql_manager.cc2
-rw-r--r--sql/sql_mode.cc2
-rw-r--r--sql/sql_parse.cc1574
-rw-r--r--sql/sql_parse.h52
-rw-r--r--sql/sql_partition.cc1322
-rw-r--r--sql/sql_partition.h52
-rw-r--r--sql/sql_partition_admin.cc172
-rw-r--r--sql/sql_plist.h10
-rw-r--r--sql/sql_plugin.cc370
-rw-r--r--sql/sql_plugin.h28
-rw-r--r--sql/sql_plugin_compat.h2
-rw-r--r--sql/sql_plugin_services.ic2
-rw-r--r--sql/sql_prepare.cc443
-rw-r--r--sql/sql_prepare.h16
-rw-r--r--sql/sql_priv.h9
-rw-r--r--sql/sql_profile.cc12
-rw-r--r--sql/sql_profile.h6
-rw-r--r--sql/sql_reload.cc36
-rw-r--r--sql/sql_rename.cc97
-rw-r--r--sql/sql_repl.cc216
-rw-r--r--sql/sql_schema.cc80
-rw-r--r--sql/sql_schema.h70
-rw-r--r--sql/sql_select.cc2455
-rw-r--r--sql/sql_select.h193
-rw-r--r--sql/sql_sequence.cc995
-rw-r--r--sql/sql_sequence.h167
-rw-r--r--sql/sql_servers.cc97
-rw-r--r--sql/sql_servers.h3
-rw-r--r--sql/sql_show.cc2003
-rw-r--r--sql/sql_show.h27
-rw-r--r--sql/sql_signal.cc186
-rw-r--r--sql/sql_signal.h21
-rw-r--r--sql/sql_sort.h14
-rw-r--r--sql/sql_state.c2
-rw-r--r--sql/sql_statistics.cc207
-rw-r--r--sql/sql_statistics.h20
-rw-r--r--sql/sql_string.cc117
-rw-r--r--sql/sql_string.h157
-rw-r--r--sql/sql_table.cc2961
-rw-r--r--sql/sql_table.h45
-rw-r--r--sql/sql_tablespace.cc11
-rw-r--r--sql/sql_test.cc25
-rw-r--r--sql/sql_time.cc207
-rw-r--r--sql/sql_time.h25
-rw-r--r--sql/sql_trigger.cc407
-rw-r--r--sql/sql_trigger.h79
-rw-r--r--sql/sql_truncate.cc93
-rw-r--r--sql/sql_tvc.cc1109
-rw-r--r--sql/sql_tvc.h72
-rw-r--r--sql/sql_type.cc5508
-rw-r--r--sql/sql_type.h3230
-rw-r--r--sql/sql_type_int.h16
-rw-r--r--sql/sql_type_real.h47
-rw-r--r--sql/sql_udf.cc67
-rw-r--r--sql/sql_udf.h8
-rw-r--r--sql/sql_union.cc971
-rw-r--r--sql/sql_union.h2
-rw-r--r--sql/sql_update.cc672
-rw-r--r--sql/sql_update.h3
-rw-r--r--sql/sql_view.cc347
-rw-r--r--sql/sql_view.h4
-rw-r--r--sql/sql_window.cc228
-rw-r--r--sql/sql_window.h39
-rw-r--r--sql/sql_yacc.yy8986
-rw-r--r--sql/sql_yacc_ora.yy18270
-rw-r--r--sql/strfunc.cc33
-rw-r--r--sql/strfunc.h21
-rw-r--r--sql/structs.h199
-rw-r--r--sql/sys_vars.cc668
-rw-r--r--sql/sys_vars.ic389
-rw-r--r--sql/sys_vars_shared.h2
-rw-r--r--sql/table.cc1294
-rw-r--r--sql/table.h666
-rw-r--r--sql/table_cache.cc58
-rw-r--r--sql/table_cache.h1
-rw-r--r--sql/temporary_tables.cc67
-rw-r--r--sql/thr_malloc.cc11
-rw-r--r--sql/thr_malloc.h6
-rw-r--r--sql/threadpool.h1
-rw-r--r--sql/threadpool_common.cc54
-rw-r--r--sql/threadpool_generic.cc18
-rw-r--r--sql/threadpool_win.cc11
-rw-r--r--sql/transaction.cc87
-rw-r--r--sql/transaction.h7
-rw-r--r--sql/tztime.cc54
-rw-r--r--sql/tztime.h1
-rw-r--r--sql/udf_example.c33
-rw-r--r--sql/udf_example.def6
-rw-r--r--sql/uniques.cc24
-rw-r--r--sql/unireg.cc176
-rw-r--r--sql/unireg.h11
-rw-r--r--sql/upgrade_conf_file.cc177
-rw-r--r--sql/vers_string.h100
-rw-r--r--sql/vers_utils.h47
-rw-r--r--sql/winservice.c9
-rw-r--r--sql/wsrep_applier.cc28
-rw-r--r--sql/wsrep_binlog.cc26
-rw-r--r--sql/wsrep_check_opts.cc1
-rw-r--r--sql/wsrep_dummy.cc11
-rw-r--r--sql/wsrep_hton.cc35
-rw-r--r--sql/wsrep_mysqld.cc165
-rw-r--r--sql/wsrep_mysqld.h5
-rw-r--r--sql/wsrep_notify.cc1
-rw-r--r--sql/wsrep_sst.cc29
-rw-r--r--sql/wsrep_sst.h3
-rw-r--r--sql/wsrep_thd.cc25
-rw-r--r--sql/wsrep_utils.cc1
-rw-r--r--sql/wsrep_var.cc11
-rw-r--r--sql/wsrep_xid.cc47
371 files changed, 98281 insertions, 34589 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index 17779adcff8..8ed75f86067 100644
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -36,7 +36,7 @@ ELSE()
ENDIF()
INCLUDE_DIRECTORIES(
-${CMAKE_SOURCE_DIR}/include
+${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/sql
${PCRE_INCLUDES}
${ZLIB_INCLUDE_DIR}
@@ -45,21 +45,10 @@ ${CMAKE_BINARY_DIR}/sql
${WSREP_INCLUDES}
)
-SET(GEN_SOURCES
-${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.h
-${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.cc
-${CMAKE_CURRENT_BINARY_DIR}/lex_hash.h
-${CMAKE_CURRENT_BINARY_DIR}/lex_token.h
-)
-SET_SOURCE_FILES_PROPERTIES(${GEN_SOURCES}
- PROPERTIES GENERATED 1)
-IF(NOT CMAKE_CROSSCOMPILING)
- ADD_EXECUTABLE(gen_lex_token gen_lex_token.cc
- ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.h)
-ENDIF()
+
ADD_CUSTOM_COMMAND(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lex_token.h
@@ -75,7 +64,8 @@ ENDIF()
SET (SQL_SOURCE
../sql-common/client.c compat56.cc derror.cc des_key_file.cc
- discover.cc ../sql-common/errmsg.c field.cc field_conv.cc
+ discover.cc ../sql-common/errmsg.c
+ field.cc field_conv.cc field_comp.cc
filesort_utils.cc
filesort.cc gstream.cc
signal_handler.cc
@@ -107,7 +97,7 @@ SET (SQL_SOURCE
debug_sync.cc
sql_repl.cc sql_select.cc sql_show.cc sql_state.c
group_by_handler.cc
- sql_statistics.cc sql_string.cc
+ sql_statistics.cc sql_string.cc lex_string.h
sql_table.cc sql_test.cc sql_trigger.cc sql_udf.cc sql_union.cc
sql_update.cc sql_view.cc strfunc.cc table.cc thr_malloc.cc
sql_time.cc tztime.cc unireg.cc item_xmlfunc.cc
@@ -119,7 +109,7 @@ SET (SQL_SOURCE
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 rpl_handler.cc mdl.cc sql_admin.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
@@ -135,15 +125,26 @@ SET (SQL_SOURCE
my_apc.cc mf_iocache_encr.cc item_jsonfunc.cc
my_json_writer.cc
rpl_gtid.cc rpl_parallel.cc
+ semisync.cc semisync_master.cc semisync_slave.cc
+ semisync_master_ack_receiver.cc
+ sql_schema.cc
sql_type.cc sql_mode.cc
item_windowfunc.cc sql_window.cc
sql_cte.cc
+ item_vers.cc
+ sql_sequence.cc sql_sequence.h ha_sequence.h
+ sql_tvc.cc sql_tvc.h
+ opt_split.cc
${WSREP_SOURCES}
table_cache.cc encryption.cc temporary_tables.cc
+ proxy_protocol.cc
${CMAKE_CURRENT_BINARY_DIR}/sql_builtin.cc
- ${GEN_SOURCES}
+ ${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
${MYSYS_LIBWRAP_SOURCE}
- )
+)
IF (CMAKE_SYSTEM_NAME MATCHES "Linux" OR
CMAKE_SYSTEM_NAME MATCHES "Windows" OR
@@ -159,9 +160,10 @@ ENDIF()
MYSQL_ADD_PLUGIN(partition ha_partition.cc STORAGE_ENGINE DEFAULT STATIC_ONLY
RECOMPILE_FOR_EMBEDDED)
+MYSQL_ADD_PLUGIN(sql_sequence ha_sequence.cc STORAGE_ENGINE MANDATORY STATIC_ONLY
+RECOMPILE_FOR_EMBEDDED)
ADD_LIBRARY(sql STATIC ${SQL_SOURCE})
-ADD_DEPENDENCIES(sql GenServerSource)
DTRACE_INSTRUMENT(sql)
TARGET_LINK_LIBRARIES(sql ${MYSQLD_STATIC_PLUGIN_LIBS}
mysys mysys_ssl dbug strings vio pcre
@@ -202,9 +204,7 @@ IF(MSVC AND NOT WITHOUT_DYNAMIC_PLUGINS)
SET(MYSQLD_LIB ${CMAKE_CURRENT_BINARY_DIR}/mysqld_lib${CMAKE_CFG_INTDIR}.lib)
SET(MYSQLD_CORELIBS sql mysys dbug strings)
FOREACH (CORELIB ${MYSQLD_CORELIBS})
- GET_TARGET_PROPERTY(LOC ${CORELIB} LOCATION)
- FILE(TO_NATIVE_PATH ${LOC} LOC)
- SET (LIB_LOCATIONS ${LIB_LOCATIONS} ${LOC})
+ SET (LIB_LOCATIONS ${LIB_LOCATIONS} $<TARGET_FILE:${CORELIB}>)
ENDFOREACH (CORELIB)
SET(_PLATFORM x86)
@@ -261,7 +261,7 @@ IF(APPLE)
# Add CoreServices framework since some dloadable plugins may need it
FIND_LIBRARY(CORESERVICES NAMES CoreServices)
IF(CORESERVICES)
- TARGET_LINK_LIBRARIES(mysqld ${CORESERVICES})
+ TARGET_LINK_LIBRARIES(mysqld LINK_PRIVATE ${CORESERVICES})
ENDIF()
ENDIF()
@@ -282,13 +282,12 @@ IF(NOT WITHOUT_DYNAMIC_PLUGINS)
ENDIF()
ENDIF(NOT WITHOUT_DYNAMIC_PLUGINS)
-TARGET_LINK_LIBRARIES(mysqld sql)
+TARGET_LINK_LIBRARIES(mysqld LINK_PRIVATE sql)
# Provide plugins with minimal set of libraries
SET(INTERFACE_LIBS ${LIBRT})
IF(INTERFACE_LIBS)
- SET_TARGET_PROPERTIES(mysqld PROPERTIES LINK_INTERFACE_LIBRARIES
- "${INTERFACE_LIBS}")
+ TARGET_LINK_LIBRARIES(mysqld LINK_PUBLIC ${INTERFACE_LIBS})
ENDIF()
# On Solaris, some extra effort is required in order to get dtrace probes
@@ -308,32 +307,48 @@ IF(WITH_MYSQLD_LDFLAGS)
"${MYSQLD_LINK_FLAGS} ${WITH_MYSQLD_LDFLAGS}")
ENDIF()
-INCLUDE(${CMAKE_SOURCE_DIR}/cmake/bison.cmake)
+
+FIND_PACKAGE(BISON 2.0)
+
# Handle out-of-source build from source package with possibly broken
# bison. Copy bison output to from source to build directory, if not already
# there
-IF (NOT BISON_USABLE)
-IF (NOT ${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_BINARY_DIR})
- IF(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/sql_yacc.cc)
- IF(NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.cc)
- CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/sql_yacc.cc
- ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.cc COPYONLY)
- CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/sql_yacc.h
- ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.h COPYONLY)
+IF (NOT BISON_FOUND)
+ IF (NOT ${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_BINARY_DIR})
+ FOREACH(file sql_yacc.cc sql_yacc.hh sql_yacc_ora.cc sql_yacc_ora.hh)
+ IF(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${file} AND (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/${file}))
+ CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/${file}
+ ${CMAKE_CURRENT_BINARY_DIR}/${file} COPYONLY)
ENDIF()
+ ENDFOREACH()
ENDIF()
-ENDIF()
-ENDIF()
-RUN_BISON(
- ${CMAKE_CURRENT_SOURCE_DIR}/sql_yacc.yy
- ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.cc
- ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.h
-)
+ IF(NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.cc)
+ # Output files are missing, bail out.
+ SET(ERRMSG
+ "Bison (GNU parser generator) is required to build MySQL."
+ "Please install bison."
+ )
+ IF(WIN32)
+ SET(ERRMSG ${ERRMSG}
+ "You can download bison from http://gnuwin32.sourceforge.net/packages/bison.htm "
+ "Choose 'Complete package, except sources' installation. We recommend to "
+ "install bison into a directory without spaces, e.g C:\\GnuWin32.")
+ ENDIF()
+ MESSAGE(FATAL_ERROR ${ERRMSG})
+ ENDIF()
+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
+ COMPILE_FLAGS "-p ORA")
+ENDIF()
-# Gen_lex_hash
IF(NOT CMAKE_CROSSCOMPILING)
+ ADD_EXECUTABLE(gen_lex_token gen_lex_token.cc
+ ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.hh)
ADD_EXECUTABLE(gen_lex_hash gen_lex_hash.cc)
ENDIF()
@@ -349,12 +364,12 @@ TARGET_LINK_LIBRARIES(mysql_tzinfo_to_sql mysys mysys_ssl)
ADD_CUSTOM_TARGET(
GenServerSource
- DEPENDS ${GEN_SOURCES}
+ DEPENDS
+ ${CMAKE_CURRENT_BINARY_DIR}/lex_hash.h
+ ${CMAKE_CURRENT_BINARY_DIR}/lex_token.h
+ ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc_ora.cc
)
-#Need this only for embedded
-SET_TARGET_PROPERTIES(GenServerSource PROPERTIES EXCLUDE_FROM_ALL TRUE)
-
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 "")
@@ -367,7 +382,8 @@ CONFIGURE_FILE(
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.h
+ 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
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
@@ -384,16 +400,8 @@ SET(DBOPT_FILE ${CMAKE_SOURCE_DIR}/support-files/db.opt )
INSTALL(FILES ${DBOPT_FILE} DESTINATION data/test COMPONENT DataFiles)
# Install initial database on windows
-IF(NOT CMAKE_CROSSCOMPILING)
- GET_TARGET_PROPERTY(MYSQLD_EXECUTABLE mysqld LOCATION)
-ENDIF()
-IF(WIN32 AND MYSQLD_EXECUTABLE)
- CONFIGURE_FILE(
- ${CMAKE_SOURCE_DIR}/cmake/create_initial_db.cmake.in
- ${CMAKE_CURRENT_BINARY_DIR}/create_initial_db.cmake
- @ONLY
- )
-
+IF(WIN32 AND TARGET mysqld AND NOT CMAKE_CROSSCOMPILING)
+
IF(MSVC_IDE OR CMAKE_GENERATOR MATCHES "Xcode")
SET (CONFIG_PARAM -DCONFIG=${CMAKE_CFG_INTDIR})
ENDIF()
@@ -403,7 +411,12 @@ IF(WIN32 AND MYSQLD_EXECUTABLE)
COMMAND ${CMAKE_COMMAND} -E remove_directory data
COMMAND ${CMAKE_COMMAND} -E make_directory data
COMMAND ${CMAKE_COMMAND} -E chdir data ${CMAKE_COMMAND}
- ${CONFIG_PARAM} -P ${CMAKE_CURRENT_BINARY_DIR}/create_initial_db.cmake
+ ${CONFIG_PARAM}
+ -DTOP_SRCDIR="${CMAKE_SOURCE_DIR}"
+ -DBINDIR="${CMAKE_CURRENT_BINARY_DIR}"
+ -DMYSQLD_EXECUTABLE="$<TARGET_FILE:mysqld>"
+ -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
@@ -421,7 +434,7 @@ IF(WIN32 AND MYSQLD_EXECUTABLE)
ELSE()
# Not windows or cross compiling, just install an empty directory
INSTALL(FILES ${DUMMY_FILE} DESTINATION data/mysql COMPONENT DataFiles)
-ENDIF(WIN32 AND MYSQLD_EXECUTABLE)
+ENDIF(WIN32 AND TARGET mysqld AND NOT CMAKE_CROSSCOMPILING)
ENDIF(INSTALL_LAYOUT STREQUAL "STANDALONE")
IF(WIN32)
@@ -432,12 +445,13 @@ IF(WIN32)
ADD_CUSTOM_COMMAND(OUTPUT
${my_bootstrap_sql}
COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_SOURCE_DIR}/scripts
- cmd /c copy mysql_system_tables.sql+mysql_system_tables_data.sql+fill_help_tables.sql+mysql_performance_tables.sql ${native_outfile}
+ cmd /c copy mysql_system_tables.sql+mysql_system_tables_data.sql+fill_help_tables.sql+mysql_performance_tables.sql+mysql_test_db.sql ${native_outfile}
DEPENDS
${CMAKE_SOURCE_DIR}/scripts/mysql_system_tables.sql
${CMAKE_SOURCE_DIR}/scripts/mysql_system_tables_data.sql
${CMAKE_SOURCE_DIR}/scripts/fill_help_tables.sql
${CMAKE_SOURCE_DIR}/scripts/mysql_performance_tables.sql
+ ${CMAKE_SOURCE_DIR}/scripts/mysql_test_db.sql
)
ADD_CUSTOM_COMMAND(
@@ -460,9 +474,11 @@ IF(WIN32)
ADD_LIBRARY(winservice STATIC winservice.c)
TARGET_LINK_LIBRARIES(winservice shell32)
+
MYSQL_ADD_EXECUTABLE(mysql_upgrade_service
mysql_upgrade_service.cc
- COMPONENT Server)
+ upgrade_conf_file.cc
+ COMPONENT Server)
TARGET_LINK_LIBRARIES(mysql_upgrade_service mysys winservice)
ENDIF(WIN32)
diff --git a/sql/authors.h b/sql/authors.h
index 4c6482ed680..251ed2c38c3 100644
--- a/sql/authors.h
+++ b/sql/authors.h
@@ -75,6 +75,8 @@ struct show_table_authors_st show_table_authors[]= {
"Prepared statements (4.1), Cursors (5.0), GET_LOCK (10.0)" },
{ "Ian Gilfillan", "South Africa", "MariaDB documentation"},
{ "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" },
/* People working on MySQL code base (not NDB) */
{ "Guilhem Bichot", "Bordeaux, France", "Replication (since 4.0)" },
diff --git a/sql/bounded_queue.h b/sql/bounded_queue.h
index 466b00bdb1f..fd733caa019 100644
--- a/sql/bounded_queue.h
+++ b/sql/bounded_queue.h
@@ -16,9 +16,8 @@
#ifndef BOUNDED_QUEUE_INCLUDED
#define BOUNDED_QUEUE_INCLUDED
-#include "my_global.h"
#include "my_base.h"
-#include "my_sys.h"
+#include <my_sys.h>
#include "queues.h"
#include <string.h>
diff --git a/sql/compat56.cc b/sql/compat56.cc
index 8a17ae16ff3..58cf5287cee 100644
--- a/sql/compat56.cc
+++ b/sql/compat56.cc
@@ -15,7 +15,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include "my_global.h"
+#include "mariadb.h"
#include "compat56.h"
#include "myisampack.h"
#include "my_time.h"
@@ -45,8 +45,10 @@
*/
longlong TIME_to_longlong_time_packed(const MYSQL_TIME *ltime)
{
- /* If month is 0, we mix day with hours: "1 00:10:10" -> "24:00:10" */
- long hms= (((ltime->month ? 0 : ltime->day * 24) + ltime->hour) << 12) |
+ DBUG_ASSERT(ltime->year == 0);
+ DBUG_ASSERT(ltime->month == 0);
+ // Mix days with hours: "1 00:10:10" -> "24:10:10"
+ long hms= ((ltime->day * 24 + ltime->hour) << 12) |
(ltime->minute << 6) | ltime->second;
longlong tmp= MY_PACKED_TIME_MAKE(hms, ltime->second_part);
return ltime->neg ? -tmp : tmp;
diff --git a/sql/create_options.cc b/sql/create_options.cc
index 4049443de2a..a8d997efaf4 100644
--- a/sql/create_options.cc
+++ b/sql/create_options.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2010, 2017, MariaDB Corporation Ab
+/* Copyright (C) 2010, 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
@@ -19,6 +19,7 @@
Engine defined options of tables/fields/keys in CREATE/ALTER TABLE.
*/
+#include "mariadb.h"
#include "create_options.h"
#include <my_getopt.h>
#include "set_var.h"
@@ -119,7 +120,7 @@ static bool report_unknown_option(THD *thd, engine_option_value *val,
#define value_ptr(STRUCT,OPT) ((char*)(STRUCT) + (OPT)->offset)
static bool set_one_value(ha_create_table_option *opt,
- THD *thd, const LEX_STRING *value, void *base,
+ THD *thd, const LEX_CSTRING *value, void *base,
bool suppress_warning,
MEM_ROOT *root)
{
@@ -131,7 +132,8 @@ static bool set_one_value(ha_create_table_option *opt,
switch (opt->type)
{
case HA_OPTION_TYPE_SYSVAR:
- DBUG_ASSERT(0); // HA_OPTION_TYPE_SYSVAR's are replaced in resolve_sysvars()
+ // HA_OPTION_TYPE_SYSVAR's are replaced in resolve_sysvars()
+ break; // to DBUG_ASSERT(0)
case HA_OPTION_TYPE_ULL:
{
ulonglong *val= (ulonglong*)value_ptr(base, opt);
@@ -311,7 +313,7 @@ bool parse_option_list(THD* thd, handlerton *hton, void *option_struct_arg,
}
if (!seen || (opt->var && !last->value.str))
{
- LEX_STRING default_val= null_lex_str;
+ LEX_CSTRING default_val= null_clex_str;
/*
Okay, here's the logic for sysvar options:
@@ -320,8 +322,8 @@ bool parse_option_list(THD* thd, handlerton *hton, void *option_struct_arg,
*current* value of the underlying sysvar.
2. But only if the underlying sysvar value is different from the
sysvar's default.
- 3. If it's ALTER TABLE and the sysvar option was not explicitly
- mentioned - do nothing, do not add it to the list.
+ 3. If it's ALTER TABLE or CREATE_SEQUENCE and the sysvar option was
+ not explicitly mentioned - do nothing, do not add it to the list.
4. But if it was ALTER TABLE with sysvar option = DEFAULT, we
add it to the list (under the same condition #2).
5. If we're here parsing the option list from the .frm file
@@ -329,7 +331,6 @@ bool parse_option_list(THD* thd, handlerton *hton, void *option_struct_arg,
do not add it to the list (makes no sense anyway) and
use the *default* value of the underlying sysvar. Because
sysvar value can change, but it should not affect existing tables.
-
This is how it's implemented: the current sysvar value is added
to the list if suppress_warning is FALSE (meaning a table is created,
that is CREATE TABLE or ALTER TABLE) and it's actually a CREATE TABLE
@@ -349,9 +350,9 @@ bool parse_option_list(THD* thd, handlerton *hton, void *option_struct_arg,
{
char buf[256];
String sbuf(buf, sizeof(buf), system_charset_info), *str;
- if ((str= sysvar->val_str(&sbuf, thd, OPT_SESSION, &null_lex_str)))
+ if ((str= sysvar->val_str(&sbuf, thd, OPT_SESSION, &null_clex_str)))
{
- LEX_STRING name= { const_cast<char*>(opt->name), opt->name_length };
+ LEX_CSTRING name= { opt->name, opt->name_length };
default_val.str= strmake_root(root, str->ptr(), str->length());
default_val.length= str->length();
val= new (root) engine_option_value(name, default_val,
@@ -545,7 +546,7 @@ uint engine_option_value::frm_length()
if value.str is NULL, this option is not written to frm (=DEFAULT)
*/
- return value.str ? 1 + name.length + 2 + value.length : 0;
+ return value.str ? (uint)(1 + name.length + 2 + value.length) : 0;
}
@@ -690,7 +691,7 @@ uchar *engine_option_value::frm_read(const uchar *buff, const uchar *buff_end,
engine_option_value **start,
engine_option_value **end, MEM_ROOT *root)
{
- LEX_STRING name, value;
+ LEX_CSTRING name, value;
uint len;
#define need_buff(N) if (buff + (N) >= buff_end) return NULL
@@ -730,7 +731,7 @@ uchar *engine_option_value::frm_read(const uchar *buff, const uchar *buff_end,
@retval FALSE OK
*/
-bool engine_table_options_frm_read(const uchar *buff, uint length,
+bool engine_table_options_frm_read(const uchar *buff, size_t length,
TABLE_SHARE *share)
{
const uchar *buff_end= buff + length;
diff --git a/sql/create_options.h b/sql/create_options.h
index 6a8d1acd8fe..ce64516794b 100644
--- a/sql/create_options.h
+++ b/sql/create_options.h
@@ -29,8 +29,8 @@ enum { ENGINE_OPTION_MAX_LENGTH=32767 };
class engine_option_value: public Sql_alloc
{
public:
- LEX_STRING name;
- LEX_STRING value;
+ LEX_CSTRING name;
+ LEX_CSTRING value;
engine_option_value *next; ///< parser puts them in a FIFO linked list
bool parsed; ///< to detect unrecognized options
bool quoted_value; ///< option=VAL vs. option='VAL'
@@ -42,28 +42,30 @@ class engine_option_value: public Sql_alloc
{
link(start, end);
}
- engine_option_value(LEX_STRING &name_arg, LEX_STRING &value_arg, bool quoted,
+ engine_option_value(LEX_CSTRING &name_arg, LEX_CSTRING &value_arg,
+ bool quoted,
engine_option_value **start, engine_option_value **end) :
name(name_arg), value(value_arg),
next(NULL), parsed(false), quoted_value(quoted)
{
link(start, end);
}
- engine_option_value(LEX_STRING &name_arg,
+ engine_option_value(LEX_CSTRING &name_arg,
engine_option_value **start, engine_option_value **end) :
- name(name_arg), value(null_lex_str),
+ name(name_arg), value(null_clex_str),
next(NULL), parsed(false), quoted_value(false)
{
link(start, end);
}
- engine_option_value(LEX_STRING &name_arg, ulonglong value_arg,
+ engine_option_value(LEX_CSTRING &name_arg, ulonglong value_arg,
engine_option_value **start, engine_option_value **end,
MEM_ROOT *root) :
name(name_arg), next(NULL), parsed(false), quoted_value(false)
{
- if ((value.str= (char *)alloc_root(root, 22)))
+ char *str;
+ if (likely((value.str= str= (char *)alloc_root(root, 22))))
{
- value.length= longlong10_to_str(value_arg, value.str, 10) - value.str;
+ value.length= longlong10_to_str(value_arg, str, 10) - str;
link(start, end);
}
}
@@ -85,7 +87,7 @@ bool parse_option_list(THD* thd, handlerton *hton, void *option_struct,
engine_option_value **option_list,
ha_create_table_option *rules,
bool suppress_warning, MEM_ROOT *root);
-bool engine_table_options_frm_read(const uchar *buff, uint length,
+bool engine_table_options_frm_read(const uchar *buff, size_t length,
TABLE_SHARE *share);
engine_option_value *merge_engine_table_options(engine_option_value *source,
engine_option_value *changes,
diff --git a/sql/datadict.cc b/sql/datadict.cc
index 77f7a0fcfcb..882f0b7232e 100644
--- a/sql/datadict.cc
+++ b/sql/datadict.cc
@@ -13,17 +13,18 @@
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>
+#include "mariadb.h"
#include "datadict.h"
#include "sql_priv.h"
#include "sql_class.h"
#include "sql_table.h"
+#include "ha_sequence.h"
static int read_string(File file, uchar**to, size_t length)
{
DBUG_ENTER("read_string");
- my_free(*to);
+ /* This can't use MY_THREAD_SPECIFIC as it's used on server start */
if (!(*to= (uchar*) my_malloc(length+1,MYF(MY_WME))) ||
mysql_file_read(file, *to, length, MYF(MY_NABP)))
{
@@ -43,56 +44,75 @@ static int read_string(File file, uchar**to, size_t length)
@param[in] path path to FRM file.
@param[in/out] engine_name table engine name (length < NAME_CHAR_LEN)
- engine_name is a LEX_STRING, where engine_name->str must point to
+ engine_name is a LEX_CSTRING, where engine_name->str must point to
a buffer of at least NAME_CHAR_LEN+1 bytes.
If engine_name is 0, then the function will only test if the file is a
view or not
- @retval FRMTYPE_ERROR error
- @retval FRMTYPE_TABLE table
- @retval FRMTYPE_VIEW view
+ @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
*/
-frm_type_enum dd_frm_type(THD *thd, char *path, LEX_STRING *engine_name)
+Table_type dd_frm_type(THD *thd, char *path, LEX_CSTRING *engine_name,
+ bool *is_sequence)
{
File file;
- uchar header[10]; //"TYPE=VIEW\n" it is 10 characters
+ uchar header[40]; //"TYPE=VIEW\n" it is 10 characters
size_t error;
- frm_type_enum type= FRMTYPE_ERROR;
+ Table_type type= TABLE_TYPE_UNKNOWN;
uchar dbt;
DBUG_ENTER("dd_frm_type");
- if ((file= mysql_file_open(key_file_frm, path, O_RDONLY | O_SHARE, MYF(0))) < 0)
- DBUG_RETURN(FRMTYPE_ERROR);
- error= mysql_file_read(file, (uchar*) header, sizeof(header), MYF(MY_NABP));
+ *is_sequence= 0;
- if (error)
- goto err;
- if (!strncmp((char*) header, "TYPE=VIEW\n", sizeof(header)))
- {
- type= FRMTYPE_VIEW;
- goto err;
- }
+ if ((file= mysql_file_open(key_file_frm, path, O_RDONLY | O_SHARE, MYF(0)))
+ < 0)
+ DBUG_RETURN(TABLE_TYPE_UNKNOWN);
/*
- We return FRMTYPE_TABLE if we can read the .frm file. This allows us
+ We return TABLE_TYPE_NORMAL if we can open the .frm file. This allows us
to drop a bad .frm file with DROP TABLE
*/
- type= FRMTYPE_TABLE;
+ type= TABLE_TYPE_NORMAL;
+
+ /*
+ Initialize engine name in case we are not able to find it out
+ The cast is safe, as engine_name->str points to a usable buffer.
+ */
+ if (engine_name)
+ {
+ engine_name->length= 0;
+ ((char*) (engine_name->str))[0]= 0;
+ }
+
+ if (unlikely((error= mysql_file_read(file, (uchar*) header, sizeof(header), MYF(MY_NABP)))))
+ goto err;
+
+ if (unlikely((!strncmp((char*) header, "TYPE=VIEW\n", 10))))
+ {
+ type= TABLE_TYPE_VIEW;
+ goto err;
+ }
/* engine_name is 0 if we only want to know if table is view or not */
if (!engine_name)
goto err;
- /* Initialize engine name in case we are not able to find it out */
- engine_name->length= 0;
- engine_name->str[0]= 0;
-
if (!is_binary_frm_header(header))
goto err;
dbt= header[3];
+ if (((header[39] >> 4) & 3) == HA_CHOICE_YES)
+ {
+ DBUG_PRINT("info", ("Sequence found"));
+ *is_sequence= 1;
+ }
+
/* cannot use ha_resolve_by_legacy_type without a THD */
if (thd && dbt < DB_TYPE_FIRST_DYNAMIC)
{
@@ -134,8 +154,14 @@ frm_type_enum dd_frm_type(THD *thd, char *path, LEX_STRING *engine_name)
{
uint len= uint2korr(next_chunk);
if (len <= NAME_CHAR_LEN)
- strmake(engine_name->str, (char*)next_chunk + 2,
+ {
+ /*
+ The following cast is safe as the caller has allocated buffer
+ and it's up to this function to generate the name.
+ */
+ strmake((char*) engine_name->str, (char*)next_chunk + 2,
engine_name->length= len);
+ }
}
}
@@ -170,7 +196,7 @@ bool dd_recreate_table(THD *thd, const char *db, const char *table_name,
char path_buf[FN_REFLEN + 1];
DBUG_ENTER("dd_recreate_table");
- memset(&create_info, 0, sizeof(create_info));
+ create_info.init();
if (path)
create_info.options|= HA_LEX_CREATE_TMP_TABLE;
diff --git a/sql/datadict.h b/sql/datadict.h
index a256c89ba84..648b176d2ac 100644
--- a/sql/datadict.h
+++ b/sql/datadict.h
@@ -1,6 +1,7 @@
#ifndef DATADICT_INCLUDED
#define DATADICT_INCLUDED
-/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2010, Oracle and/or its affiliates.
+ Copyright (c) 2017 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
@@ -21,11 +22,12 @@
Data dictionary API.
*/
-enum frm_type_enum
+enum Table_type
{
- FRMTYPE_ERROR= 0,
- FRMTYPE_TABLE,
- FRMTYPE_VIEW
+ TABLE_TYPE_UNKNOWN,
+ TABLE_TYPE_NORMAL, /* Normal table */
+ TABLE_TYPE_SEQUENCE,
+ TABLE_TYPE_VIEW
};
/*
@@ -35,11 +37,14 @@ enum frm_type_enum
Prefer to use ha_table_exists() instead.
To check whether it's an frm of a view, use dd_frm_is_view().
*/
-frm_type_enum dd_frm_type(THD *thd, char *path, LEX_STRING *engine_name);
+
+enum Table_type dd_frm_type(THD *thd, char *path, LEX_CSTRING *engine_name,
+ bool *is_sequence);
static inline bool dd_frm_is_view(THD *thd, char *path)
{
- return dd_frm_type(thd, path, NULL) == FRMTYPE_VIEW;
+ bool not_used2;
+ return dd_frm_type(thd, path, NULL, &not_used2) == 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 e50ab0891ed..39ceaadc671 100644
--- a/sql/debug_sync.cc
+++ b/sql/debug_sync.cc
@@ -15,7 +15,7 @@
/* see include/mysql/service_debug_sync.h for debug sync documentation */
-#include <my_global.h>
+#include "mariadb.h"
#include "debug_sync.h"
#if defined(ENABLED_DEBUG_SYNC)
@@ -465,13 +465,13 @@ static int debug_sync_qsort_cmp(const void* arg1, const void* arg2)
static st_debug_sync_action *debug_sync_find(st_debug_sync_action *actionarr,
int quantity,
const char *dsp_name,
- uint name_len)
+ size_t name_len)
{
st_debug_sync_action *action;
int low ;
int high ;
int mid ;
- int diff ;
+ ssize_t diff ;
DBUG_ASSERT(actionarr);
DBUG_ASSERT(dsp_name);
DBUG_ASSERT(name_len);
@@ -790,7 +790,7 @@ static bool debug_sync_set_action(THD *thd, st_debug_sync_action *action)
and shall not be reported as a result of SET DEBUG_SYNC.
Hence, we check for the first condition above.
*/
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
DBUG_RETURN(TRUE);
}
@@ -1448,7 +1448,7 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action)
DBUG_PRINT("debug_sync",
("awoke from %s global: %s error: %d",
sig_wait, sig_glob, error));});
- if (error == ETIMEDOUT || error == ETIME)
+ 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;
@@ -1457,7 +1457,7 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action)
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_ABORT(););
+ DBUG_EXECUTE_IF("debug_sync_abort_on_timeout", DBUG_ASSERT(0););
break;
}
error= 0;
diff --git a/sql/debug_sync.h b/sql/debug_sync.h
index 1a3e51b898b..7a63a52959c 100644
--- a/sql/debug_sync.h
+++ b/sql/debug_sync.h
@@ -26,8 +26,6 @@
#pragma interface /* gcc class implementation */
#endif
-#include <my_global.h>
-
class THD;
#if defined(ENABLED_DEBUG_SYNC)
diff --git a/sql/derror.cc b/sql/derror.cc
index 932c01b9dda..8b44d1bff9b 100644
--- a/sql/derror.cc
+++ b/sql/derror.cc
@@ -21,7 +21,7 @@
Read language depeneded messagefile
*/
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#include "derror.h"
@@ -246,8 +246,11 @@ static File open_error_msg_file(const char *file_name, const char *language,
O_RDONLY | O_SHARE | O_BINARY,
MYF(0))) < 0)
goto err;
- sql_print_warning("An old style --language or -lc-message-dir value with language specific part detected: %s", lc_messages_dir);
- sql_print_warning("Use --lc-messages-dir without language specific part instead.");
+ if (global_system_variables.log_warnings > 2)
+ {
+ sql_print_warning("An old style --language or -lc-message-dir value with language specific part detected: %s", lc_messages_dir);
+ sql_print_warning("Use --lc-messages-dir without language specific part instead.");
+ }
}
error_pos=1;
if (mysql_file_read(file, (uchar*) head, 32, MYF(MY_NABP)))
@@ -262,7 +265,8 @@ static File open_error_msg_file(const char *file_name, const char *language,
ret->errors= uint2korr(head+12);
ret->sections= uint2korr(head+14);
- if (ret->max_error < error_messages || ret->sections != MAX_ERROR_RANGES)
+ if (unlikely(ret->max_error < error_messages ||
+ ret->sections != MAX_ERROR_RANGES))
{
sql_print_error("\
Error message file '%s' had only %d error messages, but it should contain at least %d error messages.\nCheck that the above file is the right version for this program!",
@@ -306,8 +310,8 @@ bool read_texts(const char *file_name, const char *language,
struct st_msg_file msg_file;
DBUG_ENTER("read_texts");
- if ((file= open_error_msg_file(file_name, language, error_messages,
- &msg_file)) == FERR)
+ if (unlikely((file= open_error_msg_file(file_name, language, error_messages,
+ &msg_file)) == FERR))
DBUG_RETURN(1);
if (!(*data= (const char***)
diff --git a/sql/derror.h b/sql/derror.h
index f80ed17fd7b..34a71b731aa 100644
--- a/sql/derror.h
+++ b/sql/derror.h
@@ -16,8 +16,6 @@
#ifndef DERROR_INCLUDED
#define DERROR_INCLUDED
-#include "my_global.h" /* uint */
-
bool init_errmessage(void);
void free_error_messages();
bool read_texts(const char *file_name, const char *language,
diff --git a/sql/des_key_file.cc b/sql/des_key_file.cc
index f942feb5823..bfbe04f6015 100644
--- a/sql/des_key_file.cc
+++ b/sql/des_key_file.cc
@@ -13,7 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h> // HAVE_*
+#include "mariadb.h" // HAVE_*
#include "sql_priv.h"
#include "des_key_file.h" // st_des_keyschedule, st_des_keyblock
#include "log.h" // sql_print_error
@@ -59,7 +59,7 @@ load_des_key_file(const char *file_name)
char *start, *end;
char buf[1024], offset;
st_des_keyblock keyblock;
- uint length;
+ size_t length;
if (!(length=my_b_gets(&io,buf,sizeof(buf)-1)))
break; // End of file
diff --git a/sql/discover.cc b/sql/discover.cc
index c9dea535727..3df777c19ba 100644
--- a/sql/discover.cc
+++ b/sql/discover.cc
@@ -21,7 +21,7 @@
Functions for discover of frm file from handler
*/
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#include "discover.h"
@@ -127,7 +127,7 @@ int writefrm(const char *path, const char *db, const char *table,
File file= mysql_file_create(key_file_frm, file_name,
CREATE_MODE, create_flags, MYF(0));
- if ((error= file < 0))
+ if (unlikely((error= file < 0)))
{
if (my_errno == ENOENT)
my_error(ER_BAD_DB_ERROR, MYF(0), db);
@@ -136,7 +136,7 @@ int writefrm(const char *path, const char *db, const char *table,
}
else
{
- error= mysql_file_write(file, frmdata, len, MYF(MY_WME | MY_NABP));
+ error= (int)mysql_file_write(file, frmdata, len, MYF(MY_WME | MY_NABP));
if (!error && !tmp_table && opt_sync_frm)
error= mysql_file_sync(file, MYF(MY_WME)) ||
diff --git a/sql/discover.h b/sql/discover.h
index a3d61c4f155..f14be662dbc 100644
--- a/sql/discover.h
+++ b/sql/discover.h
@@ -16,8 +16,6 @@
#ifndef DISCOVER_INCLUDED
#define DISCOVER_INCLUDED
-#include "my_global.h" /* uchar */
-
int extension_based_table_discovery(MY_DIR *dirp, const char *ext,
handlerton::discovered_list *tl);
diff --git a/sql/encryption.cc b/sql/encryption.cc
index 4fac36dc97e..493d72ae346 100644
--- a/sql/encryption.cc
+++ b/sql/encryption.cc
@@ -13,7 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include <mysql/plugin_encryption.h>
#include "log.h"
#include "sql_plugin.h"
diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc
index 4100a031c91..833797b94d0 100644
--- a/sql/event_data_objects.cc
+++ b/sql/event_data_objects.cc
@@ -15,7 +15,7 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
#define MYSQL_LEX 1
-#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
#include "unireg.h"
#include "sql_parse.h" // parse_sql
@@ -171,13 +171,13 @@ Event_creation_ctx::load_from_db(THD *thd,
*/
bool
-Event_queue_element_for_exec::init(LEX_STRING db, LEX_STRING 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(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(n->str, name.length= n->length, MYF(MY_WME))))
{
- my_free(dbname.str);
+ my_free(const_cast<char*>(dbname.str));
return TRUE;
}
return FALSE;
@@ -193,8 +193,8 @@ Event_queue_element_for_exec::init(LEX_STRING db, LEX_STRING n)
Event_queue_element_for_exec::~Event_queue_element_for_exec()
{
- my_free(dbname.str);
- my_free(name.str);
+ my_free(const_cast<char*>(dbname.str));
+ my_free(const_cast<char*>(name.str));
}
@@ -209,7 +209,7 @@ Event_basic::Event_basic()
{
DBUG_ENTER("Event_basic::Event_basic");
/* init memory root */
- init_sql_alloc(&mem_root, 256, 512, MYF(0));
+ init_sql_alloc(&mem_root, "Event_basic", 256, 512, MYF(0));
dbname.str= name.str= NULL;
dbname.length= name.length= 0;
time_zone= NULL;
@@ -233,7 +233,7 @@ Event_basic::~Event_basic()
/*
- Short function to load a char column into a LEX_STRING
+ Short function to load a char column into a LEX_CSTRING
SYNOPSIS
Event_basic::load_string_field()
@@ -249,7 +249,7 @@ Event_basic::load_string_fields(Field **fields, ...)
bool ret= FALSE;
va_list args;
enum enum_events_table_field field_name;
- LEX_STRING *field_value;
+ LEX_CSTRING *field_value;
DBUG_ENTER("Event_basic::load_string_fields");
@@ -257,7 +257,7 @@ Event_basic::load_string_fields(Field **fields, ...)
field_name= (enum enum_events_table_field) va_arg(args, int);
while (field_name < ET_FIELD_COUNT)
{
- field_value= va_arg(args, LEX_STRING *);
+ field_value= va_arg(args, LEX_CSTRING *);
if ((field_value->str= get_field(&mem_root, fields[field_name])) == NullS)
{
ret= TRUE;
@@ -274,9 +274,9 @@ Event_basic::load_string_fields(Field **fields, ...)
bool
-Event_basic::load_time_zone(THD *thd, const LEX_STRING tz_name)
+Event_basic::load_time_zone(THD *thd, const LEX_CSTRING *tz_name)
{
- String str(tz_name.str, &my_charset_latin1);
+ String str(tz_name->str, &my_charset_latin1);
time_zone= my_tz_find(thd, &str);
return (time_zone == NULL);
@@ -391,9 +391,9 @@ Event_timed::init()
bool
Event_job_data::load_from_row(THD *thd, TABLE *table)
{
- char *ptr;
+ const char *ptr;
size_t len;
- LEX_STRING tz_name;
+ LEX_CSTRING tz_name;
DBUG_ENTER("Event_job_data::load_from_row");
@@ -412,7 +412,7 @@ Event_job_data::load_from_row(THD *thd, TABLE *table)
ET_FIELD_COUNT))
DBUG_RETURN(TRUE);
- if (load_time_zone(thd, tz_name))
+ if (load_time_zone(thd, &tz_name))
DBUG_RETURN(TRUE);
Event_creation_ctx::load_from_db(thd, &mem_root, dbname.str, name.str, table,
@@ -431,7 +431,7 @@ Event_job_data::load_from_row(THD *thd, TABLE *table)
definer_host.str= strmake_root(&mem_root, ptr + 1, len);
definer_host.length= len;
- sql_mode= (ulong) table->field[ET_FIELD_SQL_MODE]->val_int();
+ sql_mode= (sql_mode_t) table->field[ET_FIELD_SQL_MODE]->val_int();
DBUG_RETURN(FALSE);
}
@@ -452,9 +452,9 @@ Event_job_data::load_from_row(THD *thd, TABLE *table)
bool
Event_queue_element::load_from_row(THD *thd, TABLE *table)
{
- char *ptr;
+ const char *ptr;
MYSQL_TIME time;
- LEX_STRING tz_name;
+ LEX_CSTRING tz_name;
DBUG_ENTER("Event_queue_element::load_from_row");
@@ -472,7 +472,7 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table)
ET_FIELD_COUNT))
DBUG_RETURN(TRUE);
- if (load_time_zone(thd, tz_name))
+ if (load_time_zone(thd, &tz_name))
DBUG_RETURN(TRUE);
starts_null= table->field[ET_FIELD_STARTS]->is_null();
@@ -519,7 +519,7 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table)
int i;
char buff[MAX_FIELD_WIDTH];
String str(buff, sizeof(buff), &my_charset_bin);
- LEX_STRING tmp;
+ LEX_CSTRING tmp;
table->field[ET_FIELD_TRANSIENT_INTERVAL]->val_str(&str);
if (!(tmp.length= str.length()))
@@ -590,7 +590,7 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table)
bool
Event_timed::load_from_row(THD *thd, TABLE *table)
{
- char *ptr;
+ const char *ptr;
size_t len;
DBUG_ENTER("Event_timed::load_from_row");
@@ -637,7 +637,7 @@ Event_timed::load_from_row(THD *thd, TABLE *table)
else
comment.length= 0;
- sql_mode= (ulong) table->field[ET_FIELD_SQL_MODE]->val_int();
+ sql_mode= (sql_mode_t) table->field[ET_FIELD_SQL_MODE]->val_int();
DBUG_RETURN(FALSE);
}
@@ -1196,14 +1196,14 @@ Event_timed::get_create_event(THD *thd, String *buf)
buf->append(STRING_WITH_LEN("CREATE "));
append_definer(thd, buf, &definer_user, &definer_host);
buf->append(STRING_WITH_LEN("EVENT "));
- append_identifier(thd, buf, name.str, name.length);
+ append_identifier(thd, buf, &name);
if (expression)
{
buf->append(STRING_WITH_LEN(" ON SCHEDULE EVERY "));
buf->append(expr_buf);
buf->append(' ');
- LEX_STRING *ival= &interval_type_to_name[interval];
+ LEX_CSTRING *ival= &interval_type_to_name[interval];
buf->append(ival->str, ival->length);
if (!starts_null)
@@ -1236,7 +1236,7 @@ Event_timed::get_create_event(THD *thd, String *buf)
append_unescaped(buf, comment.str, comment.length);
}
buf->append(STRING_WITH_LEN(" DO "));
- buf->append(body.str, body.length);
+ buf->append(&body);
DBUG_RETURN(0);
}
@@ -1249,7 +1249,7 @@ Event_timed::get_create_event(THD *thd, String *buf)
bool
Event_job_data::construct_sp_sql(THD *thd, String *sp_sql)
{
- LEX_STRING buffer;
+ LEX_CSTRING buffer;
const uint STATIC_SQL_LENGTH= 44;
DBUG_ENTER("Event_job_data::construct_sp_sql");
@@ -1266,15 +1266,15 @@ Event_job_data::construct_sp_sql(THD *thd, String *sp_sql)
sp_sql->length(0);
- sp_sql->append(C_STRING_WITH_LEN("CREATE "));
- sp_sql->append(C_STRING_WITH_LEN("PROCEDURE "));
+ sp_sql->append(STRING_WITH_LEN("CREATE "));
+ sp_sql->append(STRING_WITH_LEN("PROCEDURE "));
/*
Let's use the same name as the event name to perhaps produce a
better error message in case it is a part of some parse error.
We're using append_identifier here to successfully parse
events with reserved names.
*/
- append_identifier(thd, sp_sql, name.str, name.length);
+ append_identifier(thd, sp_sql, &name);
/*
The default SQL security of a stored procedure is DEFINER. We
@@ -1282,9 +1282,13 @@ Event_job_data::construct_sp_sql(THD *thd, String *sp_sql)
let's execute the procedure with the invoker rights to save on
resets of security contexts.
*/
- sp_sql->append(C_STRING_WITH_LEN("() SQL SECURITY INVOKER "));
+ sp_sql->append(STRING_WITH_LEN("() SQL SECURITY INVOKER "));
- sp_sql->append(body.str, body.length);
+ if (thd->variables.sql_mode & MODE_ORACLE)
+ sp_sql->append(STRING_WITH_LEN(" AS BEGIN "));
+ sp_sql->append(&body);
+ if (thd->variables.sql_mode & MODE_ORACLE)
+ sp_sql->append(STRING_WITH_LEN("; END"));
DBUG_RETURN(thd->is_fatal_error);
}
@@ -1298,7 +1302,7 @@ Event_job_data::construct_sp_sql(THD *thd, String *sp_sql)
bool
Event_job_data::construct_drop_event_sql(THD *thd, String *sp_sql)
{
- LEX_STRING buffer;
+ LEX_CSTRING buffer;
const uint STATIC_SQL_LENGTH= 14;
DBUG_ENTER("Event_job_data::construct_drop_event_sql");
@@ -1310,10 +1314,10 @@ Event_job_data::construct_drop_event_sql(THD *thd, String *sp_sql)
sp_sql->set(buffer.str, buffer.length, system_charset_info);
sp_sql->length(0);
- sp_sql->append(C_STRING_WITH_LEN("DROP EVENT "));
- append_identifier(thd, sp_sql, dbname.str, dbname.length);
+ sp_sql->append(STRING_WITH_LEN("DROP EVENT "));
+ append_identifier(thd, sp_sql, &dbname);
sp_sql->append('.');
- append_identifier(thd, sp_sql, name.str, name.length);
+ append_identifier(thd, sp_sql, &name);
DBUG_RETURN(thd->is_fatal_error);
}
@@ -1355,7 +1359,7 @@ Event_job_data::execute(THD *thd, bool drop)
mysql_change_db will be invoked anyway later, to activate the
procedure database before it's executed.
*/
- thd->set_db(dbname.str, dbname.length);
+ thd->set_db(&dbname);
lex_start(thd);
@@ -1387,9 +1391,6 @@ Event_job_data::execute(THD *thd, bool drop)
goto end;
}
- if (construct_sp_sql(thd, &sp_sql))
- goto end;
-
/*
Set up global thread attributes to reflect the properties of
this Event. We can simply reset these instead of usual
@@ -1401,6 +1402,9 @@ Event_job_data::execute(THD *thd, bool drop)
thd->variables.sql_mode= sql_mode;
thd->variables.time_zone= time_zone;
+ if (construct_sp_sql(thd, &sp_sql))
+ goto end;
+
thd->set_query(sp_sql.c_ptr_safe(), sp_sql.length());
{
@@ -1426,7 +1430,13 @@ Event_job_data::execute(THD *thd, bool drop)
sphead->m_flags|= sp_head::LOG_SLOW_STATEMENTS;
sphead->m_flags|= sp_head::LOG_GENERAL_LOG;
- sphead->set_info(0, 0, &thd->lex->sp_chistics, sql_mode);
+ /*
+ construct_sp_sql() + parse_sql() set suid to SP_IS_NOT_SUID,
+ because we have the security context already set to the event
+ definer here. See more comments in construct_sp_sql().
+ */
+ DBUG_ASSERT(sphead->suid() == SP_IS_NOT_SUID);
+ sphead->m_sql_mode= sql_mode;
sphead->set_creation_ctx(creation_ctx);
sphead->optimize();
@@ -1438,7 +1448,7 @@ Event_job_data::execute(THD *thd, bool drop)
}
end:
- if (drop && !thd->is_fatal_error)
+ if (drop && likely(!thd->is_fatal_error))
{
/*
We must do it here since here we're under the right authentication
@@ -1491,7 +1501,7 @@ end:
if (sql_command_set)
thd->lex->sql_command = SQLCOM_DROP_EVENT;
- ret= Events::drop_event(thd, dbname, name, FALSE);
+ ret= Events::drop_event(thd, &dbname, &name, FALSE);
if (sql_command_set)
{
@@ -1533,9 +1543,9 @@ end:
*/
bool
-event_basic_db_equal(LEX_STRING db, Event_basic *et)
+event_basic_db_equal(const LEX_CSTRING *db, Event_basic *et)
{
- return !sortcmp_lex_string(et->dbname, db, system_charset_info);
+ return !sortcmp_lex_string(&et->dbname, db, system_charset_info);
}
@@ -1554,10 +1564,11 @@ event_basic_db_equal(LEX_STRING db, Event_basic *et)
*/
bool
-event_basic_identifier_equal(LEX_STRING db, LEX_STRING name, Event_basic *b)
+event_basic_identifier_equal(const LEX_CSTRING *db, const LEX_CSTRING *name,
+ Event_basic *b)
{
- return !sortcmp_lex_string(name, b->name, system_charset_info) &&
- !sortcmp_lex_string(db, b->dbname, system_charset_info);
+ return !sortcmp_lex_string(name, &b->name, system_charset_info) &&
+ !sortcmp_lex_string(db, &b->dbname, system_charset_info);
}
/**
diff --git a/sql/event_data_objects.h b/sql/event_data_objects.h
index 0acf301ef5a..e5e3e4eb087 100644
--- a/sql/event_data_objects.h
+++ b/sql/event_data_objects.h
@@ -37,10 +37,10 @@ public:
~Event_queue_element_for_exec();
bool
- init(LEX_STRING dbname, LEX_STRING name);
+ init(const LEX_CSTRING *dbname, const LEX_CSTRING *name);
- LEX_STRING dbname;
- LEX_STRING name;
+ LEX_CSTRING dbname;
+ LEX_CSTRING name;
bool dropped;
THD *thd;
@@ -58,9 +58,9 @@ protected:
public:
- LEX_STRING dbname;
- LEX_STRING name;
- LEX_STRING definer;// combination of user and host
+ LEX_CSTRING dbname;
+ LEX_CSTRING name;
+ LEX_CSTRING definer;// combination of user and host
Time_zone *time_zone;
@@ -75,7 +75,7 @@ protected:
load_string_fields(Field **fields, ...);
bool
- load_time_zone(THD *thd, const LEX_STRING tz_name);
+ load_time_zone(THD *thd, const LEX_CSTRING *tz_name);
};
@@ -122,12 +122,12 @@ class Event_timed : public Event_queue_element
void operator=(Event_timed &);
public:
- LEX_STRING body;
+ LEX_CSTRING body;
- LEX_STRING definer_user;
- LEX_STRING definer_host;
+ LEX_CSTRING definer_user;
+ LEX_CSTRING definer_host;
- LEX_STRING comment;
+ LEX_CSTRING comment;
ulonglong created;
ulonglong modified;
@@ -135,7 +135,7 @@ public:
sql_mode_t sql_mode;
class Stored_program_creation_ctx *creation_ctx;
- LEX_STRING body_utf8;
+ LEX_CSTRING body_utf8;
Event_timed();
virtual ~Event_timed();
@@ -154,9 +154,9 @@ public:
class Event_job_data : public Event_basic
{
public:
- LEX_STRING body;
- LEX_STRING definer_user;
- LEX_STRING definer_host;
+ LEX_CSTRING body;
+ LEX_CSTRING definer_user;
+ LEX_CSTRING definer_host;
sql_mode_t sql_mode;
@@ -182,11 +182,12 @@ private:
/* Compares only the schema part of the identifier */
bool
-event_basic_db_equal(LEX_STRING db, Event_basic *et);
+event_basic_db_equal(const LEX_CSTRING *db, Event_basic *et);
/* Compares the whole identifier*/
bool
-event_basic_identifier_equal(LEX_STRING db, LEX_STRING name, Event_basic *b);
+event_basic_identifier_equal(const LEX_CSTRING *db, const LEX_CSTRING *name,
+ Event_basic *b);
/**
@} (End of group Event_Scheduler)
diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc
index ea52f68acc9..399a19b4112 100644
--- a/sql/event_db_repository.cc
+++ b/sql/event_db_repository.cc
@@ -14,7 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#include "sql_base.h" // close_thread_tables
@@ -41,38 +41,38 @@ static
const TABLE_FIELD_TYPE event_table_fields[ET_FIELD_COUNT] =
{
{
- { C_STRING_WITH_LEN("db") },
- { C_STRING_WITH_LEN("char(64)") },
- { C_STRING_WITH_LEN("utf8") }
+ { STRING_WITH_LEN("db") },
+ { STRING_WITH_LEN("char(64)") },
+ { STRING_WITH_LEN("utf8") }
},
{
- { C_STRING_WITH_LEN("name") },
- { C_STRING_WITH_LEN("char(64)") },
- { C_STRING_WITH_LEN("utf8") }
+ { STRING_WITH_LEN("name") },
+ { STRING_WITH_LEN("char(64)") },
+ { STRING_WITH_LEN("utf8") }
},
{
- { C_STRING_WITH_LEN("body") },
- { C_STRING_WITH_LEN("longblob") },
+ { STRING_WITH_LEN("body") },
+ { STRING_WITH_LEN("longblob") },
{NULL, 0}
},
{
- { C_STRING_WITH_LEN("definer") },
- { C_STRING_WITH_LEN("char(") },
- { C_STRING_WITH_LEN("utf8") }
+ { STRING_WITH_LEN("definer") },
+ { STRING_WITH_LEN("char(") },
+ { STRING_WITH_LEN("utf8") }
},
{
- { C_STRING_WITH_LEN("execute_at") },
- { C_STRING_WITH_LEN("datetime") },
+ { STRING_WITH_LEN("execute_at") },
+ { STRING_WITH_LEN("datetime") },
{NULL, 0}
},
{
- { C_STRING_WITH_LEN("interval_value") },
- { C_STRING_WITH_LEN("int(11)") },
+ { STRING_WITH_LEN("interval_value") },
+ { STRING_WITH_LEN("int(11)") },
{NULL, 0}
},
{
- { C_STRING_WITH_LEN("interval_field") },
- { C_STRING_WITH_LEN("enum('YEAR','QUARTER','MONTH','DAY',"
+ { STRING_WITH_LEN("interval_field") },
+ { STRING_WITH_LEN("enum('YEAR','QUARTER','MONTH','DAY',"
"'HOUR','MINUTE','WEEK','SECOND','MICROSECOND','YEAR_MONTH','DAY_HOUR',"
"'DAY_MINUTE','DAY_SECOND','HOUR_MINUTE','HOUR_SECOND','MINUTE_SECOND',"
"'DAY_MICROSECOND','HOUR_MICROSECOND','MINUTE_MICROSECOND',"
@@ -80,43 +80,43 @@ const TABLE_FIELD_TYPE event_table_fields[ET_FIELD_COUNT] =
{NULL, 0}
},
{
- { C_STRING_WITH_LEN("created") },
- { C_STRING_WITH_LEN("timestamp") },
+ { STRING_WITH_LEN("created") },
+ { STRING_WITH_LEN("timestamp") },
{NULL, 0}
},
{
- { C_STRING_WITH_LEN("modified") },
- { C_STRING_WITH_LEN("timestamp") },
+ { STRING_WITH_LEN("modified") },
+ { STRING_WITH_LEN("timestamp") },
{NULL, 0}
},
{
- { C_STRING_WITH_LEN("last_executed") },
- { C_STRING_WITH_LEN("datetime") },
+ { STRING_WITH_LEN("last_executed") },
+ { STRING_WITH_LEN("datetime") },
{NULL, 0}
},
{
- { C_STRING_WITH_LEN("starts") },
- { C_STRING_WITH_LEN("datetime") },
+ { STRING_WITH_LEN("starts") },
+ { STRING_WITH_LEN("datetime") },
{NULL, 0}
},
{
- { C_STRING_WITH_LEN("ends") },
- { C_STRING_WITH_LEN("datetime") },
+ { STRING_WITH_LEN("ends") },
+ { STRING_WITH_LEN("datetime") },
{NULL, 0}
},
{
- { C_STRING_WITH_LEN("status") },
- { C_STRING_WITH_LEN("enum('ENABLED','DISABLED','SLAVESIDE_DISABLED')") },
+ { STRING_WITH_LEN("status") },
+ { STRING_WITH_LEN("enum('ENABLED','DISABLED','SLAVESIDE_DISABLED')") },
{NULL, 0}
},
{
- { C_STRING_WITH_LEN("on_completion") },
- { C_STRING_WITH_LEN("enum('DROP','PRESERVE')") },
+ { STRING_WITH_LEN("on_completion") },
+ { STRING_WITH_LEN("enum('DROP','PRESERVE')") },
{NULL, 0}
},
{
- { C_STRING_WITH_LEN("sql_mode") },
- { C_STRING_WITH_LEN("set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES',"
+ { STRING_WITH_LEN("sql_mode") },
+ { STRING_WITH_LEN("set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES',"
"'IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY',"
"'NO_UNSIGNED_SUBTRACTION',"
"'NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB',"
@@ -124,46 +124,49 @@ const TABLE_FIELD_TYPE event_table_fields[ET_FIELD_COUNT] =
"'ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES',"
"'STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES',"
"'ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER',"
- "'HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH')") },
+ "'HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH',"
+ "'EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT')") },
{NULL, 0}
},
{
- { C_STRING_WITH_LEN("comment") },
- { C_STRING_WITH_LEN("char(64)") },
- { C_STRING_WITH_LEN("utf8") }
+ { STRING_WITH_LEN("comment") },
+ { STRING_WITH_LEN("char(64)") },
+ { STRING_WITH_LEN("utf8") }
},
{
- { C_STRING_WITH_LEN("originator") },
- { C_STRING_WITH_LEN("int(10)") },
+ { STRING_WITH_LEN("originator") },
+ { STRING_WITH_LEN("int(10)") },
{NULL, 0}
},
{
- { C_STRING_WITH_LEN("time_zone") },
- { C_STRING_WITH_LEN("char(64)") },
- { C_STRING_WITH_LEN("latin1") }
+ { STRING_WITH_LEN("time_zone") },
+ { STRING_WITH_LEN("char(64)") },
+ { STRING_WITH_LEN("latin1") }
},
{
- { C_STRING_WITH_LEN("character_set_client") },
- { C_STRING_WITH_LEN("char(32)") },
- { C_STRING_WITH_LEN("utf8") }
+ { STRING_WITH_LEN("character_set_client") },
+ { STRING_WITH_LEN("char(32)") },
+ { STRING_WITH_LEN("utf8") }
},
{
- { C_STRING_WITH_LEN("collation_connection") },
- { C_STRING_WITH_LEN("char(32)") },
- { C_STRING_WITH_LEN("utf8") }
+ { STRING_WITH_LEN("collation_connection") },
+ { STRING_WITH_LEN("char(32)") },
+ { STRING_WITH_LEN("utf8") }
},
{
- { C_STRING_WITH_LEN("db_collation") },
- { C_STRING_WITH_LEN("char(32)") },
- { C_STRING_WITH_LEN("utf8") }
+ { STRING_WITH_LEN("db_collation") },
+ { STRING_WITH_LEN("char(32)") },
+ { STRING_WITH_LEN("utf8") }
},
{
- { C_STRING_WITH_LEN("body_utf8") },
- { C_STRING_WITH_LEN("longblob") },
+ { STRING_WITH_LEN("body_utf8") },
+ { STRING_WITH_LEN("longblob") },
{ NULL, 0 }
}
};
+static LEX_CSTRING MYSQL_EVENT_NAME= { STRING_WITH_LEN("event") };
+
static const TABLE_FIELD_DEF
event_table_def= {ET_FIELD_COUNT, event_table_fields, 0, (uint*) 0};
@@ -368,14 +371,14 @@ mysql_event_fill_row(THD *thd,
if (rs)
{
- my_error(ER_EVENT_STORE_FAILED, MYF(0), fields[f_num]->field_name, rs);
+ my_error(ER_EVENT_STORE_FAILED, MYF(0), fields[f_num]->field_name.str, rs);
DBUG_RETURN(TRUE);
}
DBUG_RETURN(FALSE);
err_truncate:
- my_error(ER_EVENT_DATA_TOO_LONG, MYF(0), fields[f_num]->field_name);
+ my_error(ER_EVENT_DATA_TOO_LONG, MYF(0), fields[f_num]->field_name.str);
DBUG_RETURN(TRUE);
}
@@ -499,7 +502,7 @@ Event_db_repository::table_scan_all_for_i_s(THD *thd, TABLE *schema_table,
*/
do
{
- ret= read_record_info.read_record(&read_record_info);
+ ret= read_record_info.read_record();
if (ret == 0)
ret= copy_event_to_schema_table(thd, schema_table, event_table);
} while (ret == 0);
@@ -538,7 +541,7 @@ Event_db_repository::fill_schema_events(THD *thd, TABLE_LIST *i_s_table,
DBUG_ENTER("Event_db_repository::fill_schema_events");
DBUG_PRINT("info",("db=%s", db? db:"(null)"));
- event_table.init_one_table("mysql", 5, "event", 5, "event", TL_READ);
+ 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))
DBUG_RETURN(TRUE);
@@ -600,7 +603,7 @@ Event_db_repository::open_event_table(THD *thd, enum thr_lock_type lock_type,
TABLE_LIST tables;
DBUG_ENTER("Event_db_repository::open_event_table");
- tables.init_one_table("mysql", 5, "event", 5, "event", lock_type);
+ tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_EVENT_NAME, 0, lock_type);
if (open_and_lock_tables(thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
DBUG_RETURN(TRUE);
@@ -670,7 +673,7 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
parse_data->name.str));
DBUG_PRINT("info", ("check existence of an event with the same name"));
- if (!find_named_event(parse_data->dbname, parse_data->name, table))
+ if (!find_named_event(&parse_data->dbname, &parse_data->name, table))
{
if (thd->lex->create_info.or_replace())
{
@@ -768,8 +771,8 @@ end:
bool
Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
- LEX_STRING *new_dbname,
- LEX_STRING *new_name)
+ LEX_CSTRING *new_dbname,
+ LEX_CSTRING *new_name)
{
CHARSET_INFO *scs= system_charset_info;
TABLE *table= NULL;
@@ -802,7 +805,7 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
if (new_name)
{
DBUG_PRINT("info", ("rename to: %s@%s", new_dbname->str, new_name->str));
- if (!find_named_event(*new_dbname, *new_name, table))
+ if (!find_named_event(new_dbname, new_name, table))
{
my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->str);
goto end;
@@ -814,7 +817,7 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
overwrite the key and SE will tell us that it cannot find the already found
row (copied into record[1] later
*/
- if (find_named_event(parse_data->dbname, parse_data->name, table))
+ if (find_named_event(&parse_data->dbname, &parse_data->name, table))
{
my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), parse_data->name.str);
goto end;
@@ -878,7 +881,8 @@ end:
*/
bool
-Event_db_repository::drop_event(THD *thd, LEX_STRING db, LEX_STRING name,
+Event_db_repository::drop_event(THD *thd, const LEX_CSTRING *db,
+ const LEX_CSTRING *name,
bool drop_if_exists)
{
TABLE *table= NULL;
@@ -891,7 +895,7 @@ Event_db_repository::drop_event(THD *thd, LEX_STRING db, LEX_STRING name,
int ret= 1;
DBUG_ENTER("Event_db_repository::drop_event");
- DBUG_PRINT("enter", ("%s@%s", db.str, name.str));
+ DBUG_PRINT("enter", ("%s@%s", db->str, name->str));
if (open_event_table(thd, TL_WRITE, &table))
goto end;
@@ -906,13 +910,13 @@ Event_db_repository::drop_event(THD *thd, LEX_STRING db, LEX_STRING name,
/* Event not found */
if (!drop_if_exists)
{
- my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str);
+ my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name->str);
goto end;
}
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_SP_DOES_NOT_EXIST, ER_THD(thd, ER_SP_DOES_NOT_EXIST),
- "Event", name.str);
+ "Event", name->str);
ret= 0;
end:
@@ -939,12 +943,13 @@ end:
*/
bool
-Event_db_repository::find_named_event(LEX_STRING db, LEX_STRING name,
+Event_db_repository::find_named_event(const LEX_CSTRING *db,
+ const LEX_CSTRING *name,
TABLE *table)
{
uchar key[MAX_KEY_LENGTH];
DBUG_ENTER("Event_db_repository::find_named_event");
- DBUG_PRINT("enter", ("name: %.*s", (int) name.length, name.str));
+ DBUG_PRINT("enter", ("name: %.*s", (int) name->length, name->str));
/*
Create key to find row. We have to use field->store() to be able to
@@ -953,16 +958,16 @@ Event_db_repository::find_named_event(LEX_STRING db, LEX_STRING name,
'db' and 'name' and the first key is the primary key over the
same fields.
*/
- if (db.length > table->field[ET_FIELD_DB]->field_length ||
- name.length > table->field[ET_FIELD_NAME]->field_length ||
+ if (db->length > table->field[ET_FIELD_DB]->field_length ||
+ name->length > table->field[ET_FIELD_NAME]->field_length ||
table->s->keys == 0 ||
table->key_info[0].user_defined_key_parts != 2 ||
table->key_info[0].key_part[0].fieldnr != ET_FIELD_DB+1 ||
table->key_info[0].key_part[1].fieldnr != ET_FIELD_NAME+1)
DBUG_RETURN(TRUE);
- table->field[ET_FIELD_DB]->store(db.str, db.length, &my_charset_bin);
- table->field[ET_FIELD_NAME]->store(name.str, name.length, &my_charset_bin);
+ table->field[ET_FIELD_DB]->store(db->str, db->length, &my_charset_bin);
+ table->field[ET_FIELD_NAME]->store(name->str, name->length, &my_charset_bin);
key_copy(key, table->record[0], table->key_info, table->key_info->key_length);
@@ -989,7 +994,7 @@ Event_db_repository::find_named_event(LEX_STRING db, LEX_STRING name,
*/
void
-Event_db_repository::drop_schema_events(THD *thd, LEX_STRING schema)
+Event_db_repository::drop_schema_events(THD *thd, const LEX_CSTRING *schema)
{
int ret= 0;
TABLE *table= NULL;
@@ -997,7 +1002,7 @@ Event_db_repository::drop_schema_events(THD *thd, LEX_STRING schema)
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));
+ DBUG_PRINT("enter", ("field: %d schema: %s", field, schema->str));
if (open_event_table(thd, TL_WRITE, &table))
DBUG_VOID_RETURN;
@@ -1006,19 +1011,19 @@ Event_db_repository::drop_schema_events(THD *thd, LEX_STRING schema)
if (init_read_record(&read_record_info, thd, table, NULL, NULL, 1, 0, FALSE))
goto end;
- while (!ret && !(read_record_info.read_record(&read_record_info)) )
+ while (!ret && !(read_record_info.read_record()))
{
char *et_field= get_field(thd->mem_root, table->field[field]);
/* et_field may be NULL if the table is corrupted or out of memory */
if (et_field)
{
- LEX_STRING et_field_lex= { et_field, strlen(et_field) };
+ LEX_CSTRING et_field_lex= { et_field, strlen(et_field) };
DBUG_PRINT("info", ("Current event %s name=%s", et_field,
get_field(thd->mem_root,
table->field[ET_FIELD_NAME])));
- if (!sortcmp_lex_string(et_field_lex, schema, system_charset_info))
+ if (!sortcmp_lex_string(&et_field_lex, schema, system_charset_info))
{
DBUG_PRINT("info", ("Dropping"));
if ((ret= table->file->ha_delete_row(table->record[0])))
@@ -1051,8 +1056,9 @@ end:
*/
bool
-Event_db_repository::load_named_event(THD *thd, LEX_STRING dbname,
- LEX_STRING name, Event_basic *etn)
+Event_db_repository::load_named_event(THD *thd, const LEX_CSTRING *dbname,
+ const LEX_CSTRING *name,
+ Event_basic *etn)
{
bool ret;
ulonglong saved_mode= thd->variables.sql_mode;
@@ -1061,9 +1067,9 @@ Event_db_repository::load_named_event(THD *thd, LEX_STRING dbname,
DBUG_ENTER("Event_db_repository::load_named_event");
DBUG_PRINT("enter",("thd: %p name: %*s", thd,
- (int) name.length, name.str));
+ (int) name->length, name->str));
- event_table.init_one_table("mysql", 5, "event", 5, "event", TL_READ);
+ event_table.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_EVENT_NAME, 0, TL_READ);
/* Reset sql_mode during data dictionary operations. */
thd->variables.sql_mode= 0;
@@ -1084,7 +1090,7 @@ Event_db_repository::load_named_event(THD *thd, LEX_STRING dbname,
}
if ((ret= find_named_event(dbname, name, event_table.table)))
- my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str);
+ 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");
@@ -1106,8 +1112,8 @@ Event_db_repository::load_named_event(THD *thd, LEX_STRING dbname,
bool
Event_db_repository::
update_timing_fields_for_event(THD *thd,
- LEX_STRING event_db_name,
- LEX_STRING event_name,
+ const LEX_CSTRING *event_db_name,
+ const LEX_CSTRING *event_name,
my_time_t last_executed,
ulonglong status)
{
@@ -1180,48 +1186,11 @@ Event_db_repository::check_system_tables(THD *thd)
{
TABLE_LIST tables;
int ret= FALSE;
- const unsigned int event_priv_column_position= 29;
-
DBUG_ENTER("Event_db_repository::check_system_tables");
DBUG_PRINT("enter", ("thd: %p", thd));
- /* Check mysql.db */
- tables.init_one_table("mysql", 5, "db", 2, "db", TL_READ);
-
- if (open_and_lock_tables(thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
- {
- ret= 1;
- sql_print_error("Cannot open mysql.db");
- }
- else
- {
- if (table_intact.check(tables.table, &mysql_db_table_def))
- ret= 1;
-
- close_mysql_tables(thd);
- }
- /* Check mysql.user */
- tables.init_one_table("mysql", 5, "user", 4, "user", TL_READ);
-
- if (open_and_lock_tables(thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
- {
- ret= 1;
- sql_print_error("Cannot open mysql.user");
- }
- else
- {
- if (tables.table->s->fields < event_priv_column_position ||
- strncmp(tables.table->field[event_priv_column_position]->field_name,
- STRING_WITH_LEN("Event_priv")))
- {
- sql_print_error("mysql.user has no `Event_priv` column at position %d",
- event_priv_column_position);
- ret= 1;
- }
- close_mysql_tables(thd);
- }
/* Check mysql.event */
- tables.init_one_table("mysql", 5, "event", 5, "event", TL_READ);
+ tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_EVENT_NAME, 0, TL_READ);
if (open_and_lock_tables(thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
{
diff --git a/sql/event_db_repository.h b/sql/event_db_repository.h
index d6834d5aec6..b89a1a15155 100644
--- a/sql/event_db_repository.h
+++ b/sql/event_db_repository.h
@@ -77,20 +77,24 @@ public:
create_event(THD *thd, Event_parse_data *parse_data,
bool *event_already_exists);
bool
- update_event(THD *thd, Event_parse_data *parse_data, LEX_STRING *new_dbname,
- LEX_STRING *new_name);
+ update_event(THD *thd, Event_parse_data *parse_data, LEX_CSTRING *new_dbname,
+ LEX_CSTRING *new_name);
bool
- drop_event(THD *thd, LEX_STRING db, LEX_STRING name, bool drop_if_exists);
+ drop_event(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *name,
+ bool drop_if_exists);
void
- drop_schema_events(THD *thd, LEX_STRING schema);
+ drop_schema_events(THD *thd, const LEX_CSTRING *schema);
bool
- find_named_event(LEX_STRING db, LEX_STRING name, TABLE *table);
+ find_named_event(const LEX_CSTRING *db, const LEX_CSTRING *name,
+ TABLE *table);
bool
- load_named_event(THD *thd, LEX_STRING dbname, LEX_STRING name, Event_basic *et);
+ load_named_event(THD *thd, const LEX_CSTRING *dbname,
+ const LEX_CSTRING *name,
+ Event_basic *et);
static bool
open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table);
@@ -100,8 +104,8 @@ public:
bool
update_timing_fields_for_event(THD *thd,
- LEX_STRING event_db_name,
- LEX_STRING event_name,
+ const LEX_CSTRING *event_db_name,
+ const LEX_CSTRING *event_name,
my_time_t last_executed,
ulonglong status);
public:
diff --git a/sql/event_parse_data.cc b/sql/event_parse_data.cc
index 3606fdbdd3d..1ec2ab877fe 100644
--- a/sql/event_parse_data.cc
+++ b/sql/event_parse_data.cc
@@ -14,7 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#include "sp_head.h"
@@ -88,9 +88,6 @@ Event_parse_data::init_name(THD *thd, sp_name *spn)
name.length= spn->m_name.length;
name.str= thd->strmake(spn->m_name.str, spn->m_name.length);
- if (spn->m_qname.length == 0)
- spn->init_qname(thd);
-
DBUG_VOID_RETURN;
}
@@ -531,7 +528,7 @@ Event_parse_data::init_definer(THD *thd)
const char *definer_host= thd->lex->definer->host.str;
size_t definer_user_len= thd->lex->definer->user.length;
size_t definer_host_len= thd->lex->definer->host.length;
-
+ char *tmp;
DBUG_PRINT("info",("init definer_user thd->mem_root: %p "
"definer_user: %p", thd->mem_root,
definer_user));
@@ -539,15 +536,14 @@ Event_parse_data::init_definer(THD *thd)
/* + 1 for @ */
DBUG_PRINT("info",("init definer as whole"));
definer.length= definer_user_len + definer_host_len + 1;
- definer.str= (char*) thd->alloc(definer.length + 1);
+ definer.str= tmp= (char*) thd->alloc(definer.length + 1);
DBUG_PRINT("info",("copy the user"));
- memcpy(definer.str, definer_user, definer_user_len);
- definer.str[definer_user_len]= '@';
+ strmake(tmp, definer_user, definer_user_len);
+ tmp[definer_user_len]= '@';
DBUG_PRINT("info",("copy the host"));
- memcpy(definer.str + definer_user_len + 1, definer_host, definer_host_len);
- definer.str[definer.length]= '\0';
+ strmake(tmp + definer_user_len + 1, definer_host, definer_host_len);
DBUG_PRINT("info",("definer [%s] initted", definer.str));
DBUG_VOID_RETURN;
diff --git a/sql/event_parse_data.h b/sql/event_parse_data.h
index 03f6e8c3d14..4e68295ab5d 100644
--- a/sql/event_parse_data.h
+++ b/sql/event_parse_data.h
@@ -17,7 +17,7 @@
#ifndef _EVENT_PARSE_DATA_H_
#define _EVENT_PARSE_DATA_H_
-#include "sql_list.h" /* Sql_alloc */
+#include "sql_alloc.h"
class Item;
class THD;
@@ -66,10 +66,10 @@ public:
bool body_changed;
- LEX_STRING dbname;
- LEX_STRING name;
- LEX_STRING definer;// combination of user and host
- LEX_STRING comment;
+ LEX_CSTRING dbname;
+ LEX_CSTRING name;
+ LEX_CSTRING definer;// combination of user and host
+ LEX_CSTRING comment;
Item* item_starts;
Item* item_ends;
diff --git a/sql/event_queue.cc b/sql/event_queue.cc
index 904efe26f36..91c243b3f70 100644
--- a/sql/event_queue.cc
+++ b/sql/event_queue.cc
@@ -13,7 +13,7 @@
along with this program; if not, write to the Free Software Foundation,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#include "event_queue.h"
@@ -238,11 +238,13 @@ Event_queue::create_event(THD *thd, Event_queue_element *new_element,
*/
void
-Event_queue::update_event(THD *thd, LEX_STRING dbname, LEX_STRING name,
+Event_queue::update_event(THD *thd, const LEX_CSTRING *dbname,
+ const LEX_CSTRING *name,
Event_queue_element *new_element)
{
DBUG_ENTER("Event_queue::update_event");
- DBUG_PRINT("enter", ("thd: %p et=[%s.%s]", thd, dbname.str, name.str));
+ DBUG_PRINT("enter", ("thd: %p et: [%s.%s]", thd, dbname->str,
+ name->str));
if ((new_element->status == Event_parse_data::DISABLED) ||
(new_element->status == Event_parse_data::SLAVESIDE_DISABLED))
@@ -287,11 +289,12 @@ Event_queue::update_event(THD *thd, LEX_STRING dbname, LEX_STRING name,
*/
void
-Event_queue::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name)
+Event_queue::drop_event(THD *thd, const LEX_CSTRING *dbname,
+ const LEX_CSTRING *name)
{
DBUG_ENTER("Event_queue::drop_event");
- DBUG_PRINT("enter", ("thd: %p db :%s name: %s", thd,
- dbname.str, name.str));
+ DBUG_PRINT("enter", ("thd: %p db: %s name: %s", thd,
+ dbname->str, name->str));
LOCK_QUEUE_DATA();
find_n_remove_event(dbname, name);
@@ -325,12 +328,12 @@ Event_queue::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name)
*/
void
-Event_queue::drop_matching_events(THD *thd, LEX_STRING pattern,
- bool (*comparator)(LEX_STRING, Event_basic *))
+Event_queue::drop_matching_events(THD *thd, const LEX_CSTRING *pattern,
+ bool (*comparator)(const LEX_CSTRING *, Event_basic *))
{
uint i;
DBUG_ENTER("Event_queue::drop_matching_events");
- DBUG_PRINT("enter", ("pattern=%s", pattern.str));
+ DBUG_PRINT("enter", ("pattern: %s", pattern->str));
for (i= queue_first_element(&queue) ;
i <= queue_last_element(&queue) ;
@@ -380,7 +383,7 @@ Event_queue::drop_matching_events(THD *thd, LEX_STRING pattern,
*/
void
-Event_queue::drop_schema_events(THD *thd, LEX_STRING schema)
+Event_queue::drop_schema_events(THD *thd, const LEX_CSTRING *schema)
{
DBUG_ENTER("Event_queue::drop_schema_events");
LOCK_QUEUE_DATA();
@@ -404,7 +407,8 @@ Event_queue::drop_schema_events(THD *thd, LEX_STRING schema)
*/
void
-Event_queue::find_n_remove_event(LEX_STRING db, LEX_STRING name)
+Event_queue::find_n_remove_event(const LEX_CSTRING *db,
+ const LEX_CSTRING *name)
{
uint i;
DBUG_ENTER("Event_queue::find_n_remove_event");
@@ -414,7 +418,7 @@ Event_queue::find_n_remove_event(LEX_STRING db, LEX_STRING name)
i++)
{
Event_queue_element *et= (Event_queue_element *) queue_element(&queue, i);
- DBUG_PRINT("info", ("[%s.%s]==[%s.%s]?", db.str, name.str,
+ DBUG_PRINT("info", ("[%s.%s]==[%s.%s]?", db->str, name->str,
et->dbname.str, et->name.str));
if (event_basic_identifier_equal(db, name, et))
{
@@ -613,7 +617,7 @@ Event_queue::get_top_for_execution_if_time(THD *thd,
top= (Event_queue_element*) queue_top(&queue);
- thd->set_current_time(); /* Get current time */
+ thd->set_start_time(); /* Get current time */
next_activation_at= top->execute_at;
if (next_activation_at > thd->query_start())
@@ -633,7 +637,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;
@@ -683,7 +687,7 @@ end:
Event_db_repository *db_repository= Events::get_db_repository();
(void) db_repository->update_timing_fields_for_event(thd,
- (*event_name)->dbname, (*event_name)->name,
+ &(*event_name)->dbname, &(*event_name)->name,
last_executed, (ulonglong) status);
}
diff --git a/sql/event_queue.h b/sql/event_queue.h
index 2f1e9a59c9d..2b6a0a594cb 100644
--- a/sql/event_queue.h
+++ b/sql/event_queue.h
@@ -31,7 +31,7 @@ extern PSI_cond_key key_COND_queue_state;
#endif /* HAVE_PSI_INTERFACE */
#include "queues.h" // QUEUE
-#include "sql_string.h" /* LEX_STRING */
+#include "sql_string.h" /* LEX_CSTRING */
#include "my_time.h" /* my_time_t, interval_type */
class Event_basic;
@@ -60,14 +60,14 @@ public:
bool *created);
void
- update_event(THD *thd, LEX_STRING dbname, LEX_STRING name,
+ update_event(THD *thd, const LEX_CSTRING *dbname, const LEX_CSTRING *name,
Event_queue_element *new_element);
void
- drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name);
+ drop_event(THD *thd, const LEX_CSTRING *dbname, const LEX_CSTRING *name);
void
- drop_schema_events(THD *thd, LEX_STRING schema);
+ drop_schema_events(THD *thd, const LEX_CSTRING *schema);
void
recalculate_activation_times(THD *thd);
@@ -98,12 +98,12 @@ private:
const char *src_func, const char *src_file, uint src_line);
void
- find_n_remove_event(LEX_STRING db, LEX_STRING name);
+ find_n_remove_event(const LEX_CSTRING *db, const LEX_CSTRING *name);
void
- drop_matching_events(THD *thd, LEX_STRING pattern,
- bool (*)(LEX_STRING, Event_basic *));
+ drop_matching_events(THD *thd, const LEX_CSTRING *pattern,
+ bool (*)(const LEX_CSTRING*, Event_basic *));
void
diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc
index 2a5399fb94a..2ebe1f5bb69 100644
--- a/sql/event_scheduler.cc
+++ b/sql/event_scheduler.cc
@@ -14,7 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#include "event_scheduler.h"
@@ -50,11 +50,11 @@ Event_db_repository *Event_worker_thread::db_repository;
static
-const LEX_STRING scheduler_states_names[] =
+const LEX_CSTRING scheduler_states_names[] =
{
- { C_STRING_WITH_LEN("INITIALIZED") },
- { C_STRING_WITH_LEN("RUNNING") },
- { C_STRING_WITH_LEN("STOPPING") }
+ { STRING_WITH_LEN("INITIALIZED") },
+ { STRING_WITH_LEN("RUNNING") },
+ { STRING_WITH_LEN("STOPPING") }
};
struct scheduler_param {
@@ -86,7 +86,7 @@ Event_worker_thread::print_warnings(THD *thd, Event_job_data *et)
char prefix_buf[5 * STRING_BUFFER_USUAL_SIZE];
String prefix(prefix_buf, sizeof(prefix_buf), system_charset_info);
prefix.length(0);
- prefix.append("Event Scheduler: [");
+ prefix.append(STRING_WITH_LEN("Event Scheduler: ["));
prefix.append(et->definer.str, et->definer.length, system_charset_info);
prefix.append("][", 2);
@@ -298,11 +298,10 @@ Event_worker_thread::run(THD *thd, Event_queue_element_for_exec *event)
DBUG_ENTER("Event_worker_thread::run");
DBUG_PRINT("info", ("Time is %u, THD: %p", (uint)my_time(0), thd));
- inc_thread_running();
if (res)
goto end;
- if ((res= db_repository->load_named_event(thd, event->dbname, event->name,
+ if ((res= db_repository->load_named_event(thd, &event->dbname, &event->name,
&job_data)))
{
DBUG_PRINT("error", ("Got error from load_named_event"));
@@ -327,7 +326,6 @@ end:
event->name.str));
delete event;
- dec_thread_running();
deinit_event_thread(thd);
DBUG_VOID_RETURN;
@@ -650,14 +648,11 @@ Event_scheduler::stop()
state= STOPPING;
DBUG_PRINT("info", ("Scheduler thread has id %lu",
(ulong) scheduler_thd->thread_id));
- /* Lock from delete */
- mysql_mutex_lock(&scheduler_thd->LOCK_thd_data);
/* This will wake up the thread if it waits on Queue's conditional */
sql_print_information("Event Scheduler: Killing the scheduler thread, "
"thread id %lu",
(ulong) scheduler_thd->thread_id);
scheduler_thd->awake(KILL_CONNECTION);
- mysql_mutex_unlock(&scheduler_thd->LOCK_thd_data);
/* thd could be 0x0, when shutting down */
sql_print_information("Event Scheduler: "
diff --git a/sql/events.cc b/sql/events.cc
index abac2833833..7c7c5c0c2ac 100644
--- a/sql/events.cc
+++ b/sql/events.cc
@@ -15,7 +15,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#include "sql_parse.h" // check_access
@@ -100,10 +100,11 @@ ulong Events::inited;
1 s > t
*/
-int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs)
+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->coll->strnncollsp(cs, (uchar *) s->str, s->length,
+ (uchar *) t->str, t->length);
}
@@ -319,7 +320,7 @@ Events::create_event(THD *thd, Event_parse_data *parse_data)
enum_binlog_format save_binlog_format;
DBUG_ENTER("Events::create_event");
- if (check_if_system_tables_error())
+ if (unlikely(check_if_system_tables_error()))
DBUG_RETURN(TRUE);
/*
@@ -356,7 +357,7 @@ Events::create_event(THD *thd, Event_parse_data *parse_data)
save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
if (thd->lex->create_info.or_replace() && event_queue)
- event_queue->drop_event(thd, parse_data->dbname, parse_data->name);
+ event_queue->drop_event(thd, &parse_data->dbname, &parse_data->name);
/* On error conditions my_error() is called so no need to handle here */
if (!(ret= db_repository->create_event(thd, parse_data,
@@ -369,12 +370,12 @@ Events::create_event(THD *thd, Event_parse_data *parse_data)
{
if (!(new_element= new Event_queue_element()))
ret= TRUE; // OOM
- else if ((ret= db_repository->load_named_event(thd, parse_data->dbname,
- parse_data->name,
+ else if ((ret= db_repository->load_named_event(thd, &parse_data->dbname,
+ &parse_data->name,
new_element)))
{
- if (!db_repository->drop_event(thd, parse_data->dbname,
- parse_data->name, TRUE))
+ if (!db_repository->drop_event(thd, &parse_data->dbname,
+ &parse_data->name, TRUE))
dropped= 1;
delete new_element;
}
@@ -452,7 +453,7 @@ WSREP_ERROR_LABEL:
bool
Events::update_event(THD *thd, Event_parse_data *parse_data,
- LEX_STRING *new_dbname, LEX_STRING *new_name)
+ LEX_CSTRING *new_dbname, LEX_CSTRING *new_name)
{
int ret;
enum_binlog_format save_binlog_format;
@@ -460,7 +461,7 @@ Events::update_event(THD *thd, Event_parse_data *parse_data,
DBUG_ENTER("Events::update_event");
- if (check_if_system_tables_error())
+ if (unlikely(check_if_system_tables_error()))
DBUG_RETURN(TRUE);
if (parse_data->check_parse_data(thd) || parse_data->do_not_create)
@@ -485,9 +486,9 @@ Events::update_event(THD *thd, Event_parse_data *parse_data,
if (new_dbname) /* It's a rename */
{
/* Check that the new and the old names differ. */
- if ( !sortcmp_lex_string(parse_data->dbname, *new_dbname,
+ if ( !sortcmp_lex_string(&parse_data->dbname, new_dbname,
system_charset_info) &&
- !sortcmp_lex_string(parse_data->name, *new_name,
+ !sortcmp_lex_string(&parse_data->name, new_name,
system_charset_info))
{
my_error(ER_EVENT_SAME_NAME, MYF(0));
@@ -528,12 +529,12 @@ Events::update_event(THD *thd, Event_parse_data *parse_data,
if (!(ret= db_repository->update_event(thd, parse_data,
new_dbname, new_name)))
{
- LEX_STRING dbname= new_dbname ? *new_dbname : parse_data->dbname;
- LEX_STRING name= new_name ? *new_name : parse_data->name;
+ LEX_CSTRING dbname= new_dbname ? *new_dbname : parse_data->dbname;
+ LEX_CSTRING name= new_name ? *new_name : parse_data->name;
if (!(new_element= new Event_queue_element()))
ret= TRUE; // OOM
- else if ((ret= db_repository->load_named_event(thd, dbname, name,
+ else if ((ret= db_repository->load_named_event(thd, &dbname, &name,
new_element)))
delete new_element;
else
@@ -545,7 +546,7 @@ Events::update_event(THD *thd, Event_parse_data *parse_data,
it right away.
*/
if (event_queue)
- event_queue->update_event(thd, parse_data->dbname, parse_data->name,
+ event_queue->update_event(thd, &parse_data->dbname, &parse_data->name,
new_element);
/* Binlog the alter event. */
DBUG_ASSERT(thd->query() && thd->query_length());
@@ -586,16 +587,17 @@ WSREP_ERROR_LABEL:
*/
bool
-Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists)
+Events::drop_event(THD *thd, const LEX_CSTRING *dbname,
+ const LEX_CSTRING *name, bool if_exists)
{
int ret;
enum_binlog_format save_binlog_format;
DBUG_ENTER("Events::drop_event");
- if (check_if_system_tables_error())
+ if (unlikely(check_if_system_tables_error()))
DBUG_RETURN(TRUE);
- if (check_access(thd, EVENT_ACL, dbname.str, NULL, NULL, 0, 0))
+ if (check_access(thd, EVENT_ACL, dbname->str, NULL, NULL, 0, 0))
DBUG_RETURN(TRUE);
WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
@@ -607,7 +609,7 @@ Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists)
save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
if (lock_object_name(thd, MDL_key::EVENT,
- dbname.str, name.str))
+ dbname->str, name->str))
DBUG_RETURN(TRUE);
/* On error conditions my_error() is called so no need to handle here */
if (!(ret= db_repository->drop_event(thd, dbname, name, if_exists)))
@@ -639,22 +641,22 @@ WSREP_ERROR_LABEL:
*/
void
-Events::drop_schema_events(THD *thd, char *db)
+Events::drop_schema_events(THD *thd, const char *db)
{
- LEX_STRING const db_lex= { db, strlen(db) };
+ const LEX_CSTRING db_lex= { db, strlen(db) };
DBUG_ENTER("Events::drop_schema_events");
DBUG_PRINT("enter", ("dropping events from %s", db));
- DBUG_ASSERT(ok_for_lower_case_names(db));
+ DBUG_SLOW_ASSERT(ok_for_lower_case_names(db));
/*
Sic: no check if the scheduler is disabled or system tables
are damaged, as intended.
*/
if (event_queue)
- event_queue->drop_schema_events(thd, db_lex);
- db_repository->drop_schema_events(thd, db_lex);
+ event_queue->drop_schema_events(thd, &db_lex);
+ db_repository->drop_schema_events(thd, &db_lex);
DBUG_VOID_RETURN;
}
@@ -671,7 +673,7 @@ send_show_create_event(THD *thd, Event_timed *et, Protocol *protocol)
char show_str_buf[10 * STRING_BUFFER_USUAL_SIZE];
String show_str(show_str_buf, sizeof(show_str_buf), system_charset_info);
List<Item> field_list;
- LEX_STRING sql_mode;
+ LEX_CSTRING sql_mode;
const String *tz_name;
MEM_ROOT *mem_root= thd->mem_root;
DBUG_ENTER("send_show_create_event");
@@ -754,18 +756,19 @@ send_show_create_event(THD *thd, Event_timed *et, Protocol *protocol)
*/
bool
-Events::show_create_event(THD *thd, LEX_STRING dbname, LEX_STRING name)
+Events::show_create_event(THD *thd, const LEX_CSTRING *dbname,
+ const LEX_CSTRING *name)
{
Event_timed et;
bool ret;
DBUG_ENTER("Events::show_create_event");
- DBUG_PRINT("enter", ("name: %s@%s", dbname.str, name.str));
+ DBUG_PRINT("enter", ("name: %s@%s", dbname->str, name->str));
- if (check_if_system_tables_error())
+ if (unlikely(check_if_system_tables_error()))
DBUG_RETURN(TRUE);
- if (check_access(thd, EVENT_ACL, dbname.str, NULL, NULL, 0, 0))
+ if (check_access(thd, EVENT_ACL, dbname->str, NULL, NULL, 0, 0))
DBUG_RETURN(TRUE);
/*
@@ -806,8 +809,9 @@ Events::show_create_event(THD *thd, LEX_STRING dbname, LEX_STRING name)
int
Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
{
- char *db= NULL;
+ const char *db= NULL;
int ret;
+ char db_tmp[SAFE_NAME_LEN];
DBUG_ENTER("Events::fill_schema_events");
/*
@@ -817,7 +821,7 @@ Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
if (opt_noacl)
DBUG_RETURN(0);
- if (check_if_system_tables_error())
+ if (unlikely(check_if_system_tables_error()))
DBUG_RETURN(1);
/*
@@ -826,15 +830,12 @@ Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
*/
if (thd->lex->sql_command == SQLCOM_SHOW_EVENTS)
{
- DBUG_ASSERT(thd->lex->select_lex.db);
- if (!is_infoschema_db(thd->lex->select_lex.db) && // There is no events in I_S
- check_access(thd, EVENT_ACL, thd->lex->select_lex.db,
+ DBUG_ASSERT(thd->lex->select_lex.db.str);
+ if (!is_infoschema_db(&thd->lex->select_lex.db) && // There is no events in I_S
+ check_access(thd, EVENT_ACL, thd->lex->select_lex.db.str,
NULL, NULL, 0, 0))
DBUG_RETURN(1);
- db= thd->lex->select_lex.db;
-
- if (lower_case_table_names)
- my_casedn_str(system_charset_info, db);
+ db= normalize_db_name(thd->lex->select_lex.db.str, db_tmp, sizeof(db_tmp));
}
ret= db_repository->fill_schema_events(thd, tables, db);
@@ -1178,7 +1179,7 @@ Events::load_events_from_db(THD *thd)
DBUG_RETURN(TRUE);
}
- while (!(read_record_info.read_record(&read_record_info)))
+ while (!(read_record_info.read_record()))
{
Event_queue_element *et;
bool created, dropped;
@@ -1295,7 +1296,7 @@ int wsrep_create_event_query(THD *thd, uchar** buf, size_t* buf_len)
if (create_query_string(thd, &log_query))
{
WSREP_WARN("events create string failed: schema: %s, query: %s",
- (thd->db ? thd->db : "(null)"), thd->query());
+ thd->get_db(), thd->query());
return 1;
}
return wsrep_to_buf_helper(thd, log_query.ptr(), log_query.length(), buf, buf_len);
diff --git a/sql/events.h b/sql/events.h
index 7268c569b03..9e1651c767a 100644
--- a/sql/events.h
+++ b/sql/events.h
@@ -36,7 +36,7 @@ extern PSI_stage_info stage_waiting_on_empty_queue;
extern PSI_stage_info stage_waiting_for_next_activation;
extern PSI_stage_info stage_waiting_for_scheduler_to_stop;
-#include "sql_string.h" /* LEX_STRING */
+#include "sql_string.h" /* LEX_CSTRING */
#include "my_time.h" /* interval_type */
class Event_db_repository;
@@ -48,7 +48,8 @@ class THD;
typedef class Item COND;
int
-sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs);
+sortcmp_lex_string(const LEX_CSTRING *s, const LEX_CSTRING *t,
+ const CHARSET_INFO *cs);
/**
@brief A facade to the functionality of the Event Scheduler.
@@ -109,16 +110,18 @@ public:
static bool
update_event(THD *thd, Event_parse_data *parse_data,
- LEX_STRING *new_dbname, LEX_STRING *new_name);
+ LEX_CSTRING *new_dbname, LEX_CSTRING *new_name);
static bool
- drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists);
+ drop_event(THD *thd, const LEX_CSTRING *dbname, const LEX_CSTRING *name,
+ bool if_exists);
static void
- drop_schema_events(THD *thd, char *db);
+ drop_schema_events(THD *thd, const char *db);
static bool
- show_create_event(THD *thd, LEX_STRING dbname, LEX_STRING name);
+ show_create_event(THD *thd, const LEX_CSTRING *dbname,
+ const LEX_CSTRING *name);
/* Needed for both SHOW CREATE EVENT and INFORMATION_SCHEMA */
static int
diff --git a/sql/field.cc b/sql/field.cc
index bdaaecc2026..f100963903a 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -27,7 +27,7 @@
#pragma implementation // gcc: Class implementation
#endif
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "sql_select.h"
#include "rpl_rli.h" // Pull in Relay_log_info
@@ -51,15 +51,7 @@
*****************************************************************************/
static const char *zero_timestamp="0000-00-00 00:00:00.000000";
-
-/* number of bytes to store second_part part of the TIMESTAMP(N) */
-static uint sec_part_bytes[MAX_DATETIME_PRECISION+1]= { 0, 1, 1, 2, 2, 3, 3 };
-
-/* number of bytes to store DATETIME(N) */
-static uint datetime_hires_bytes[MAX_DATETIME_PRECISION+1]= { 5, 6, 6, 7, 7, 7, 8 };
-
-/* number of bytes to store TIME(N) */
-static uint time_hires_bytes[MAX_DATETIME_PRECISION+1]= { 3, 4, 4, 5, 5, 5, 6 };
+LEX_CSTRING temp_lex_str= {STRING_WITH_LEN("temp")};
uchar Field_null::null[1]={1};
const char field_separator=',';
@@ -95,18 +87,35 @@ const char field_separator=',';
following #defines describe that gap and how to canculate number of fields
and index of field in this array.
*/
-#define FIELDTYPE_TEAR_FROM (MYSQL_TYPE_BIT + 1)
-#define FIELDTYPE_TEAR_TO (MYSQL_TYPE_NEWDECIMAL - 1)
-#define FIELDTYPE_NUM (FIELDTYPE_TEAR_FROM + (255 - FIELDTYPE_TEAR_TO))
+const int FIELDTYPE_TEAR_FROM= (MYSQL_TYPE_BIT + 1);
+const int FIELDTYPE_TEAR_TO= (MYSQL_TYPE_NEWDECIMAL - 1);
+const int FIELDTYPE_LAST= 254;
+const int FIELDTYPE_NUM= FIELDTYPE_TEAR_FROM + (FIELDTYPE_LAST -
+ FIELDTYPE_TEAR_TO);
+
static inline int field_type2index (enum_field_types field_type)
{
+ DBUG_ASSERT(real_type_to_type(field_type) < FIELDTYPE_TEAR_FROM ||
+ real_type_to_type(field_type) > FIELDTYPE_TEAR_TO);
+ DBUG_ASSERT(field_type <= FIELDTYPE_LAST);
field_type= real_type_to_type(field_type);
- return (field_type < FIELDTYPE_TEAR_FROM ?
- field_type :
- ((int)FIELDTYPE_TEAR_FROM) + (field_type - FIELDTYPE_TEAR_TO) - 1);
+ if (field_type < FIELDTYPE_TEAR_FROM)
+ return field_type;
+ return FIELDTYPE_TEAR_FROM + (field_type - FIELDTYPE_TEAR_TO) - 1;
}
+/**
+ Implements data type merge rules for the built-in traditional data types.
+ Used for operations such as:
+ - UNION
+ - CASE and its abbreviations COALESCE, IF, IFNULL
+ - LEAST/GREATEST
+
+ Given Fields A and B of real_types a and b, we find the result type of
+ COALESCE(A, B) by querying:
+ field_types_merge_rules[field_type_to_index(a)][field_type_to_index(b)].
+*/
static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
{
/* MYSQL_TYPE_DECIMAL -> */
@@ -120,7 +129,7 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
//MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP
MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24
- MYSQL_TYPE_DECIMAL, MYSQL_TYPE_DECIMAL,
+ MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_NEWDECIMAL,
//MYSQL_TYPE_DATE MYSQL_TYPE_TIME
MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
//MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR
@@ -137,8 +146,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY
- MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
+ //MYSQL_TYPE_STRING
+ MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_TINY -> */
{
@@ -168,8 +177,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY
- MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
+ //MYSQL_TYPE_STRING
+ MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_SHORT -> */
{
@@ -199,8 +208,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY
- MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
+ //MYSQL_TYPE_STRING
+ MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_LONG -> */
{
@@ -230,8 +239,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY
- MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
+ //MYSQL_TYPE_STRING
+ MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_FLOAT -> */
{
@@ -261,8 +270,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY
- MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
+ //MYSQL_TYPE_STRING
+ MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_DOUBLE -> */
{
@@ -292,8 +301,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY
- MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
+ //MYSQL_TYPE_STRING
+ MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_NULL -> */
{
@@ -323,8 +332,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY
- MYSQL_TYPE_STRING, MYSQL_TYPE_GEOMETRY
+ //MYSQL_TYPE_STRING
+ MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_TIMESTAMP -> */
{
@@ -354,8 +363,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY
- MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
+ //MYSQL_TYPE_STRING
+ MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_LONGLONG -> */
{
@@ -385,8 +394,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY
- MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
+ //MYSQL_TYPE_STRING
+ MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_INT24 -> */
{
@@ -416,8 +425,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY
- MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
+ //MYSQL_TYPE_STRING
+ MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_DATE -> */
{
@@ -447,8 +456,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY
- MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
+ //MYSQL_TYPE_STRING
+ MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_TIME -> */
{
@@ -478,8 +487,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY
- MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
+ //MYSQL_TYPE_STRING
+ MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_DATETIME -> */
{
@@ -509,13 +518,13 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY
- MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
+ //MYSQL_TYPE_STRING
+ MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_YEAR -> */
{
//MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY
- MYSQL_TYPE_DECIMAL, MYSQL_TYPE_TINY,
+ MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_TINY,
//MYSQL_TYPE_SHORT MYSQL_TYPE_LONG
MYSQL_TYPE_SHORT, MYSQL_TYPE_LONG,
//MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE
@@ -540,8 +549,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY
- MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
+ //MYSQL_TYPE_STRING
+ MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_NEWDATE -> */
{
@@ -571,8 +580,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY
- MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
+ //MYSQL_TYPE_STRING
+ MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_VARCHAR -> */
{
@@ -602,8 +611,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY
- MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR
+ //MYSQL_TYPE_STRING
+ MYSQL_TYPE_VARCHAR
},
/* MYSQL_TYPE_BIT -> */
{
@@ -633,8 +642,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY
- MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
+ //MYSQL_TYPE_STRING
+ MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_NEWDECIMAL -> */
{
@@ -664,8 +673,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY
- MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
+ //MYSQL_TYPE_STRING
+ MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_ENUM -> */
{
@@ -695,8 +704,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY
- MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
+ //MYSQL_TYPE_STRING
+ MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_SET -> */
{
@@ -726,8 +735,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY
- MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR
+ //MYSQL_TYPE_STRING
+ MYSQL_TYPE_STRING
},
/* MYSQL_TYPE_TINY_BLOB -> */
{
@@ -757,8 +766,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_TINY_BLOB,
- //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY
- MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB
+ //MYSQL_TYPE_STRING
+ MYSQL_TYPE_TINY_BLOB
},
/* MYSQL_TYPE_MEDIUM_BLOB -> */
{
@@ -788,8 +797,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
- //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY
- MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB
+ //MYSQL_TYPE_STRING
+ MYSQL_TYPE_MEDIUM_BLOB
},
/* MYSQL_TYPE_LONG_BLOB -> */
{
@@ -819,8 +828,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB,
- //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY
- MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB
+ //MYSQL_TYPE_STRING
+ MYSQL_TYPE_LONG_BLOB
},
/* MYSQL_TYPE_BLOB -> */
{
@@ -850,8 +859,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB,
- //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY
- MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB
+ //MYSQL_TYPE_STRING
+ MYSQL_TYPE_BLOB
},
/* MYSQL_TYPE_VAR_STRING -> */
{
@@ -881,8 +890,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY
- MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR
+ //MYSQL_TYPE_STRING
+ MYSQL_TYPE_VARCHAR
},
/* MYSQL_TYPE_STRING -> */
{
@@ -912,39 +921,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
//MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY
- MYSQL_TYPE_STRING, MYSQL_TYPE_STRING
- },
- /* MYSQL_TYPE_GEOMETRY -> */
- {
- //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY
- MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG
- MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE
- MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP
- MYSQL_TYPE_GEOMETRY, MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24
- MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_DATE MYSQL_TYPE_TIME
- MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR
- MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR
- MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_BIT <16>-<245>
- MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM
- MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB
- MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB,
- //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB
- MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
- //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
- MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR,
- //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY
- MYSQL_TYPE_STRING, MYSQL_TYPE_GEOMETRY
+ //MYSQL_TYPE_STRING
+ MYSQL_TYPE_STRING
}
};
@@ -961,46 +939,19 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
enum_field_types Field::field_type_merge(enum_field_types a,
enum_field_types b)
{
- DBUG_ASSERT(real_type_to_type(a) < FIELDTYPE_TEAR_FROM ||
- real_type_to_type(a) > FIELDTYPE_TEAR_TO);
- DBUG_ASSERT(real_type_to_type(b) < FIELDTYPE_TEAR_FROM ||
- real_type_to_type(b) > FIELDTYPE_TEAR_TO);
return field_types_merge_rules[field_type2index(a)]
[field_type2index(b)];
}
-
-static Item_result field_types_result_type [FIELDTYPE_NUM]=
+const Type_handler *
+Type_handler::aggregate_for_result_traditional(const Type_handler *a,
+ const Type_handler *b)
{
- //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY
- DECIMAL_RESULT, INT_RESULT,
- //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG
- INT_RESULT, INT_RESULT,
- //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE
- REAL_RESULT, REAL_RESULT,
- //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP
- STRING_RESULT, STRING_RESULT,
- //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24
- INT_RESULT, INT_RESULT,
- //MYSQL_TYPE_DATE MYSQL_TYPE_TIME
- STRING_RESULT, STRING_RESULT,
- //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR
- STRING_RESULT, INT_RESULT,
- //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR
- STRING_RESULT, STRING_RESULT,
- //MYSQL_TYPE_BIT <16>-<245>
- STRING_RESULT,
- //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM
- DECIMAL_RESULT, STRING_RESULT,
- //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB
- STRING_RESULT, STRING_RESULT,
- //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB
- STRING_RESULT, STRING_RESULT,
- //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING
- STRING_RESULT, STRING_RESULT,
- //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY
- STRING_RESULT, STRING_RESULT
-};
+ enum_field_types ta= a->real_field_type();
+ enum_field_types tb= b->real_field_type();
+ return
+ Type_handler::get_handler_by_real_type(Field::field_type_merge(ta, tb));
+}
/*
@@ -1047,57 +998,11 @@ int compare(unsigned int a, unsigned int b)
CPP_UNNAMED_NS_END
-/**
- Detect Item_result by given field type of UNION merge result.
-
- @param field_type given field type
-
- @return
- Item_result (type of internal MySQL expression result)
-*/
-
-Item_result Field::result_merge_type(enum_field_types field_type)
-{
- DBUG_ASSERT(real_type_to_type(field_type) < FIELDTYPE_TEAR_FROM ||
- real_type_to_type(field_type) > FIELDTYPE_TEAR_TO);
- return field_types_result_type[field_type2index(field_type)];
-}
/*****************************************************************************
Static help functions
*****************************************************************************/
-/**
- Check whether a field type can be partially indexed by a key.
-
- This is a static method, rather than a virtual function, because we need
- to check the type of a non-Field in mysql_alter_table().
-
- @param type field type
-
- @retval
- TRUE Type can have a prefixed key
- @retval
- FALSE Type can not have a prefixed key
-*/
-
-bool Field::type_can_have_key_part(enum enum_field_types type)
-{
- switch (type) {
- case MYSQL_TYPE_VARCHAR:
- 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:
- return TRUE;
- default:
- return FALSE;
- }
-}
-
void Field::make_sort_key(uchar *buff,uint length)
{
@@ -1278,7 +1183,7 @@ bool Field::test_if_equality_guarantees_uniqueness(const Item *item) const
bool Field::can_be_substituted_to_equal_item(const Context &ctx,
const Item_equal *item_equal)
{
- DBUG_ASSERT(item_equal->compare_type() != STRING_RESULT);
+ DBUG_ASSERT(item_equal->compare_type_handler()->cmp_type() != STRING_RESULT);
DBUG_ASSERT(cmp_type() != STRING_RESULT);
switch (ctx.subst_constraint()) {
case ANY_SUBST:
@@ -1291,7 +1196,7 @@ bool Field::can_be_substituted_to_equal_item(const Context &ctx,
Items don't know the context they are in and there are functions like
IF (<hex_string>, 'yes', 'no').
*/
- return ctx.compare_type() == item_equal->compare_type();
+ return ctx.compare_type_handler() == item_equal->compare_type_handler();
case IDENTITY_SUBST:
return true;
}
@@ -1324,19 +1229,19 @@ bool Field::can_optimize_group_min_max(const Item_bool_func *cond,
/*
- This covers all numeric types, ENUM, SET, BIT
+ This covers all numeric types, BIT
*/
bool Field::can_optimize_range(const Item_bool_func *cond,
const Item *item,
bool is_eq_func) const
{
DBUG_ASSERT(cmp_type() != TIME_RESULT); // Handled in Field_temporal
- DBUG_ASSERT(cmp_type() != STRING_RESULT); // Handled in Field_longstr
+ DBUG_ASSERT(cmp_type() != STRING_RESULT); // Handled in Field_str descendants
return item->cmp_type() != TIME_RESULT;
}
-int Field::store_hex_hybrid(const char *str, uint length)
+int Field::store_hex_hybrid(const char *str, size_t length)
{
DBUG_ASSERT(result_type() != STRING_RESULT);
ulonglong nr;
@@ -1428,6 +1333,45 @@ void Field::load_data_set_value(const char *pos, uint length,
}
+bool Field::sp_prepare_and_store_item(THD *thd, Item **value)
+{
+ DBUG_ENTER("Field::sp_prepare_and_store_item");
+ DBUG_ASSERT(value);
+
+ Item *expr_item;
+
+ if (!(expr_item= thd->sp_prepare_func_item(value, 1)))
+ goto error;
+
+ /*
+ expr_item is now fixed, it's safe to call cmp_type()
+ */
+ if (expr_item->cmp_type() == ROW_RESULT)
+ {
+ my_error(ER_OPERAND_COLUMNS, MYF(0), 1);
+ goto error;
+ }
+
+ /* Save the value in the field. Convert the value if needed. */
+
+ expr_item->save_in_field(this, 0);
+
+ if (likely(!thd->is_error()))
+ DBUG_RETURN(false);
+
+error:
+ /*
+ In case of error during evaluation, leave the result field set to NULL.
+ Sic: we can't do it in the beginning of the function because the
+ result field might be needed for its own re-evaluation, e.g. case of
+ set x = x + 1;
+ */
+ set_null();
+ DBUG_ASSERT(thd->is_error());
+ DBUG_RETURN(true);
+}
+
+
void Field::error_generated_column_function_is_not_allowed(THD *thd,
bool error) const
{
@@ -1439,7 +1383,7 @@ void Field::error_generated_column_function_is_not_allowed(THD *thd,
my_error(ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED,
MYF(error ? 0 : ME_JUST_WARNING),
tmp.c_ptr_safe(), vcol_info->get_vcol_type_name(),
- const_cast<const char*>(field_name));
+ const_cast<const char*>(field_name.str));
}
@@ -1473,7 +1417,7 @@ bool Field::check_vcol_sql_mode_dependency(THD *thd, vcol_init_mode mode) const
*/
Field_num::Field_num(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg, utype unireg_check_arg,
- const char *field_name_arg,
+ const LEX_CSTRING *field_name_arg,
uint8 dec_arg, bool zero_arg, bool unsigned_arg)
:Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg),
@@ -1492,7 +1436,7 @@ void Field_num::prepend_zeros(String *value) const
if ((diff= (int) (field_length - value->length())) > 0)
{
const bool error= value->realloc(field_length);
- if (!error)
+ if (likely(!error))
{
bmove_upp((uchar*) value->ptr()+field_length,
(uchar*) value->ptr()+value->length(),
@@ -1520,7 +1464,7 @@ Item *Field_num::get_equal_zerofill_const_item(THD *thd, const Context &ctx,
break;
}
DBUG_ASSERT(const_item->const_item());
- DBUG_ASSERT(ctx.compare_type() != STRING_RESULT);
+ DBUG_ASSERT(ctx.compare_type_handler()->cmp_type() != STRING_RESULT);
return const_item;
}
@@ -1635,8 +1579,7 @@ Value_source::Converter_string_to_number::check_edom_and_truncation(THD *thd,
int Field_num::check_edom_and_important_data_truncation(const char *type,
bool edom,
CHARSET_INFO *cs,
- const char *str,
- uint length,
+ const char *str, size_t length,
const char *end)
{
/* Test if we get an empty string or garbage */
@@ -1658,7 +1601,7 @@ int Field_num::check_edom_and_important_data_truncation(const char *type,
int Field_num::check_edom_and_truncation(const char *type, bool edom,
CHARSET_INFO *cs,
- const char *str, uint length,
+ const char *str, size_t length,
const char *end)
{
int rc= check_edom_and_important_data_truncation(type, edom,
@@ -1692,7 +1635,7 @@ int Field_num::check_edom_and_truncation(const char *type, bool edom,
1 error
*/
-bool Field_num::get_int(CHARSET_INFO *cs, const char *from, uint len,
+bool Field_num::get_int(CHARSET_INFO *cs, const char *from, size_t len,
longlong *rnd, ulonglong unsigned_max,
longlong signed_min, longlong signed_max)
{
@@ -1725,7 +1668,7 @@ bool Field_num::get_int(CHARSET_INFO *cs, const char *from, uint len,
goto out_of_range;
}
}
- if (get_thd()->count_cuted_fields &&
+ if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION &&
check_int(cs, from, len, end, error))
return 1;
return 0;
@@ -1736,17 +1679,17 @@ out_of_range:
}
-double Field_real::get_double(const char *str, uint length, CHARSET_INFO *cs,
+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);
- if (*error)
+ if (unlikely(*error))
{
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
*error= 1;
}
- else if (get_thd()->count_cuted_fields &&
+ else if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION &&
check_edom_and_truncation("double", str == end,
cs, str, length, end))
*error= 1;
@@ -1809,9 +1752,10 @@ String *Field::val_int_as_str(String *val_buffer, bool unsigned_val)
/// This is used as a table name when the table structure is not set up
Field::Field(uchar *ptr_arg,uint32 length_arg,uchar *null_ptr_arg,
uchar null_bit_arg,
- utype unireg_check_arg, const char *field_name_arg)
- :ptr(ptr_arg), null_ptr(null_ptr_arg), table(0), orig_table(0),
- table_name(0), field_name(field_name_arg), option_list(0),
+ utype unireg_check_arg, const LEX_CSTRING *field_name_arg)
+ :ptr(ptr_arg), invisible(VISIBLE),
+ null_ptr(null_ptr_arg), table(0), orig_table(0),
+ table_name(0), field_name(*field_name_arg), option_list(0),
option_struct(0), key_start(0), part_of_key(0),
part_of_key_not_clustered(0), part_of_sortkey(0),
unireg_check(unireg_check_arg), field_length(length_arg),
@@ -1920,7 +1864,7 @@ bool Field::compatible_field_size(uint field_metadata,
}
-int Field::store(const char *to, uint length, CHARSET_INFO *cs,
+int Field::store(const char *to, size_t length, CHARSET_INFO *cs,
enum_check_fields check_level)
{
int res;
@@ -1933,30 +1877,11 @@ int Field::store(const char *to, uint length, CHARSET_INFO *cs,
}
-static int timestamp_to_TIME(THD *thd, MYSQL_TIME *ltime, my_time_t ts,
- ulong sec_part, ulonglong fuzzydate)
-{
- thd->time_zone_used= 1;
- if (ts == 0 && sec_part == 0)
- {
- if (fuzzydate & TIME_NO_ZERO_DATE)
- return 1;
- set_zero_time(ltime, MYSQL_TIMESTAMP_DATETIME);
- }
- else
- {
- thd->variables.time_zone->gmt_sec_to_TIME(ltime, ts);
- ltime->second_part= sec_part;
- }
- return 0;
-}
-
-
int Field::store_timestamp(my_time_t ts, ulong sec_part)
{
MYSQL_TIME ltime;
THD *thd= get_thd();
- timestamp_to_TIME(thd, &ltime, ts, sec_part, 0);
+ thd->timestamp_to_TIME(&ltime, ts, sec_part, 0);
return store_time_dec(&ltime, decimals());
}
@@ -2074,12 +1999,12 @@ void Field_num::add_zerofill_and_unsigned(String &res) const
}
-void Field::make_field(Send_field *field)
+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;
- if (orig_table->pos_in_table_list &&
+ 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);
@@ -2096,10 +2021,9 @@ void Field::make_field(Send_field *field)
else
{
field->table_name= "";
- field->org_col_name= "";
+ field->org_col_name= empty_clex_str;
}
field->col_name= field_name;
- field->charsetnr= charset()->number;
field->length=field_length;
field->type=type();
field->flags=table->maybe_null ? (flags & ~NOT_NULL_FLAG) : flags;
@@ -2165,7 +2089,7 @@ longlong Field::convert_decimal2longlong(const my_decimal *val,
!=0 error
*/
-int Field_num::store_decimal(const my_decimal *val)
+int Field_int::store_decimal(const my_decimal *val)
{
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int err= 0;
@@ -2188,37 +2112,79 @@ int Field_num::store_decimal(const my_decimal *val)
pointer to decimal buffer with value of field
*/
-my_decimal* Field_num::val_decimal(my_decimal *decimal_value)
+my_decimal* Field_int::val_decimal(my_decimal *decimal_value)
{
ASSERT_COLUMN_MARKED_FOR_READ;
- DBUG_ASSERT(result_type() == INT_RESULT);
longlong nr= val_int();
int2my_decimal(E_DEC_FATAL_ERROR, nr, unsigned_flag, decimal_value);
return decimal_value;
}
-bool Field_num::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
+bool Field_int::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
{
ASSERT_COLUMN_MARKED_FOR_READ;
longlong nr= val_int();
bool neg= !(flags & UNSIGNED_FLAG) && nr < 0;
return int_to_datetime_with_warn(neg, neg ? -nr : nr, ltime, fuzzydate,
- table->s, field_name);
+ table->s, field_name.str);
+}
+
+
+bool Field_vers_trx_id::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate, ulonglong trx_id)
+{
+ ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(ltime);
+ if (!table || !table->s)
+ return true;
+ DBUG_ASSERT(table->versioned(VERS_TRX_ID) ||
+ (table->versioned() && table->s->table_category == TABLE_CATEGORY_TEMPORARY));
+ if (!trx_id)
+ return true;
+
+ THD *thd= get_thd();
+ DBUG_ASSERT(thd);
+ if (trx_id == ULONGLONG_MAX)
+ {
+ thd->variables.time_zone->gmt_sec_to_TIME(ltime, TIMESTAMP_MAX_VALUE);
+ ltime->second_part= TIME_MAX_SECOND_PART;
+ return false;
+ }
+ if (cached == trx_id)
+ {
+ *ltime= cache;
+ return false;
+ }
+
+ TR_table trt(thd);
+ bool found= trt.query(trx_id);
+ if (found)
+ {
+ trt[TR_table::FLD_COMMIT_TS]->get_date(&cache, fuzzydate);
+ *ltime= cache;
+ cached= trx_id;
+ return false;
+ }
+
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_VERS_NO_TRX_ID, ER_THD(thd, ER_VERS_NO_TRX_ID),
+ (longlong) trx_id);
+ return true;
}
Field_str::Field_str(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg, utype unireg_check_arg,
- const char *field_name_arg, CHARSET_INFO *charset_arg)
+ const LEX_CSTRING *field_name_arg,
+ const DTCollation &collation)
:Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg)
{
- field_charset= charset_arg;
- if (charset_arg->state & MY_CS_BINSORT)
+ field_charset= collation.collation;
+ if (collation.collation->state & MY_CS_BINSORT)
flags|=BINARY_FLAG;
- field_derivation= DERIVATION_IMPLICIT;
- field_repertoire= my_charset_repertoire(charset_arg);
+ field_derivation= collation.derivation;
+ field_repertoire= collation.repertoire;
}
@@ -2251,11 +2217,11 @@ bool Field_str::test_if_equality_guarantees_uniqueness(const Item *item) const
bool Field_str::can_be_substituted_to_equal_item(const Context &ctx,
const Item_equal *item_equal)
{
- DBUG_ASSERT(item_equal->compare_type() == STRING_RESULT);
+ DBUG_ASSERT(item_equal->compare_type_handler()->cmp_type() == STRING_RESULT);
switch (ctx.subst_constraint()) {
case ANY_SUBST:
- return ctx.compare_type() == item_equal->compare_type() &&
- (ctx.compare_type() != STRING_RESULT ||
+ return ctx.compare_type_handler() == item_equal->compare_type_handler() &&
+ (ctx.compare_type_handler()->cmp_type() != STRING_RESULT ||
ctx.compare_collation() == item_equal->compare_collation());
case IDENTITY_SUBST:
return ((charset()->state & MY_CS_BINSORT) &&
@@ -2265,9 +2231,9 @@ bool Field_str::can_be_substituted_to_equal_item(const Context &ctx,
}
-void Field_num::make_field(Send_field *field)
+void Field_num::make_send_field(Send_field *field)
{
- Field::make_field(field);
+ Field::make_send_field(field);
field->decimals= dec;
}
@@ -2363,7 +2329,7 @@ bool Field::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
Needs to be changed if/when we want to support different time formats.
*/
-int Field::store_time_dec(MYSQL_TIME *ltime, uint dec)
+int Field::store_time_dec(const MYSQL_TIME *ltime, uint dec)
{
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
char buff[MAX_DATE_STRING_REP_LENGTH];
@@ -2374,7 +2340,7 @@ int Field::store_time_dec(MYSQL_TIME *ltime, uint dec)
}
-bool Field::optimize_range(uint idx, uint part)
+bool Field::optimize_range(uint idx, uint part) const
{
return MY_TEST(table->file->index_flags(idx, part, 1) & HA_READ_RANGE);
}
@@ -2399,8 +2365,11 @@ Field *Field::make_new_field(MEM_ROOT *root, TABLE *new_table,
*/
tmp->unireg_check= Field::NONE;
tmp->flags&= (NOT_NULL_FLAG | BLOB_FLAG | UNSIGNED_FLAG |
- ZEROFILL_FLAG | BINARY_FLAG | ENUM_FLAG | SET_FLAG);
+ ZEROFILL_FLAG | BINARY_FLAG | ENUM_FLAG | SET_FLAG |
+ VERS_SYS_START_FLAG | VERS_SYS_END_FLAG |
+ VERS_UPDATE_UNVERSIONED_FLAG);
tmp->reset_fields();
+ tmp->invisible= VISIBLE;
return tmp;
}
@@ -2480,6 +2449,51 @@ void Field_null::sql_type(String &res) const
/****************************************************************************
+ Field_row, e.g. for ROW-type SP variables
+****************************************************************************/
+
+Field_row::~Field_row()
+{
+ delete m_table;
+}
+
+
+bool Field_row::sp_prepare_and_store_item(THD *thd, Item **value)
+{
+ DBUG_ENTER("Field_row::sp_prepare_and_store_item");
+
+ if (value[0]->type() == Item::NULL_ITEM)
+ {
+ /*
+ We're in a auto-generated sp_inst_set, to assign
+ the explicit default NULL value to a ROW variable.
+ */
+ m_table->set_all_fields_to_null();
+ DBUG_RETURN(false);
+ }
+
+ /**
+ - In case if we're assigning a ROW variable from another ROW variable,
+ value[0] points to Item_splocal. sp_fix_func_item() will return the
+ fixed underlying Item_field pointing to Field_row.
+ - In case if we're assigning from a ROW() value, src and value[0] will
+ point to the same Item_row.
+ */
+ Item *src;
+ if (!(src= thd->sp_fix_func_item(value)) ||
+ src->cmp_type() != ROW_RESULT ||
+ src->cols() != m_table->s->fields)
+ {
+ my_error(ER_OPERAND_COLUMNS, MYF(0), m_table->s->fields);
+ m_table->set_all_fields_to_null();
+ DBUG_RETURN(true);
+ }
+
+ DBUG_RETURN(m_table->sp_set_all_fields_from_item(thd, src));
+}
+
+
+/****************************************************************************
Functions for the Field_decimal class
This is an number stored as a pre-space (or pre-zero) string
****************************************************************************/
@@ -2530,7 +2544,7 @@ void Field_decimal::overflow(bool negative)
}
-int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs)
+int Field_decimal::store(const char *from_arg, size_t len, CHARSET_INFO *cs)
{
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
char buff[STRING_BUFFER_USUAL_SIZE];
@@ -2674,7 +2688,7 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs)
it makes the code easier to read.
*/
- if (get_thd()->count_cuted_fields)
+ if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION)
{
// Skip end spaces
for (;from != end && my_isspace(&my_charset_bin, *from); from++) ;
@@ -2844,7 +2858,8 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs)
{
if (pos == right_wall)
{
- if (get_thd()->count_cuted_fields && !is_cuted_fields_incr)
+ if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION &&
+ !is_cuted_fields_incr)
break; // Go on below to see if we lose non zero digits
return 0;
}
@@ -2910,7 +2925,6 @@ int Field_decimal::store(double nr)
return 1;
}
- uint i;
size_t length;
uchar fyllchar,*to;
char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE];
@@ -2926,7 +2940,7 @@ int Field_decimal::store(double nr)
else
{
to=ptr;
- for (i=field_length-length ; i-- > 0 ;)
+ for (size_t i=field_length-length ; i-- > 0 ;)
*to++ = fyllchar;
memcpy(to,buff,length);
return 0;
@@ -3094,7 +3108,7 @@ Field *Field_decimal::make_new_field(MEM_ROOT *root, TABLE *new_table,
Field *field= new (root) Field_new_decimal(NULL, field_length,
maybe_null() ? (uchar*) "" : 0, 0,
- NONE, field_name,
+ NONE, &field_name,
dec, flags & ZEROFILL_FLAG,
unsigned_flag);
if (field)
@@ -3107,86 +3121,29 @@ Field *Field_decimal::make_new_field(MEM_ROOT *root, TABLE *new_table,
** Field_new_decimal
****************************************************************************/
+static uint get_decimal_precision(uint len, uint8 dec, bool unsigned_val)
+{
+ uint precision= my_decimal_length_to_precision(len, dec, unsigned_val);
+ return MY_MIN(precision, DECIMAL_MAX_PRECISION);
+}
+
Field_new_decimal::Field_new_decimal(uchar *ptr_arg,
uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
enum utype unireg_check_arg,
- const char *field_name_arg,
+ const LEX_CSTRING *field_name_arg,
uint8 dec_arg,bool zero_arg,
bool unsigned_arg)
:Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg, dec_arg, zero_arg, unsigned_arg)
{
- precision= my_decimal_length_to_precision(len_arg, dec_arg, unsigned_arg);
- set_if_smaller(precision, DECIMAL_MAX_PRECISION);
- DBUG_ASSERT((precision <= DECIMAL_MAX_PRECISION) &&
- (dec <= DECIMAL_MAX_SCALE));
- bin_size= my_decimal_get_binary_size(precision, dec);
-}
-
-
-Field_new_decimal::Field_new_decimal(uint32 len_arg,
- bool maybe_null_arg,
- const char *name,
- uint8 dec_arg,
- bool unsigned_arg)
- :Field_num((uchar*) 0, len_arg,
- maybe_null_arg ? (uchar*) "": 0, 0,
- NONE, name, dec_arg, 0, unsigned_arg)
-{
- precision= my_decimal_length_to_precision(len_arg, dec_arg, unsigned_arg);
- set_if_smaller(precision, DECIMAL_MAX_PRECISION);
+ precision= get_decimal_precision(len_arg, dec_arg, unsigned_arg);
DBUG_ASSERT((precision <= DECIMAL_MAX_PRECISION) &&
(dec <= DECIMAL_MAX_SCALE));
bin_size= my_decimal_get_binary_size(precision, dec);
}
-Field *Field_new_decimal::create_from_item(MEM_ROOT *mem_root, Item *item)
-{
- uint8 dec= item->decimals;
- uint8 intg= item->decimal_precision() - dec;
- uint32 len= item->max_char_length();
-
- DBUG_ASSERT (item->result_type() == DECIMAL_RESULT);
-
- /*
- Trying to put too many digits overall in a DECIMAL(prec,dec)
- will always throw a warning. We must limit dec to
- DECIMAL_MAX_SCALE however to prevent an assert() later.
- */
-
- if (dec > 0)
- {
- signed int overflow;
-
- dec= MY_MIN(dec, DECIMAL_MAX_SCALE);
-
- /*
- If the value still overflows the field with the corrected dec,
- we'll throw out decimals rather than integers. This is still
- bad and of course throws a truncation warning.
- +1: for decimal point
- */
-
- const int required_length=
- my_decimal_precision_to_length(intg + dec, dec,
- item->unsigned_flag);
-
- overflow= required_length - len;
-
- if (overflow > 0)
- dec= MY_MAX(0, dec - overflow); // too long, discard fract
- else
- /* Corrected value fits. */
- len= required_length;
- }
- return new (mem_root)
- Field_new_decimal(len, item->maybe_null, item->name,
- dec, item->unsigned_flag);
-}
-
-
int Field_new_decimal::reset(void)
{
store_value(&decimal_zero);
@@ -3266,7 +3223,7 @@ bool Field_new_decimal::store_value(const my_decimal *decimal_value,
*native_error= my_decimal2binary(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW,
decimal_value, ptr, precision, dec);
- if (*native_error == E_DEC_OVERFLOW)
+ if (unlikely(*native_error == E_DEC_OVERFLOW))
{
my_decimal buff;
DBUG_PRINT("info", ("overflow"));
@@ -3285,13 +3242,13 @@ bool Field_new_decimal::store_value(const my_decimal *decimal_value)
{
int native_error;
bool rc= store_value(decimal_value, &native_error);
- if (!rc && native_error == E_DEC_TRUNCATED)
+ if (unlikely(!rc && native_error == E_DEC_TRUNCATED))
set_note(WARN_DATA_TRUNCATED, 1);
return rc;
}
-int Field_new_decimal::store(const char *from, uint length,
+int Field_new_decimal::store(const char *from, size_t length,
CHARSET_INFO *charset_arg)
{
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
@@ -3316,7 +3273,7 @@ int Field_new_decimal::store(const char *from, uint length,
DBUG_RETURN(1);
}
- if (thd->count_cuted_fields)
+ if (thd->count_cuted_fields > CHECK_FIELD_EXPRESSION)
{
if (check_edom_and_important_data_truncation("decimal",
err && err != E_DEC_TRUNCATED,
@@ -3361,7 +3318,7 @@ int Field_new_decimal::store(const char *from, uint length,
- in err2: store_value() truncated 1.123 to 1.12, e.g. for DECIMAL(10,2)
Also, we send a note if a string had some trailing spaces: '1.12 '
*/
- if (thd->count_cuted_fields &&
+ if (thd->count_cuted_fields > CHECK_FIELD_EXPRESSION &&
(err == E_DEC_TRUNCATED ||
err2 == E_DEC_TRUNCATED ||
end < from + length))
@@ -3430,7 +3387,7 @@ int Field_new_decimal::store_decimal(const my_decimal *decimal_value)
}
-int Field_new_decimal::store_time_dec(MYSQL_TIME *ltime, uint dec_arg)
+int Field_new_decimal::store_time_dec(const MYSQL_TIME *ltime, uint dec_arg)
{
my_decimal decimal_value;
return store_value(date2my_decimal(ltime, &decimal_value));
@@ -3497,7 +3454,8 @@ bool Field_new_decimal::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
{
my_decimal value;
return decimal_to_datetime_with_warn(val_decimal(&value),
- ltime, fuzzydate, table->s, field_name);
+ ltime, fuzzydate, table->s,
+ field_name.str);
}
@@ -3533,7 +3491,7 @@ void Field_new_decimal::sql_type(String &str) const
@returns number of bytes written to metadata_ptr
*/
-int Field_new_decimal::do_save_field_metadata(uchar *metadata_ptr)
+int Field_new_decimal::save_field_metadata(uchar *metadata_ptr)
{
*metadata_ptr= precision;
*(metadata_ptr + 1)= decimals();
@@ -3578,7 +3536,7 @@ bool Field_new_decimal::compatible_field_size(uint field_metadata,
uint Field_new_decimal::is_equal(Create_field *new_field)
{
- return ((new_field->sql_type == real_type()) &&
+ return ((new_field->type_handler() == type_handler()) &&
((new_field->flags & UNSIGNED_FLAG) ==
(uint) (flags & UNSIGNED_FLAG)) &&
((new_field->flags & AUTO_INCREMENT_FLAG) ==
@@ -3666,7 +3624,8 @@ Item *Field_new_decimal::get_equal_const_item(THD *thd, const Context &ctx,
Field_time::get_equal_const_item().
*/
my_decimal_round(E_DEC_FATAL_ERROR, val, decimals(), true, &val_buffer2);
- return new (thd->mem_root) Item_decimal(thd, field_name, &val_buffer2,
+ return new (thd->mem_root) Item_decimal(thd, field_name.str,
+ &val_buffer2,
decimals(), field_length);
}
break;
@@ -3677,7 +3636,7 @@ Item *Field_new_decimal::get_equal_const_item(THD *thd, const Context &ctx,
}
-int Field_num::store_time_dec(MYSQL_TIME *ltime, uint dec_arg)
+int Field_int::store_time_dec(const MYSQL_TIME *ltime, uint dec_arg)
{
longlong v= TIME_to_ulonglong(ltime);
if (ltime->neg == 0)
@@ -3690,7 +3649,7 @@ int Field_num::store_time_dec(MYSQL_TIME *ltime, uint dec_arg)
** tiny int
****************************************************************************/
-int Field_tiny::store(const char *from,uint len,CHARSET_INFO *cs)
+int Field_tiny::store(const char *from,size_t len,CHARSET_INFO *cs)
{
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int error;
@@ -3812,24 +3771,8 @@ String *Field_tiny::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
ASSERT_COLUMN_MARKED_FOR_READ;
- CHARSET_INFO *cs= &my_charset_numeric;
- uint length;
- uint mlength=MY_MAX(field_length+1,5*cs->mbmaxlen);
- val_buffer->alloc(mlength);
- char *to=(char*) val_buffer->ptr();
-
- if (unsigned_flag)
- length= (uint) cs->cset->long10_to_str(cs,to,mlength, 10,
- (long) *ptr);
- else
- length= (uint) cs->cset->long10_to_str(cs,to,mlength,-10,
- (long) *((signed char*) ptr));
-
- val_buffer->length(length);
- if (zerofill)
- prepend_zeros(val_buffer);
- val_buffer->set_charset(cs);
- return val_buffer;
+ long nr= unsigned_flag ? (long) ptr[0] : (long) ((signed char*) ptr)[0];
+ return val_str_from_long(val_buffer, 5, -10, nr);
}
bool Field_tiny::send_binary(Protocol *protocol)
@@ -3866,7 +3809,7 @@ void Field_tiny::sql_type(String &res) const
Field type short int (2 byte)
****************************************************************************/
-int Field_short::store(const char *from,uint len,CHARSET_INFO *cs)
+int Field_short::store(const char *from,size_t len,CHARSET_INFO *cs)
{
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int store_tmp;
@@ -3994,24 +3937,9 @@ String *Field_short::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
ASSERT_COLUMN_MARKED_FOR_READ;
- CHARSET_INFO *cs= &my_charset_numeric;
- uint length;
- uint mlength=MY_MAX(field_length+1,7*cs->mbmaxlen);
- val_buffer->alloc(mlength);
- char *to=(char*) val_buffer->ptr();
- short j;
- j=sint2korr(ptr);
-
- if (unsigned_flag)
- length=(uint) cs->cset->long10_to_str(cs, to, mlength, 10,
- (long) (uint16) j);
- else
- length=(uint) cs->cset->long10_to_str(cs, to, mlength,-10, (long) j);
- val_buffer->length(length);
- if (zerofill)
- prepend_zeros(val_buffer);
- val_buffer->set_charset(cs);
- return val_buffer;
+ short j= sint2korr(ptr);
+ long nr= unsigned_flag ? (long) (unsigned short) j : (long) j;
+ return val_str_from_long(val_buffer, 7, -10, nr);
}
@@ -4055,7 +3983,7 @@ void Field_short::sql_type(String &res) const
Field type medium int (3 byte)
****************************************************************************/
-int Field_medium::store(const char *from,uint len,CHARSET_INFO *cs)
+int Field_medium::store(const char *from,size_t len,CHARSET_INFO *cs)
{
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int store_tmp;
@@ -4184,14 +4112,21 @@ String *Field_medium::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
ASSERT_COLUMN_MARKED_FOR_READ;
+ long nr= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr);
+ return val_str_from_long(val_buffer, 10, -10, nr);
+}
+
+
+String *Field_int::val_str_from_long(String *val_buffer,
+ uint max_char_length,
+ int radix, long nr)
+{
CHARSET_INFO *cs= &my_charset_numeric;
uint length;
- uint mlength=MY_MAX(field_length+1,10*cs->mbmaxlen);
+ uint mlength= MY_MAX(field_length + 1, max_char_length * cs->mbmaxlen);
val_buffer->alloc(mlength);
char *to=(char*) val_buffer->ptr();
- long j= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr);
-
- length=(uint) cs->cset->long10_to_str(cs,to,mlength,-10,j);
+ length= (uint) cs->cset->long10_to_str(cs, to, mlength, radix, nr);
val_buffer->length(length);
if (zerofill)
prepend_zeros(val_buffer); /* purecov: inspected */
@@ -4246,7 +4181,7 @@ void Field_medium::sql_type(String &res) const
** long int
****************************************************************************/
-int Field_long::store(const char *from,uint len,CHARSET_INFO *cs)
+int Field_long::store(const char *from,size_t len,CHARSET_INFO *cs)
{
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
long store_tmp;
@@ -4297,7 +4232,7 @@ int Field_long::store(double nr)
else
res=(int32) (longlong) nr;
}
- if (error)
+ if (unlikely(error))
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
int4store(ptr,res);
@@ -4343,7 +4278,7 @@ int Field_long::store(longlong nr, bool unsigned_val)
else
res=(int32) nr;
}
- if (error)
+ if (unlikely(error))
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
int4store(ptr,res);
@@ -4369,27 +4304,13 @@ longlong Field_long::val_int(void)
return unsigned_flag ? (longlong) (uint32) j : (longlong) j;
}
+
String *Field_long::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
ASSERT_COLUMN_MARKED_FOR_READ;
- CHARSET_INFO *cs= &my_charset_numeric;
- uint length;
- uint mlength=MY_MAX(field_length+1,12*cs->mbmaxlen);
- val_buffer->alloc(mlength);
- char *to=(char*) val_buffer->ptr();
- int32 j;
- j=sint4korr(ptr);
-
- if (unsigned_flag)
- length=cs->cset->long10_to_str(cs,to,mlength, 10,(long) (uint32)j);
- else
- length=cs->cset->long10_to_str(cs,to,mlength,-10,(long) j);
- val_buffer->length(length);
- if (zerofill)
- prepend_zeros(val_buffer);
- val_buffer->set_charset(cs);
- return val_buffer;
+ long nr= unsigned_flag ? (long) uint4korr(ptr) : sint4korr(ptr);
+ return val_str_from_long(val_buffer, 12, unsigned_flag ? 10 : -10, nr);
}
@@ -4433,7 +4354,7 @@ void Field_long::sql_type(String &res) const
Field type longlong int (8 bytes)
****************************************************************************/
-int Field_longlong::store(const char *from,uint len,CHARSET_INFO *cs)
+int Field_longlong::store(const char *from,size_t len,CHARSET_INFO *cs)
{
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int error= 0;
@@ -4441,12 +4362,12 @@ int Field_longlong::store(const char *from,uint len,CHARSET_INFO *cs)
ulonglong tmp;
tmp= cs->cset->strntoull10rnd(cs,from,len,unsigned_flag,&end,&error);
- if (error == MY_ERRNO_ERANGE)
+ if (unlikely(error == MY_ERRNO_ERANGE))
{
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
- else if (get_thd()->count_cuted_fields &&
+ else if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION &&
check_int(cs, from, len, end, error))
error= 1;
else
@@ -4461,7 +4382,7 @@ int Field_longlong::store(double nr)
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
Converter_double_to_longlong conv(nr, unsigned_flag);
- if (conv.error())
+ if (unlikely(conv.error()))
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
int8store(ptr, conv.result());
@@ -4474,7 +4395,7 @@ int Field_longlong::store(longlong nr, bool unsigned_val)
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int error= 0;
- if (nr < 0) // Only possible error
+ if (unlikely(nr < 0)) // Only possible error
{
/*
if field is unsigned and value is signed (< 0) or
@@ -4580,6 +4501,26 @@ void Field_longlong::sql_type(String &res) const
add_zerofill_and_unsigned(res);
}
+void Field_longlong::set_max()
+{
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ set_notnull();
+ int8store(ptr, unsigned_flag ? ULONGLONG_MAX : LONGLONG_MAX);
+}
+
+bool Field_longlong::is_max()
+{
+ ASSERT_COLUMN_MARKED_FOR_READ;
+ if (unsigned_flag)
+ {
+ ulonglong j;
+ j= uint8korr(ptr);
+ return j == ULONGLONG_MAX;
+ }
+ longlong j;
+ j= sint8korr(ptr);
+ return j == LONGLONG_MAX;
+}
/*
Floating-point numbers
@@ -4589,7 +4530,7 @@ void Field_longlong::sql_type(String &res) const
single precision float
****************************************************************************/
-int Field_float::store(const char *from,uint len,CHARSET_INFO *cs)
+int Field_float::store(const char *from,size_t len,CHARSET_INFO *cs)
{
int error;
Field_float::store(get_double(from, len, cs, &error));
@@ -4603,7 +4544,7 @@ int Field_float::store(double nr)
int error= truncate_double(&nr, field_length,
not_fixed ? NOT_FIXED_DEC : dec,
unsigned_flag, FLT_MAX);
- if (error)
+ if (unlikely(error))
{
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
if (error < 0) // Wrong double value
@@ -4647,34 +4588,15 @@ String *Field_float::val_str(String *val_buffer,
{
ASSERT_COLUMN_MARKED_FOR_READ;
DBUG_ASSERT(!zerofill || field_length <= MAX_FIELD_CHARLENGTH);
- float nr;
- float4get(nr,ptr);
- uint to_length= 70;
- if (val_buffer->alloc(to_length))
+ if (Float(ptr).to_string(val_buffer, dec))
{
my_error(ER_OUT_OF_RESOURCES, MYF(0));
return val_buffer;
}
- char *to=(char*) val_buffer->ptr();
- size_t len;
-
- if (dec >= FLOATING_POINT_DECIMALS)
- len= my_gcvt(nr, MY_GCVT_ARG_FLOAT, to_length - 1, to, NULL);
- else
- {
- /*
- We are safe here because the buffer length is 70, and
- fabs(float) < 10^39, dec < FLOATING_POINT_DECIMALS. So the resulting string
- will be not longer than 69 chars + terminating '\0'.
- */
- len= my_fcvt(nr, dec, to, NULL);
- }
- val_buffer->length((uint) len);
if (zerofill)
prepend_zeros(val_buffer);
- val_buffer->set_charset(&my_charset_numeric);
return val_buffer;
}
@@ -4741,7 +4663,7 @@ bool Field_float::send_binary(Protocol *protocol)
@returns number of bytes written to metadata_ptr
*/
-int Field_float::do_save_field_metadata(uchar *metadata_ptr)
+int Field_float::save_field_metadata(uchar *metadata_ptr)
{
*metadata_ptr= pack_length();
return 1;
@@ -4768,7 +4690,7 @@ void Field_float::sql_type(String &res) const
double precision floating point numbers
****************************************************************************/
-int Field_double::store(const char *from,uint len,CHARSET_INFO *cs)
+int Field_double::store(const char *from,size_t len,CHARSET_INFO *cs)
{
int error;
Field_double::store(get_double(from, len, cs, &error));
@@ -4782,7 +4704,7 @@ int Field_double::store(double nr)
int error= truncate_double(&nr, field_length,
not_fixed ? NOT_FIXED_DEC : dec,
unsigned_flag, DBL_MAX);
- if (error)
+ if (unlikely(error))
{
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
if (error < 0) // Wrong double value
@@ -4932,7 +4854,7 @@ int Field_real::store_decimal(const my_decimal *dm)
return store(dbl);
}
-int Field_real::store_time_dec(MYSQL_TIME *ltime, uint dec_arg)
+int Field_real::store_time_dec(const MYSQL_TIME *ltime, uint dec_arg)
{
return store(TIME_to_double(ltime));
}
@@ -4950,7 +4872,7 @@ double Field_double::val_real(void)
longlong Field_double::val_int_from_real(bool want_unsigned_result)
{
Converter_double_to_longlong conv(val_real(), want_unsigned_result);
- if (!want_unsigned_result && conv.error())
+ if (unlikely(!want_unsigned_result && conv.error()))
conv.push_warning(get_thd(), Field_double::val_real(), false);
return conv.result();
}
@@ -4969,7 +4891,7 @@ bool Field_real::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
ASSERT_COLUMN_MARKED_FOR_READ;
double nr= val_real();
return double_to_datetime_with_warn(nr, ltime, fuzzydate,
- table->s, field_name);
+ table->s, field_name.str);
}
@@ -5060,7 +4982,7 @@ void Field_double::sort_string(uchar *to,uint length __attribute__((unused)))
@returns number of bytes written to metadata_ptr
*/
-int Field_double::do_save_field_metadata(uchar *metadata_ptr)
+int Field_double::save_field_metadata(uchar *metadata_ptr)
{
*metadata_ptr= pack_length();
return 1;
@@ -5124,7 +5046,7 @@ void Field_double::sql_type(String &res) const
Field_timestamp::Field_timestamp(uchar *ptr_arg, uint32 len_arg,
uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg,
- const char *field_name_arg,
+ const LEX_CSTRING *field_name_arg,
TABLE_SHARE *share)
:Field_temporal(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg)
@@ -5188,7 +5110,7 @@ int Field_timestamp::store_TIME_with_warning(THD *thd, MYSQL_TIME *l_time,
timestamp= TIME_to_timestamp(thd, l_time, &conversion_error);
if (timestamp == 0 && l_time->second_part == 0)
conversion_error= ER_WARN_DATA_OUT_OF_RANGE;
- if (conversion_error)
+ if (unlikely(conversion_error))
{
set_datetime_warning(conversion_error,
str, MYSQL_TIMESTAMP_DATETIME, !error);
@@ -5215,7 +5137,14 @@ copy_or_convert_to_datetime(THD *thd, const MYSQL_TIME *from, MYSQL_TIME *to)
}
-int Field_timestamp::store_time_dec(MYSQL_TIME *ltime, uint dec)
+sql_mode_t Field_timestamp::sql_mode_for_timestamp(THD *thd) const
+{
+ // We don't want to store invalid or fuzzy datetime values in TIMESTAMP
+ return (thd->variables.sql_mode & MODE_NO_ZERO_DATE) | MODE_NO_ZERO_IN_DATE;
+}
+
+
+int Field_timestamp::store_time_dec(const MYSQL_TIME *ltime, uint dec)
{
int unused;
ErrConvTime str(ltime);
@@ -5223,14 +5152,12 @@ int Field_timestamp::store_time_dec(MYSQL_TIME *ltime, uint dec)
MYSQL_TIME l_time;
bool valid= !copy_or_convert_to_datetime(thd, ltime, &l_time) &&
!check_date(&l_time, pack_time(&l_time) != 0,
- (thd->variables.sql_mode & MODE_NO_ZERO_DATE) |
- MODE_NO_ZERO_IN_DATE, &unused);
-
+ sql_mode_for_timestamp(thd), &unused);
return store_TIME_with_warning(thd, &l_time, &str, false, valid);
}
-int Field_timestamp::store(const char *from,uint len,CHARSET_INFO *cs)
+int Field_timestamp::store(const char *from,size_t len,CHARSET_INFO *cs)
{
MYSQL_TIME l_time;
MYSQL_TIME_STATUS status;
@@ -5238,11 +5165,8 @@ int Field_timestamp::store(const char *from,uint len,CHARSET_INFO *cs)
ErrConvString str(from, len, cs);
THD *thd= get_thd();
- /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */
have_smth_to_conv= !str_to_datetime(cs, from, len, &l_time,
- (thd->variables.sql_mode &
- MODE_NO_ZERO_DATE) |
- MODE_NO_ZERO_IN_DATE, &status);
+ sql_mode_for_timestamp(thd), &status);
return store_TIME_with_warning(thd, &l_time, &str,
status.warnings, have_smth_to_conv);
}
@@ -5255,9 +5179,8 @@ int Field_timestamp::store(double nr)
ErrConvDouble str(nr);
THD *thd= get_thd();
- longlong tmp= double_to_datetime(nr, &l_time, (thd->variables.sql_mode &
- MODE_NO_ZERO_DATE) |
- MODE_NO_ZERO_IN_DATE, &error);
+ longlong tmp= double_to_datetime(nr, &l_time, sql_mode_for_timestamp(thd),
+ &error);
return store_TIME_with_warning(thd, &l_time, &str, error, tmp != -1);
}
@@ -5269,10 +5192,8 @@ int Field_timestamp::store(longlong nr, bool unsigned_val)
ErrConvInteger str(nr, unsigned_val);
THD *thd= get_thd();
- /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */
- longlong tmp= number_to_datetime(nr, 0, &l_time, (thd->variables.sql_mode &
- MODE_NO_ZERO_DATE) |
- MODE_NO_ZERO_IN_DATE, &error);
+ longlong tmp= number_to_datetime(nr, 0, &l_time, sql_mode_for_timestamp(thd),
+ &error);
return store_TIME_with_warning(thd, &l_time, &str, error, tmp != -1);
}
@@ -5398,7 +5319,7 @@ bool Field_timestamp::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
{
ulong sec_part;
my_time_t ts= get_timestamp(&sec_part);
- return timestamp_to_TIME(get_thd(), ltime, ts, sec_part, fuzzydate);
+ return get_thd()->timestamp_to_TIME(ltime, ts, sec_part, fuzzydate);
}
@@ -5535,47 +5456,17 @@ static longlong read_lowendian(const uchar *from, uint bytes)
}
}
-static void store_bigendian(ulonglong num, uchar *to, uint bytes)
-{
- switch(bytes) {
- case 1: mi_int1store(to, num); break;
- case 2: mi_int2store(to, num); break;
- case 3: mi_int3store(to, num); break;
- case 4: mi_int4store(to, num); break;
- case 5: mi_int5store(to, num); break;
- case 6: mi_int6store(to, num); break;
- case 7: mi_int7store(to, num); break;
- case 8: mi_int8store(to, num); break;
- default: DBUG_ASSERT(0);
- }
-}
-
-static longlong read_bigendian(const uchar *from, uint bytes)
-{
- switch(bytes) {
- case 1: return mi_uint1korr(from);
- case 2: return mi_uint2korr(from);
- case 3: return mi_uint3korr(from);
- case 4: return mi_uint4korr(from);
- case 5: return mi_uint5korr(from);
- case 6: return mi_uint6korr(from);
- case 7: return mi_uint7korr(from);
- case 8: return mi_sint8korr(from);
- default: DBUG_ASSERT(0); return 0;
- }
-}
-
void Field_timestamp_hires::store_TIME(my_time_t timestamp, ulong sec_part)
{
mi_int4store(ptr, timestamp);
- store_bigendian(sec_part_shift(sec_part, dec), ptr+4, sec_part_bytes[dec]);
+ store_bigendian(sec_part_shift(sec_part, dec), ptr+4, sec_part_bytes(dec));
}
my_time_t Field_timestamp_hires::get_timestamp(const uchar *pos,
ulong *sec_part) const
{
ASSERT_COLUMN_MARKED_FOR_READ;
- *sec_part= (long)sec_part_unshift(read_bigendian(pos+4, sec_part_bytes[dec]), dec);
+ *sec_part= (long)sec_part_unshift(read_bigendian(pos+4, sec_part_bytes(dec)), dec);
return mi_uint4korr(pos);
}
@@ -5613,10 +5504,8 @@ int Field_timestamp::store_decimal(const my_decimal *d)
error= 2;
}
else
- tmp= number_to_datetime(nr, sec_part, &ltime, TIME_NO_ZERO_IN_DATE |
- (thd->variables.sql_mode &
- MODE_NO_ZERO_DATE), &error);
-
+ tmp= number_to_datetime(nr, sec_part, &ltime, sql_mode_for_timestamp(thd),
+ &error);
return store_TIME_with_warning(thd, &ltime, &str, error, tmp != -1);
}
@@ -5642,22 +5531,17 @@ int Field_timestamp_hires::cmp(const uchar *a_ptr, const uchar *b_ptr)
int32 a,b;
ulong a_sec_part, b_sec_part;
a= mi_uint4korr(a_ptr);
- a_sec_part= (ulong)read_bigendian(a_ptr+4, sec_part_bytes[dec]);
+ a_sec_part= (ulong)read_bigendian(a_ptr+4, sec_part_bytes(dec));
b= mi_uint4korr(b_ptr);
- b_sec_part= (ulong)read_bigendian(b_ptr+4, sec_part_bytes[dec]);
+ b_sec_part= (ulong)read_bigendian(b_ptr+4, sec_part_bytes(dec));
return ((uint32) a < (uint32) b) ? -1 : ((uint32) a > (uint32) b) ? 1 :
a_sec_part < b_sec_part ? -1 : a_sec_part > b_sec_part ? 1 : 0;
}
-uint32 Field_timestamp_hires::pack_length() const
+void Field_timestamp_with_dec::make_send_field(Send_field *field)
{
- return 4 + sec_part_bytes[dec];
-}
-
-void Field_timestamp_with_dec::make_field(Send_field *field)
-{
- Field::make_field(field);
+ Field::make_send_field(field);
field->decimals= dec;
}
@@ -5675,6 +5559,27 @@ void Field_timestampf::store_TIME(my_time_t timestamp, ulong sec_part)
my_timestamp_to_binary(&tm, ptr, dec);
}
+void Field_timestampf::set_max()
+{
+ DBUG_ENTER("Field_timestampf::set_max");
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(dec == TIME_SECOND_PART_DIGITS);
+
+ set_notnull();
+ mi_int4store(ptr, TIMESTAMP_MAX_VALUE);
+ mi_int3store(ptr + 4, TIME_MAX_SECOND_PART);
+
+ DBUG_VOID_RETURN;
+}
+
+bool Field_timestampf::is_max()
+{
+ DBUG_ENTER("Field_timestampf::is_max");
+ ASSERT_COLUMN_MARKED_FOR_READ;
+
+ DBUG_RETURN(mi_sint4korr(ptr) == TIMESTAMP_MAX_VALUE &&
+ mi_sint3korr(ptr + 4) == TIME_MAX_SECOND_PART);
+}
my_time_t Field_timestampf::get_timestamp(const uchar *pos,
ulong *sec_part) const
@@ -5695,7 +5600,7 @@ sql_mode_t Field_temporal::can_handle_sql_mode_dependency_on_store() const
uint Field_temporal::is_equal(Create_field *new_field)
{
- return new_field->sql_type == real_type() &&
+ return new_field->type_handler() == type_handler() &&
new_field->length == max_display_length();
}
@@ -5715,11 +5620,9 @@ void Field_temporal::set_warnings(Sql_condition::enum_warning_level trunc_level,
a DATE field and non-zero time part is thrown away.
*/
if (was_cut & MYSQL_TIME_WARN_TRUNCATED)
- set_datetime_warning(trunc_level, WARN_DATA_TRUNCATED,
- str, mysql_type_to_time_type(type()), 1);
+ set_datetime_warning(trunc_level, WARN_DATA_TRUNCATED, str, ts_type, 1);
if (was_cut & MYSQL_TIME_WARN_OUT_OF_RANGE)
- set_datetime_warning(ER_WARN_DATA_OUT_OF_RANGE,
- str, mysql_type_to_time_type(type()), 1);
+ set_datetime_warning(ER_WARN_DATA_OUT_OF_RANGE, str, ts_type, 1);
}
@@ -5755,20 +5658,21 @@ int Field_temporal_with_date::store_TIME_with_warning(MYSQL_TIME *ltime,
}
else if (!MYSQL_TIME_WARN_HAVE_WARNINGS(was_cut) &&
(MYSQL_TIME_WARN_HAVE_NOTES(was_cut) ||
- (mysql_type_to_time_type(type()) == MYSQL_TIMESTAMP_DATE &&
+ (type_handler()->mysql_timestamp_type() == MYSQL_TIMESTAMP_DATE &&
(ltime->hour || ltime->minute || ltime->second || ltime->second_part))))
{
trunc_level= Sql_condition::WARN_LEVEL_NOTE;
was_cut|= MYSQL_TIME_WARN_TRUNCATED;
ret= 3;
}
- set_warnings(trunc_level, str, was_cut, mysql_type_to_time_type(type()));
+ set_warnings(trunc_level, str, was_cut,
+ type_handler()->mysql_timestamp_type());
store_TIME(ltime);
return was_cut ? ret : 0;
}
-int Field_temporal_with_date::store(const char *from, uint len, CHARSET_INFO *cs)
+int Field_temporal_with_date::store(const char *from, size_t len, CHARSET_INFO *cs)
{
MYSQL_TIME ltime;
MYSQL_TIME_STATUS status;
@@ -5808,7 +5712,7 @@ int Field_temporal_with_date::store(longlong nr, bool unsigned_val)
}
-int Field_temporal_with_date::store_time_dec(MYSQL_TIME *ltime, uint dec)
+int Field_temporal_with_date::store_time_dec(const MYSQL_TIME *ltime, uint dec)
{
int error= 0, have_smth_to_conv= 1;
ErrConvTime str(ltime);
@@ -5852,7 +5756,7 @@ my_decimal *Field_temporal::val_decimal(my_decimal *d)
if (get_date(&ltime, 0))
{
bzero(&ltime, sizeof(ltime));
- ltime.time_type= mysql_type_to_time_type(type());
+ ltime.time_type= type_handler()->mysql_timestamp_type();
}
return TIME_to_my_decimal(&ltime, d);
}
@@ -5882,30 +5786,28 @@ Item *Field_temporal::get_equal_const_item_datetime(THD *thd,
const_item->field_type() != MYSQL_TYPE_TIMESTAMP) ||
const_item->decimals != decimals())
{
- MYSQL_TIME ltime;
- if (const_item->field_type() == MYSQL_TYPE_TIME ?
- const_item->get_date_with_conversion(&ltime, 0) :
- const_item->get_date(&ltime, 0))
+ Datetime dt(thd, const_item, 0);
+ if (!dt.is_valid_datetime())
return NULL;
/*
See comments about truncation in the same place in
Field_time::get_equal_const_item().
*/
- return new (thd->mem_root) Item_datetime_literal(thd, &ltime,
+ return new (thd->mem_root) Item_datetime_literal(thd,
+ dt.get_mysql_time(),
decimals());
}
break;
case ANY_SUBST:
if (!is_temporal_type_with_date(const_item->field_type()))
{
- MYSQL_TIME ltime;
- if (const_item->get_date_with_conversion(&ltime,
- TIME_FUZZY_DATES |
- TIME_INVALID_DATES))
+ Datetime dt(thd, const_item, TIME_FUZZY_DATES | TIME_INVALID_DATES);
+ if (!dt.is_valid_datetime())
return NULL;
return new (thd->mem_root)
- Item_datetime_literal_for_invalid_dates(thd, &ltime,
- ltime.second_part ?
+ Item_datetime_literal_for_invalid_dates(thd, dt.get_mysql_time(),
+ dt.get_mysql_time()->
+ second_part ?
TIME_SECOND_PART_DIGITS : 0);
}
break;
@@ -5925,34 +5827,38 @@ int Field_time::store_TIME_with_warning(MYSQL_TIME *ltime,
int was_cut,
int have_smth_to_conv)
{
- Sql_condition::enum_warning_level trunc_level= Sql_condition::WARN_LEVEL_WARN;
- int ret= 2;
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
if (!have_smth_to_conv)
{
bzero(ltime, sizeof(*ltime));
- was_cut= MYSQL_TIME_WARN_TRUNCATED;
- ret= 1;
+ store_TIME(ltime);
+ set_warnings(Sql_condition::WARN_LEVEL_WARN, str, MYSQL_TIME_WARN_TRUNCATED);
+ return 1;
}
- else if (!MYSQL_TIME_WARN_HAVE_WARNINGS(was_cut) &&
- ((ltime->year || ltime->month) ||
- MYSQL_TIME_WARN_HAVE_NOTES(was_cut)))
+ if (ltime->year != 0 || ltime->month != 0)
{
- if (ltime->year || ltime->month)
- ltime->year= ltime->month= ltime->day= 0;
- trunc_level= Sql_condition::WARN_LEVEL_NOTE;
- was_cut|= MYSQL_TIME_WARN_TRUNCATED;
- ret= 3;
+ ltime->year= ltime->month= ltime->day= 0;
+ was_cut|= MYSQL_TIME_NOTE_TRUNCATED;
}
- set_warnings(trunc_level, str, was_cut, MYSQL_TIMESTAMP_TIME);
+ my_time_trunc(ltime, decimals());
store_TIME(ltime);
- return was_cut ? ret : 0;
+ if (!MYSQL_TIME_WARN_HAVE_WARNINGS(was_cut) &&
+ MYSQL_TIME_WARN_HAVE_NOTES(was_cut))
+ {
+ set_warnings(Sql_condition::WARN_LEVEL_NOTE, str,
+ was_cut | MYSQL_TIME_WARN_TRUNCATED);
+ return 3;
+ }
+ set_warnings(Sql_condition::WARN_LEVEL_WARN, str, was_cut);
+ return was_cut ? 2 : 0;
}
-void Field_time::store_TIME(MYSQL_TIME *ltime)
+void Field_time::store_TIME(const MYSQL_TIME *ltime)
{
+ DBUG_ASSERT(ltime->year == 0);
+ DBUG_ASSERT(ltime->month == 0);
long tmp= (ltime->day*24L+ltime->hour)*10000L +
(ltime->minute*100+ltime->second);
if (ltime->neg)
@@ -5960,7 +5866,7 @@ void Field_time::store_TIME(MYSQL_TIME *ltime)
int3store(ptr,tmp);
}
-int Field_time::store(const char *from,uint len,CHARSET_INFO *cs)
+int Field_time::store(const char *from,size_t len,CHARSET_INFO *cs)
{
MYSQL_TIME ltime;
MYSQL_TIME_STATUS status;
@@ -5986,7 +5892,10 @@ static void calc_datetime_days_diff(MYSQL_TIME *ltime, long days)
long daydiff= calc_daynr(ltime->year, ltime->month, ltime->day) - days;
ltime->year= ltime->month= 0;
if (daydiff >=0 )
+ {
ltime->day= daydiff;
+ ltime->time_type= MYSQL_TIMESTAMP_TIME;
+ }
else
{
longlong timediff= ((((daydiff * 24LL +
@@ -5994,20 +5903,12 @@ static void calc_datetime_days_diff(MYSQL_TIME *ltime, long days)
ltime->minute) * 60LL +
ltime->second) * 1000000LL +
ltime->second_part);
- unpack_time(timediff, ltime);
- /*
- unpack_time() broke down hours into ltime members hour,day,month.
- Mix them back to ltime->hour using the same factors
- that pack_time()/unpack_time() use (i.e. 32 for month).
- */
- ltime->hour+= (ltime->month * 32 + ltime->day) * 24;
- ltime->month= ltime->day= 0;
+ unpack_time(timediff, ltime, MYSQL_TIMESTAMP_TIME);
}
- ltime->time_type= MYSQL_TIMESTAMP_TIME;
}
-int Field_time::store_time_dec(MYSQL_TIME *ltime, uint dec)
+int Field_time::store_time_dec(const MYSQL_TIME *ltime, uint dec)
{
MYSQL_TIME l_time= *ltime;
ErrConvTime str(ltime);
@@ -6114,7 +6015,7 @@ bool Field_time::check_zero_in_date_with_warn(ulonglong fuzzydate)
THD *thd= get_thd();
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_DATA_OUT_OF_RANGE,
- ER_THD(thd, ER_WARN_DATA_OUT_OF_RANGE), field_name,
+ ER_THD(thd, ER_WARN_DATA_OUT_OF_RANGE), field_name.str,
thd->get_stmt_da()->current_row_for_warning());
return true;
}
@@ -6193,8 +6094,10 @@ int Field_time_hires::reset()
}
-void Field_time_hires::store_TIME(MYSQL_TIME *ltime)
+void Field_time_hires::store_TIME(const MYSQL_TIME *ltime)
{
+ DBUG_ASSERT(ltime->year == 0);
+ DBUG_ASSERT(ltime->month == 0);
ulonglong packed= sec_part_shift(pack_time(ltime), dec) + zero_point;
store_bigendian(packed, ptr, Field_time_hires::pack_length());
}
@@ -6214,6 +6117,39 @@ int Field_time::store_decimal(const my_decimal *d)
}
+bool Field_time::can_be_substituted_to_equal_item(const Context &ctx,
+ const Item_equal *item_equal)
+{
+ DBUG_ASSERT(item_equal->compare_type_handler()->cmp_type() != STRING_RESULT);
+ switch (ctx.subst_constraint()) {
+ case ANY_SUBST:
+ /*
+ A TIME field in a DATETIME comparison can be substituted to
+ Item_equal with TIME comparison.
+
+ SET timestamp=UNIX_TIMESTAMP('2015-08-30 10:20:30');
+ CREATE OR REPLACE TABLE t1 (a TIME);
+ INSERT INTO t1 VALUES ('00:00:00'),('00:00:01');
+ SELECT * FROM t1 WHERE a>=TIMESTAMP'2015-08-30 00:00:00'
+ AND a='00:00:00';
+
+ The above query can be simplified to:
+ SELECT * FROM t1 WHERE TIME'00:00:00'>=TIMESTAMP'2015-08-30 00:00:00'
+ AND a='00:00:00';
+ And further to:
+ SELECT * FROM t1 WHERE a=TIME'00:00:00';
+ */
+ if (ctx.compare_type_handler() == &type_handler_datetime &&
+ item_equal->compare_type_handler() == &type_handler_time)
+ return true;
+ return ctx.compare_type_handler() == item_equal->compare_type_handler();
+ case IDENTITY_SUBST:
+ return true;
+ }
+ return false;
+}
+
+
Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx,
Item *const_item)
{
@@ -6223,10 +6159,8 @@ Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx,
{
MYSQL_TIME ltime;
// Get the value of const_item with conversion from DATETIME to TIME
- if (const_item->get_time_with_conversion(thd, &ltime,
- TIME_TIME_ONLY |
- TIME_FUZZY_DATES |
- TIME_INVALID_DATES))
+ ulonglong fuzzydate= Time::comparison_flags_for_get_date();
+ if (const_item->get_time_with_conversion(thd, &ltime, fuzzydate))
return NULL;
/*
Replace a DATE/DATETIME constant to a TIME constant:
@@ -6273,11 +6207,6 @@ Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx,
}
-uint32 Field_time_hires::pack_length() const
-{
- return time_hires_bytes[dec];
-}
-
longlong Field_time_with_dec::val_int(void)
{
ASSERT_COLUMN_MARKED_FOR_READ;
@@ -6304,14 +6233,7 @@ bool Field_time_hires::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
packed= sec_part_unshift(packed - zero_point, dec);
- unpack_time(packed, ltime);
- /*
- unpack_time() returns MYSQL_TIMESTAMP_DATETIME.
- To get MYSQL_TIMESTAMP_TIME we need few adjustments
- */
- ltime->time_type= MYSQL_TIMESTAMP_TIME;
- ltime->hour+= (ltime->month*32+ltime->day)*24;
- ltime->month= ltime->day= 0;
+ unpack_time(packed, ltime, MYSQL_TIMESTAMP_TIME);
return false;
}
@@ -6330,9 +6252,9 @@ void Field_time_hires::sort_string(uchar *to,uint length __attribute__((unused))
to[0]^= 128;
}
-void Field_time_with_dec::make_field(Send_field *field)
+void Field_time_with_dec::make_send_field(Send_field *field)
{
- Field::make_field(field);
+ Field::make_send_field(field);
field->decimals= dec;
}
@@ -6348,9 +6270,8 @@ int Field_timef::reset()
return 0;
}
-void Field_timef::store_TIME(MYSQL_TIME *ltime)
+void Field_timef::store_TIME(const MYSQL_TIME *ltime)
{
- my_time_trunc(ltime, decimals());
longlong tmp= TIME_to_longlong_time_packed(ltime);
my_time_packed_to_binary(tmp, ptr, dec);
}
@@ -6370,7 +6291,7 @@ bool Field_timef::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
** Can handle 2 byte or 4 byte years!
****************************************************************************/
-int Field_year::store(const char *from, uint len,CHARSET_INFO *cs)
+int Field_year::store(const char *from, size_t len,CHARSET_INFO *cs)
{
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
char *end;
@@ -6384,10 +6305,10 @@ int Field_year::store(const char *from, uint len,CHARSET_INFO *cs)
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
return 1;
}
- if (get_thd()->count_cuted_fields &&
+ if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION &&
(error= check_int(cs, from, len, end, error)))
{
- if (error == 1) /* empty or incorrect string */
+ if (unlikely(error == 1) /* empty or incorrect string */)
{
*ptr= 0;
return 1;
@@ -6439,7 +6360,7 @@ int Field_year::store(longlong nr, bool unsigned_val)
}
-int Field_year::store_time_dec(MYSQL_TIME *ltime, uint dec_arg)
+int Field_year::store_time_dec(const MYSQL_TIME *ltime, uint dec_arg)
{
ErrConvTime str(ltime);
if (Field_year::store(ltime->year, 0))
@@ -6495,7 +6416,7 @@ bool Field_year::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
if (tmp || field_length != 4)
tmp+= 1900;
return int_to_datetime_with_warn(false, tmp * 10000,
- ltime, fuzzydate, table->s, field_name);
+ ltime, fuzzydate, table->s, field_name.str);
}
@@ -6712,10 +6633,9 @@ Item *Field_newdate::get_equal_const_item(THD *thd, const Context &ctx,
case ANY_SUBST:
if (!is_temporal_type_with_date(const_item->field_type()))
{
- MYSQL_TIME ltime;
// Get the value of const_item with conversion from TIME to DATETIME
- if (const_item->get_date_with_conversion(&ltime,
- TIME_FUZZY_DATES | TIME_INVALID_DATES))
+ Datetime dt(thd, const_item, TIME_FUZZY_DATES | TIME_INVALID_DATES);
+ if (!dt.is_valid_datetime())
return NULL;
/*
Replace the constant to a DATE or DATETIME constant.
@@ -6728,26 +6648,23 @@ Item *Field_newdate::get_equal_const_item(THD *thd, const Context &ctx,
(assuming CURRENT_DATE is '2015-08-30'
*/
- if (non_zero_hhmmssuu(&ltime))
+ if (!dt.hhmmssff_is_zero())
return new (thd->mem_root)
- Item_datetime_literal_for_invalid_dates(thd, &ltime,
- ltime.second_part ?
+ Item_datetime_literal_for_invalid_dates(thd, dt.get_mysql_time(),
+ dt.get_mysql_time()->
+ second_part ?
TIME_SECOND_PART_DIGITS : 0);
- datetime_to_date(&ltime);
return new (thd->mem_root)
- Item_date_literal_for_invalid_dates(thd, &ltime);
+ Item_date_literal_for_invalid_dates(thd, Date(&dt).get_mysql_time());
}
break;
case IDENTITY_SUBST:
if (const_item->field_type() != MYSQL_TYPE_DATE)
{
- MYSQL_TIME ltime;
- if (const_item->field_type() == MYSQL_TYPE_TIME ?
- const_item->get_date_with_conversion(&ltime, 0) :
- const_item->get_date(&ltime, 0))
+ Date d(thd, const_item, 0);
+ if (!d.is_valid_date())
return NULL;
- datetime_to_date(&ltime);
- return new (thd->mem_root) Item_date_literal(thd, &ltime);
+ return new (thd->mem_root) Item_date_literal(thd, d.get_mysql_time());
}
break;
}
@@ -6975,16 +6892,11 @@ bool Field_datetime_hires::get_TIME(MYSQL_TIME *ltime, const uchar *pos,
{
ASSERT_COLUMN_MARKED_FOR_READ;
ulonglong packed= read_bigendian(pos, Field_datetime_hires::pack_length());
- unpack_time(sec_part_unshift(packed, dec), ltime);
+ unpack_time(sec_part_unshift(packed, dec), ltime, MYSQL_TIMESTAMP_DATETIME);
return validate_MMDD(packed, ltime->month, ltime->day, fuzzydate);
}
-uint32 Field_datetime_hires::pack_length() const
-{
- return datetime_hires_bytes[dec];
-}
-
int Field_datetime_hires::cmp(const uchar *a_ptr, const uchar *b_ptr)
{
ulonglong a=read_bigendian(a_ptr, Field_datetime_hires::pack_length());
@@ -6992,9 +6904,9 @@ int Field_datetime_hires::cmp(const uchar *a_ptr, const uchar *b_ptr)
return a < b ? -1 : a > b ? 1 : 0;
}
-void Field_datetime_with_dec::make_field(Send_field *field)
+void Field_datetime_with_dec::make_send_field(Send_field *field)
{
- Field::make_field(field);
+ Field::make_send_field(field);
field->decimals= dec;
}
@@ -7063,10 +6975,11 @@ Field_longstr::check_string_copy_error(const String_copier *copier,
const char *pos;
char tmp[32];
- if (!(pos= copier->most_important_error_pos()))
+ if (likely(!(pos= copier->most_important_error_pos())))
return FALSE;
- if (get_thd()->count_cuted_fields)
+ /* Ignore errors from internal expressions */
+ if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION)
{
convert_to_printable(tmp, sizeof(tmp), pos, (end - pos), cs, 6);
set_warning_truncated_wrong_value("string", tmp);
@@ -7101,7 +7014,7 @@ Field_longstr::report_if_important_data(const char *pstr, const char *end,
{
THD *thd;
if ((pstr < end) &&
- (thd=get_thd())->count_cuted_fields)
+ (thd= get_thd())->count_cuted_fields > CHECK_FIELD_EXPRESSION)
{
if (test_if_important_data(field_charset, pstr, end))
{
@@ -7124,19 +7037,19 @@ Field_longstr::report_if_important_data(const char *pstr, const char *end,
/* Copy a string and fill with space */
-int Field_string::store(const char *from,uint length,CHARSET_INFO *cs)
+int Field_string::store(const char *from, size_t length,CHARSET_INFO *cs)
{
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
uint copy_length;
- String_copier copier;
+ int rc;
/* See the comment for Field_long::store(long long) */
DBUG_ASSERT(!table || table->in_use == current_thd);
- copy_length= copier.well_formed_copy(field_charset,
- (char*) ptr, field_length,
- cs, from, length,
- field_length / field_charset->mbmaxlen);
+ rc= well_formed_copy_with_check((char*) ptr, field_length,
+ cs, from, length,
+ field_length / field_charset->mbmaxlen,
+ false, &copy_length);
/* Append spaces if the string was shorter than the field. */
if (copy_length < field_length)
@@ -7144,7 +7057,21 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs)
field_length-copy_length,
field_charset->pad_char);
- return check_conversion_status(&copier, from + length, cs, false);
+ return rc;
+}
+
+
+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);
}
@@ -7160,46 +7087,36 @@ int Field_str::store(double nr)
{
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE];
- uint local_char_length= field_length / charset()->mbmaxlen;
+ uint local_char_length= MY_MIN(sizeof(buff),
+ field_length / field_charset->mbmaxlen);
size_t length= 0;
my_bool error= (local_char_length == 0);
// my_gcvt() requires width > 0, and we may have a CHAR(0) column.
- if (!error)
+ if (likely(!error))
length= my_gcvt(nr, MY_GCVT_ARG_DOUBLE, local_char_length, buff, &error);
- if (error)
+ if (unlikely(error))
{
if (get_thd()->abort_on_warning)
set_warning(ER_DATA_TOO_LONG, 1);
else
set_warning(WARN_DATA_TRUNCATED, 1);
}
- return store(buff, length, &my_charset_numeric);
+ return store(buff, (uint)length, &my_charset_numeric);
}
uint Field::is_equal(Create_field *new_field)
{
- return (new_field->sql_type == real_type());
+ return new_field->type_handler() == type_handler();
}
uint Field_str::is_equal(Create_field *new_field)
{
- return ((new_field->sql_type == real_type()) &&
- new_field->charset == field_charset &&
- new_field->length == max_display_length());
-}
-
-
-int Field_string::store(longlong nr, bool unsigned_val)
-{
- char buff[64];
- int l;
- CHARSET_INFO *cs=charset();
- l= (cs->cset->longlong10_to_str)(cs,buff,sizeof(buff),
- unsigned_val ? 10 : -10, nr);
- return Field_string::store(buff,(uint)l,cs);
+ return new_field->type_handler() == type_handler() &&
+ new_field->charset == field_charset &&
+ new_field->length == max_display_length();
}
@@ -7330,7 +7247,7 @@ String *Field_string::val_str(String *val_buffer __attribute__((unused)),
ASSERT_COLUMN_MARKED_FOR_READ;
/* See the comment for Field_long::store(long long) */
DBUG_ASSERT(!table || table->in_use == current_thd);
- uint length;
+ size_t length;
if (get_thd()->variables.sql_mode &
MODE_PAD_CHAR_TO_FULL_LENGTH)
length= my_charpos(field_charset, ptr, ptr + field_length,
@@ -7368,7 +7285,7 @@ check_field_for_37426(const void *param_arg)
Check_field_param *param= (Check_field_param*) param_arg;
DBUG_ASSERT(param->field->real_type() == MYSQL_TYPE_STRING);
DBUG_PRINT("debug", ("Field %s - type: %d, size: %d",
- param->field->field_name,
+ param->field->field_name.str,
param->field->real_type(),
param->field->row_pack_length()));
return param->field->row_pack_length() > 255;
@@ -7393,11 +7310,11 @@ Field_string::compatible_field_size(uint field_metadata,
int Field_string::cmp(const uchar *a_ptr, const uchar *b_ptr)
{
- uint a_len, b_len;
+ size_t a_len, b_len;
if (field_charset->mbmaxlen != 1)
{
- uint char_len= field_length/field_charset->mbmaxlen;
+ 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);
}
@@ -7415,7 +7332,9 @@ int Field_string::cmp(const uchar *a_ptr, const uchar *b_ptr)
void Field_string::sort_string(uchar *to,uint length)
{
- uint tmp __attribute__((unused))=
+#ifdef DBUG_ASSERT_EXISTS
+ size_t tmp=
+#endif
field_charset->coll->strnxfrm(field_charset,
to, length,
char_length() *
@@ -7431,7 +7350,7 @@ void Field_string::sql_type(String &res) const
{
THD *thd= table->in_use;
CHARSET_INFO *cs=res.charset();
- ulong length;
+ size_t length;
length= cs->cset->snprintf(cs,(char*) res.ptr(),
res.alloced_length(), "%s(%d)",
@@ -7470,9 +7389,10 @@ void Field_string::sql_rpl_type(String *res) const
uchar *Field_string::pack(uchar *to, const uchar *from, uint max_length)
{
- uint length= MY_MIN(field_length,max_length);
- uint local_char_length= max_length/field_charset->mbmaxlen;
- DBUG_PRINT("debug", ("Packing field '%s' - length: %u ", field_name, length));
+ 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,
@@ -7515,7 +7435,7 @@ uchar *Field_string::pack(uchar *to, const uchar *from, uint max_length)
the master.
@note For information about how the length is packed, see @c
- Field_string::do_save_field_metadata
+ Field_string::save_field_metadata
@param to Destination of the data
@param from Source of the data
@@ -7598,7 +7518,7 @@ Field_string::unpack(uchar *to, const uchar *from, const uchar *from_end,
@returns number of bytes written to metadata_ptr
*/
-int Field_string::do_save_field_metadata(uchar *metadata_ptr)
+int Field_string::save_field_metadata(uchar *metadata_ptr)
{
DBUG_ASSERT(field_length < 1024);
DBUG_ASSERT((real_type() & 0xF0) == 0xF0);
@@ -7626,14 +7546,14 @@ uint Field_string::max_packed_col_length(uint max_length)
uint Field_string::get_key_image(uchar *buff, uint length, imagetype type_arg)
{
- uint bytes = my_charpos(field_charset, (char*) ptr,
+ size_t bytes = my_charpos(field_charset, (char*) ptr,
(char*) ptr + field_length,
length / field_charset->mbmaxlen);
memcpy(buff, ptr, bytes);
if (bytes < length)
field_charset->cset->fill(field_charset, (char*) buff + bytes,
length - bytes, field_charset->pad_char);
- return bytes;
+ return (uint)bytes;
}
@@ -7644,7 +7564,7 @@ Field *Field_string::make_new_field(MEM_ROOT *root, TABLE *new_table,
if (type() != MYSQL_TYPE_VAR_STRING || keep_type)
field= Field::make_new_field(root, new_table, keep_type);
else if ((field= new (root) Field_varstring(field_length, maybe_null(),
- field_name,
+ &field_name,
new_table->s, charset())))
{
/*
@@ -7687,44 +7607,38 @@ const uint Field_varstring::MAX_SIZE= UINT_MAX16;
@returns number of bytes written to metadata_ptr
*/
-int Field_varstring::do_save_field_metadata(uchar *metadata_ptr)
+int Field_varstring::save_field_metadata(uchar *metadata_ptr)
{
DBUG_ASSERT(field_length <= 65535);
int2store((char*)metadata_ptr, field_length);
return 2;
}
-int Field_varstring::store(const char *from,uint length,CHARSET_INFO *cs)
+
+bool Field_varstring::memcpy_field_possible(const Field *from) const
+{
+ return (Field_str::memcpy_field_possible(from) &&
+ !compression_method() == !from->compression_method() &&
+ length_bytes == ((Field_varstring*) from)->length_bytes &&
+ (table->file && !(table->file->ha_table_flags() &
+ HA_RECORD_MUST_BE_CLEAN_ON_WRITE)));
+}
+
+
+int Field_varstring::store(const char *from,size_t length,CHARSET_INFO *cs)
{
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
uint copy_length;
- String_copier copier;
+ int rc;
- copy_length= copier.well_formed_copy(field_charset,
- (char*) ptr + length_bytes,
- field_length,
- cs, from, length,
- field_length / field_charset->mbmaxlen);
- if (length_bytes == 1)
- *ptr= (uchar) copy_length;
- else
- int2store(ptr, copy_length);
-
- return check_conversion_status(&copier, from + length, cs, true);
-}
+ rc= well_formed_copy_with_check((char*) get_data(), field_length,
+ cs, from, length,
+ field_length / field_charset->mbmaxlen,
+ true, &copy_length);
+ store_length(copy_length);
-int Field_varstring::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 Field_varstring::store(buff, length, field_charset);
+ return rc;
}
@@ -7773,7 +7687,7 @@ my_decimal *Field_varstring::val_decimal(my_decimal *decimal_value)
}
-#ifdef HAVE_valgrind_or_MSAN
+#ifdef HAVE_valgrind
void Field_varstring::mark_unused_memory_as_defined()
{
uint used_length= get_length();
@@ -7854,8 +7768,8 @@ int Field_varstring::cmp_prefix(const uchar *a_ptr, const uchar *b_ptr,
int Field_varstring::key_cmp(const uchar *key_ptr, uint max_key_length)
{
- uint length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
- uint local_char_length= max_key_length / field_charset->mbmaxlen;
+ size_t length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
+ size_t local_char_length= max_key_length / field_charset->mbmaxlen;
local_char_length= my_charpos(field_charset, ptr + length_bytes,
ptr + length_bytes + length, local_char_length);
@@ -7889,26 +7803,29 @@ int Field_varstring::key_cmp(const uchar *a,const uchar *b)
void Field_varstring::sort_string(uchar *to,uint length)
{
- uint tot_length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
+ String buf;
+
+ val_str(&buf, &buf);
if (field_charset == &my_charset_bin)
{
/* Store length last in high-byte order to sort longer strings first */
if (length_bytes == 1)
- to[length-1]= tot_length;
+ to[length - 1]= buf.length();
else
- mi_int2store(to+length-2, tot_length);
+ mi_int2store(to + length - 2, buf.length());
length-= length_bytes;
}
-
- tot_length= field_charset->coll->strnxfrm(field_charset,
- to, length,
- char_length() *
- field_charset->strxfrm_multiply,
- ptr + length_bytes, tot_length,
- MY_STRXFRM_PAD_WITH_SPACE |
- MY_STRXFRM_PAD_TO_MAXLEN);
- DBUG_ASSERT(tot_length == length);
+
+#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);
+ DBUG_ASSERT(rc == length);
}
@@ -7924,16 +7841,21 @@ enum ha_base_keytype Field_varstring::key_type() const
}
+/*
+ Compressed columns need one extra byte to store the compression method.
+ This byte is invisible to the end user, but not for the storage engine.
+*/
+
void Field_varstring::sql_type(String &res) const
{
THD *thd= table->in_use;
CHARSET_INFO *cs=res.charset();
- ulong length;
+ size_t length;
length= cs->cset->snprintf(cs,(char*) res.ptr(),
- res.alloced_length(), "%s(%d)",
+ res.alloced_length(), "%s(%u)",
(has_charset() ? "varchar" : "varbinary"),
- (int) field_length / charset()->mbmaxlen);
+ (uint) char_length());
res.length(length);
if ((thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) &&
has_charset() && (charset()->state & MY_CS_BINSORT))
@@ -8058,32 +7980,36 @@ uint Field_varstring::max_packed_col_length(uint max_length)
uint Field_varstring::get_key_image(uchar *buff, uint length,
imagetype type_arg)
{
- uint f_length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
- uint local_char_length= length / field_charset->mbmaxlen;
- uchar *pos= ptr+length_bytes;
- local_char_length= my_charpos(field_charset, pos, pos + f_length,
- local_char_length);
- set_if_smaller(f_length, local_char_length);
+ String val;
+ uint local_char_length;
+ my_bitmap_map *old_map;
+
+ 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);
+ if (local_char_length < val.length())
+ val.length(local_char_length);
/* Key is always stored with 2 bytes */
- int2store(buff,f_length);
- memcpy(buff+HA_KEY_BLOB_LENGTH, pos, f_length);
- if (f_length < length)
+ int2store(buff, val.length());
+ memcpy(buff + HA_KEY_BLOB_LENGTH, val.ptr(), val.length());
+ if (val.length() < length)
{
/*
Must clear this as we do a memcmp in opt_range.cc to detect
identical keys
*/
- bzero(buff+HA_KEY_BLOB_LENGTH+f_length, (length-f_length));
+ memset(buff + HA_KEY_BLOB_LENGTH + val.length(), 0, length - val.length());
}
- return HA_KEY_BLOB_LENGTH+f_length;
+ return HA_KEY_BLOB_LENGTH + val.length();
}
void Field_varstring::set_key_image(const uchar *buff,uint length)
{
length= uint2korr(buff); // Real length is here
- (void) Field_varstring::store((const char*) buff+HA_KEY_BLOB_LENGTH, length,
- field_charset);
+ (void) store((const char*) buff + HA_KEY_BLOB_LENGTH, length, field_charset);
}
@@ -8139,14 +8065,15 @@ Field *Field_varstring::new_key_field(MEM_ROOT *root, TABLE *new_table,
uint Field_varstring::is_equal(Create_field *new_field)
{
- if (new_field->sql_type == real_type() &&
- new_field->charset == field_charset)
+ if (new_field->type_handler() == type_handler() &&
+ new_field->charset == field_charset &&
+ !new_field->compression_method() == !compression_method())
{
- if (new_field->length == max_display_length())
+ if (new_field->length == field_length)
return IS_EQUAL_YES;
- if (new_field->length > max_display_length() &&
- ((new_field->length <= 255 && max_display_length() <= 255) ||
- (new_field->length > 255 && max_display_length() > 255)))
+ if (new_field->length > field_length &&
+ ((new_field->length <= 255 && field_length <= 255) ||
+ (new_field->length > 255 && field_length > 255)))
return IS_EQUAL_PACK_LENGTH; // VARCHAR, longer variable length
}
return IS_EQUAL_NO;
@@ -8168,6 +8095,206 @@ void Field_varstring::hash(ulong *nr, ulong *nr2)
}
+/**
+ Compress field
+
+ @param[out] to destination buffer for compressed data
+ @param[in] to_length size of to
+ @param[in] from data to compress
+ @param[in] length from length
+ @param[in] max_length truncate `from' to this length
+ @param[out] out_length compessed data length
+ @param[in] cs from character set
+ @param[in] nchars copy no more than "nchars" characters
+
+ In worst case (no compression performed) storage requirement is increased by
+ 1 byte to store header. If it exceeds field length, normal data truncation is
+ performed.
+
+ Generic compressed header format (1 byte):
+
+ Bits 1-4: method specific bits
+ Bits 5-8: compression method
+
+ If compression method is 0 then header is immediately followed by
+ uncompressed data.
+
+ If compression method is zlib:
+
+ Bits 1-3: number of bytes occupied by original data length
+ Bits 4: true if zlib wrapper not present
+ Bits 5-8: store 8 (zlib)
+
+ Header is immediately followed by original data length,
+ followed by compressed data.
+*/
+
+int Field_longstr::compress(char *to, uint to_length,
+ const char *from, uint length,
+ uint max_length,
+ uint *out_length,
+ CHARSET_INFO *cs, size_t nchars)
+{
+ THD *thd= get_thd();
+ char *buf;
+ uint buf_length;
+ int rc= 0;
+
+ 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))))
+ {
+ *out_length= 0;
+ return -1;
+ }
+
+ rc= well_formed_copy_with_check(buf, max_length, cs, from, length,
+ nchars, true, &buf_length);
+ }
+ else
+ {
+ buf= const_cast<char*>(from);
+ buf_length= length;
+ }
+
+ if (buf_length == 0)
+ *out_length= 0;
+ else if (buf_length >= thd->variables.column_compression_threshold &&
+ (*out_length= compression_method()->compress(thd, to, buf, buf_length)))
+ status_var_increment(thd->status_var.column_compressions);
+ else
+ {
+ /* Store uncompressed */
+ to[0]= 0;
+ if (buf_length < to_length)
+ memcpy(to + 1, buf, buf_length);
+ else
+ {
+ /* Storing string at blob capacity, e.g. 255 bytes string to TINYBLOB. */
+ rc= well_formed_copy_with_check(to + 1, to_length - 1, cs, from, length,
+ nchars, true, &buf_length);
+ }
+ *out_length= buf_length + 1;
+ }
+
+ if (buf != from)
+ my_free(buf);
+ return rc;
+}
+
+
+/*
+ Memory is allocated only when original data was actually compressed.
+ Otherwise val_ptr points at data located immediately after header.
+
+ Data can be stored uncompressed if data was shorter than threshold
+ or compressed data was longer than original data.
+*/
+
+String *Field_longstr::uncompress(String *val_buffer, String *val_ptr,
+ const uchar *from, uint from_length)
+{
+ if (from_length)
+ {
+ uchar method= (*from & 0xF0) >> 4;
+
+ /* Uncompressed data */
+ if (!method)
+ {
+ val_ptr->set((const char*) from + 1, from_length - 1, field_charset);
+ return val_ptr;
+ }
+
+ if (compression_methods[method].uncompress)
+ {
+ if (!compression_methods[method].uncompress(val_buffer, from, from_length,
+ field_length))
+ {
+ val_buffer->set_charset(field_charset);
+ status_var_increment(get_thd()->status_var.column_decompressions);
+ return val_buffer;
+ }
+ }
+ }
+
+ /*
+ It would be better to return 0 in case of errors, but to take the
+ safer route, let's return a zero string and let the general
+ handler catch the error.
+ */
+ val_ptr->set("", 0, field_charset);
+ return val_ptr;
+}
+
+
+int Field_varstring_compressed::store(const char *from, size_t length,
+ CHARSET_INFO *cs)
+{
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ uint compressed_length;
+ int rc= compress((char*) get_data(), field_length, from, (uint) length,
+ Field_varstring_compressed::max_display_length(),
+ &compressed_length, cs,
+ Field_varstring_compressed::char_length());
+ store_length(compressed_length);
+ return rc;
+}
+
+
+String *Field_varstring_compressed::val_str(String *val_buffer, String *val_ptr)
+{
+ ASSERT_COLUMN_MARKED_FOR_READ;
+ return uncompress(val_buffer, val_ptr, get_data(), get_length());
+}
+
+
+double Field_varstring_compressed::val_real(void)
+{
+ ASSERT_COLUMN_MARKED_FOR_READ;
+ THD *thd= get_thd();
+ String buf;
+ val_str(&buf, &buf);
+ return Converter_strntod_with_warn(thd, Warn_filter(thd), field_charset,
+ buf.ptr(), buf.length()).result();
+}
+
+
+longlong Field_varstring_compressed::val_int(void)
+{
+ ASSERT_COLUMN_MARKED_FOR_READ;
+ THD *thd= get_thd();
+ String buf;
+ val_str(&buf, &buf);
+ 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)
+{
+ String a, b;
+ uint a_length, b_length;
+
+ if (length_bytes == 1)
+ {
+ a_length= (uint) *a_ptr;
+ b_length= (uint) *b_ptr;
+ }
+ else
+ {
+ a_length= uint2korr(a_ptr);
+ b_length= uint2korr(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);
+}
+
+
/****************************************************************************
** blob type
** A blob is saved as a length and a pointer. The length is stored in the
@@ -8175,12 +8302,13 @@ void Field_varstring::hash(ulong *nr, ulong *nr2)
****************************************************************************/
Field_blob::Field_blob(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
- enum utype unireg_check_arg, const char *field_name_arg,
+ enum utype unireg_check_arg,
+ const LEX_CSTRING *field_name_arg,
TABLE_SHARE *share, uint blob_pack_length,
- CHARSET_INFO *cs)
+ const DTCollation &collation)
:Field_longstr(ptr_arg, BLOB_PACK_LENGTH_TO_MAX_LENGH(blob_pack_length),
null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg,
- cs),
+ collation),
packlength(blob_pack_length)
{
DBUG_ASSERT(blob_pack_length <= 4); // Only pack lengths 1-4 supported currently
@@ -8209,6 +8337,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(!compression_method() == !from->compression_method());
int rc= 0;
uint32 length= from->get_length();
uchar *data= from->get_ptr();
@@ -8227,14 +8356,15 @@ int Field_blob::copy_value(Field_blob *from)
}
-int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
+int Field_blob::store(const char *from,size_t length,CHARSET_INFO *cs)
{
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
- uint copy_length, new_length;
- String_copier copier;
+ size_t copy_length, new_length;
+ uint copy_len;
char *tmp;
char buff[STRING_BUFFER_USUAL_SIZE];
String tmpstr(buff,sizeof(buff), &my_charset_bin);
+ int rc;
if (!length)
{
@@ -8255,7 +8385,7 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
DBUG_ASSERT(length <= max_data_length());
new_length= length;
- copy_length= (uint)MY_MIN(UINT_MAX,table->in_use->variables.group_concat_max_len);
+ copy_length= (size_t)MY_MIN(UINT_MAX,table->in_use->variables.group_concat_max_len);
if (new_length > copy_length)
{
new_length= Well_formed_prefix(cs,
@@ -8307,13 +8437,13 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
bmove(ptr + packlength, (uchar*) &tmp, sizeof(char*));
return 0;
}
- copy_length= copier.well_formed_copy(field_charset,
- (char*) value.ptr(), new_length,
- cs, from, length);
- Field_blob::store_length(copy_length);
+ rc= well_formed_copy_with_check((char*) value.ptr(), (uint) new_length,
+ cs, from, length,
+ length, true, &copy_len);
+ Field_blob::store_length(copy_len);
bmove(ptr+packlength,(uchar*) &tmp,sizeof(char*));
- return check_conversion_status(&copier, from + length, cs, true);
+ return rc;
oom_error:
/* Fatal OOM error */
@@ -8322,22 +8452,6 @@ oom_error:
}
-int Field_blob::store(double nr)
-{
- CHARSET_INFO *cs=charset();
- value.set_real(nr, NOT_FIXED_DEC, cs);
- return Field_blob::store(value.ptr(),(uint) value.length(), cs);
-}
-
-
-int Field_blob::store(longlong nr, bool unsigned_val)
-{
- CHARSET_INFO *cs=charset();
- value.set_int(nr, unsigned_val, cs);
- return Field_blob::store(value.ptr(), (uint) value.length(), cs);
-}
-
-
double Field_blob::val_real(void)
{
ASSERT_COLUMN_MARKED_FOR_READ;
@@ -8417,7 +8531,7 @@ int Field_blob::cmp(const uchar *a_ptr, const uchar *b_ptr)
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(blob1, a_len, blob2, b_len);
+ return cmp(blob1, (uint32)a_len, blob2, (uint32)b_len);
}
@@ -8455,7 +8569,7 @@ int Field_blob::cmp_binary(const uchar *a_ptr, const uchar *b_ptr,
uint Field_blob::get_key_image(uchar *buff,uint length, imagetype type_arg)
{
- uint32 blob_length= get_length(ptr);
+ size_t blob_length= get_length(ptr);
uchar *blob;
#ifdef HAVE_SPATIAL
@@ -8473,7 +8587,7 @@ uint Field_blob::get_key_image(uchar *buff,uint length, imagetype type_arg)
return image_length;
}
blob= get_ptr();
- gobj= Geometry::construct(&buffer, (char*) blob, blob_length);
+ gobj= Geometry::construct(&buffer, (char*) blob, (uint32)blob_length);
if (!gobj || gobj->get_mbr(&mbr, &dummy))
bzero(buff, image_length);
else
@@ -8488,12 +8602,12 @@ uint Field_blob::get_key_image(uchar *buff,uint length, imagetype type_arg)
#endif /*HAVE_SPATIAL*/
blob= get_ptr();
- uint local_char_length= length / field_charset->mbmaxlen;
+ size_t local_char_length= length / field_charset->mbmaxlen;
local_char_length= my_charpos(field_charset, blob, blob + blob_length,
local_char_length);
set_if_smaller(blob_length, local_char_length);
- if ((uint32) length > blob_length)
+ if (length > blob_length)
{
/*
Must clear this as we do a memcmp in opt_range.cc to detect
@@ -8519,14 +8633,14 @@ void Field_blob::set_key_image(const uchar *buff,uint length)
int Field_blob::key_cmp(const uchar *key_ptr, uint max_key_length)
{
uchar *blob1;
- uint blob_length=get_length(ptr);
+ size_t blob_length=get_length(ptr);
memcpy(&blob1, ptr+packlength, sizeof(char*));
CHARSET_INFO *cs= charset();
- uint local_char_length= max_key_length / cs->mbmaxlen;
+ size_t local_char_length= max_key_length / cs->mbmaxlen;
local_char_length= my_charpos(cs, blob1, blob1+blob_length,
local_char_length);
set_if_smaller(blob_length, local_char_length);
- return Field_blob::cmp(blob1, blob_length,
+ return Field_blob::cmp(blob1, (uint32)blob_length,
key_ptr+HA_KEY_BLOB_LENGTH,
uint2korr(key_ptr));
}
@@ -8543,8 +8657,10 @@ Field *Field_blob::new_key_field(MEM_ROOT *root, TABLE *new_table,
uchar *new_null_ptr, uint new_null_bit)
{
Field_varstring *res= new (root) Field_varstring(new_ptr, length, 2,
- new_null_ptr, new_null_bit, Field::NONE,
- field_name, table->s, charset());
+ new_null_ptr,
+ new_null_bit, Field::NONE,
+ &field_name,
+ table->s, charset());
res->init(new_table);
return res;
}
@@ -8560,9 +8676,9 @@ Field *Field_blob::new_key_field(MEM_ROOT *root, TABLE *new_table,
@returns number of bytes written to metadata_ptr
*/
-int Field_blob::do_save_field_metadata(uchar *metadata_ptr)
+int Field_blob::save_field_metadata(uchar *metadata_ptr)
{
- DBUG_ENTER("Field_blob::do_save_field_metadata");
+ 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);
@@ -8578,37 +8694,50 @@ uint32 Field_blob::sort_length() const
void Field_blob::sort_string(uchar *to,uint length)
{
- uchar *blob;
- uint blob_length=get_length();
+ String buf;
- if (!blob_length && field_charset->pad_char == 0)
+ val_str(&buf, &buf);
+ if (!buf.length() && field_charset->pad_char == 0)
bzero(to,length);
else
{
if (field_charset == &my_charset_bin)
{
- uchar *pos;
-
/*
Store length of blob last in blob to shorter blobs before longer blobs
*/
length-= packlength;
- pos= to+length;
-
- store_bigendian(blob_length, pos, packlength);
+ store_bigendian(buf.length(), to + length, packlength);
}
- memcpy(&blob, ptr+packlength, sizeof(char*));
-
- blob_length= field_charset->coll->strnxfrm(field_charset,
- to, length, length,
- blob, blob_length,
- MY_STRXFRM_PAD_WITH_SPACE |
- MY_STRXFRM_PAD_TO_MAXLEN);
- DBUG_ASSERT(blob_length == length);
+
+#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);
+ DBUG_ASSERT(rc == length);
}
}
+/*
+ Return the data type handler, according to packlength.
+ Implemented in field.cc rather than in field.h
+ to avoid exporting type_handler_xxx with MYSQL_PLUGIN_IMPORT.
+*/
+const Type_handler *Field_blob::type_handler() const
+{
+ switch (packlength) {
+ case 1: return &type_handler_tiny_blob;
+ case 2: return &type_handler_blob;
+ case 3: return &type_handler_medium_blob;
+ }
+ return &type_handler_long_blob;
+}
+
+
void Field_blob::sql_type(String &res) const
{
const char *str;
@@ -8621,7 +8750,11 @@ void Field_blob::sql_type(String &res) const
}
res.set_ascii(str,length);
if (charset() == &my_charset_bin)
+ {
res.append(STRING_WITH_LEN("blob"));
+ if (packlength == 2 && (get_thd()->variables.sql_mode & MODE_ORACLE))
+ res.append(STRING_WITH_LEN("(65535)"));
+ }
else
{
res.append(STRING_WITH_LEN("text"));
@@ -8671,6 +8804,7 @@ uchar *Field_blob::pack(uchar *to, const uchar *from, uint max_length)
@return New pointer into memory based on from + length of the data
*/
+
const uchar *Field_blob::unpack(uchar *to, const uchar *from,
const uchar *from_end, uint param_data)
{
@@ -8683,12 +8817,9 @@ const uchar *Field_blob::unpack(uchar *to, const uchar *from,
DBUG_RETURN(0); // Error in data
uint32 const length= get_length(from, master_packlength);
DBUG_DUMP("packed", from, length + master_packlength);
- bitmap_set_bit(table->write_set, field_index);
if (from + master_packlength + length > from_end)
DBUG_RETURN(0);
- store(reinterpret_cast<const char*>(from) + master_packlength,
- length, field_charset);
- DBUG_DUMP("record", to, table->s->reclength);
+ set_ptr(length, const_cast<uchar*> (from) + master_packlength);
DBUG_RETURN(from + master_packlength + length);
}
@@ -8707,11 +8838,77 @@ uint Field_blob::max_packed_col_length(uint max_length)
}
+/*
+ Blob fields are regarded equal if they have same character set,
+ same blob store length and if either both are compressed or both are
+ uncompressed.
+ The logic for compression is that we don't have to uncompress and compress
+ again an already compressed field just because compression method changes.
+*/
+
uint Field_blob::is_equal(Create_field *new_field)
{
- return ((new_field->sql_type == get_blob_type_from_length(max_data_length()))
- && new_field->charset == field_charset &&
- new_field->pack_length == pack_length());
+ return new_field->type_handler() == type_handler() &&
+ new_field->charset == field_charset &&
+ new_field->pack_length == pack_length() &&
+ !new_field->compression_method() == !compression_method();
+}
+
+
+int Field_blob_compressed::store(const char *from, size_t length,
+ CHARSET_INFO *cs)
+{
+ ASSERT_COLUMN_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);
+ String tmp(from, length, cs);
+ int rc;
+
+ if (from >= value.ptr() && from <= value.end() && tmp.copy(from, length, cs))
+ goto oom;
+
+ if (value.alloc(to_length))
+ goto oom;
+
+ rc= compress((char*) value.ptr(), to_length, tmp.ptr(), (uint) length,
+ max_length, &compressed_length, cs, (uint) length);
+ set_ptr(compressed_length, (uchar*) value.ptr());
+ return rc;
+
+oom:
+ set_ptr((uint32) 0, NULL);
+ return -1;
+}
+
+
+String *Field_blob_compressed::val_str(String *val_buffer, String *val_ptr)
+{
+ ASSERT_COLUMN_MARKED_FOR_READ;
+ return uncompress(val_buffer, val_ptr, get_ptr(), get_length());
+}
+
+
+double Field_blob_compressed::val_real(void)
+{
+ ASSERT_COLUMN_MARKED_FOR_READ;
+ THD *thd= get_thd();
+ String buf;
+ val_str(&buf, &buf);
+ return Converter_strntod_with_warn(thd, Warn_filter(thd), field_charset,
+ buf.ptr(), buf.length()).result();
+}
+
+
+longlong Field_blob_compressed::val_int(void)
+{
+ ASSERT_COLUMN_MARKED_FOR_READ;
+ THD *thd= get_thd();
+ String buf;
+ val_str(&buf, &buf);
+ return Converter_strntoll_with_warn(thd, Warn_filter(thd), field_charset,
+ buf.ptr(), buf.length()).result();
}
@@ -8738,7 +8935,7 @@ uint gis_field_options_image(uchar *buff, List<Create_field> &create_fields)
Create_field *field;
while ((field= it++))
{
- if (field->sql_type != MYSQL_TYPE_GEOMETRY)
+ if (field->real_field_type() != MYSQL_TYPE_GEOMETRY)
continue;
if (buff)
{
@@ -8765,7 +8962,7 @@ uint gis_field_options_image(uchar *buff, List<Create_field> &create_fields)
}
-uint gis_field_options_read(const uchar *buf, uint buf_len,
+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;
@@ -8871,7 +9068,7 @@ int Field_geom::store_decimal(const my_decimal *)
}
-int Field_geom::store(const char *from, uint length, CHARSET_INFO *cs)
+int Field_geom::store(const char *from, size_t length, CHARSET_INFO *cs)
{
if (!length)
bzero(ptr, Field_blob::pack_length());
@@ -8888,7 +9085,7 @@ int Field_geom::store(const char *from, uint length, CHARSET_INFO *cs)
wkb_type > (uint32) Geometry::wkb_last)
goto err;
- if (geom_type != Field::GEOM_GEOMETRY &&
+ if (geom_type != Field::GEOM_GEOMETRY &&
geom_type != Field::GEOM_GEOMETRYCOLLECTION &&
(uint32) geom_type != wkb_type)
{
@@ -8904,13 +9101,13 @@ int Field_geom::store(const char *from, uint length, CHARSET_INFO *cs)
if (!tab_name)
tab_name= "";
wkt.set_charset(&my_charset_latin1);
- if (!(geom= Geometry::construct(&buffer, from, length)) ||
+ 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,
+ wkt.c_ptr_safe(), db, tab_name, field_name.str,
(ulong) table->in_use->get_stmt_da()->
current_row_for_warning());
@@ -8947,7 +9144,7 @@ Field::geometry_type Field_geom::geometry_type_merge(geometry_type a,
uint Field_geom::is_equal(Create_field *new_field)
{
- return new_field->sql_type == MYSQL_TYPE_GEOMETRY &&
+ return new_field->type_handler() == type_handler() &&
/*
- Allow ALTER..INPLACE to supertype (GEOMETRY),
e.g. POINT to GEOMETRY or POLYGON to GEOMETRY.
@@ -8977,7 +9174,7 @@ 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,
+ my_error(ER_WARN_NULL_TO_NOTNULL, MYF(0), field_name.str,
thd->get_stmt_da()->current_row_for_warning());
return true;
}
@@ -9024,7 +9221,7 @@ void Field_enum::store_type(ulonglong value)
(if there isn't a empty value in the enum)
*/
-int Field_enum::store(const char *from,uint length,CHARSET_INFO *cs)
+int Field_enum::store(const char *from,size_t length,CHARSET_INFO *cs)
{
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int err= 0;
@@ -9041,7 +9238,7 @@ int Field_enum::store(const char *from,uint length,CHARSET_INFO *cs)
}
/* Remove end space */
- length= field_charset->cset->lengthsp(field_charset, from, length);
+ length= (uint)field_charset->cset->lengthsp(field_charset, from, length);
uint tmp=find_type2(typelib, from, length, field_charset);
if (!tmp)
{
@@ -9054,12 +9251,16 @@ int Field_enum::store(const char *from,uint length,CHARSET_INFO *cs)
{
tmp=0;
set_warning(WARN_DATA_TRUNCATED, 1);
+ err= 1;
}
- if (!get_thd()->count_cuted_fields)
+ if ((get_thd()->count_cuted_fields <= CHECK_FIELD_EXPRESSION) && !length)
err= 0;
}
else
+ {
set_warning(WARN_DATA_TRUNCATED, 1);
+ err= 1;
+ }
}
store_type((ulonglong) tmp);
return err;
@@ -9079,7 +9280,7 @@ int Field_enum::store(longlong nr, bool unsigned_val)
if ((ulonglong) nr > typelib->count || nr == 0)
{
set_warning(WARN_DATA_TRUNCATED, 1);
- if (nr != 0 || get_thd()->count_cuted_fields)
+ if (nr != 0 || get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION)
{
nr= 0;
error= 1;
@@ -9114,7 +9315,7 @@ longlong Field_enum::val_int(void)
@returns number of bytes written to metadata_ptr
*/
-int Field_enum::do_save_field_metadata(uchar *metadata_ptr)
+int Field_enum::save_field_metadata(uchar *metadata_ptr)
{
*metadata_ptr= real_type();
*(metadata_ptr + 1)= pack_length();
@@ -9203,7 +9404,7 @@ Field *Field_enum::make_new_field(MEM_ROOT *root, TABLE *new_table,
*/
-int Field_set::store(const char *from,uint length,CHARSET_INFO *cs)
+int Field_set::store(const char *from,size_t length,CHARSET_INFO *cs)
{
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
bool got_warning= 0;
@@ -9233,6 +9434,7 @@ int Field_set::store(const char *from,uint length,CHARSET_INFO *cs)
{
tmp=0;
set_warning(WARN_DATA_TRUNCATED, 1);
+ err= 1;
}
}
else if (got_warning)
@@ -9397,7 +9599,7 @@ uint Field_enum::is_equal(Create_field *new_field)
The fields are compatible if they have the same flags,
type, charset and have the same underlying length.
*/
- if (new_field->sql_type != real_type() ||
+ if (new_field->type_handler() != type_handler() ||
new_field->charset != field_charset ||
new_field->pack_length != pack_length())
return IS_EQUAL_NO;
@@ -9462,7 +9664,7 @@ bool Field_num::eq_def(const Field *field) const
uint Field_num::is_equal(Create_field *new_field)
{
- return ((new_field->sql_type == real_type()) &&
+ return ((new_field->type_handler() == type_handler()) &&
((new_field->flags & UNSIGNED_FLAG) ==
(uint) (flags & UNSIGNED_FLAG)) &&
((new_field->flags & AUTO_INCREMENT_FLAG) ==
@@ -9471,12 +9673,17 @@ uint Field_num::is_equal(Create_field *new_field)
}
+bool Field_enum::can_optimize_range(const Item_bool_func *cond,
+ const Item *item,
+ bool is_eq_func) const
+{
+ return item->cmp_type() != TIME_RESULT;
+}
+
+
bool Field_enum::can_optimize_keypart_ref(const Item_bool_func *cond,
const Item *item) const
{
- DBUG_ASSERT(cmp_type() == INT_RESULT);
- DBUG_ASSERT(result_type() == STRING_RESULT);
-
switch (item->cmp_type())
{
case TIME_RESULT:
@@ -9526,7 +9733,8 @@ bool Field_enum::can_optimize_keypart_ref(const Item_bool_func *cond,
Field_bit::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 char *field_name_arg)
+ enum utype unireg_check_arg,
+ const LEX_CSTRING *field_name_arg)
: Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg),
bit_ptr(bit_ptr_arg), bit_ofs(bit_ofs_arg), bit_len(len_arg & 7),
@@ -9610,19 +9818,19 @@ Field *Field_bit::new_key_field(MEM_ROOT *root, TABLE *new_table,
uint Field_bit::is_equal(Create_field *new_field)
{
- return (new_field->sql_type == real_type() &&
- new_field->length == max_display_length());
+ return new_field->type_handler() == type_handler() &&
+ new_field->length == max_display_length();
}
-int Field_bit::store(const char *from, uint length, CHARSET_INFO *cs)
+int Field_bit::store(const char *from, size_t length, CHARSET_INFO *cs)
{
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int delta;
for (; length && !*from; from++, length--) // skip left 0's
;
- delta= bytes_in_rec - length;
+ delta= (int)(bytes_in_rec - length);
if (delta < -1 ||
(delta == -1 && (uchar) *from > ((1 << bit_len) - 1)) ||
@@ -9826,9 +10034,9 @@ uint Field_bit::get_key_image(uchar *buff, uint length, imagetype type_arg)
@returns number of bytes written to metadata_ptr
*/
-int Field_bit::do_save_field_metadata(uchar *metadata_ptr)
+int Field_bit::save_field_metadata(uchar *metadata_ptr)
{
- DBUG_ENTER("Field_bit::do_save_field_metadata");
+ DBUG_ENTER("Field_bit::save_field_metadata");
DBUG_PRINT("debug", ("bit_len: %d, bytes_in_rec: %d",
bit_len, bytes_in_rec));
/*
@@ -9898,9 +10106,9 @@ Field_bit::compatible_field_size(uint field_metadata,
void Field_bit::sql_type(String &res) const
{
CHARSET_INFO *cs= res.charset();
- ulong length= cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
+ size_t length= cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
"bit(%d)", (int) field_length);
- res.length((uint) length);
+ res.length(length);
}
@@ -10041,7 +10249,7 @@ int Field_bit::set_default()
Field_bit_as_char::Field_bit_as_char(uchar *ptr_arg, uint32 len_arg,
uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg,
- const char *field_name_arg)
+ const LEX_CSTRING *field_name_arg)
:Field_bit(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, 0, 0,
unireg_check_arg, field_name_arg)
{
@@ -10051,7 +10259,7 @@ Field_bit_as_char::Field_bit_as_char(uchar *ptr_arg, uint32 len_arg,
}
-int Field_bit_as_char::store(const char *from, uint length, CHARSET_INFO *cs)
+int Field_bit_as_char::store(const char *from, size_t length, CHARSET_INFO *cs)
{
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int delta;
@@ -10059,7 +10267,7 @@ int Field_bit_as_char::store(const char *from, uint length, CHARSET_INFO *cs)
for (; length && !*from; from++, length--) // skip left 0's
;
- delta= bytes_in_rec - length;
+ delta= (int)(bytes_in_rec - length);
if (delta < 0 ||
(delta == 0 && bits && (uint) (uchar) *from >= (uint) (1 << bits)))
@@ -10082,9 +10290,9 @@ int Field_bit_as_char::store(const char *from, uint length, CHARSET_INFO *cs)
void Field_bit_as_char::sql_type(String &res) const
{
CHARSET_INFO *cs= res.charset();
- ulong length= cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
+ size_t length= cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
"bit(%d)", (int) field_length);
- res.length((uint) length);
+ res.length(length);
}
@@ -10092,65 +10300,202 @@ void Field_bit_as_char::sql_type(String &res) const
Handling of field and Create_field
*****************************************************************************/
-/**
- Convert create_field::length from number of characters to number of bytes.
-*/
-
-void Column_definition::create_length_to_internal_length(void)
-{
- switch (sql_type) {
- case MYSQL_TYPE_TINY_BLOB:
- case MYSQL_TYPE_MEDIUM_BLOB:
- case MYSQL_TYPE_LONG_BLOB:
- case MYSQL_TYPE_BLOB:
- case MYSQL_TYPE_GEOMETRY:
- case MYSQL_TYPE_VAR_STRING:
- case MYSQL_TYPE_STRING:
- case MYSQL_TYPE_VARCHAR:
- length*= charset->mbmaxlen;
- set_if_smaller(length, UINT_MAX32);
- key_length= (uint32)length;
- pack_length= calc_pack_length(sql_type, key_length);
- break;
- case MYSQL_TYPE_ENUM:
- case MYSQL_TYPE_SET:
- /* Pack_length already calculated in sql_parse.cc */
- length*= charset->mbmaxlen;
- key_length= pack_length;
- break;
- case MYSQL_TYPE_BIT:
- if (f_bit_as_char(pack_flag))
+bool Column_definition::create_interval_from_interval_list(MEM_ROOT *mem_root,
+ bool reuse_interval_list_values)
+{
+ DBUG_ENTER("Column_definition::create_interval_from_interval_list");
+ DBUG_ASSERT(!interval);
+ if (!(interval= (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));
+ DBUG_ASSERT(comma_length >= 0 && comma_length <= (int) sizeof(comma_buf));
+
+ if (!multi_alloc_root(mem_root,
+ &interval->type_names,
+ sizeof(char*) * (interval_list.elements + 1),
+ &interval->type_lengths,
+ sizeof(uint) * (interval_list.elements + 1),
+ NullS))
+ goto err; // EOM
+
+ interval->name= "";
+ interval->count= interval_list.elements;
+
+ for (uint i= 0; i < interval->count; i++)
+ {
+ uint32 dummy;
+ String *tmp= it++;
+ LEX_CSTRING value;
+ if (String::needs_conversion(tmp->length(), tmp->charset(),
+ charset, &dummy))
{
- key_length= pack_length= ((length + 7) & ~7) / 8;
+ uint cnv_errs;
+ conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), charset, &cnv_errs);
+ value.str= strmake_root(mem_root, conv.ptr(), conv.length());
+ value.length= conv.length();
}
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);
+ value.str= reuse_interval_list_values ? tmp->ptr() :
+ strmake_root(mem_root,
+ tmp->ptr(),
+ tmp->length());
+ value.length= tmp->length();
}
- break;
- case MYSQL_TYPE_NEWDECIMAL:
+ if (!value.str)
+ goto err; // EOM
+
+ // Strip trailing spaces.
+ value.length= charset->cset->lengthsp(charset, 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))
+ {
+ 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;
+ }
+ interval->type_names[interval->count]= 0; // End marker
+ interval->type_lengths[interval->count]= 0;
+ interval_list.empty(); // Don't need interval_list anymore
+ DBUG_RETURN(false);
+err:
+ interval= NULL; // Avoid having both non-empty interval_list and interval
+ DBUG_RETURN(true);
+}
+
+
+bool Column_definition::prepare_interval_field(MEM_ROOT *mem_root,
+ bool reuse_interval_list_values)
+{
+ DBUG_ENTER("Column_definition::prepare_interval_field");
+ DBUG_ASSERT(real_field_type() == MYSQL_TYPE_ENUM ||
+ real_field_type() == MYSQL_TYPE_SET);
+ /*
+ Interval values are either in "interval" or in "interval_list",
+ but not in both at the same time, and are not empty at the same time.
+ - Values are in "interval_list" when we're coming from the parser
+ in CREATE TABLE or in CREATE {FUNCTION|PROCEDURE}.
+ - Values are in "interval" when we're in ALTER TABLE.
+
+ In a corner case with an empty set like SET(''):
+ - after the parser we have interval_list.elements==1
+ - in ALTER TABLE we have a non-NULL interval with interval->count==1,
+ with interval->type_names[0]=="" and interval->type_lengths[0]==0.
+ So the assert is still valid for this corner case.
+
+ ENUM and SET with no values at all (e.g. ENUM(), SET()) are not possible,
+ as the parser requires at least one element, so for a ENUM or SET field it
+ should never happen that both internal_list.elements and interval are 0.
+ */
+ DBUG_ASSERT((interval == NULL) == (interval_list.elements > 0));
+
+ /*
+ Create typelib from interval_list, and if necessary
+ convert strings from client character set to the
+ column character set.
+ */
+ if (interval_list.elements &&
+ create_interval_from_interval_list(mem_root,
+ reuse_interval_list_values))
+ DBUG_RETURN(true);
+
+ if (!reuse_interval_list_values)
{
/*
- This code must be identical to code in
- Field_new_decimal::Field_new_decimal as otherwise the record layout
- gets out of sync.
+ We're initializing from an existing table or view Field_enum
+ (e.g. for a %TYPE variable) rather than from the parser.
+ The constructor Column_definition(THD*,Field*,Field*) has already
+ copied the TYPELIB pointer from the original Field_enum.
+ Now we need to make a permanent copy of that TYPELIB,
+ as the original field can be freed before the end of the life
+ cycle of "this".
*/
- uint precision= my_decimal_length_to_precision((uint)length, decimals,
- flags & UNSIGNED_FLAG);
- set_if_smaller(precision, DECIMAL_MAX_PRECISION);
- key_length= pack_length= my_decimal_get_binary_size(precision, decimals);
- break;
+ DBUG_ASSERT(interval);
+ if (!(interval= copy_typelib(mem_root, interval)))
+ DBUG_RETURN(true);
}
- default:
- key_length= pack_length= calc_pack_length(sql_type, (uint)length);
- break;
+ prepare_interval_field_calc_length();
+ DBUG_RETURN(false);
+}
+
+
+void Column_definition::set_attributes(const Lex_field_type_st &type,
+ CHARSET_INFO *cs)
+{
+ 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;
+
+#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
+
+ if (type.length())
+ {
+ int err;
+ length= my_strtoll10(type.length(), NULL, &err);
+ if (err)
+ length= ~0ULL; // safety
+ }
+
+ if (type.dec())
+ decimals= (uint) atoi(type.dec());
+}
+
+
+void Column_definition::create_length_to_internal_length_bit()
+{
+ if (f_bit_as_char(pack_flag))
+ {
+ key_length= 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);
}
}
-bool check_expression(Virtual_column_info *vcol, const char *name,
+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);
+}
+
+
+bool check_expression(Virtual_column_info *vcol, LEX_CSTRING *name,
enum_vcol_info_type type)
{
@@ -10158,7 +10503,7 @@ bool check_expression(Virtual_column_info *vcol, const char *name,
Item::vcol_func_processor_result res;
if (!vcol->name.length)
- vcol->name.str= const_cast<char*>(name);
+ vcol->name= *name;
/*
Walk through the Item tree checking if all items are valid
@@ -10171,11 +10516,13 @@ bool check_expression(Virtual_column_info *vcol, const char *name,
uint filter= VCOL_IMPOSSIBLE;
if (type != VCOL_GENERATED_VIRTUAL && type != VCOL_DEFAULT)
filter|= VCOL_NOT_STRICTLY_DETERMINISTIC;
+ if (type == VCOL_GENERATED_VIRTUAL)
+ filter|= VCOL_NOT_VIRTUAL;
- if (ret || (res.errors & filter))
+ if (unlikely(ret || (res.errors & filter)))
{
my_error(ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(0), res.name,
- vcol_type_name(type), name);
+ vcol_type_name(type), name->str);
return TRUE;
}
/*
@@ -10188,31 +10535,118 @@ bool check_expression(Virtual_column_info *vcol, const char *name,
}
+bool Column_definition::check_length(uint mysql_errno, uint limit) const
+{
+ if (length <= limit)
+ return false;
+ my_error(mysql_errno, MYF(0), field_name.str, static_cast<ulong>(limit));
+ return true;
+}
+
+
+bool Column_definition::fix_attributes_int(uint default_length)
+{
+ if (length)
+ return check_length(ER_TOO_BIG_DISPLAYWIDTH, MAX_FIELD_CHARLENGTH);
+ length= default_length;
+ return false;
+}
+
+
+bool Column_definition::fix_attributes_real(uint default_length)
+{
+ /* change FLOAT(precision) to FLOAT or DOUBLE */
+ if (!length && !decimals)
+ {
+ length= default_length;
+ decimals= NOT_FIXED_DEC;
+ }
+ if (length < decimals && decimals != NOT_FIXED_DEC)
+ {
+ my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name.str);
+ return true;
+ }
+ if (decimals != NOT_FIXED_DEC && decimals >= FLOATING_POINT_DECIMALS)
+ {
+ my_error(ER_TOO_BIG_SCALE, MYF(0), static_cast<ulonglong>(decimals),
+ field_name.str, static_cast<uint>(FLOATING_POINT_DECIMALS-1));
+ return true;
+ }
+ return check_length(ER_TOO_BIG_DISPLAYWIDTH, MAX_FIELD_CHARLENGTH);
+}
+
+
+bool Column_definition::fix_attributes_decimal()
+{
+ if (decimals >= NOT_FIXED_DEC)
+ {
+ my_error(ER_TOO_BIG_SCALE, MYF(0), static_cast<ulonglong>(decimals),
+ field_name.str, static_cast<uint>(NOT_FIXED_DEC - 1));
+ return true;
+ }
+ my_decimal_trim(&length, &decimals);
+ if (length > DECIMAL_MAX_PRECISION)
+ {
+ my_error(ER_TOO_BIG_PRECISION, MYF(0), length, field_name.str,
+ DECIMAL_MAX_PRECISION);
+ return true;
+ }
+ if (length < decimals)
+ {
+ my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name.str);
+ return true;
+ }
+ length= my_decimal_precision_to_length((uint) length, decimals,
+ flags & UNSIGNED_FLAG);
+ pack_length= my_decimal_get_binary_size((uint) length, decimals);
+ return false;
+}
+
+
+bool Column_definition::fix_attributes_bit()
+{
+ if (!length)
+ length= 1;
+ pack_length= ((uint) length + 7) / 8;
+ return check_length(ER_TOO_BIG_DISPLAYWIDTH, MAX_BIT_FIELD_LENGTH);
+}
+
+
+bool Column_definition::fix_attributes_temporal_with_time(uint int_part_length)
+{
+ if (length > MAX_DATETIME_PRECISION)
+ {
+ my_error(ER_TOO_BIG_PRECISION, MYF(0), length, field_name.str,
+ MAX_DATETIME_PRECISION);
+ return true;
+ }
+ length+= int_part_length + (length ? 1 : 0);
+ return false;
+}
+
+
bool Column_definition::check(THD *thd)
{
- const uint conditional_type_modifiers= AUTO_INCREMENT_FLAG;
- uint sign_len, allowed_type_modifier= 0;
- ulong max_field_charlength= MAX_FIELD_CHARLENGTH;
DBUG_ENTER("Column_definition::check");
/* Initialize data for a computed field */
if (vcol_info)
{
DBUG_ASSERT(vcol_info->expr);
- vcol_info->set_field_type(sql_type);
- if (check_expression(vcol_info, field_name, vcol_info->stored_in_db
+ vcol_info->set_field_type(real_field_type());
+ if (check_expression(vcol_info, &field_name, vcol_info->stored_in_db
? VCOL_GENERATED_STORED : VCOL_GENERATED_VIRTUAL))
DBUG_RETURN(TRUE);
}
if (check_constraint &&
- check_expression(check_constraint, field_name, VCOL_CHECK_FIELD))
+ check_expression(check_constraint, &field_name, VCOL_CHECK_FIELD))
DBUG_RETURN(1);
if (default_value)
{
Item *def_expr= default_value->expr;
- if (check_expression(default_value, field_name, VCOL_DEFAULT))
+ if (check_expression(default_value, &field_name, VCOL_DEFAULT))
DBUG_RETURN(TRUE);
/* Constant's are stored in the 'empty_record', except for blobs */
@@ -10223,7 +10657,7 @@ bool Column_definition::check(THD *thd)
default_value= 0;
if ((flags & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) == NOT_NULL_FLAG)
{
- my_error(ER_INVALID_DEFAULT, MYF(0), field_name);
+ my_error(ER_INVALID_DEFAULT, MYF(0), field_name.str);
DBUG_RETURN(1);
}
}
@@ -10232,12 +10666,12 @@ bool Column_definition::check(THD *thd)
if (default_value && (flags & AUTO_INCREMENT_FLAG))
{
- my_error(ER_INVALID_DEFAULT, MYF(0), field_name);
+ my_error(ER_INVALID_DEFAULT, MYF(0), field_name.str);
DBUG_RETURN(1);
}
if (default_value && !default_value->expr->basic_const_item() &&
- mysql_type_to_time_type(sql_type) == MYSQL_TIMESTAMP_DATETIME &&
+ mysql_timestamp_type() == MYSQL_TIMESTAMP_DATETIME &&
default_value->expr->type() == Item::FUNC_ITEM)
{
/*
@@ -10255,10 +10689,10 @@ bool Column_definition::check(THD *thd)
if (on_update)
{
- if (mysql_type_to_time_type(sql_type) != MYSQL_TIMESTAMP_DATETIME ||
+ if (mysql_timestamp_type() != MYSQL_TIMESTAMP_DATETIME ||
on_update->decimals < length)
{
- my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name);
+ my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name.str);
DBUG_RETURN(TRUE);
}
unireg_check= unireg_check == Field::NONE ? Field::TIMESTAMP_UN_FIELD
@@ -10267,186 +10701,9 @@ bool Column_definition::check(THD *thd)
else if (flags & AUTO_INCREMENT_FLAG)
unireg_check= Field::NEXT_NUMBER;
- sign_len= flags & UNSIGNED_FLAG ? 0 : 1;
+ if (type_handler()->Column_definition_fix_attributes(this))
+ DBUG_RETURN(true);
- switch (sql_type) {
- case MYSQL_TYPE_TINY:
- if (!length)
- length= MAX_TINYINT_WIDTH+sign_len;
- allowed_type_modifier= AUTO_INCREMENT_FLAG;
- break;
- case MYSQL_TYPE_SHORT:
- if (!length)
- length= MAX_SMALLINT_WIDTH+sign_len;
- allowed_type_modifier= AUTO_INCREMENT_FLAG;
- break;
- case MYSQL_TYPE_INT24:
- if (!length)
- length= MAX_MEDIUMINT_WIDTH+sign_len;
- allowed_type_modifier= AUTO_INCREMENT_FLAG;
- break;
- case MYSQL_TYPE_LONG:
- if (!length)
- length= MAX_INT_WIDTH+sign_len;
- allowed_type_modifier= AUTO_INCREMENT_FLAG;
- break;
- case MYSQL_TYPE_LONGLONG:
- if (!length)
- length= MAX_BIGINT_WIDTH;
- allowed_type_modifier= AUTO_INCREMENT_FLAG;
- break;
- case MYSQL_TYPE_NULL:
- break;
- case MYSQL_TYPE_NEWDECIMAL:
- if (decimals >= NOT_FIXED_DEC)
- {
- my_error(ER_TOO_BIG_SCALE, MYF(0), static_cast<ulonglong>(decimals),
- field_name, static_cast<uint>(NOT_FIXED_DEC - 1));
- DBUG_RETURN(TRUE);
- }
- my_decimal_trim(&length, &decimals);
- if (length > DECIMAL_MAX_PRECISION)
- {
- my_error(ER_TOO_BIG_PRECISION, MYF(0), length, field_name,
- DECIMAL_MAX_PRECISION);
- DBUG_RETURN(TRUE);
- }
- if (length < decimals)
- {
- my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name);
- DBUG_RETURN(TRUE);
- }
- length=
- my_decimal_precision_to_length((uint)length, decimals, flags & UNSIGNED_FLAG);
- pack_length=
- my_decimal_get_binary_size((uint)length, decimals);
- break;
- case MYSQL_TYPE_VARCHAR:
- /*
- Long VARCHAR's are automatically converted to blobs in mysql_prepare_table
- if they don't have a default value
- */
- max_field_charlength= MAX_FIELD_VARCHARLENGTH;
- break;
- case MYSQL_TYPE_STRING:
- break;
- case MYSQL_TYPE_BLOB:
- case MYSQL_TYPE_TINY_BLOB:
- case MYSQL_TYPE_LONG_BLOB:
- case MYSQL_TYPE_MEDIUM_BLOB:
- case MYSQL_TYPE_GEOMETRY:
- flags|= BLOB_FLAG;
- break;
- case MYSQL_TYPE_YEAR:
- if (!length || length != 2)
- length= 4; /* Default length */
- flags|= ZEROFILL_FLAG | UNSIGNED_FLAG;
- break;
- case MYSQL_TYPE_FLOAT:
- /* change FLOAT(precision) to FLOAT or DOUBLE */
- allowed_type_modifier= AUTO_INCREMENT_FLAG;
- if (!length && !decimals)
- {
- length= MAX_FLOAT_STR_LENGTH;
- decimals= NOT_FIXED_DEC;
- }
- if (length < decimals &&
- decimals != NOT_FIXED_DEC)
- {
- my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name);
- DBUG_RETURN(TRUE);
- }
- if (decimals != NOT_FIXED_DEC && decimals >= FLOATING_POINT_DECIMALS)
- {
- my_error(ER_TOO_BIG_SCALE, MYF(0), static_cast<ulonglong>(decimals),
- field_name, static_cast<uint>(FLOATING_POINT_DECIMALS-1));
- DBUG_RETURN(TRUE);
- }
- break;
- case MYSQL_TYPE_DOUBLE:
- allowed_type_modifier= AUTO_INCREMENT_FLAG;
- if (!length && !decimals)
- {
- length= DBL_DIG+7;
- decimals= NOT_FIXED_DEC;
- }
- if (length < decimals &&
- decimals != NOT_FIXED_DEC)
- {
- my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name);
- DBUG_RETURN(TRUE);
- }
- if (decimals != NOT_FIXED_DEC && decimals >= FLOATING_POINT_DECIMALS)
- {
- my_error(ER_TOO_BIG_SCALE, MYF(0), static_cast<ulonglong>(decimals),
- field_name, static_cast<uint>(FLOATING_POINT_DECIMALS-1));
- DBUG_RETURN(TRUE);
- }
- break;
- case MYSQL_TYPE_TIMESTAMP:
- case MYSQL_TYPE_TIMESTAMP2:
- if (length > MAX_DATETIME_PRECISION)
- {
- my_error(ER_TOO_BIG_PRECISION, MYF(0), length, field_name,
- MAX_DATETIME_PRECISION);
- DBUG_RETURN(TRUE);
- }
- length+= MAX_DATETIME_WIDTH + (length ? 1 : 0);
- flags|= UNSIGNED_FLAG;
- break;
- case MYSQL_TYPE_DATE:
- /* We don't support creation of MYSQL_TYPE_DATE anymore */
- sql_type= MYSQL_TYPE_NEWDATE;
- /* fall through */
- case MYSQL_TYPE_NEWDATE:
- length= MAX_DATE_WIDTH;
- break;
- case MYSQL_TYPE_TIME:
- case MYSQL_TYPE_TIME2:
- if (length > MAX_DATETIME_PRECISION)
- {
- my_error(ER_TOO_BIG_PRECISION, MYF(0), length, field_name,
- MAX_DATETIME_PRECISION);
- DBUG_RETURN(TRUE);
- }
- length+= MIN_TIME_WIDTH + (length ? 1 : 0);
- break;
- case MYSQL_TYPE_DATETIME:
- case MYSQL_TYPE_DATETIME2:
- if (length > MAX_DATETIME_PRECISION)
- {
- my_error(ER_TOO_BIG_PRECISION, MYF(0), length, field_name,
- MAX_DATETIME_PRECISION);
- DBUG_RETURN(TRUE);
- }
- length+= MAX_DATETIME_WIDTH + (length ? 1 : 0);
- break;
- case MYSQL_TYPE_SET:
- pack_length= get_set_pack_length(interval_list.elements);
- break;
- case MYSQL_TYPE_ENUM:
- /* Should be safe. */
- pack_length= get_enum_pack_length(interval_list.elements);
- break;
- case MYSQL_TYPE_VAR_STRING:
- DBUG_ASSERT(0); /* Impossible. */
- break;
- case MYSQL_TYPE_BIT:
- {
- if (!length)
- length= 1;
- if (length > MAX_BIT_FIELD_LENGTH)
- {
- my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), field_name,
- static_cast<ulong>(MAX_BIT_FIELD_LENGTH));
- DBUG_RETURN(TRUE);
- }
- pack_length= ((uint)length + 7) / 8;
- break;
- }
- case MYSQL_TYPE_DECIMAL:
- DBUG_ASSERT(0); /* Was obsolete */
- }
/* Remember the value of length */
char_length= (uint)length;
@@ -10462,38 +10719,18 @@ bool Column_definition::check(THD *thd)
TIMESTAMP columns get implicit DEFAULT value when
explicit_defaults_for_timestamp is not set.
*/
- if (opt_explicit_defaults_for_timestamp ||
- !is_timestamp_type(sql_type))
+ if ((opt_explicit_defaults_for_timestamp ||
+ !is_timestamp_type()) && !vers_sys_field())
{
flags|= NO_DEFAULT_VALUE_FLAG;
}
}
- if (!(flags & BLOB_FLAG) &&
- ((length > max_field_charlength &&
- sql_type != MYSQL_TYPE_VARCHAR) ||
- (length == 0 &&
- sql_type != MYSQL_TYPE_ENUM && sql_type != MYSQL_TYPE_SET &&
- sql_type != MYSQL_TYPE_STRING && sql_type != MYSQL_TYPE_VARCHAR &&
- sql_type != MYSQL_TYPE_GEOMETRY)))
- {
- my_error((sql_type == MYSQL_TYPE_VAR_STRING ||
- sql_type == MYSQL_TYPE_VARCHAR ||
- sql_type == MYSQL_TYPE_STRING) ? ER_TOO_BIG_FIELDLENGTH :
- ER_TOO_BIG_DISPLAYWIDTH,
- MYF(0),
- field_name, max_field_charlength); /* purecov: inspected */
- DBUG_RETURN(TRUE);
- }
- else if (length > MAX_FIELD_BLOBLENGTH)
- {
- my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), field_name, MAX_FIELD_BLOBLENGTH);
- DBUG_RETURN(1);
- }
- if ((~allowed_type_modifier) & flags & conditional_type_modifiers)
+ if ((flags & AUTO_INCREMENT_FLAG) &&
+ !type_handler()->type_can_have_auto_increment_attribute())
{
- my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name);
+ my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name.str);
DBUG_RETURN(TRUE);
}
@@ -10515,64 +10752,6 @@ enum_field_types get_blob_type_from_length(ulong length)
}
-/*
- Make a field from the .frm file info
-*/
-
-uint32 calc_pack_length(enum_field_types type,uint32 length)
-{
- switch (type) {
- case MYSQL_TYPE_VAR_STRING:
- case MYSQL_TYPE_STRING:
- case MYSQL_TYPE_DECIMAL: return (length);
- case MYSQL_TYPE_VARCHAR: return (length + (length < 256 ? 1: 2));
- case MYSQL_TYPE_YEAR:
- case MYSQL_TYPE_TINY : return 1;
- case MYSQL_TYPE_SHORT : return 2;
- case MYSQL_TYPE_INT24:
- case MYSQL_TYPE_NEWDATE: return 3;
- case MYSQL_TYPE_TIME: return length > MIN_TIME_WIDTH
- ? time_hires_bytes[length - 1 - MIN_TIME_WIDTH]
- : 3;
- case MYSQL_TYPE_TIME2:
- return length > MIN_TIME_WIDTH ?
- my_time_binary_length(length - MIN_TIME_WIDTH - 1) : 3;
- case MYSQL_TYPE_TIMESTAMP:
- return length > MAX_DATETIME_WIDTH
- ? 4 + sec_part_bytes[length - 1 - MAX_DATETIME_WIDTH]
- : 4;
- case MYSQL_TYPE_TIMESTAMP2:
- return length > MAX_DATETIME_WIDTH ?
- my_timestamp_binary_length(length - MAX_DATETIME_WIDTH - 1) : 4;
- case MYSQL_TYPE_DATE:
- case MYSQL_TYPE_LONG : return 4;
- case MYSQL_TYPE_FLOAT : return sizeof(float);
- case MYSQL_TYPE_DOUBLE: return sizeof(double);
- case MYSQL_TYPE_DATETIME:
- return length > MAX_DATETIME_WIDTH
- ? datetime_hires_bytes[length - 1 - MAX_DATETIME_WIDTH]
- : 8;
- case MYSQL_TYPE_DATETIME2:
- return length > MAX_DATETIME_WIDTH ?
- my_datetime_binary_length(length - MAX_DATETIME_WIDTH - 1) : 5;
- case MYSQL_TYPE_LONGLONG: return 8; /* Don't crash if no longlong */
- case MYSQL_TYPE_NULL : return 0;
- case MYSQL_TYPE_TINY_BLOB: return 1+portable_sizeof_char_ptr;
- case MYSQL_TYPE_BLOB: return 2+portable_sizeof_char_ptr;
- case MYSQL_TYPE_MEDIUM_BLOB: return 3+portable_sizeof_char_ptr;
- case MYSQL_TYPE_LONG_BLOB: return 4+portable_sizeof_char_ptr;
- case MYSQL_TYPE_GEOMETRY: return 4+portable_sizeof_char_ptr;
- case MYSQL_TYPE_SET:
- case MYSQL_TYPE_ENUM:
- case MYSQL_TYPE_NEWDECIMAL:
- abort(); return 0; // This shouldn't happen
- case MYSQL_TYPE_BIT: return length / 8;
- default:
- return 0;
- }
-}
-
-
uint pack_length_to_packflag(uint type)
{
switch (type) {
@@ -10591,17 +10770,33 @@ Field *make_field(TABLE_SHARE *share,
uchar *ptr, uint32 field_length,
uchar *null_pos, uchar null_bit,
uint pack_flag,
- enum_field_types field_type,
+ const Type_handler *handler,
CHARSET_INFO *field_charset,
Field::geometry_type geom_type, uint srid,
Field::utype unireg_check,
TYPELIB *interval,
- const char *field_name)
+ const LEX_CSTRING *field_name,
+ uint32 flags)
{
uchar *UNINIT_VAR(bit_ptr);
uchar UNINIT_VAR(bit_offset);
- if (field_type == MYSQL_TYPE_BIT && !f_bit_as_char(pack_flag))
+ DBUG_PRINT("debug", ("field_type: %s, field_length: %u, interval: %p, pack_flag: %s%s%s%s%s",
+ handler->name().ptr(), field_length, interval,
+ FLAGSTR(pack_flag, FIELDFLAG_BINARY),
+ FLAGSTR(pack_flag, FIELDFLAG_INTERVAL),
+ FLAGSTR(pack_flag, FIELDFLAG_NUMBER),
+ FLAGSTR(pack_flag, FIELDFLAG_PACK),
+ FLAGSTR(pack_flag, FIELDFLAG_BLOB)));
+
+ if (handler == &type_handler_row)
+ {
+ DBUG_ASSERT(field_length == 0);
+ DBUG_ASSERT(f_maybe_null(pack_flag));
+ return new (mem_root) Field_row(ptr, field_name);
+ }
+
+ if (handler->real_field_type() == MYSQL_TYPE_BIT && !f_bit_as_char(pack_flag))
{
bit_ptr= null_pos;
bit_offset= null_bit;
@@ -10622,18 +10817,12 @@ Field *make_field(TABLE_SHARE *share,
null_bit= ((uchar) 1) << null_bit;
}
- DBUG_PRINT("debug", ("field_type: %d, field_length: %u, interval: %p, pack_flag: %s%s%s%s%s",
- field_type, field_length, interval,
- FLAGSTR(pack_flag, FIELDFLAG_BINARY),
- FLAGSTR(pack_flag, FIELDFLAG_INTERVAL),
- FLAGSTR(pack_flag, FIELDFLAG_NUMBER),
- FLAGSTR(pack_flag, FIELDFLAG_PACK),
- FLAGSTR(pack_flag, FIELDFLAG_BLOB)));
if (f_is_alpha(pack_flag))
{
if (!f_is_packed(pack_flag))
{
+ enum_field_types field_type= handler->real_field_type();
if (field_type == MYSQL_TYPE_STRING ||
field_type == MYSQL_TYPE_DECIMAL || // 3.23 or 4.0 string
field_type == MYSQL_TYPE_VAR_STRING)
@@ -10642,6 +10831,16 @@ Field *make_field(TABLE_SHARE *share,
unireg_check, field_name,
field_charset);
if (field_type == MYSQL_TYPE_VARCHAR)
+ {
+ if (unireg_check == Field::TMYSQL_COMPRESSED)
+ return new (mem_root)
+ Field_varstring_compressed(
+ ptr, field_length,
+ HA_VARCHAR_PACKLENGTH(field_length),
+ null_pos, null_bit,
+ unireg_check, field_name,
+ share, field_charset, zlib_compression_method);
+
return new (mem_root)
Field_varstring(ptr,field_length,
HA_VARCHAR_PACKLENGTH(field_length),
@@ -10649,12 +10848,16 @@ Field *make_field(TABLE_SHARE *share,
unireg_check, field_name,
share,
field_charset);
+ }
return 0; // Error
}
- uint pack_length=calc_pack_length((enum_field_types)
- f_packtype(pack_flag),
- field_length);
+ // MYSQL_TYPE_VAR_STRING is handled above
+ DBUG_ASSERT(f_packtype(pack_flag) != MYSQL_TYPE_VAR_STRING);
+ const Type_handler *tmp;
+ tmp= Type_handler::get_handler_by_real_type((enum_field_types)
+ f_packtype(pack_flag));
+ uint pack_length= tmp->calc_pack_length(field_length);
#ifdef HAVE_SPATIAL
if (f_is_geom(pack_flag))
@@ -10667,10 +10870,18 @@ Field *make_field(TABLE_SHARE *share,
}
#endif
if (f_is_blob(pack_flag))
+ {
+ if (unireg_check == Field::TMYSQL_COMPRESSED)
+ return new (mem_root)
+ Field_blob_compressed(ptr, null_pos, null_bit,
+ unireg_check, field_name, share,
+ pack_length, field_charset, zlib_compression_method);
+
return new (mem_root)
Field_blob(ptr,null_pos,null_bit,
unireg_check, field_name, share,
pack_length, field_charset);
+ }
if (interval)
{
if (f_is_enum(pack_flag))
@@ -10686,7 +10897,7 @@ Field *make_field(TABLE_SHARE *share,
}
}
- switch (field_type) {
+ switch (handler->real_field_type()) {
case MYSQL_TYPE_DECIMAL:
return new (mem_root)
Field_decimal(ptr,field_length,null_pos,null_bit,
@@ -10750,11 +10961,22 @@ Field *make_field(TABLE_SHARE *share,
f_is_zerofill(pack_flag) != 0,
f_is_dec(pack_flag) == 0);
case MYSQL_TYPE_LONGLONG:
- return new (mem_root)
- Field_longlong(ptr,field_length,null_pos,null_bit,
- unireg_check, field_name,
- f_is_zerofill(pack_flag) != 0,
- f_is_dec(pack_flag) == 0);
+ if (flags & (VERS_SYS_START_FLAG|VERS_SYS_END_FLAG))
+ {
+ return new (mem_root)
+ Field_vers_trx_id(ptr, field_length, null_pos, null_bit,
+ unireg_check, field_name,
+ f_is_zerofill(pack_flag) != 0,
+ f_is_dec(pack_flag) == 0);
+ }
+ else
+ {
+ return new (mem_root)
+ Field_longlong(ptr,field_length,null_pos,null_bit,
+ unireg_check, field_name,
+ f_is_zerofill(pack_flag) != 0,
+ f_is_dec(pack_flag) == 0);
+ }
case MYSQL_TYPE_TIMESTAMP:
{
uint dec= field_length > MAX_DATETIME_WIDTH ?
@@ -10831,42 +11053,63 @@ Field *make_field(TABLE_SHARE *share,
return 0;
}
+bool Field_vers_trx_id::test_if_equality_guarantees_uniqueness(const Item* item) const
+{
+ return item->type() == Item::DATE_ITEM;
+}
+
/** Create a field suitable for create of table. */
Column_definition::Column_definition(THD *thd, Field *old_field,
Field *orig_field)
{
+ on_update= NULL;
field_name= old_field->field_name;
length= old_field->field_length;
flags= old_field->flags;
unireg_check=old_field->unireg_check;
pack_length=old_field->pack_length();
key_length= old_field->key_length();
- sql_type= old_field->real_type();
+ set_handler(old_field->type_handler());
charset= old_field->charset(); // May be NULL ptr
comment= old_field->comment;
decimals= old_field->decimals();
vcol_info= old_field->vcol_info;
- default_value= orig_field ? orig_field->default_value : 0;
- check_constraint= orig_field ? orig_field->check_constraint : 0;
option_list= old_field->option_list;
+ pack_flag= 0;
+ compression_method_ptr= 0;
+ versioning= VERSIONING_NOT_SET;
+ invisible= old_field->invisible;
- switch (sql_type) {
- case MYSQL_TYPE_BLOB:
- switch (pack_length - portable_sizeof_char_ptr) {
- case 1: sql_type= MYSQL_TYPE_TINY_BLOB; break;
- case 2: sql_type= MYSQL_TYPE_BLOB; break;
- case 3: sql_type= MYSQL_TYPE_MEDIUM_BLOB; break;
- default: sql_type= MYSQL_TYPE_LONG_BLOB; break;
+ if (orig_field)
+ {
+ default_value= orig_field->default_value;
+ check_constraint= orig_field->check_constraint;
+ if (orig_field->unireg_check == Field::TMYSQL_COMPRESSED)
+ {
+ unireg_check= Field::TMYSQL_COMPRESSED;
+ compression_method_ptr= zlib_compression_method;
}
+ }
+ else
+ {
+ default_value= 0;
+ check_constraint= 0;
+ }
+
+ switch (real_field_type()) {
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
length/= charset->mbmaxlen;
key_length/= charset->mbmaxlen;
break;
case MYSQL_TYPE_STRING:
/* Change CHAR -> VARCHAR if dynamic record length */
if (old_field->type() == MYSQL_TYPE_VAR_STRING)
- sql_type= MYSQL_TYPE_VARCHAR;
+ set_handler(&type_handler_varchar);
/* fall through */
case MYSQL_TYPE_ENUM:
@@ -10874,7 +11117,8 @@ Column_definition::Column_definition(THD *thd, Field *old_field,
case MYSQL_TYPE_VARCHAR:
case MYSQL_TYPE_VAR_STRING:
/* This is corrected in create_length_to_internal_length */
- length= (length+charset->mbmaxlen-1) / charset->mbmaxlen;
+ length= (length+charset->mbmaxlen-1) / charset->mbmaxlen -
+ MY_TEST(old_field->compression_method());
break;
#ifdef HAVE_SPATIAL
case MYSQL_TYPE_GEOMETRY:
@@ -10910,6 +11154,9 @@ Column_definition::Column_definition(THD *thd, Field *old_field,
interval= ((Field_enum*) old_field)->typelib;
else
interval=0;
+
+ interval_list.empty(); // prepare_interval_field() needs this
+
char_length= (uint)length;
/*
@@ -10949,6 +11196,35 @@ Column_definition::Column_definition(THD *thd, Field *old_field,
/**
+ The common part for data type redefinition:
+ CREATE TABLE t1 (a INT) AS SELECT a FROM t2;
+ See Type_handler::Column_definition_redefine_stage1()
+ for data type specific code.
+*/
+void
+Column_definition::redefine_stage1_common(const Column_definition *dup_field,
+ const handler *file,
+ const Schema_specification_st *schema)
+{
+ set_handler(dup_field->type_handler());
+ default_value= dup_field->default_value;
+ charset= dup_field->charset ? dup_field->charset :
+ 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;
+ interval= dup_field->interval;
+ vcol_info= dup_field->vcol_info;
+ invisible= dup_field->invisible;
+ check_constraint= dup_field->check_constraint;
+}
+
+
+
+/**
maximum possible character length for blob.
This method is used in Item_field::set_field to calculate
@@ -10964,6 +11240,12 @@ Column_definition::Column_definition(THD *thd, Field *old_field,
uint32 Field_blob::char_length() const
{
+ return Field_blob::octet_length();
+}
+
+
+uint32 Field_blob::octet_length() const
+{
switch (packlength)
{
case 1:
@@ -10973,7 +11255,7 @@ uint32 Field_blob::char_length() const
case 3:
return 16777215;
case 4:
- return (uint32) 4294967295U;
+ return (uint32) UINT_MAX32;
default:
DBUG_ASSERT(0); // we should never go here
return 0;
@@ -11008,6 +11290,57 @@ bool Column_definition::has_default_expression()
(flags & BLOB_FLAG)));
}
+
+bool Column_definition::set_compressed(const char *method)
+{
+ if (!method || !strcmp(method, zlib_compression_method->name))
+ {
+ unireg_check= Field::TMYSQL_COMPRESSED;
+ compression_method_ptr= zlib_compression_method;
+ return false;
+ }
+ my_error(ER_UNKNOWN_COMPRESSION_METHOD, MYF(0), method);
+ return true;
+}
+
+
+bool Column_definition::set_compressed_deprecated(THD *thd, const char *method)
+{
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_WARN_DEPRECATED_SYNTAX,
+ ER_THD(thd, ER_WARN_DEPRECATED_SYNTAX),
+ "<data type> <character set clause> ... COMPRESSED...",
+ "'<data type> COMPRESSED... <character set clause> ...'");
+ return set_compressed(method);
+}
+
+
+bool
+Column_definition::set_compressed_deprecated_column_attribute(THD *thd,
+ const char *pos,
+ const char *method)
+{
+ if (compression_method_ptr)
+ {
+ /*
+ Compression method has already been set, e.g.:
+ a VARCHAR(10) COMPRESSED DEFAULT 10 COMPRESSED
+ */
+ thd->parse_error(ER_SYNTAX_ERROR, pos);
+ return true;
+ }
+ enum enum_field_types sql_type= real_field_type();
+ /* We can't use f_is_blob here as pack_flag is not yet set */
+ if (sql_type == MYSQL_TYPE_VARCHAR || sql_type == MYSQL_TYPE_TINY_BLOB ||
+ sql_type == MYSQL_TYPE_BLOB || sql_type == MYSQL_TYPE_MEDIUM_BLOB ||
+ sql_type == MYSQL_TYPE_LONG_BLOB)
+ return set_compressed_deprecated(thd, method);
+ else
+ my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name.str);
+ return true;
+}
+
+
/**
maximum possible display length for blob.
@@ -11015,7 +11348,7 @@ bool Column_definition::has_default_expression()
length
*/
-uint32 Field_blob::max_display_length()
+uint32 Field_blob::max_display_length() const
{
switch (packlength)
{
@@ -11026,7 +11359,7 @@ uint32 Field_blob::max_display_length()
case 3:
return 16777215 * field_charset->mbmaxlen;
case 4:
- return (uint32) 4294967295U;
+ return (uint32) UINT_MAX32;
default:
DBUG_ASSERT(0); // we should never go here
return 0;
@@ -11044,13 +11377,15 @@ uint32 Field_blob::max_display_length()
@param level - level of message (Note/Warning/Error)
@param code - error code of message to be produced
@param cut_increment - whenever we should increase cut fields count
+ @current_row - current row number
@note
- This function won't produce warning and increase cut fields counter
- if count_cuted_fields == CHECK_FIELD_IGNORE for current thread.
+ This function won't produce warning or notes or increase cut fields counter
+ if count_cuted_fields == CHECK_FIELD_IGNORE or CHECK_FIELD_EXPRESSION
+ for the current thread.
- if count_cuted_fields == CHECK_FIELD_IGNORE then we ignore notes.
- This allows us to avoid notes in optimisation, like convert_constant_item().
+ This allows us to avoid notes in optimisation, like
+ convert_constant_item().
@retval
1 if count_cuted_fields == CHECK_FIELD_IGNORE and error level is not NOTE
@@ -11060,18 +11395,19 @@ uint32 Field_blob::max_display_length()
bool
Field::set_warning(Sql_condition::enum_warning_level level, uint code,
- int cut_increment) const
+ int cut_increment, ulong current_row) const
{
/*
If this field was created only for type conversion purposes it
will have table == NULL.
*/
THD *thd= get_thd();
- if (thd->count_cuted_fields)
+ if (thd->count_cuted_fields > CHECK_FIELD_EXPRESSION)
{
thd->cuted_fields+= cut_increment;
- push_warning_printf(thd, level, code, ER_THD(thd, code), field_name,
- thd->get_stmt_da()->current_row_for_warning());
+ push_warning_printf(thd, level, code, ER_THD(thd, code), field_name.str,
+ current_row ? current_row
+ : thd->get_stmt_da()->current_row_for_warning());
return 0;
}
return level >= Sql_condition::WARN_LEVEL_WARN;
@@ -11104,7 +11440,8 @@ void Field::set_datetime_warning(Sql_condition::enum_warning_level level,
THD *thd= get_thd();
if (thd->really_abort_on_warning() && level >= Sql_condition::WARN_LEVEL_WARN)
make_truncated_value_warning(thd, level, str, ts_type,
- table->s->db.str, table->s->table_name.str, field_name);
+ table->s->db.str, table->s->table_name.str,
+ field_name.str);
else
set_warning(level, code, cuted_increment);
}
@@ -11129,8 +11466,7 @@ void Field::set_warning_truncated_wrong_value(const char *type_arg,
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
ER_THD(thd, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
- type_arg, value,
- db_name, table_name, field_name,
+ type_arg, value, db_name, table_name, field_name.str,
static_cast<ulong>(thd->get_stmt_da()->
current_row_for_warning()));
}
@@ -11172,7 +11508,7 @@ bool Field::validate_value_in_record_with_warn(THD *thd, const uchar *record)
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_INVALID_DEFAULT_VALUE_FOR_FIELD,
ER_THD(thd, ER_INVALID_DEFAULT_VALUE_FOR_FIELD),
- ErrConvString(&tmp).ptr(), field_name);
+ ErrConvString(&tmp).ptr(), field_name.str);
}
dbug_tmp_restore_column_map(table->read_set, old_map);
return rc;
@@ -11183,8 +11519,15 @@ bool Field::save_in_field_default_value(bool view_error_processing)
{
THD *thd= table->in_use;
- if (flags & NO_DEFAULT_VALUE_FLAG &&
- real_type() != MYSQL_TYPE_ENUM)
+ /*
+ TODO: MDEV-19597 Refactor TABLE::vers_update_fields() via stored virtual columns
+ This condition will go away as well as other conditions with vers_sys_field().
+ */
+ if (vers_sys_field())
+ return false;
+
+ if (unlikely(flags & NO_DEFAULT_VALUE_FLAG &&
+ real_type() != MYSQL_TYPE_ENUM))
{
if (reset())
{
@@ -11207,7 +11550,7 @@ bool Field::save_in_field_default_value(bool view_error_processing)
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_NO_DEFAULT_FOR_FIELD,
ER_THD(thd, ER_NO_DEFAULT_FOR_FIELD),
- field_name);
+ field_name.str);
}
return true;
}
@@ -11240,3 +11583,22 @@ void Field::register_field_in_read_map()
}
bitmap_set_bit(table->read_set, field_index);
}
+
+
+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;
+
+ val_str(&str);
+ if (!(to->length= str.length()))
+ *to= empty_clex_str;
+ 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;
+}
diff --git a/sql/field.h b/sql/field.h
index 18e44f1d9d4..be3a648617b 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -31,6 +31,8 @@
#include "my_decimal.h" /* my_decimal */
#include "sql_error.h" /* Sql_condition */
#include "compat56.h"
+#include "sql_type.h" /* Type_std_attributes */
+#include "field_comp.h"
class Send_field;
class Copy_field;
@@ -43,12 +45,16 @@ class Column_statistics_collected;
class Item_func;
class Item_bool_func;
class Item_equal;
+class Virtual_tmp_table;
+class Qualified_column_ident;
+class Table_ident;
enum enum_check_fields
{
CHECK_FIELD_IGNORE,
+ CHECK_FIELD_EXPRESSION,
CHECK_FIELD_WARN,
- CHECK_FIELD_ERROR_FOR_NULL
+ CHECK_FIELD_ERROR_FOR_NULL,
};
/*
@@ -211,7 +217,8 @@ protected:
CHARSET_INFO *cs, const char *str, size_t length,
my_decimal *buf)
{
- m_error= str2my_decimal(mask, str,(uint) length, cs,
+ DBUG_ASSERT(length < UINT_MAX32);
+ m_error= str2my_decimal(mask, str, length, cs,
buf, (const char **) &m_end_of_num);
// E_DEC_TRUNCATED means a very minor truncation: '1e-100' -> 0
m_edom= m_error && m_error != E_DEC_TRUNCATED;
@@ -309,7 +316,7 @@ protected:
return decimal_value;
}
- longlong longlong_from_hex_hybrid(const char *str, uint32 length)
+ longlong longlong_from_hex_hybrid(const char *str, size_t length)
{
const char *end= str + length;
const char *ptr= end - MY_MIN(length, sizeof(longlong));
@@ -364,24 +371,25 @@ public:
Subst_constraint m_subst_constraint;
/*
Comparison type.
- Impostant only when ANY_SUBSTS.
+ Important only when ANY_SUBSTS.
*/
- Item_result m_compare_type;
+ const Type_handler *m_compare_handler;
/*
Collation of the comparison operation.
Important only when ANY_SUBST.
*/
CHARSET_INFO *m_compare_collation;
public:
- Context(Subst_constraint subst, Item_result type, CHARSET_INFO *cs)
+ Context(Subst_constraint subst, const Type_handler *h, CHARSET_INFO *cs)
:m_subst_constraint(subst),
- m_compare_type(type),
- m_compare_collation(cs) { }
+ m_compare_handler(h),
+ m_compare_collation(cs)
+ { DBUG_ASSERT(h == h->type_handler_for_comparison()); }
Subst_constraint subst_constraint() const { return m_subst_constraint; }
- Item_result compare_type() const
+ const Type_handler *compare_type_handler() const
{
DBUG_ASSERT(m_subst_constraint == ANY_SUBST);
- return m_compare_type;
+ return m_compare_handler;
}
CHARSET_INFO *compare_collation() const
{
@@ -393,35 +401,21 @@ public:
{ // Use this to request only exact value, no invariants.
public:
Context_identity()
- :Context(IDENTITY_SUBST, STRING_RESULT, &my_charset_bin) { }
+ :Context(IDENTITY_SUBST, &type_handler_long_blob, &my_charset_bin) { }
};
class Context_boolean: public Context
{ // Use this when an item is [a part of] a boolean expression
public:
- Context_boolean() :Context(ANY_SUBST, INT_RESULT, &my_charset_bin) { }
+ Context_boolean()
+ :Context(ANY_SUBST, &type_handler_longlong, &my_charset_bin) { }
};
};
-enum Derivation
-{
- DERIVATION_IGNORABLE= 6,
- DERIVATION_NUMERIC= 5,
- DERIVATION_COERCIBLE= 4,
- DERIVATION_SYSCONST= 3,
- DERIVATION_IMPLICIT= 2,
- DERIVATION_NONE= 1,
- DERIVATION_EXPLICIT= 0
-};
-
-
#define STORAGE_TYPE_MASK 7
#define COLUMN_FORMAT_MASK 7
#define COLUMN_FORMAT_SHIFT 3
-#define my_charset_numeric my_charset_latin1
-#define MY_REPERTOIRE_NUMERIC MY_REPERTOIRE_ASCII
-
/* The length of the header part for each virtual column in the .frm file */
#define FRM_VCOL_OLD_HEADER_SIZE(b) (3 + MY_TEST(b))
#define FRM_VCOL_NEW_BASE_SIZE 16
@@ -467,6 +461,7 @@ inline bool is_temporal_type_with_date(enum_field_types type)
case MYSQL_TYPE_DATETIME2:
case MYSQL_TYPE_TIMESTAMP2:
DBUG_ASSERT(0); // field->real_type() should not get to here.
+ /* fall through */
default:
return false;
}
@@ -474,16 +469,6 @@ inline bool is_temporal_type_with_date(enum_field_types type)
/**
- Recognizer for concrete data type (called real_type for some reason),
- returning true if it is one of the TIMESTAMP types.
-*/
-inline bool is_timestamp_type(enum_field_types type)
-{
- return type == MYSQL_TYPE_TIMESTAMP || type == MYSQL_TYPE_TIMESTAMP2;
-}
-
-
-/**
Convert temporal real types as returned by field->real_type()
to field type as returned by field->type().
@@ -508,58 +493,6 @@ inline enum_field_types real_type_to_type(enum_field_types real_type)
}
-static inline enum enum_mysql_timestamp_type
-mysql_type_to_time_type(enum enum_field_types mysql_type)
-{
- switch(mysql_type) {
- case MYSQL_TYPE_TIME2:
- case MYSQL_TYPE_TIME: return MYSQL_TIMESTAMP_TIME;
- case MYSQL_TYPE_TIMESTAMP2:
- case MYSQL_TYPE_TIMESTAMP:
- case MYSQL_TYPE_DATETIME2:
- case MYSQL_TYPE_DATETIME: return MYSQL_TIMESTAMP_DATETIME;
- case MYSQL_TYPE_NEWDATE:
- case MYSQL_TYPE_DATE: return MYSQL_TIMESTAMP_DATE;
- default: return MYSQL_TIMESTAMP_ERROR;
- }
-}
-
-
-/**
- Tests if field type is temporal, i.e. represents
- DATE, TIME, DATETIME or TIMESTAMP types in SQL.
-
- @param type Field type, as returned by field->type().
- @retval true If field type is temporal
- @retval false If field type is not temporal
-*/
-inline bool is_temporal_type(enum_field_types type)
-{
- return mysql_type_to_time_type(type) != MYSQL_TIMESTAMP_ERROR;
-}
-
-
-/**
- Tests if field type is temporal and has time part,
- i.e. represents TIME, DATETIME or TIMESTAMP types in SQL.
-
- @param type Field type, as returned by field->type().
- @retval true If field type is temporal type with time part.
- @retval false If field type is not temporal type with time part.
-*/
-inline bool is_temporal_type_with_time(enum_field_types type)
-{
- switch (type)
- {
- case MYSQL_TYPE_TIME:
- case MYSQL_TYPE_DATETIME:
- case MYSQL_TYPE_TIMESTAMP:
- return true;
- default:
- return false;
- }
-}
-
enum enum_vcol_info_type
{
VCOL_GENERATED_VIRTUAL, VCOL_GENERATED_STORED,
@@ -598,6 +531,7 @@ static inline const char *vcol_type_name(enum_vcol_info_type type)
#define VCOL_TIME_FUNC 8
#define VCOL_AUTO_INC 16
#define VCOL_IMPOSSIBLE 32
+#define VCOL_NOT_VIRTUAL 64 /* Function can't be virtual */
#define VCOL_NOT_STRICTLY_DETERMINISTIC \
(VCOL_NON_DETERMINISTIC | VCOL_TIME_FUNC | VCOL_SESSION_FUNC)
@@ -630,7 +564,7 @@ public:
bool utf8; /* Already in utf8 */
bool automatic_name;
Item *expr;
- LEX_STRING name; /* Name of constraint */
+ LEX_CSTRING name; /* Name of constraint */
/* see VCOL_* (VCOL_FIELD_REF, ...) */
uint flags;
@@ -709,12 +643,25 @@ public:
static void *operator new(size_t size, MEM_ROOT *mem_root) throw ()
{ return alloc_root(mem_root, size); }
static void *operator new(size_t size) throw ()
- { return thd_alloc(current_thd, size); }
+ {
+ DBUG_ASSERT(size < UINT_MAX32);
+ return thd_alloc(current_thd, (uint) size);
+ }
static void operator delete(void *ptr_arg, size_t size) { TRASH_FREE(ptr_arg, size); }
static void operator delete(void *ptr, MEM_ROOT *mem_root)
{ DBUG_ASSERT(0); }
+ /**
+ Used by System Versioning.
+ */
+ virtual void set_max()
+ { DBUG_ASSERT(0); }
+ virtual bool is_max()
+ { DBUG_ASSERT(0); return false; }
+
uchar *ptr; // Position to field in record
+
+ field_visibility_t invisible;
/**
Byte where the @c NULL bit is stored inside a record. If this Field is a
@c NOT @c NULL field, this member is @c NULL.
@@ -726,12 +673,12 @@ public:
*/
TABLE *table; // Pointer for table
TABLE *orig_table; // Pointer to original table
- const char * const *table_name;
- const char *field_name;
+ const char * const *table_name; // Pointer to alias in TABLE
+ LEX_CSTRING field_name;
+ LEX_CSTRING comment;
/** reference to the list of options or NULL */
engine_option_value *option_list;
ha_field_option_struct *option_struct; /* structure with parsed options */
- LEX_STRING comment;
/* Field is part of the following keys */
key_map key_start, part_of_key, part_of_key_not_clustered;
@@ -755,7 +702,8 @@ public:
TIMESTAMP_OLD_FIELD=18, // TIMESTAMP created before 4.1.3
TIMESTAMP_DN_FIELD=21, // TIMESTAMP DEFAULT NOW()
TIMESTAMP_UN_FIELD=22, // TIMESTAMP ON UPDATE NOW()
- TIMESTAMP_DNUN_FIELD=23 // TIMESTAMP DEFAULT NOW() ON UPDATE NOW()
+ TIMESTAMP_DNUN_FIELD=23, // TIMESTAMP DEFAULT NOW() ON UPDATE NOW()
+ TMYSQL_COMPRESSED= 24, // Compatibility with TMySQL
};
enum geometry_type
{
@@ -817,8 +765,22 @@ public:
Field(uchar *ptr_arg,uint32 length_arg,uchar *null_ptr_arg,
uchar null_bit_arg, utype unireg_check_arg,
- const char *field_name_arg);
+ const LEX_CSTRING *field_name_arg);
virtual ~Field() {}
+
+ DTCollation dtcollation() const
+ {
+ return DTCollation(charset(), derivation(), repertoire());
+ }
+ virtual Type_std_attributes type_std_attributes() const
+ {
+ return Type_std_attributes(field_length, decimals(),
+ MY_TEST(flags & UNSIGNED_FLAG),
+ dtcollation());
+ }
+
+ bool is_unsigned() const { return flags & UNSIGNED_FLAG; }
+
/**
Convenience definition of a copy function returned by
Field::get_copy_func()
@@ -837,20 +799,34 @@ public:
@retval false - conversion is needed
*/
virtual bool memcpy_field_possible(const Field *from) const= 0;
- virtual int store(const char *to, uint length,CHARSET_INFO *cs)=0;
- virtual int store_hex_hybrid(const char *str, uint length);
+ virtual int store(const char *to, size_t length,CHARSET_INFO *cs)=0;
+ 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;
virtual int store_decimal(const my_decimal *d)=0;
- virtual int store_time_dec(MYSQL_TIME *ltime, uint dec);
+ virtual int store_time_dec(const MYSQL_TIME *ltime, uint dec);
virtual int store_timestamp(my_time_t timestamp, ulong sec_part);
- int store_time(MYSQL_TIME *ltime)
+ int store_time(const MYSQL_TIME *ltime)
{ return store_time_dec(ltime, TIME_SECOND_PART_DIGITS); }
- int store(const char *to, uint length, CHARSET_INFO *cs,
+ int store(const char *to, size_t length, CHARSET_INFO *cs,
enum_check_fields check_level);
int store(const LEX_STRING *ls, CHARSET_INFO *cs)
- { return store(ls->str, (uint32) ls->length, cs); }
-#ifdef HAVE_valgrind_or_MSAN
+ {
+ DBUG_ASSERT(ls->length < UINT_MAX32);
+ return store(ls->str, (uint) ls->length, cs);
+ }
+ int store(const LEX_CSTRING *ls, CHARSET_INFO *cs)
+ {
+ DBUG_ASSERT(ls->length < UINT_MAX32);
+ return store(ls->str, (uint) ls->length, cs);
+ }
+ int store(const LEX_CSTRING &ls, CHARSET_INFO *cs)
+ {
+ DBUG_ASSERT(ls.length < UINT_MAX32);
+ return store(ls.str, (uint) ls.length, cs);
+ }
+
+#ifdef HAVE_valgrind
/**
Mark unused memory in the field as defined. Mainly used to ensure
that if we write full field to disk (for example in
@@ -890,17 +866,36 @@ public:
*/
virtual String *val_str(String*,String *)=0;
String *val_int_as_str(String *val_buffer, bool unsigned_flag);
+ /*
+ Return the field value as a LEX_CSTRING, without padding to full length
+ (MODE_PAD_CHAR_TO_FULL_LENGTH is temporarily suppressed during the call).
+
+ In case of an empty value, to[0] is assigned to empty_clex_string,
+ memory is not allocated.
+ In case of a non-empty value, the memory is allocated on mem_root.
+ In case of a memory allocation failure, to[0] is assigned to {NULL,0}.
+
+ @param [IN] mem_root store non-empty values here
+ @param [OUT to return the string here
+ @retval false (success)
+ @retval true (EOM)
+ */
+ bool val_str_nopad(MEM_ROOT *mem_root, LEX_CSTRING *to);
fast_field_copier get_fast_field_copier(const Field *from);
/*
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 Item_result result_type () const=0;
- virtual Item_result cmp_type () const { return result_type(); }
- static bool type_can_have_key_part(enum_field_types);
+ Item_result result_type () const
+ {
+ return type_handler()->result_type();
+ }
+ Item_result cmp_type () const
+ {
+ return type_handler()->cmp_type();
+ }
static enum_field_types field_type_merge(enum_field_types, enum_field_types);
- static Item_result result_merge_type(enum_field_types);
virtual bool eq(Field *field)
{
return (ptr == field->ptr && null_ptr == field->null_ptr &&
@@ -929,8 +924,21 @@ public:
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 do_save_field_metadata(first_byte); }
+ { return 0; }
+
/*
data_length() return the "real size" of the data in memory.
@@ -999,13 +1007,31 @@ public:
{
return bitmap_is_set(&table->has_value_set, field_index);
}
+ void clear_has_explicit_value()
+ {
+ bitmap_clear_bit(&table->has_value_set, field_index);
+ }
+
+ virtual my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const
+ { DBUG_ASSERT(0); return 0; }
+ my_time_t get_timestamp(ulong *sec_part) const
+ {
+ return get_timestamp(ptr, sec_part);
+ }
virtual bool binary() const { return 1; }
virtual bool zero_pack() const { return 1; }
virtual enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; }
virtual uint32 key_length() const { return pack_length(); }
- virtual enum_field_types type() const =0;
- virtual enum_field_types real_type() const { return type(); }
+ virtual const Type_handler *type_handler() const= 0;
+ virtual enum_field_types type() const
+ {
+ return type_handler()->field_type();
+ }
+ virtual enum_field_types real_type() const
+ {
+ return type_handler()->real_field_type();
+ }
virtual enum_field_types binlog_type() const
{
/*
@@ -1107,6 +1133,16 @@ public:
memcpy(ptr, val, len);
}
virtual uint decimals() const { return 0; }
+ virtual Information_schema_numeric_attributes
+ information_schema_numeric_attributes() const
+ {
+ return Information_schema_numeric_attributes();
+ }
+ virtual Information_schema_character_attributes
+ information_schema_character_attributes() const
+ {
+ return Information_schema_character_attributes();
+ }
/*
Caller beware: sql_type can change str.Ptr, so check
ptr() to see if it changed if you are using your own buffer
@@ -1221,7 +1257,7 @@ public:
}
void make_sort_key(uchar *buff, uint length);
- virtual void make_field(Send_field *);
+ virtual void make_send_field(Send_field *);
/*
Some implementations actually may write up to 8 bytes regardless of what
@@ -1230,7 +1266,7 @@ public:
*/
virtual void sort_string(uchar *buff,uint length)=0;
- virtual bool optimize_range(uint idx, uint part);
+ virtual bool optimize_range(uint idx, uint part) const;
virtual void free() {}
virtual Field *make_new_field(MEM_ROOT *root, TABLE *new_table,
bool keep_type);
@@ -1347,6 +1383,7 @@ public:
uint fill_cache_field(struct st_cache_field *copy);
virtual bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
bool get_time(MYSQL_TIME *ltime) { return get_date(ltime, TIME_TIME_ONLY); }
+ virtual TYPELIB *get_typelib() const { return NULL; }
virtual CHARSET_INFO *charset(void) const { return &my_charset_bin; }
virtual CHARSET_INFO *charset_for_protocol(void) const
{ return binary() ? &my_charset_bin : charset(); }
@@ -1355,12 +1392,9 @@ public:
virtual enum Derivation derivation(void) const
{ return DERIVATION_IMPLICIT; }
virtual uint repertoire(void) const { return MY_REPERTOIRE_UNICODE30; }
- virtual void set_derivation(enum Derivation derivation_arg,
- uint repertoire_arg)
- { }
virtual int set_time() { return 1; }
bool set_warning(Sql_condition::enum_warning_level, unsigned int code,
- int cuted_increment) const;
+ int cuted_increment, ulong current_row=0) const;
protected:
bool set_warning(unsigned int code, int cuted_increment) const
{
@@ -1370,7 +1404,7 @@ protected:
{
return set_warning(Sql_condition::WARN_LEVEL_NOTE, code, cuted_increment);
}
- void set_datetime_warning(Sql_condition::enum_warning_level, uint code,
+ void set_datetime_warning(Sql_condition::enum_warning_level, uint code,
const ErrConv *str, timestamp_type ts_type,
int cuted_increment) const;
void set_datetime_warning(uint code,
@@ -1411,8 +1445,7 @@ public:
}
/* maximum possible display length */
- virtual uint32 max_display_length()= 0;
-
+ virtual uint32 max_display_length() const= 0;
/**
Whether a field being created is compatible with a existing one.
@@ -1463,6 +1496,16 @@ public:
FIELD_FLAGS_COLUMN_FORMAT;
}
+ bool vers_sys_field() const
+ {
+ return flags & (VERS_SYS_START_FLAG | VERS_SYS_END_FLAG);
+ }
+
+ bool vers_update_unversioned() const
+ {
+ return flags & VERS_UPDATE_UNVERSIONED_FLAG;
+ }
+
/*
Validate a non-null field value stored in the given record
according to the current thread settings, e.g. sql_mode.
@@ -1561,6 +1604,14 @@ public:
/* Mark field in read map. Updates also virtual fields */
void register_field_in_read_map();
+ virtual Compression_method *compression_method() const { return 0; }
+
+ virtual Virtual_tmp_table **virtual_tmp_table_addr()
+ {
+ return NULL;
+ }
+ virtual bool sp_prepare_and_store_item(THD *thd, Item **value);
+
friend int cre_myisam(char * name, TABLE *form, uint options,
ulonglong auto_increment_value);
friend class Copy_field;
@@ -1589,19 +1640,6 @@ private:
*/
virtual size_t do_last_null_byte() const;
-/**
- Retrieve the field metadata for fields.
-
- This default implementation returns 0 and saves 0 in the metadata_ptr
- value.
-
- @param metadata_ptr First byte of field metadata
-
- @returns 0 no bytes written.
-*/
- virtual int do_save_field_metadata(uchar *metadata_ptr)
- { return 0; }
-
protected:
uchar *pack_int(uchar *to, const uchar *from, size_t size)
{
@@ -1609,7 +1647,7 @@ protected:
return to + size;
}
- const uchar *unpack_int(uchar* to, const uchar *from,
+ const uchar *unpack_int(uchar* to, const uchar *from,
const uchar *from_end, size_t size)
{
if (from + size > from_end)
@@ -1644,20 +1682,20 @@ class Field_num :public Field {
protected:
int check_edom_and_important_data_truncation(const char *type, bool edom,
CHARSET_INFO *cs,
- const char *str, uint length,
+ const char *str, size_t length,
const char *end_of_num);
int check_edom_and_truncation(const char *type, bool edom,
CHARSET_INFO *cs,
- const char *str, uint length,
+ const char *str, size_t length,
const char *end_of_num);
- int check_int(CHARSET_INFO *cs, const char *str, uint length,
+ int check_int(CHARSET_INFO *cs, const char *str, size_t length,
const char *int_end, int error)
{
return check_edom_and_truncation("integer",
error == MY_ERRNO_EDOM || str == int_end,
cs, str, length, int_end);
}
- bool get_int(CHARSET_INFO *cs, const char *from, uint len,
+ bool get_int(CHARSET_INFO *cs, const char *from, size_t len,
longlong *rnd, ulonglong unsigned_max,
longlong signed_min, longlong signed_max);
void prepend_zeros(String *value) const;
@@ -1668,9 +1706,8 @@ public:
bool zerofill,unsigned_flag; // Purify cannot handle bit fields
Field_num(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg, utype unireg_check_arg,
- const char *field_name_arg,
+ const LEX_CSTRING *field_name_arg,
uint8 dec_arg, bool zero_arg, bool unsigned_arg);
- enum Item_result result_type () const { return INT_RESULT; }
enum Derivation derivation(void) const { return DERIVATION_NUMERIC; }
uint repertoire(void) const { return MY_REPERTOIRE_NUMERIC; }
CHARSET_INFO *charset(void) const { return &my_charset_numeric; }
@@ -1683,7 +1720,7 @@ public:
}
void add_zerofill_and_unsigned(String &res) const;
friend class Create_field;
- void make_field(Send_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;
@@ -1697,16 +1734,6 @@ public:
{
return to->store(val_int(), MY_TEST(flags & UNSIGNED_FLAG));
}
- bool memcpy_field_possible(const Field *from) const
- {
- return real_type() == from->real_type() &&
- pack_length() == from->pack_length() &&
- !((flags & UNSIGNED_FLAG) && !(from->flags & UNSIGNED_FLAG)) &&
- decimals() == from->decimals();
- }
- int store_decimal(const my_decimal *);
- my_decimal *val_decimal(my_decimal *);
- bool val_bool() { return val_int() != 0; }
uint is_equal(Create_field *new_field);
uint row_pack_length() const { return pack_length(); }
uint32 pack_length_from_metadata(uint field_metadata) {
@@ -1715,12 +1742,10 @@ public:
field_metadata, length));
return length;
}
- int store_time_dec(MYSQL_TIME *ltime, uint dec);
double pos_in_interval(Field *min, Field *max)
{
return pos_in_interval_val_real(min, max);
}
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
};
@@ -1735,8 +1760,8 @@ public:
const Item_equal *item_equal);
Field_str(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg, utype unireg_check_arg,
- const char *field_name_arg, CHARSET_INFO *charset);
- Item_result result_type () const { return STRING_RESULT; }
+ 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
@@ -1746,24 +1771,25 @@ public:
charset() == from->charset();
}
int store(double nr);
- int store(longlong nr, bool unsigned_val)=0;
+ int store(longlong nr, bool unsigned_val);
int store_decimal(const my_decimal *);
- int store(const char *to,uint length,CHARSET_INFO *cs)=0;
- int store_hex_hybrid(const char *str, uint length)
+ int store(const char *to,size_t length,CHARSET_INFO *cs)=0;
+ int store_hex_hybrid(const char *str, size_t length)
{
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; }
- void set_derivation(enum Derivation derivation_arg,
- uint repertoire_arg)
+ bool binary() const { return field_charset == &my_charset_bin; }
+ uint32 max_display_length() const { return field_length; }
+ uint32 char_length() const { return field_length / field_charset->mbmaxlen; }
+ Information_schema_character_attributes
+ information_schema_character_attributes() const
{
- field_derivation= derivation_arg;
- field_repertoire= repertoire_arg;
+ return Information_schema_character_attributes(max_display_length(),
+ char_length());
}
- bool binary() const { return field_charset == &my_charset_bin; }
- uint32 max_display_length() { return field_length; }
friend class Create_field;
my_decimal *val_decimal(my_decimal *);
bool val_bool() { return val_real() != 0e0; }
@@ -1796,16 +1822,38 @@ protected:
return report_if_important_data(copier->source_end_pos(),
end, count_spaces);
}
+ int well_formed_copy_with_check(char *to, size_t to_length,
+ CHARSET_INFO *from_cs,
+ const char *from, size_t from_length,
+ size_t nchars, bool count_spaces,
+ uint *copy_length)
+ {
+ String_copier copier;
+
+ *copy_length= copier.well_formed_copy(field_charset, to, to_length,
+ from_cs, from, from_length,
+ nchars);
+
+ return check_conversion_status(&copier, from + from_length, from_cs, count_spaces);
+ }
bool cmp_to_string_with_same_collation(const Item_bool_func *cond,
const Item *item) const;
bool cmp_to_string_with_stricter_collation(const Item_bool_func *cond,
const Item *item) const;
+ int compress(char *to, uint to_length,
+ const char *from, uint length,
+ uint max_length,
+ uint *out_length,
+ CHARSET_INFO *cs, size_t nchars);
+ String *uncompress(String *val_buffer, String *val_ptr,
+ const uchar *from, uint from_length);
public:
Field_longstr(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg, utype unireg_check_arg,
- const char *field_name_arg, CHARSET_INFO *charset_arg)
+ const LEX_CSTRING *field_name_arg,
+ const DTCollation &collation)
:Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg,
- field_name_arg, charset_arg)
+ field_name_arg, collation)
{}
int store_decimal(const my_decimal *d);
@@ -1832,23 +1880,29 @@ public:
/* base class for float and double and decimal (old one) */
class Field_real :public Field_num {
protected:
- double get_double(const char *str, uint length, CHARSET_INFO *cs, int *err);
+ double get_double(const char *str, size_t length, CHARSET_INFO *cs, int *err);
public:
bool not_fixed;
Field_real(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg, utype unireg_check_arg,
- const char *field_name_arg,
+ const LEX_CSTRING *field_name_arg,
uint8 dec_arg, bool zero_arg, bool unsigned_arg)
:Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg,
field_name_arg, dec_arg, zero_arg, unsigned_arg),
not_fixed(dec_arg >= FLOATING_POINT_DECIMALS)
{}
- Item_result result_type () const { return REAL_RESULT; }
Copy_func *get_copy_func(const Field *from) const
{
return do_field_real;
}
+ Information_schema_numeric_attributes
+ information_schema_numeric_attributes() const
+ {
+ 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
{
@@ -1857,15 +1911,18 @@ public:
e.g. a DOUBLE(53,10) into a DOUBLE(10,10).
But it should be OK the other way around.
*/
- return Field_num::memcpy_field_possible(from) &&
+ return real_type() == from->real_type() &&
+ pack_length() == from->pack_length() &&
+ is_unsigned() <= from->is_unsigned() &&
+ decimals() == from->decimals() &&
field_length >= from->field_length;
}
int store_decimal(const my_decimal *);
- int store_time_dec(MYSQL_TIME *ltime, uint dec);
+ int store_time_dec(const MYSQL_TIME *ltime, uint dec);
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
my_decimal *val_decimal(my_decimal *);
bool val_bool() { return val_real() != 0e0; }
- uint32 max_display_length() { return field_length; }
+ 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);
};
@@ -1875,22 +1932,28 @@ class Field_decimal :public Field_real {
public:
Field_decimal(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
- enum utype unireg_check_arg, const char *field_name_arg,
+ enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
uint8 dec_arg,bool zero_arg,bool unsigned_arg)
:Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
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);
- enum_field_types type() const { return MYSQL_TYPE_DECIMAL;}
+ const Type_handler *type_handler() const { return &type_handler_olddecimal; }
enum ha_base_keytype key_type() const
{ return zerofill ? HA_KEYTYPE_BINARY : HA_KEYTYPE_NUM; }
+ Information_schema_numeric_attributes
+ information_schema_numeric_attributes() const
+ {
+ 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
{
return eq_def(from) ? get_identical_copy_func() : do_field_string;
}
int reset(void);
- int store(const char *to,uint length,CHARSET_INFO *charset);
+ 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);
@@ -1911,7 +1974,7 @@ public:
/* New decimal/numeric field which use fixed point arithmetic */
class Field_new_decimal :public Field_num {
private:
- int do_save_field_metadata(uchar *first_byte);
+ int save_field_metadata(uchar *first_byte);
public:
/* The maximum number of decimal digits can be stored */
uint precision;
@@ -1924,14 +1987,11 @@ public:
*/
Field_new_decimal(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
- enum utype unireg_check_arg, const char *field_name_arg,
+ enum utype unireg_check_arg,
+ const LEX_CSTRING *field_name_arg,
uint8 dec_arg, bool zero_arg, bool unsigned_arg);
- Field_new_decimal(uint32 len_arg, bool maybe_null_arg,
- const char *field_name_arg, uint8 dec_arg,
- bool unsigned_arg);
- enum_field_types type() const { return MYSQL_TYPE_NEWDECIMAL;}
+ const Type_handler *type_handler() const { return &type_handler_newdecimal; }
enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; }
- Item_result result_type () const { return DECIMAL_RESULT; }
Copy_func *get_copy_func(const Field *from) const
{
// if (from->real_type() == MYSQL_TYPE_BIT) // QQ: why?
@@ -1945,17 +2005,20 @@ public:
}
bool memcpy_field_possible(const Field *from) const
{
- return Field_num::memcpy_field_possible(from) &&
+ return real_type() == from->real_type() &&
+ pack_length() == from->pack_length() &&
+ is_unsigned() <= from->is_unsigned() &&
+ decimals() == from->decimals() &&
field_length == from->field_length;
}
int reset(void);
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, uint length, CHARSET_INFO *charset);
+ 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(MYSQL_TIME *ltime, uint dec);
+ int store_time_dec(const MYSQL_TIME *ltime, uint dec);
int store_decimal(const my_decimal *);
double val_real(void);
longlong val_int(void);
@@ -1973,7 +2036,12 @@ public:
void sort_string(uchar *buff, uint length);
bool zero_pack() const { return 0; }
void sql_type(String &str) const;
- uint32 max_display_length() { return field_length; }
+ uint32 max_display_length() const { return field_length; }
+ Information_schema_numeric_attributes
+ information_schema_numeric_attributes() const
+ {
+ 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);
@@ -1982,44 +2050,84 @@ public:
uint16 mflags, int *order_var);
uint is_equal(Create_field *new_field);
virtual const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end, uint param_data);
- static Field *create_from_item(MEM_ROOT *root, Item *);
Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item);
};
-class Field_integer: public Field_num
+class Field_int :public Field_num
{
+protected:
+ String *val_str_from_long(String *val_buffer, uint max_char_length,
+ int radix, long nr);
public:
- Field_integer(uchar *ptr_arg, uint32 len_arg,
- uchar *null_ptr_arg, uchar null_bit_arg,
- enum utype unireg_check_arg, const char *field_name_arg,
- bool zero_arg, bool unsigned_arg)
- :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg, 0,
- zero_arg, unsigned_arg)
- { }
+ Field_int(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, bool zero_arg, bool unsigned_arg)
+ :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
+ {
+ 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()
{
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, ulonglong fuzzydate);
+ virtual const Type_limits_int *type_limits_int() const= 0;
+ uint32 max_display_length() const
+ {
+ return type_limits_int()->char_length();
+ }
+ Type_std_attributes type_std_attributes() const
+ {
+ /*
+ For integer data types, the user-specified length does not constrain the
+ supported range, so e.g. a column of the INT(1) data type supports the
+ full integer range anyway.
+ Choose the maximum from the user-specified length and the maximum
+ possible length determined by the data type capacity:
+ INT(1) -> 11
+ INT(10) -> 11
+ INT(40) -> 40
+ */
+ uint32 length1= max_display_length();
+ uint32 length2= field_length;
+ return Type_std_attributes(MY_MAX(length1, length2), decimals(),
+ MY_TEST(flags & UNSIGNED_FLAG),
+ dtcollation());
+ }
+ Information_schema_numeric_attributes
+ information_schema_numeric_attributes() const
+ {
+ uint32 prec= type_limits_int()->precision();
+ return Information_schema_numeric_attributes(prec, 0);
+ }
};
-class Field_tiny :public Field_integer {
+class Field_tiny :public Field_int
+{
public:
Field_tiny(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
- uchar null_bit_arg,
- enum utype unireg_check_arg, const char *field_name_arg,
- bool zero_arg, bool unsigned_arg)
- :Field_integer(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg,
- zero_arg, unsigned_arg)
+ uchar null_bit_arg,
+ enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
+ bool zero_arg, bool unsigned_arg)
+ :Field_int(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg, zero_arg, unsigned_arg)
{}
- enum_field_types type() const { return MYSQL_TYPE_TINY;}
+ const Type_handler *type_handler() const { return &type_handler_tiny; }
enum ha_base_keytype key_type() const
{ return unsigned_flag ? HA_KEYTYPE_BINARY : HA_KEYTYPE_INT8; }
- int store(const char *to,uint length,CHARSET_INFO *charset);
+ 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; }
@@ -2031,7 +2139,10 @@ public:
void sort_string(uchar *buff,uint length);
uint32 pack_length() const { return 1; }
void sql_type(String &str) const;
- uint32 max_display_length() { return 4; }
+ const Type_limits_int *type_limits_int() const
+ {
+ return type_handler_tiny.type_limits_int_by_unsigned_flag(is_unsigned());
+ }
virtual uchar *pack(uchar* to, const uchar *from, uint max_length)
{
@@ -2047,7 +2158,6 @@ public:
*to= *from;
return from + 1;
}
-
virtual ulonglong get_max_int_value() const
{
return unsigned_flag ? 0xFFULL : 0x7FULL;
@@ -2055,25 +2165,26 @@ public:
};
-class Field_short :public Field_integer {
+class Field_short :public Field_int
+{
public:
Field_short(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
- uchar null_bit_arg,
- enum utype unireg_check_arg, const char *field_name_arg,
- bool zero_arg, bool unsigned_arg)
- :Field_integer(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg,
- zero_arg, unsigned_arg)
+ uchar null_bit_arg,
+ enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
+ bool zero_arg, bool unsigned_arg)
+ :Field_int(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg, zero_arg, unsigned_arg)
{}
- Field_short(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg,
- bool unsigned_arg)
- :Field_integer((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, 0,
- NONE, field_name_arg, 0, unsigned_arg)
+ Field_short(uint32 len_arg,bool maybe_null_arg,
+ const LEX_CSTRING *field_name_arg,
+ bool unsigned_arg)
+ :Field_int((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0,
+ NONE, field_name_arg, 0, unsigned_arg)
{}
- enum_field_types type() const { return MYSQL_TYPE_SHORT;}
+ const Type_handler *type_handler() const { return &type_handler_short; }
enum ha_base_keytype key_type() const
{ return unsigned_flag ? HA_KEYTYPE_USHORT_INT : HA_KEYTYPE_SHORT_INT;}
- int store(const char *to,uint length,CHARSET_INFO *charset);
+ 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; }
@@ -2085,35 +2196,36 @@ public:
void sort_string(uchar *buff,uint length);
uint32 pack_length() const { return 2; }
void sql_type(String &str) const;
- uint32 max_display_length() { return 6; }
-
+ 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)
{ return pack_int16(to, from); }
virtual const uchar *unpack(uchar* to, const uchar *from,
const uchar *from_end, uint param_data)
{ return unpack_int16(to, from, from_end); }
-
virtual ulonglong get_max_int_value() const
{
return unsigned_flag ? 0xFFFFULL : 0x7FFFULL;
}
};
-class Field_medium :public Field_integer {
+class Field_medium :public Field_int
+{
public:
Field_medium(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
- uchar null_bit_arg,
- enum utype unireg_check_arg, const char *field_name_arg,
- bool zero_arg, bool unsigned_arg)
- :Field_integer(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg,
- zero_arg, unsigned_arg)
+ uchar null_bit_arg,
+ enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
+ bool zero_arg, bool unsigned_arg)
+ :Field_int(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg, zero_arg, unsigned_arg)
{}
- enum_field_types type() const { return MYSQL_TYPE_INT24;}
+ const Type_handler *type_handler() const { return &type_handler_int24; }
enum ha_base_keytype key_type() const
{ return unsigned_flag ? HA_KEYTYPE_UINT24 : HA_KEYTYPE_INT24; }
- int store(const char *to,uint length,CHARSET_INFO *charset);
+ 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; }
@@ -2125,13 +2237,14 @@ public:
void sort_string(uchar *buff,uint length);
uint32 pack_length() const { return 3; }
void sql_type(String &str) const;
- uint32 max_display_length() { return 8; }
-
+ 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)
{
return Field::pack(to, from, max_length);
}
-
virtual ulonglong get_max_int_value() const
{
return unsigned_flag ? 0xFFFFFFULL : 0x7FFFFFULL;
@@ -2139,25 +2252,26 @@ public:
};
-class Field_long :public Field_integer {
+class Field_long :public Field_int
+{
public:
Field_long(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
- uchar null_bit_arg,
- enum utype unireg_check_arg, const char *field_name_arg,
- bool zero_arg, bool unsigned_arg)
- :Field_integer(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg,
- zero_arg, unsigned_arg)
+ uchar null_bit_arg,
+ enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
+ bool zero_arg, bool unsigned_arg)
+ :Field_int(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg, zero_arg, unsigned_arg)
{}
- Field_long(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg,
- bool unsigned_arg)
- :Field_integer((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, 0,
- NONE, field_name_arg, 0, unsigned_arg)
+ Field_long(uint32 len_arg,bool maybe_null_arg,
+ const LEX_CSTRING *field_name_arg,
+ bool unsigned_arg)
+ :Field_int((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0,
+ NONE, field_name_arg, 0, unsigned_arg)
{}
- enum_field_types type() const { return MYSQL_TYPE_LONG;}
+ const Type_handler *type_handler() const { return &type_handler_long; }
enum ha_base_keytype key_type() const
{ return unsigned_flag ? HA_KEYTYPE_ULONG_INT : HA_KEYTYPE_LONG_INT; }
- int store(const char *to,uint length,CHARSET_INFO *charset);
+ 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; }
@@ -2169,7 +2283,10 @@ public:
void sort_string(uchar *buff,uint length);
uint32 pack_length() const { return 4; }
void sql_type(String &str) const;
- uint32 max_display_length() { return MY_INT32_NUM_DECIMAL_DIGITS; }
+ 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)))
{
@@ -2181,7 +2298,6 @@ public:
{
return unpack_int32(to, from, from_end);
}
-
virtual ulonglong get_max_int_value() const
{
return unsigned_flag ? 0xFFFFFFFFULL : 0x7FFFFFFFULL;
@@ -2189,26 +2305,26 @@ public:
};
-class Field_longlong :public Field_integer {
+class Field_longlong :public Field_int
+{
public:
Field_longlong(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
- uchar null_bit_arg,
- enum utype unireg_check_arg, const char *field_name_arg,
- bool zero_arg, bool unsigned_arg)
- :Field_integer(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg,
- zero_arg, unsigned_arg)
+ uchar null_bit_arg,
+ enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
+ bool zero_arg, bool unsigned_arg)
+ :Field_int(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg, zero_arg, unsigned_arg)
{}
Field_longlong(uint32 len_arg,bool maybe_null_arg,
- const char *field_name_arg,
- bool unsigned_arg)
- :Field_integer((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, 0,
- NONE, field_name_arg,0, unsigned_arg)
+ const LEX_CSTRING *field_name_arg,
+ bool unsigned_arg)
+ :Field_int((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0,
+ NONE, field_name_arg, 0, unsigned_arg)
{}
- enum_field_types type() const { return MYSQL_TYPE_LONGLONG;}
+ 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,uint length,CHARSET_INFO *charset);
+ 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)
@@ -2224,7 +2340,10 @@ public:
void sort_string(uchar *buff,uint length);
uint32 pack_length() const { return 8; }
void sql_type(String &str) const;
- uint32 max_display_length() { return 20; }
+ const Type_limits_int *type_limits_int() const
+ {
+ return type_handler_longlong.type_limits_int_by_unsigned_flag(is_unsigned());
+ }
virtual uchar *pack(uchar* to, const uchar *from,
uint max_length __attribute__((unused)))
{
@@ -2235,6 +2354,8 @@ public:
{
return unpack_int64(to, from, from_end);
}
+ void set_max();
+ bool is_max();
virtual ulonglong get_max_int_value() const
{
return unsigned_flag ? 0xFFFFFFFFFFFFFFFFULL : 0x7FFFFFFFFFFFFFFFULL;
@@ -2242,11 +2363,54 @@ public:
};
+class Field_vers_trx_id :public Field_longlong {
+ MYSQL_TIME cache;
+ ulonglong cached;
+public:
+ Field_vers_trx_id(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, bool zero_arg,
+ bool unsigned_arg)
+ : Field_longlong(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg, zero_arg,
+ unsigned_arg),
+ cached(0)
+ {}
+ const Type_handler *type_handler() const { return &type_handler_vers_trx_id; }
+ uint size_of() const { return sizeof(*this); }
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate, ulonglong trx_id);
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ {
+ 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
+ {
+ return true;
+ }
+
+ bool can_optimize_group_min_max(const Item_bool_func *cond,
+ const Item *const_item) const
+ {
+ return true;
+ }
+ bool can_optimize_range(const Item_bool_func *cond,
+ const Item *item,
+ bool is_eq_func) const
+ {
+ return true;
+ }
+ /* cmp_type() cannot be TIME_RESULT, because we want to compare this field against
+ integers. But in all other cases we treat it as TIME_RESULT! */
+};
+
+
class Field_float :public Field_real {
public:
Field_float(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
- enum utype unireg_check_arg, const char *field_name_arg,
+ enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
uint8 dec_arg,bool zero_arg,bool unsigned_arg)
:Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg,
@@ -2255,17 +2419,17 @@ public:
if (dec_arg >= FLOATING_POINT_DECIMALS)
dec_arg= NOT_FIXED_DEC;
}
- Field_float(uint32 len_arg, bool maybe_null_arg, const char *field_name_arg,
- uint8 dec_arg)
+ Field_float(uint32 len_arg, bool maybe_null_arg,
+ const LEX_CSTRING *field_name_arg, uint8 dec_arg)
:Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, (uint) 0,
NONE, field_name_arg, dec_arg, 0, 0)
{
if (dec_arg >= FLOATING_POINT_DECIMALS)
dec_arg= NOT_FIXED_DEC;
}
- enum_field_types type() const { return MYSQL_TYPE_FLOAT;}
+ 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,uint length,CHARSET_INFO *charset);
+ 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; }
@@ -2286,7 +2450,7 @@ public:
return 0x1000000ULL;
}
private:
- int do_save_field_metadata(uchar *first_byte);
+ int save_field_metadata(uchar *first_byte);
};
@@ -2295,7 +2459,7 @@ class Field_double :public Field_real {
public:
Field_double(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
- enum utype unireg_check_arg, const char *field_name_arg,
+ enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
uint8 dec_arg,bool zero_arg,bool unsigned_arg)
:Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg,
@@ -2304,15 +2468,16 @@ public:
if (dec_arg >= FLOATING_POINT_DECIMALS)
dec_arg= NOT_FIXED_DEC;
}
- Field_double(uint32 len_arg, bool maybe_null_arg, const char *field_name_arg,
- uint8 dec_arg)
+ Field_double(uint32 len_arg, bool maybe_null_arg,
+ const LEX_CSTRING *field_name_arg, uint8 dec_arg)
:Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, (uint) 0,
NONE, field_name_arg, dec_arg, 0, 0)
{
if (dec_arg >= FLOATING_POINT_DECIMALS)
dec_arg= NOT_FIXED_DEC;
}
- Field_double(uint32 len_arg, bool maybe_null_arg, const char *field_name_arg,
+ Field_double(uint32 len_arg, bool maybe_null_arg,
+ const LEX_CSTRING *field_name_arg,
uint8 dec_arg, bool not_fixed_arg)
:Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, (uint) 0,
NONE, field_name_arg, dec_arg, 0, 0)
@@ -2321,9 +2486,9 @@ public:
if (dec_arg >= FLOATING_POINT_DECIMALS)
dec_arg= NOT_FIXED_DEC;
}
- enum_field_types type() const { return MYSQL_TYPE_DOUBLE;}
+ 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,uint length,CHARSET_INFO *charset);
+ 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; }
@@ -2345,7 +2510,7 @@ public:
return 0x20000000000000ULL;
}
private:
- int do_save_field_metadata(uchar *first_byte);
+ int save_field_metadata(uchar *first_byte);
};
@@ -2355,17 +2520,22 @@ class Field_null :public Field_str {
static uchar null[1];
public:
Field_null(uchar *ptr_arg, uint32 len_arg,
- enum utype unireg_check_arg, const char *field_name_arg,
- CHARSET_INFO *cs)
+ enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
+ const DTCollation &collation)
:Field_str(ptr_arg, len_arg, null, 1,
- unireg_check_arg, field_name_arg, cs)
+ unireg_check_arg, field_name_arg, collation)
{}
- enum_field_types type() const { return MYSQL_TYPE_NULL;}
+ const Type_handler *type_handler() const { return &type_handler_null; }
+ Information_schema_character_attributes
+ information_schema_character_attributes() const
+ {
+ return Information_schema_character_attributes();
+ }
Copy_func *get_copy_func(const Field *from) const
{
return do_field_string;
}
- int store(const char *to, uint length, CHARSET_INFO *cs)
+ int store(const char *to, size_t length, CHARSET_INFO *cs)
{ 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; }
@@ -2382,18 +2552,16 @@ public:
uint32 pack_length() const { return 0; }
void sql_type(String &str) const;
uint size_of() const { return sizeof(*this); }
- uint32 max_display_length() { return 4; }
+ uint32 max_display_length() const { return 4; }
void move_field_offset(my_ptrdiff_t ptr_diff) {}
bool can_optimize_keypart_ref(const Item_bool_func *cond,
const Item *item) const
{
- DBUG_ASSERT(0);
return false;
}
bool can_optimize_group_min_max(const Item_bool_func *cond,
const Item *const_item) const
{
- DBUG_ASSERT(0);
return false;
}
};
@@ -2406,16 +2574,15 @@ protected:
public:
Field_temporal(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg, utype unireg_check_arg,
- const char *field_name_arg)
+ const LEX_CSTRING *field_name_arg)
:Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg,
field_name_arg)
{ flags|= BINARY_FLAG; }
- Item_result result_type () const { return STRING_RESULT; }
- sql_mode_t can_handle_sql_mode_dependency_on_store() const;
- int store_hex_hybrid(const char *str, uint length)
+ int store_hex_hybrid(const char *str, size_t length)
{
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)
{
@@ -2425,14 +2592,13 @@ public:
return to->store_time_dec(&ltime, decimals());
}
bool memcpy_field_possible(const Field *from) const;
- uint32 max_display_length() { return field_length; }
+ 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; }
- enum Item_result cmp_type () const { return TIME_RESULT; }
bool val_bool() { return val_real() != 0e0; }
uint is_equal(Create_field *new_field);
bool eq_def(const Field *field) const
@@ -2486,14 +2652,14 @@ public:
Field_temporal_with_date(uchar *ptr_arg, uint32 len_arg,
uchar *null_ptr_arg, uchar null_bit_arg,
utype unireg_check_arg,
- const char *field_name_arg)
+ const LEX_CSTRING *field_name_arg)
:Field_temporal(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg)
{}
- int store(const char *to, uint length, CHARSET_INFO *charset);
+ int store(const char *to, size_t length, CHARSET_INFO *charset);
int store(double nr);
int store(longlong nr, bool unsigned_val);
- int store_time_dec(MYSQL_TIME *ltime, uint dec);
+ int store_time_dec(const MYSQL_TIME *ltime, uint dec);
int store_decimal(const my_decimal *);
bool validate_value_in_record(THD *thd, const uchar *record) const;
};
@@ -2501,20 +2667,22 @@ public:
class Field_timestamp :public Field_temporal {
protected:
+ sql_mode_t sql_mode_for_timestamp(THD *thd) const;
int store_TIME_with_warning(THD *, MYSQL_TIME *, const ErrConv *,
int warnings, bool have_smth_to_conv);
public:
Field_timestamp(uchar *ptr_arg, uint32 len_arg,
uchar *null_ptr_arg, uchar null_bit_arg,
- enum utype unireg_check_arg, const char *field_name_arg,
+ enum utype unireg_check_arg,
+ const LEX_CSTRING *field_name_arg,
TABLE_SHARE *share);
- enum_field_types type() const { return MYSQL_TYPE_TIMESTAMP;}
+ 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;
- int store(const char *to,uint length,CHARSET_INFO *charset);
+ 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(MYSQL_TIME *ltime, uint dec);
+ int store_time_dec(const MYSQL_TIME *ltime, uint dec);
int store_decimal(const my_decimal *);
int store_timestamp(my_time_t timestamp, ulong sec_part);
int save_in_field(Field *to);
@@ -2529,7 +2697,7 @@ public:
bool zero_pack() const { return 0; }
int set_time();
/* Get TIMESTAMP field value as seconds since begging of Unix Epoch */
- virtual my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const;
+ 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);
@@ -2572,7 +2740,7 @@ public:
Field_timestamp_with_dec(uchar *ptr_arg,
uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg,
- const char *field_name_arg,
+ const LEX_CSTRING *field_name_arg,
TABLE_SHARE *share, uint dec_arg) :
Field_timestamp(ptr_arg,
MAX_DATETIME_WIDTH + dec_arg + MY_TEST(dec_arg), null_ptr_arg,
@@ -2588,7 +2756,7 @@ public:
const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end,
uint param_data)
{ return Field::unpack(to, from, from_end, param_data); }
- void make_field(Send_field *field);
+ void make_send_field(Send_field *field);
void sort_string(uchar *to, uint length)
{
DBUG_ASSERT(length == pack_length());
@@ -2602,11 +2770,15 @@ public:
class Field_timestamp_hires :public Field_timestamp_with_dec {
+ uint sec_part_bytes(uint dec) const
+ {
+ return Type_handler_timestamp::sec_part_bytes(dec);
+ }
public:
Field_timestamp_hires(uchar *ptr_arg,
uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg,
- const char *field_name_arg,
+ const LEX_CSTRING *field_name_arg,
TABLE_SHARE *share, uint dec_arg) :
Field_timestamp_with_dec(ptr_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg, share, dec_arg)
@@ -2616,7 +2788,7 @@ public:
my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const;
void store_TIME(my_time_t timestamp, ulong sec_part);
int cmp(const uchar *,const uchar *);
- uint32 pack_length() const;
+ uint32 pack_length() const { return 4 + sec_part_bytes(dec); }
uint size_of() const { return sizeof(*this); }
};
@@ -2625,21 +2797,21 @@ public:
TIMESTAMP(0..6) - MySQL56 version
*/
class Field_timestampf :public Field_timestamp_with_dec {
- int do_save_field_metadata(uchar *metadata_ptr)
+ int save_field_metadata(uchar *metadata_ptr)
{
- *metadata_ptr= decimals();
+ *metadata_ptr= (uchar) decimals();
return 1;
}
public:
Field_timestampf(uchar *ptr_arg,
uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg,
- const char *field_name_arg,
+ const LEX_CSTRING *field_name_arg,
TABLE_SHARE *share, uint dec_arg) :
Field_timestamp_with_dec(ptr_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg, share, dec_arg)
{}
- enum_field_types real_type() const { return MYSQL_TYPE_TIMESTAMP2; }
+ const Type_handler *type_handler() const { return &type_handler_timestamp2; }
enum_field_types binlog_type() const { return MYSQL_TYPE_TIMESTAMP2; }
uint32 pack_length() const
{
@@ -2656,8 +2828,14 @@ public:
{
return memcmp(a_ptr, b_ptr, pack_length());
}
+ void set_max();
+ bool is_max();
void store_TIME(my_time_t timestamp, ulong sec_part);
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);
+ }
uint size_of() const { return sizeof(*this); }
};
@@ -2666,18 +2844,23 @@ class Field_year :public Field_tiny {
public:
Field_year(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
- enum utype unireg_check_arg, const char *field_name_arg)
+ enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg)
:Field_tiny(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg, 1, 1)
{}
- enum_field_types type() const { return MYSQL_TYPE_YEAR;}
+ const Type_handler *type_handler() const { return &type_handler_year; }
Copy_func *get_copy_func(const Field *from) const
{
if (eq_def(from))
return get_identical_copy_func();
switch (from->cmp_type()) {
case STRING_RESULT:
+ {
+ const Type_handler *handler= from->type_handler();
+ if (handler == &type_handler_enum || handler == &type_handler_set)
+ return do_field_int;
return do_field_string;
+ }
case TIME_RESULT:
return do_field_temporal;
case DECIMAL_RESULT:
@@ -2693,16 +2876,21 @@ public:
}
return do_field_int;
}
- int store(const char *to,uint length,CHARSET_INFO *charset);
+ 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(MYSQL_TIME *ltime, uint dec);
+ 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, ulonglong fuzzydate);
bool send_binary(Protocol *protocol);
- uint32 max_display_length() { return field_length; }
+ Information_schema_numeric_attributes
+ information_schema_numeric_attributes() const
+ {
+ return Information_schema_numeric_attributes();
+ }
+ uint32 max_display_length() const { return field_length; }
void sql_type(String &str) const;
};
@@ -2712,10 +2900,10 @@ class Field_date :public Field_temporal_with_date {
bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const;
public:
Field_date(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
- enum utype unireg_check_arg, const char *field_name_arg)
+ enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg)
:Field_temporal_with_date(ptr_arg, MAX_DATE_WIDTH, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg) {}
- enum_field_types type() const { return MYSQL_TYPE_DATE;}
+ 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, ulonglong fuzzydate)
@@ -2747,12 +2935,11 @@ class Field_newdate :public Field_temporal_with_date {
bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const;
public:
Field_newdate(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
- enum utype unireg_check_arg, const char *field_name_arg)
+ enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg)
:Field_temporal_with_date(ptr_arg, MAX_DATE_WIDTH, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg)
{}
- enum_field_types type() const { return MYSQL_TYPE_DATE;}
- enum_field_types real_type() const { return MYSQL_TYPE_NEWDATE; }
+ 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);
@@ -2778,19 +2965,26 @@ class Field_time :public Field_temporal {
*/
long curdays;
protected:
- virtual void store_TIME(MYSQL_TIME *ltime);
+ virtual void store_TIME(const MYSQL_TIME *ltime);
int store_TIME_with_warning(MYSQL_TIME *ltime, const ErrConv *str,
int was_cut, int have_smth_to_conv);
+ void set_warnings(Sql_condition::enum_warning_level level,
+ const ErrConv *str, int was_cut)
+ {
+ Field_temporal::set_warnings(level, str, was_cut, MYSQL_TIMESTAMP_TIME);
+ }
bool check_zero_in_date_with_warn(ulonglong fuzzydate);
static void do_field_time(Copy_field *copy);
public:
Field_time(uchar *ptr_arg, uint length_arg, uchar *null_ptr_arg,
uchar null_bit_arg, enum utype unireg_check_arg,
- const char *field_name_arg)
+ const LEX_CSTRING *field_name_arg)
:Field_temporal(ptr_arg, length_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg), curdays(0)
{}
- enum_field_types type() const { return MYSQL_TYPE_TIME;}
+ 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
{
@@ -2805,8 +2999,8 @@ public:
return real_type() == from->real_type() &&
decimals() == from->decimals();
}
- int store_time_dec(MYSQL_TIME *ltime, uint dec);
- int store(const char *to,uint length,CHARSET_INFO *charset);
+ 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 *);
@@ -2838,7 +3032,8 @@ protected:
uint dec;
public:
Field_time_with_dec(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
- enum utype unireg_check_arg, const char *field_name_arg,
+ enum utype unireg_check_arg,
+ const LEX_CSTRING *field_name_arg,
uint dec_arg)
:Field_time(ptr_arg, MIN_TIME_WIDTH + dec_arg + MY_TEST(dec_arg),
null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg),
@@ -2850,7 +3045,7 @@ public:
enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; }
longlong val_int(void);
double val_real(void);
- void make_field(Send_field *);
+ void make_send_field(Send_field *);
};
@@ -2859,10 +3054,10 @@ public:
*/
class Field_time_hires :public Field_time_with_dec {
longlong zero_point;
- void store_TIME(MYSQL_TIME *ltime);
+ void store_TIME(const MYSQL_TIME *);
public:
Field_time_hires(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
- enum utype unireg_check_arg, const char *field_name_arg,
+ enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
uint dec_arg)
:Field_time_with_dec(ptr_arg, null_ptr_arg,
null_bit_arg, unireg_check_arg, field_name_arg,
@@ -2876,7 +3071,7 @@ public:
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
int cmp(const uchar *,const uchar *);
void sort_string(uchar *buff,uint length);
- uint32 pack_length() const;
+ uint32 pack_length() const { return Type_handler_time::hires_bytes(dec); }
uint size_of() const { return sizeof(*this); }
};
@@ -2885,15 +3080,15 @@ public:
TIME(0..6) - MySQL56 version
*/
class Field_timef :public Field_time_with_dec {
- void store_TIME(MYSQL_TIME *ltime);
- int do_save_field_metadata(uchar *metadata_ptr)
+ void store_TIME(const MYSQL_TIME *ltime);
+ int save_field_metadata(uchar *metadata_ptr)
{
- *metadata_ptr= decimals();
+ *metadata_ptr= (uchar) decimals();
return 1;
}
public:
Field_timef(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
- enum utype unireg_check_arg, const char *field_name_arg,
+ enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
uint dec_arg)
:Field_time_with_dec(ptr_arg, null_ptr_arg,
null_bit_arg, unireg_check_arg, field_name_arg,
@@ -2901,7 +3096,7 @@ public:
{
DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
}
- enum_field_types real_type() const { return MYSQL_TYPE_TIME2; }
+ const Type_handler *type_handler() const { return &type_handler_time2; }
enum_field_types binlog_type() const { return MYSQL_TYPE_TIME2; }
uint32 pack_length() const
{
@@ -2935,7 +3130,7 @@ class Field_datetime :public Field_temporal_with_date {
public:
Field_datetime(uchar *ptr_arg, uint length_arg, uchar *null_ptr_arg,
uchar null_bit_arg, enum utype unireg_check_arg,
- const char *field_name_arg)
+ const LEX_CSTRING *field_name_arg)
:Field_temporal_with_date(ptr_arg, length_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg)
{
@@ -2943,7 +3138,7 @@ public:
unireg_check == TIMESTAMP_DNUN_FIELD)
flags|= ON_UPDATE_NOW_FLAG;
}
- enum_field_types type() const { return MYSQL_TYPE_DATETIME;}
+ const Type_handler *type_handler() const { return &type_handler_datetime; }
enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONGLONG; }
double val_real(void);
longlong val_int(void);
@@ -2985,7 +3180,7 @@ protected:
public:
Field_datetime_with_dec(uchar *ptr_arg, uchar *null_ptr_arg,
uchar null_bit_arg, enum utype unireg_check_arg,
- const char *field_name_arg, uint dec_arg)
+ const LEX_CSTRING *field_name_arg, uint dec_arg)
:Field_datetime(ptr_arg, MAX_DATETIME_WIDTH + dec_arg + MY_TEST(dec_arg),
null_ptr_arg, null_bit_arg, unireg_check_arg,
field_name_arg), dec(dec_arg)
@@ -2994,7 +3189,7 @@ public:
}
uint decimals() const { return dec; }
enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; }
- void make_field(Send_field *field);
+ void make_send_field(Send_field *field);
bool send_binary(Protocol *protocol);
uchar *pack(uchar *to, const uchar *from, uint max_length)
{ return Field::pack(to, from, max_length); }
@@ -3021,14 +3216,14 @@ class Field_datetime_hires :public Field_datetime_with_dec {
public:
Field_datetime_hires(uchar *ptr_arg, uchar *null_ptr_arg,
uchar null_bit_arg, enum utype unireg_check_arg,
- const char *field_name_arg, uint dec_arg)
+ const LEX_CSTRING *field_name_arg, uint dec_arg)
:Field_datetime_with_dec(ptr_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg, dec_arg)
{
DBUG_ASSERT(dec);
}
int cmp(const uchar *,const uchar *);
- uint32 pack_length() const;
+ uint32 pack_length() const { return Type_handler_datetime::hires_bytes(dec); }
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
{ return Field_datetime_hires::get_TIME(ltime, ptr, fuzzydate); }
uint size_of() const { return sizeof(*this); }
@@ -3041,19 +3236,19 @@ public:
class Field_datetimef :public Field_datetime_with_dec {
void store_TIME(MYSQL_TIME *ltime);
bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const;
- int do_save_field_metadata(uchar *metadata_ptr)
+ int save_field_metadata(uchar *metadata_ptr)
{
- *metadata_ptr= decimals();
+ *metadata_ptr= (uchar) decimals();
return 1;
}
public:
Field_datetimef(uchar *ptr_arg, uchar *null_ptr_arg,
uchar null_bit_arg, enum utype unireg_check_arg,
- const char *field_name_arg, uint dec_arg)
+ const LEX_CSTRING *field_name_arg, uint dec_arg)
:Field_datetime_with_dec(ptr_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg, dec_arg)
{}
- enum_field_types real_type() const { return MYSQL_TYPE_DATETIME2; }
+ const Type_handler *type_handler() const { return &type_handler_datetime2; }
enum_field_types binlog_type() const { return MYSQL_TYPE_DATETIME2; }
uint32 pack_length() const
{
@@ -3079,7 +3274,8 @@ public:
static inline Field_timestamp *
new_Field_timestamp(MEM_ROOT *root,uchar *ptr, uchar *null_ptr, uchar null_bit,
- enum Field::utype unireg_check, const char *field_name,
+ enum Field::utype unireg_check,
+ const LEX_CSTRING *field_name,
TABLE_SHARE *share, uint dec)
{
if (dec==0)
@@ -3095,7 +3291,7 @@ new_Field_timestamp(MEM_ROOT *root,uchar *ptr, uchar *null_ptr, uchar null_bit,
static inline Field_time *
new_Field_time(MEM_ROOT *root, uchar *ptr, uchar *null_ptr, uchar null_bit,
- enum Field::utype unireg_check, const char *field_name,
+ enum Field::utype unireg_check, const LEX_CSTRING *field_name,
uint dec)
{
if (dec == 0)
@@ -3111,7 +3307,7 @@ new_Field_time(MEM_ROOT *root, uchar *ptr, uchar *null_ptr, uchar null_bit,
static inline Field_datetime *
new_Field_datetime(MEM_ROOT *root, uchar *ptr, uchar *null_ptr, uchar null_bit,
enum Field::utype unireg_check,
- const char *field_name, uint dec)
+ const LEX_CSTRING *field_name, uint dec)
{
if (dec == 0)
return new (root)
@@ -3130,28 +3326,35 @@ class Field_string :public Field_longstr {
public:
Warn_filter_string(const THD *thd, const Field_string *field);
};
+ bool is_var_string() const
+ {
+ return can_alter_field_type &&
+ orig_table &&
+ (orig_table->s->db_create_options & HA_OPTION_PACK_RECORD) &&
+ field_length >= 4 &&
+ orig_table->s->frm_version < FRM_VER_TRUE_VARCHAR;
+ }
public:
bool can_alter_field_type;
Field_string(uchar *ptr_arg, uint32 len_arg,uchar *null_ptr_arg,
uchar null_bit_arg,
- enum utype unireg_check_arg, const char *field_name_arg,
- CHARSET_INFO *cs)
+ enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
+ const DTCollation &collation)
:Field_longstr(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg, cs),
+ unireg_check_arg, field_name_arg, collation),
can_alter_field_type(1) {};
- Field_string(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg,
- CHARSET_INFO *cs)
+ Field_string(uint32 len_arg,bool maybe_null_arg,
+ const LEX_CSTRING *field_name_arg,
+ const DTCollation &collation)
:Field_longstr((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, 0,
- NONE, field_name_arg, cs),
+ NONE, field_name_arg, collation),
can_alter_field_type(1) {};
- enum_field_types type() const
+ const Type_handler *type_handler() const
{
- return ((can_alter_field_type && orig_table &&
- orig_table->s->db_create_options & HA_OPTION_PACK_RECORD &&
- field_length >= 4) &&
- orig_table->s->frm_version < FRM_VER_TRUE_VARCHAR ?
- MYSQL_TYPE_VAR_STRING : MYSQL_TYPE_STRING);
+ if (is_var_string())
+ return &type_handler_var_string;
+ return &type_handler_string;
}
enum ha_base_keytype key_type() const
{ return binary() ? HA_KEYTYPE_BINARY : HA_KEYTYPE_TEXT; }
@@ -3163,9 +3366,8 @@ public:
(has_charset() ? ' ' : 0));
return 0;
}
- int store(const char *to,uint length,CHARSET_INFO *charset);
- int store(longlong nr, bool unsigned_val);
- int store(double nr) { return Field_str::store(nr); } /* QQ: To be deleted */
+ int store(const char *to,size_t length,CHARSET_INFO *charset);
+ using Field_str::store;
double val_real(void);
longlong val_int(void);
String *val_str(String*,String *);
@@ -3194,7 +3396,6 @@ public:
uint packed_col_length(const uchar *to, uint length);
uint max_packed_col_length(uint max_length);
uint size_of() const { return sizeof(*this); }
- enum_field_types real_type() const { return MYSQL_TYPE_STRING; }
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);
@@ -3202,7 +3403,7 @@ public:
sql_mode_t value_depends_on_sql_mode() const;
sql_mode_t can_handle_sql_mode_dependency_on_store() const;
private:
- int do_save_field_metadata(uchar *first_byte);
+ int save_field_metadata(uchar *first_byte);
};
@@ -3216,6 +3417,15 @@ public:
{
return length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
}
+protected:
+ void store_length(uint32 number)
+ {
+ if (length_bytes == 1)
+ *ptr= (uchar) number;
+ else
+ int2store(ptr, number);
+ }
+public:
/*
The maximum space available in a Field_varstring, in bytes. See
length_bytes.
@@ -3226,25 +3436,25 @@ public:
Field_varstring(uchar *ptr_arg,
uint32 len_arg, uint length_bytes_arg,
uchar *null_ptr_arg, uchar null_bit_arg,
- enum utype unireg_check_arg, const char *field_name_arg,
- TABLE_SHARE *share, CHARSET_INFO *cs)
+ enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
+ TABLE_SHARE *share, const DTCollation &collation)
:Field_longstr(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg, cs),
+ unireg_check_arg, field_name_arg, collation),
length_bytes(length_bytes_arg)
{
share->varchar_fields++;
}
Field_varstring(uint32 len_arg,bool maybe_null_arg,
- const char *field_name_arg,
- TABLE_SHARE *share, CHARSET_INFO *cs)
+ const LEX_CSTRING *field_name_arg,
+ TABLE_SHARE *share, const DTCollation &collation)
:Field_longstr((uchar*) 0,len_arg, maybe_null_arg ? (uchar*) "": 0, 0,
- NONE, field_name_arg, cs),
+ NONE, field_name_arg, collation),
length_bytes(len_arg < 256 ? 1 :2)
{
share->varchar_fields++;
}
- enum_field_types type() const { return MYSQL_TYPE_VARCHAR; }
+ 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; }
@@ -3257,17 +3467,12 @@ public:
length_bytes : 0);
}
Copy_func *get_copy_func(const Field *from) const;
- bool memcpy_field_possible(const Field *from) const
- {
- return Field_str::memcpy_field_possible(from) &&
- length_bytes == ((Field_varstring*) from)->length_bytes;
- }
- int store(const char *to,uint length,CHARSET_INFO *charset);
- int store(longlong nr, bool unsigned_val);
- int store(double nr) { return Field_str::store(nr); } /* QQ: To be deleted */
-#ifdef HAVE_valgrind_or_MSAN
+ bool memcpy_field_possible(const Field *from) const;
+ int store(const char *to,size_t length,CHARSET_INFO *charset);
+ using Field_str::store;
+#ifdef HAVE_valgrind
void mark_unused_memory_as_defined();
-#endif /* HAVE_valgrind_or_MSAN */
+#endif
double val_real(void);
longlong val_int(void);
String *val_str(String*,String *);
@@ -3289,7 +3494,6 @@ public:
uint max_packed_col_length(uint max_length);
uint32 data_length();
uint size_of() const { return sizeof(*this); }
- enum_field_types real_type() const { return MYSQL_TYPE_VARCHAR; }
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);
@@ -3300,10 +3504,97 @@ public:
void hash(ulong *nr, ulong *nr2);
uint length_size() { return length_bytes; }
private:
- int do_save_field_metadata(uchar *first_byte);
+ int save_field_metadata(uchar *first_byte);
+};
+
+
+class Field_varstring_compressed: public Field_varstring {
+public:
+ Field_varstring_compressed(uchar *ptr_arg,
+ uint32 len_arg, uint length_bytes_arg,
+ uchar *null_ptr_arg, uchar null_bit_arg,
+ enum utype unireg_check_arg,
+ const LEX_CSTRING *field_name_arg,
+ TABLE_SHARE *share, const DTCollation &collation,
+ Compression_method *compression_method_arg):
+ Field_varstring(ptr_arg, len_arg, length_bytes_arg, null_ptr_arg,
+ 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
+ { return compression_method_ptr; }
+private:
+ Compression_method *compression_method_ptr;
+ int store(const char *to, size_t length, CHARSET_INFO *charset);
+ 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
+ {
+ Field_varstring::sql_type(str);
+ str.append(STRING_WITH_LEN(" /*!100301 COMPRESSED*/"));
+ }
+ uint32 max_display_length() const { return field_length - 1; }
+ uint32 char_length() const
+ {
+ return (field_length - 1) / field_charset->mbmaxlen;
+ }
+ int cmp(const uchar *a_ptr, const uchar *b_ptr);
+
+ /*
+ 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)
+ { DBUG_ASSERT(0); return 0; }
+ using Field_varstring::key_cmp;
};
+static inline uint8 number_storage_requirement(uint32 n)
+{
+ return n < 256 ? 1 : n < 65536 ? 2 : n < 16777216 ? 3 : 4;
+}
+
+
+static inline void store_bigendian(ulonglong num, uchar *to, uint bytes)
+{
+ switch(bytes) {
+ case 1: mi_int1store(to, num); break;
+ case 2: mi_int2store(to, num); break;
+ case 3: mi_int3store(to, num); break;
+ case 4: mi_int4store(to, num); break;
+ case 5: mi_int5store(to, num); break;
+ case 6: mi_int6store(to, num); break;
+ case 7: mi_int7store(to, num); break;
+ case 8: mi_int8store(to, num); break;
+ default: DBUG_ASSERT(0);
+ }
+}
+
+
+static inline longlong read_bigendian(const uchar *from, uint bytes)
+{
+ switch(bytes) {
+ case 1: return mi_uint1korr(from);
+ case 2: return mi_uint2korr(from);
+ case 3: return mi_uint3korr(from);
+ case 4: return mi_uint4korr(from);
+ case 5: return mi_uint5korr(from);
+ case 6: return mi_uint6korr(from);
+ case 7: return mi_uint7korr(from);
+ case 8: return mi_sint8korr(from);
+ default: DBUG_ASSERT(0); return 0;
+ }
+}
+
+
+extern LEX_CSTRING temp_lex_str;
+
class Field_blob :public Field_longstr {
protected:
/**
@@ -3326,37 +3617,63 @@ protected:
static void do_conv_blob(Copy_field *copy);
public:
Field_blob(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
- enum utype unireg_check_arg, const char *field_name_arg,
- TABLE_SHARE *share, uint blob_pack_length, CHARSET_INFO *cs);
- Field_blob(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg,
- CHARSET_INFO *cs)
+ enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
+ TABLE_SHARE *share, uint blob_pack_length,
+ const DTCollation &collation);
+ Field_blob(uint32 len_arg,bool maybe_null_arg, const LEX_CSTRING *field_name_arg,
+ const DTCollation &collation)
:Field_longstr((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, 0,
- NONE, field_name_arg, cs),
+ NONE, field_name_arg, collation),
packlength(4)
{
flags|= BLOB_FLAG;
}
- Field_blob(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg,
- CHARSET_INFO *cs, bool set_packlength)
+ Field_blob(uint32 len_arg,bool maybe_null_arg,
+ const LEX_CSTRING *field_name_arg,
+ const DTCollation &collation, bool set_packlength)
:Field_longstr((uchar*) 0,len_arg, maybe_null_arg ? (uchar*) "": 0, 0,
- NONE, field_name_arg, cs)
+ NONE, field_name_arg, collation)
{
flags|= BLOB_FLAG;
- packlength= 4;
- if (set_packlength)
- {
- packlength= len_arg <= 255 ? 1 :
- len_arg <= 65535 ? 2 :
- len_arg <= 16777215 ? 3 : 4;
- }
+ packlength= set_packlength ? number_storage_requirement(len_arg) : 4;
}
Field_blob(uint32 packlength_arg)
- :Field_longstr((uchar*) 0, 0, (uchar*) "", 0, NONE, "temp", system_charset_info),
+ :Field_longstr((uchar*) 0, 0, (uchar*) "", 0, NONE, &temp_lex_str,
+ system_charset_info),
packlength(packlength_arg) {}
+ const Type_handler *type_handler() const;
/* Note that the default copy constructor is used, in clone() */
- enum_field_types type() const { return MYSQL_TYPE_BLOB;}
+ enum_field_types type() const
+ {
+ /*
+ We cannot return type_handler()->field_type() here.
+ Some pieces of the code (e.g. in engines) rely on the fact
+ that Field::type(), Field::real_type() and Item_field::field_type()
+ return MYSQL_TYPE_BLOB for all blob variants.
+ We should eventually fix all such code pieces to expect
+ all BLOB type codes.
+ */
+ return MYSQL_TYPE_BLOB;
+ }
+ enum_field_types real_type() const
+ {
+ return MYSQL_TYPE_BLOB;
+ }
enum ha_base_keytype key_type() const
{ return binary() ? HA_KEYTYPE_VARBINARY2 : HA_KEYTYPE_VARTEXT2; }
+ Type_std_attributes type_std_attributes() const
+ {
+ return Type_std_attributes(Field_blob::max_display_length(), decimals(),
+ MY_TEST(flags & UNSIGNED_FLAG),
+ dtcollation());
+ }
+ Information_schema_character_attributes
+ information_schema_character_attributes() const
+ {
+ uint32 octets= Field_blob::octet_length();
+ uint32 chars= octets / field_charset->mbminlen;
+ return Information_schema_character_attributes(octets, chars);
+ }
Copy_func *get_copy_func(const Field *from) const
{
/*
@@ -3364,7 +3681,8 @@ public:
if (from->type() == MYSQL_TYPE_BIT)
return do_field_int;
*/
- if (!(from->flags & BLOB_FLAG) || from->charset() != charset())
+ if (!(from->flags & BLOB_FLAG) || from->charset() != charset() ||
+ !from->compression_method() != !compression_method())
return do_conv_blob;
if (from->pack_length() != Field_blob::pack_length())
return do_copy_blob;
@@ -3381,11 +3699,11 @@ public:
bool memcpy_field_possible(const Field *from) const
{
return Field_str::memcpy_field_possible(from) &&
+ !compression_method() == !from->compression_method() &&
!table->copy_blobs;
}
- int store(const char *to,uint length,CHARSET_INFO *charset);
- int store(double nr);
- int store(longlong nr, bool unsigned_val);
+ int store(const char *to, size_t length, CHARSET_INFO *charset);
+ using Field_str::store;
double val_real(void);
longlong val_int(void);
String *val_str(String*,String *);
@@ -3426,9 +3744,10 @@ public:
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(); }
void store_length(uchar *i_ptr, uint i_packlength, uint32 i_number);
- inline void store_length(uint32 number)
+ inline void store_length(size_t number)
{
- store_length(ptr, packlength, number);
+ DBUG_ASSERT(number < UINT_MAX32);
+ store_length(ptr, packlength, (uint32)number);
}
inline uint32 get_length(my_ptrdiff_t row_offset= 0) const
{ return get_length(ptr+row_offset, this->packlength); }
@@ -3514,15 +3833,63 @@ public:
uint size_of() const { return sizeof(*this); }
bool has_charset(void) const
{ return charset() == &my_charset_bin ? FALSE : TRUE; }
- uint32 max_display_length();
+ uint32 max_display_length() const;
uint32 char_length() const;
+ uint32 octet_length() const;
uint is_equal(Create_field *new_field);
friend void TABLE::remember_blob_values(String *blob_storage);
friend void TABLE::restore_blob_values(String *blob_storage);
private:
- int do_save_field_metadata(uchar *first_byte);
+ int save_field_metadata(uchar *first_byte);
+};
+
+
+class Field_blob_compressed: public Field_blob {
+public:
+ Field_blob_compressed(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 DTCollation &collation,
+ Compression_method *compression_method_arg):
+ 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
+ { return compression_method_ptr; }
+private:
+ Compression_method *compression_method_ptr;
+ int store(const char *to, size_t length, CHARSET_INFO *charset);
+ 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
+ {
+ Field_blob::sql_type(str);
+ str.append(STRING_WITH_LEN(" /*!100301 COMPRESSED*/"));
+ }
+
+ /*
+ Compressed fields can't have keys as two rows may have different
+ compression methods or compression levels.
+ */
+
+ uint get_key_image(uchar *buff, uint length, imagetype type_arg)
+ { DBUG_ASSERT(0); return 0; }
+ void set_key_image(const uchar *buff, uint length)
+ { DBUG_ASSERT(0); }
+ int key_cmp(const uchar *a, const uchar *b)
+ { DBUG_ASSERT(0); return 0; }
+ int key_cmp(const uchar *str, uint length)
+ { 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)
+ { DBUG_ASSERT(0); return 0; }
};
@@ -3535,25 +3902,37 @@ public:
enum storage_type { GEOM_STORAGE_WKB= 0, GEOM_STORAGE_BINARY= 1};
enum storage_type storage;
- Field_geom(uchar *ptr_arg, uchar *null_ptr_arg, uint null_bit_arg,
- enum utype unireg_check_arg, const char *field_name_arg,
+ 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_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; }
- Field_geom(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg,
- TABLE_SHARE *share, enum geometry_type geom_type_arg)
- :Field_blob(len_arg, maybe_null_arg, field_name_arg, &my_charset_bin)
- { geom_type= geom_type_arg; srid= 0; }
enum ha_base_keytype key_type() const { return HA_KEYTYPE_VARBINARY2; }
- enum_field_types type() const { return MYSQL_TYPE_GEOMETRY; }
+ 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();
+ }
bool can_optimize_range(const Item_bool_func *cond,
const Item *item,
bool is_eq_func) const;
void sql_type(String &str) const;
uint is_equal(Create_field *new_field);
- int store(const char *to, uint length, CHARSET_INFO *charset);
+ 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 *);
@@ -3581,7 +3960,7 @@ public:
};
uint gis_field_options_image(uchar *buff, List<Create_field> &create_fields);
-uint gis_field_options_read(const uchar *buf, uint buf_len,
+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*/
@@ -3595,19 +3974,18 @@ public:
TYPELIB *typelib;
Field_enum(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
- enum utype unireg_check_arg, const char *field_name_arg,
+ enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
uint packlength_arg,
TYPELIB *typelib_arg,
- CHARSET_INFO *charset_arg)
+ const DTCollation &collation)
:Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg, charset_arg),
+ unireg_check_arg, field_name_arg, collation),
packlength(packlength_arg),typelib(typelib_arg)
{
flags|=ENUM_FLAG;
}
Field *make_new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type);
- enum_field_types type() const { return MYSQL_TYPE_STRING; }
- enum Item_result cmp_type () const { return INT_RESULT; }
+ 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
@@ -3637,7 +4015,7 @@ public:
return save_in_field_str(to);
}
bool memcpy_field_possible(const Field *from) const { return false; }
- int store(const char *to,uint length,CHARSET_INFO *charset);
+ 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);
@@ -3649,17 +4027,17 @@ public:
void store_type(ulonglong value);
void sql_type(String &str) const;
uint size_of() const { return sizeof(*this); }
- enum_field_types real_type() const { return MYSQL_TYPE_ENUM; }
uint pack_length_from_metadata(uint field_metadata)
{ 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) { 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; }
/* 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; }
virtual uchar *pack(uchar *to, const uchar *from, uint max_length);
virtual const uchar *unpack(uchar *to, const uchar *from,
@@ -3679,8 +4057,11 @@ public:
*/
return false;
}
+ bool can_optimize_range(const Item_bool_func *cond,
+ const Item *item,
+ bool is_eq_func) const;
private:
- int do_save_field_metadata(uchar *first_byte);
+ int save_field_metadata(uchar *first_byte);
uint is_equal(Create_field *new_field);
};
@@ -3689,19 +4070,19 @@ class Field_set :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 char *field_name_arg,
+ enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
uint32 packlength_arg,
- TYPELIB *typelib_arg, CHARSET_INFO *charset_arg)
+ 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,
- typelib_arg,charset_arg),
- empty_set_string("", 0, charset_arg)
+ typelib_arg, collation),
+ empty_set_string("", 0, collation.collation)
{
flags=(flags & ~ENUM_FLAG) | SET_FLAG;
}
int store_field(Field *from) { return from->save_in_field(this); }
- int store(const char *to,uint length,CHARSET_INFO *charset);
+ 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);
@@ -3709,7 +4090,7 @@ public:
String *val_str(String*,String *);
void sql_type(String &str) const;
uint size_of() const { return sizeof(*this); }
- enum_field_types real_type() const { return MYSQL_TYPE_SET; }
+ const Type_handler *type_handler() const { return &type_handler_set; }
bool has_charset(void) const { return TRUE; }
private:
const String empty_set_string;
@@ -3738,14 +4119,18 @@ public:
uint bytes_in_rec;
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 char *field_name_arg);
- enum_field_types type() const { return MYSQL_TYPE_BIT; }
+ 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() { return field_length; }
+ uint32 max_display_length() const { return field_length; }
+ Information_schema_numeric_attributes
+ information_schema_numeric_attributes() const
+ {
+ return Information_schema_numeric_attributes(field_length);
+ }
uint size_of() const { return sizeof(*this); }
- Item_result result_type () const { return INT_RESULT; }
int reset(void) {
bzero(ptr, bytes_in_rec);
if (bit_ptr && (bit_len > 0)) // reset odd bits among null bits
@@ -3760,7 +4145,7 @@ public:
}
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, uint length, CHARSET_INFO *charset);
+ 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 *);
@@ -3861,7 +4246,7 @@ public:
private:
virtual size_t do_last_null_byte() const;
- int do_save_field_metadata(uchar *first_byte);
+ int save_field_metadata(uchar *first_byte);
};
@@ -3876,10 +4261,10 @@ class Field_bit_as_char: 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 char *field_name_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, uint length, CHARSET_INFO *charset);
+ 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)
{ return Field_bit::store(nr, unsigned_val); }
@@ -3887,33 +4272,97 @@ public:
};
-extern const LEX_STRING null_lex_str;
+class Field_row: public Field_null
+{
+ class Virtual_tmp_table *m_table;
+public:
+ Field_row(uchar *ptr_arg, const LEX_CSTRING *field_name_arg)
+ :Field_null(ptr_arg, 0, Field::NONE, field_name_arg, &my_charset_bin),
+ m_table(NULL)
+ {}
+ ~Field_row();
+ Virtual_tmp_table **virtual_tmp_table_addr() { return &m_table; }
+ bool sp_prepare_and_store_item(THD *thd, Item **value);
+};
+extern const LEX_CSTRING null_clex_str;
+
Field *make_field(TABLE_SHARE *share, MEM_ROOT *mem_root,
uchar *ptr, uint32 field_length,
uchar *null_pos, uchar null_bit,
- uint pack_flag, enum_field_types field_type,
+ uint pack_flag, const Type_handler *handler,
CHARSET_INFO *cs,
Field::geometry_type geom_type, uint srid,
Field::utype unireg_check,
- TYPELIB *interval, const char *field_name);
+ TYPELIB *interval, const LEX_CSTRING *field_name,
+ uint32 flags);
/*
Create field class for CREATE TABLE
*/
-class Column_definition: public Sql_alloc
+class Column_definition: public Sql_alloc,
+ public Type_handler_hybrid_field_type
{
+ /**
+ Create "interval" from "interval_list".
+ @param mem_root - memory root to create the TYPELIB
+ instance and its values on
+ @param reuse_interval_list_values - determines if TYPELIB can reuse strings
+ from interval_list, or should always
+ allocate a copy on mem_root, even if
+ character set conversion is not needed
+ @retval false on success
+ @retval true on error (bad values, or EOM)
+ */
+ bool create_interval_from_interval_list(MEM_ROOT *mem_root,
+ bool reuse_interval_list_values);
+
+ /*
+ Calculate TYPELIB (set or enum) max and total lengths
+
+ @param cs charset+collation pair of the interval
+ @param max_length length of the longest item
+ @param tot_length sum of the item lengths
+
+ After this method call:
+ - ENUM uses max_length
+ - SET uses tot_length.
+ */
+ void calculate_interval_lengths(uint32 *max_length, uint32 *tot_length)
+ {
+ const char **pos;
+ uint *len;
+ *max_length= *tot_length= 0;
+ for (pos= interval->type_names, len= interval->type_lengths;
+ *pos ; pos++, len++)
+ {
+ size_t length= charset->cset->numchars(charset, *pos, *pos + *len);
+ DBUG_ASSERT(length < UINT_MAX32);
+ *tot_length+= (uint) length;
+ set_if_bigger(*max_length, (uint32)length);
+ }
+ }
+ bool prepare_stage1_check_typelib_default();
+ bool prepare_stage1_convert_default(THD *, MEM_ROOT *, CHARSET_INFO *to);
+ const Type_handler *field_type() const; // Prevent using this
+ Compression_method *compression_method_ptr;
public:
- const char *field_name;
- LEX_STRING comment; // Comment for field
+ LEX_CSTRING field_name;
+ LEX_CSTRING comment; // Comment for field
+ enum enum_column_versioning
+ {
+ VERSIONING_NOT_SET,
+ WITH_VERSIONING,
+ WITHOUT_VERSIONING
+ };
Item *on_update; // ON UPDATE NOW()
- enum enum_field_types sql_type;
/*
At various stages in execution this can be length of field in bytes or
max number of characters.
*/
ulonglong length;
+ field_visibility_t invisible;
/*
The value of `length' as set by parser: is the number of characters
for most of the types, or of bytes for BLOBs or numeric types.
@@ -3940,19 +4389,124 @@ public:
*default_value, // Default value
*check_constraint; // Check constraint
- Column_definition():
- comment(null_lex_str),
- on_update(0), sql_type(MYSQL_TYPE_NULL),
+ enum_column_versioning versioning;
+
+ Column_definition()
+ :Type_handler_hybrid_field_type(&type_handler_null),
+ compression_method_ptr(0),
+ comment(null_clex_str),
+ on_update(NULL), length(0), invisible(VISIBLE), char_length(0),
+ decimals(0),
flags(0), pack_length(0), key_length(0), unireg_check(Field::NONE),
- interval(0), srid(0), geom_type(Field::GEOM_GEOMETRY),
- option_list(NULL),
- vcol_info(0), default_value(0), check_constraint(0)
+ interval(0), charset(&my_charset_bin),
+ srid(0), geom_type(Field::GEOM_GEOMETRY),
+ option_list(NULL), pack_flag(0),
+ vcol_info(0), default_value(0), check_constraint(0),
+ versioning(VERSIONING_NOT_SET)
{
interval_list.empty();
}
Column_definition(THD *thd, Field *field, Field *orig_field);
- void create_length_to_internal_length(void);
+ void set_attributes(const Lex_field_type_st &type, CHARSET_INFO *cs);
+ void create_length_to_internal_length_null()
+ {
+ DBUG_ASSERT(length == 0);
+ key_length= pack_length= 0;
+ }
+ void create_length_to_internal_length_simple()
+ {
+ key_length= pack_length= type_handler()->calc_pack_length((uint32) length);
+ }
+ void create_length_to_internal_length_string()
+ {
+ length*= charset->mbmaxlen;
+ 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
+ {
+ return flags & (VERS_SYS_START_FLAG | VERS_SYS_END_FLAG);
+ }
+ void create_length_to_internal_length_bit();
+ void create_length_to_internal_length_newdecimal();
+
+ /**
+ Prepare a SET/ENUM field.
+ Create "interval" from "interval_list" if needed, and adjust "length".
+ @param mem_root - Memory root to allocate TYPELIB and
+ its values on
+ @param reuse_interval_list_values - determines if TYPELIB can reuse value
+ buffers from interval_list, or should
+ always allocate a copy on mem_root,
+ even if character set conversion
+ is not needed
+ */
+ bool prepare_interval_field(MEM_ROOT *mem_root,
+ bool reuse_interval_list_values);
+
+ void prepare_interval_field_calc_length()
+ {
+ uint32 field_length, dummy;
+ if (real_field_type() == MYSQL_TYPE_SET)
+ {
+ calculate_interval_lengths(&dummy, &field_length);
+ length= field_length + (interval->count - 1);
+ }
+ else /* MYSQL_TYPE_ENUM */
+ {
+ calculate_interval_lengths(&field_length, &dummy);
+ length= field_length;
+ }
+ set_if_smaller(length, MAX_FIELD_WIDTH - 1);
+ }
+
+ bool prepare_blob_field(THD *thd);
+
+ bool sp_prepare_create_field(THD *thd, MEM_ROOT *mem_root);
+
+ bool prepare_stage1(THD *thd, MEM_ROOT *mem_root,
+ handler *file, ulonglong table_flags);
+ bool prepare_stage1_typelib(THD *thd, MEM_ROOT *mem_root,
+ handler *file, ulonglong table_flags);
+ bool prepare_stage1_string(THD *thd, MEM_ROOT *mem_root,
+ handler *file, ulonglong table_flags);
+ bool prepare_stage1_bit(THD *thd, MEM_ROOT *mem_root,
+ handler *file, ulonglong table_flags);
+
+ void redefine_stage1_common(const Column_definition *dup_field,
+ const handler *file,
+ const Schema_specification_st *schema);
+ bool redefine_stage1(const Column_definition *dup_field, const handler *file,
+ const Schema_specification_st *schema)
+ {
+ const Type_handler *handler= dup_field->type_handler();
+ return handler->Column_definition_redefine_stage1(this, dup_field,
+ file, schema);
+ }
+ bool prepare_stage2(handler *handler, ulonglong table_flags);
+ bool prepare_stage2_blob(handler *handler,
+ ulonglong table_flags, uint field_flags);
+ 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 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);
+ bool fix_attributes_int(uint default_length);
+ bool fix_attributes_decimal();
+ bool fix_attributes_temporal_with_time(uint int_part_length);
+ bool fix_attributes_bit();
bool check(THD *thd);
@@ -3977,16 +4531,16 @@ public:
Field *make_field(TABLE_SHARE *share, MEM_ROOT *mem_root,
uchar *ptr, uchar *null_pos, uchar null_bit,
- const char *field_name_arg) const
+ const LEX_CSTRING *field_name_arg) const
{
return ::make_field(share, mem_root, ptr,
(uint32)length, null_pos, null_bit,
- pack_flag, sql_type, charset,
+ pack_flag, type_handler(), charset,
geom_type, srid, unireg_check, interval,
- field_name_arg);
+ field_name_arg, flags);
}
Field *make_field(TABLE_SHARE *share, MEM_ROOT *mem_root,
- const char *field_name_arg)
+ const LEX_CSTRING *field_name_arg) const
{
return make_field(share, mem_root, (uchar *) 0, (uchar *) "", 0,
field_name_arg);
@@ -3999,14 +4553,205 @@ public:
return unireg_check == Field::TIMESTAMP_DN_FIELD
|| unireg_check == Field::TIMESTAMP_DNUN_FIELD;
}
+
+ void set_type(const Column_definition &other)
+ {
+ set_handler(other.type_handler());
+ length= other.length;
+ char_length= other.char_length;
+ 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;
+ }
+
+ // Replace the entire value by another definition
+ void set_column_definition(const Column_definition *def)
+ {
+ *this= *def;
+ }
+ bool set_compressed(const char *method);
+ bool set_compressed_deprecated(THD *thd, const char *method);
+ bool set_compressed_deprecated_column_attribute(THD *thd,
+ const char *pos,
+ const char *method);
+ void set_compression_method(Compression_method *compression_method_arg)
+ { compression_method_ptr= compression_method_arg; }
+ Compression_method *compression_method() const
+ { return compression_method_ptr; }
+};
+
+
+/**
+ List of ROW element definitions, e.g.:
+ DECLARE a ROW(a INT,b VARCHAR(10))
+*/
+class Row_definition_list: public List<class Spvar_definition>
+{
+public:
+ inline bool eq_name(const Spvar_definition *def, const LEX_CSTRING *name) const;
+ /**
+ Find a ROW field by name.
+ @param [IN] name - the name
+ @param [OUT] offset - if the ROW field found, its offset it returned here
+ @retval NULL - the ROW field was not found
+ @retval !NULL - the pointer to the found ROW field
+ */
+ Spvar_definition *find_row_field_by_name(const LEX_CSTRING *name, uint *offset) const
+ {
+ // Cast-off the "const" qualifier
+ List_iterator<Spvar_definition> it(*((List<Spvar_definition>*)this));
+ Spvar_definition *def;
+ for (*offset= 0; (def= it++); (*offset)++)
+ {
+ if (eq_name(def, name))
+ return def;
+ }
+ return 0;
+ }
+ bool adjust_formal_params_to_actual_params(THD *thd, List<Item> *args);
+ bool adjust_formal_params_to_actual_params(THD *thd,
+ Item **args, uint arg_count);
+ bool resolve_type_refs(THD *);
+};
+
+/**
+ This class is used during a stored routine or a trigger execution,
+ at sp_rcontext::create() time.
+ Currently it can represent:
+ - variables with explicit data types: DECLARE a INT;
+ - variables with data type references: DECLARE a t1.a%TYPE;
+ - ROW type variables
+
+ Notes:
+ - Scalar variables have m_field_definitions==NULL.
+ - ROW variables are defined as having MYSQL_TYPE_NULL,
+ with a non-empty m_field_definitions.
+
+ Data type references to other object types will be added soon, e.g.:
+ - DECLARE a table_name%ROWTYPE;
+ - DECLARE a cursor_name%ROWTYPE;
+ - DECLARE a record_name%TYPE;
+ - DECLARE a variable_name%TYPE;
+*/
+class Spvar_definition: public Column_definition
+{
+ Qualified_column_ident *m_column_type_ref; // for %TYPE
+ Table_ident *m_table_rowtype_ref; // for table%ROWTYPE
+ bool m_cursor_rowtype_ref; // for cursor%ROWTYPE
+ uint m_cursor_rowtype_offset; // for cursor%ROWTYPE
+ Row_definition_list *m_row_field_definitions; // for ROW
+public:
+ Spvar_definition()
+ :m_column_type_ref(NULL),
+ m_table_rowtype_ref(NULL),
+ m_cursor_rowtype_ref(false),
+ m_cursor_rowtype_offset(0),
+ m_row_field_definitions(NULL)
+ { }
+ Spvar_definition(THD *thd, Field *field)
+ :Column_definition(thd, field, NULL),
+ m_column_type_ref(NULL),
+ m_table_rowtype_ref(NULL),
+ m_cursor_rowtype_ref(false),
+ m_cursor_rowtype_offset(0),
+ m_row_field_definitions(NULL)
+ { }
+ const Type_handler *type_handler() const
+ {
+ return Type_handler_hybrid_field_type::type_handler();
+ }
+ bool is_column_type_ref() const { return m_column_type_ref != 0; }
+ bool is_table_rowtype_ref() const { return m_table_rowtype_ref != 0; }
+ bool is_cursor_rowtype_ref() const { return m_cursor_rowtype_ref; }
+ bool is_explicit_data_type() const
+ {
+ return !is_column_type_ref() &&
+ !is_table_rowtype_ref() &&
+ !is_cursor_rowtype_ref();
+ }
+ Qualified_column_ident *column_type_ref() const
+ {
+ return m_column_type_ref;
+ }
+ void set_column_type_ref(Qualified_column_ident *ref)
+ {
+ m_column_type_ref= ref;
+ }
+
+ Table_ident *table_rowtype_ref() const
+ {
+ return m_table_rowtype_ref;
+ }
+ void set_table_rowtype_ref(Table_ident *ref)
+ {
+ DBUG_ASSERT(ref);
+ set_handler(&type_handler_row);
+ m_table_rowtype_ref= ref;
+ }
+
+ uint cursor_rowtype_offset() const
+ {
+ return m_cursor_rowtype_offset;
+ }
+ void set_cursor_rowtype_ref(uint offset)
+ {
+ set_handler(&type_handler_row);
+ m_cursor_rowtype_ref= true;
+ m_cursor_rowtype_offset= offset;
+ }
+
+ /*
+ Find a ROW field by name.
+ See Row_field_list::find_row_field_by_name() for details.
+ */
+ Spvar_definition *find_row_field_by_name(const LEX_CSTRING *name, uint *offset) const
+ {
+ DBUG_ASSERT(m_row_field_definitions);
+ return m_row_field_definitions->find_row_field_by_name(name, offset);
+ }
+ uint is_row() const
+ {
+ return m_row_field_definitions != NULL;
+ }
+ // Check if "this" defines a ROW variable with n elements
+ uint is_row(uint n) const
+ {
+ return m_row_field_definitions != NULL &&
+ m_row_field_definitions->elements == n;
+ }
+ Row_definition_list *row_field_definitions() const
+ {
+ return m_row_field_definitions;
+ }
+ void set_row_field_definitions(Row_definition_list *list)
+ {
+ DBUG_ASSERT(list);
+ set_handler(&type_handler_row);
+ m_row_field_definitions= list;
+ }
+
};
+inline bool Row_definition_list::eq_name(const Spvar_definition *def,
+ const LEX_CSTRING *name) const
+{
+ return def->field_name.length == name->length && my_strcasecmp(system_charset_info, def->field_name.str, name->str) == 0;
+}
+
+
class Create_field :public Column_definition
{
public:
- const char *change; // If done with alter table
- const char *after; // Put column after this one
+ 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
// Used only for UCS2 intervals
@@ -4018,16 +4763,20 @@ public:
bool create_if_not_exists; // Used in ALTER TABLE IF NOT EXISTS
Create_field():
- Column_definition(), change(0), after(0),
+ Column_definition(),
field(0), option_struct(NULL),
create_if_not_exists(false)
- { }
+ {
+ change= after= null_clex_str;
+ }
Create_field(THD *thd, Field *old_field, Field *orig_field):
Column_definition(thd, old_field, orig_field),
- change(old_field->field_name), after(0),
+ change(old_field->field_name),
field(old_field), option_struct(old_field->option_struct),
create_if_not_exists(false)
- { }
+ {
+ after= null_clex_str;
+ }
/* Used to make a clone of this object for ALTER/CREATE TABLE */
Create_field *clone(MEM_ROOT *mem_root) const;
};
@@ -4041,9 +4790,9 @@ class Send_field :public Sql_alloc {
public:
const char *db_name;
const char *table_name,*org_table_name;
- const char *col_name,*org_col_name;
+ LEX_CSTRING col_name, org_col_name;
ulong length;
- uint charsetnr, flags, decimals;
+ uint flags, decimals;
enum_field_types type;
Send_field() {}
};
@@ -4091,11 +4840,10 @@ public:
uint pack_length_to_packflag(uint type);
enum_field_types get_blob_type_from_length(ulong length);
-uint32 calc_pack_length(enum_field_types type,uint32 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, const char *name,
+bool check_expression(Virtual_column_info *vcol, LEX_CSTRING *name,
enum_vcol_info_type type);
/*
@@ -4140,5 +4888,21 @@ bool check_expression(Virtual_column_info *vcol, const char *name,
#define f_no_default(x) ((x) & FIELDFLAG_NO_DEFAULT)
#define f_bit_as_char(x) ((x) & FIELDFLAG_TREAT_BIT_AS_CHAR)
#define f_is_hex_escape(x) ((x) & FIELDFLAG_HEX_ESCAPE)
+#define f_visibility(x) (static_cast<field_visibility_t> ((x) & INVISIBLE_MAX_BITS))
+
+inline
+ulonglong TABLE::vers_end_id() const
+{
+ DBUG_ASSERT(versioned(VERS_TRX_ID));
+ return static_cast<ulonglong>(vers_end_field()->val_int());
+}
+
+inline
+ulonglong TABLE::vers_start_id() const
+{
+ DBUG_ASSERT(versioned(VERS_TRX_ID));
+ return static_cast<ulonglong>(vers_start_field()->val_int());
+}
+
#endif /* FIELD_INCLUDED */
diff --git a/sql/field_comp.cc b/sql/field_comp.cc
new file mode 100644
index 00000000000..ab97c8ccf08
--- /dev/null
+++ b/sql/field_comp.cc
@@ -0,0 +1,154 @@
+/* Copyright (C) 2017 MariaDB Foundation
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+
+#include <my_global.h>
+#include "sql_string.h"
+#include "sql_class.h"
+#include "field_comp.h"
+#include <zlib.h>
+
+
+/**
+ Compresses string using zlib
+
+ @param[out] to destination buffer for compressed data
+ @param[in] from data to compress
+ @param[in] length from length
+
+ Requirement is such that string stored at `to' must not exceed `from' length.
+ Otherwise 0 is returned and caller stores string uncompressed.
+
+ `to' must be large enough to hold `length' bytes.
+
+ length == 1 is an edge case that may break stream.avail_out calculation: at
+ least 2 bytes required to store metadata.
+*/
+
+static uint compress_zlib(THD *thd, char *to, const char *from, uint length)
+{
+ uint level= thd->variables.column_compression_zlib_level;
+
+ /* Caller takes care of empty strings. */
+ DBUG_ASSERT(length);
+
+ if (level > 0 && length > 1)
+ {
+ z_stream stream;
+ int wbits= thd->variables.column_compression_zlib_wrap ? MAX_WBITS :
+ -MAX_WBITS;
+ uint strategy= thd->variables.column_compression_zlib_strategy;
+ /* Store only meaningful bytes of original data length. */
+ uchar original_pack_length= number_storage_requirement(length);
+
+ *to= 0x80 + original_pack_length + (wbits < 0 ? 8 : 0);
+ store_bigendian(length, (uchar*) to + 1, original_pack_length);
+
+ stream.avail_in= length;
+ stream.next_in= (Bytef*) from;
+
+ DBUG_ASSERT(length >= static_cast<uint>(original_pack_length) + 1);
+ stream.avail_out= length - original_pack_length - 1;
+ stream.next_out= (Bytef*) to + original_pack_length + 1;
+
+ stream.zalloc= 0;
+ stream.zfree= 0;
+ stream.opaque= 0;
+
+ if (deflateInit2(&stream, level, Z_DEFLATED, wbits, 8, strategy) == Z_OK)
+ {
+ int res= deflate(&stream, Z_FINISH);
+ if (deflateEnd(&stream) == Z_OK && res == Z_STREAM_END)
+ return (uint) (stream.next_out - (Bytef*) to);
+ }
+ }
+ return 0;
+}
+
+
+static int uncompress_zlib(String *to, const uchar *from, uint from_length,
+ uint field_length)
+{
+ z_stream stream;
+ uchar original_pack_length;
+ int wbits;
+ ulonglong avail_out;
+
+ original_pack_length= *from & 0x07;
+ wbits= *from & 8 ? -MAX_WBITS : MAX_WBITS;
+
+ from++;
+ from_length--;
+
+ if (from_length < original_pack_length)
+ {
+ my_error(ER_ZLIB_Z_DATA_ERROR, MYF(0));
+ return 1;
+ }
+
+ avail_out= (ulonglong)read_bigendian(from, original_pack_length);
+
+ if (avail_out > field_length)
+ {
+ my_error(ER_ZLIB_Z_DATA_ERROR, MYF(0));
+ return 1;
+ }
+
+ stream.avail_out= (uint)avail_out;
+ if (to->alloc(stream.avail_out))
+ return 1;
+
+ stream.next_out= (Bytef*) to->ptr();
+
+ stream.avail_in= from_length - original_pack_length;
+ stream.next_in= (Bytef*) from + original_pack_length;
+
+ stream.zalloc= 0;
+ stream.zfree= 0;
+ stream.opaque= 0;
+
+ if (inflateInit2(&stream, wbits) == Z_OK)
+ {
+ int res= inflate(&stream, Z_FINISH);
+ if (inflateEnd(&stream) == Z_OK && res == Z_STREAM_END)
+ {
+ to->length(stream.total_out);
+ return 0;
+ }
+ }
+ my_error(ER_ZLIB_Z_DATA_ERROR, MYF(0));
+ return 1;
+}
+
+
+Compression_method compression_methods[MAX_COMPRESSION_METHODS]=
+{
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { "zlib", compress_zlib, uncompress_zlib },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 }
+};
diff --git a/sql/field_comp.h b/sql/field_comp.h
new file mode 100644
index 00000000000..7eb8ab1e75e
--- /dev/null
+++ b/sql/field_comp.h
@@ -0,0 +1,33 @@
+#ifndef FIELD_COMP_H_INCLUDED
+#define FIELD_COMP_H_INCLUDED
+/* Copyright (C) 2017 MariaDB Foundation
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+
+#define MAX_COMPRESSION_METHODS 16
+
+struct Compression_method
+{
+ const char *name;
+ uint (*compress)(THD *thd, char *to, const char *from, uint length);
+ int (*uncompress)(String *to, const uchar *from, uint from_length,
+ uint field_length);
+};
+
+
+extern Compression_method compression_methods[MAX_COMPRESSION_METHODS];
+#define zlib_compression_method (&compression_methods[8])
+
+#endif
diff --git a/sql/field_conv.cc b/sql/field_conv.cc
index 39136b830ba..88fdb1387a3 100644
--- a/sql/field_conv.cc
+++ b/sql/field_conv.cc
@@ -25,7 +25,7 @@
gives much more speed.
*/
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "sql_class.h" // THD
#include <m_ctype.h>
@@ -122,10 +122,11 @@ static int set_bad_null_error(Field *field, int err)
field->set_warning(Sql_condition::WARN_LEVEL_WARN, err, 1);
/* fall through */
case CHECK_FIELD_IGNORE:
+ case CHECK_FIELD_EXPRESSION:
return 0;
case CHECK_FIELD_ERROR_FOR_NULL:
if (!field->table->in_use->no_errors)
- my_error(ER_BAD_NULL_ERROR, MYF(0), field->field_name);
+ my_error(ER_BAD_NULL_ERROR, MYF(0), field->field_name.str);
return -1;
}
DBUG_ASSERT(0); // impossible
@@ -388,7 +389,7 @@ static void do_field_varbinary_pre50(Copy_field *copy)
copy->from_field->val_str(&copy->tmp);
/* Use the same function as in 4.1 to trim trailing spaces */
- uint length= my_lengthsp_8bit(&my_charset_bin, copy->tmp.c_ptr_quick(),
+ size_t length= my_lengthsp_8bit(&my_charset_bin, copy->tmp.c_ptr_quick(),
copy->from_field->field_length);
copy->to_field->store(copy->tmp.c_ptr_quick(), length,
@@ -480,16 +481,17 @@ static void do_cut_string_complex(Copy_field *copy)
(char*) copy->from_ptr,
(char*) from_end,
copy->to_length / cs->mbmaxlen);
- uint copy_length= prefix.length();
+ size_t copy_length= prefix.length();
if (copy->to_length < copy_length)
copy_length= copy->to_length;
memcpy(copy->to_ptr, copy->from_ptr, copy_length);
/* Check if we lost any important characters */
- if (prefix.well_formed_error_pos() ||
- cs->cset->scan(cs, (char*) copy->from_ptr + copy_length,
- (char*) from_end,
- MY_SEQ_SPACES) < (copy->from_length - copy_length))
+ if (unlikely(prefix.well_formed_error_pos() ||
+ cs->cset->scan(cs, (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,
WARN_DATA_TRUNCATED, 1);
@@ -528,7 +530,8 @@ static void do_varstring1(Copy_field *copy)
if (length > copy->to_length- 1)
{
length=copy->to_length - 1;
- if (copy->from_field->table->in_use->count_cuted_fields &&
+ if (copy->from_field->table->in_use->count_cuted_fields >
+ CHECK_FIELD_EXPRESSION &&
copy->to_field)
copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN,
WARN_DATA_TRUNCATED, 1);
@@ -547,7 +550,7 @@ static void do_varstring1_mb(Copy_field *copy)
Well_formed_prefix prefix(cs, (char*) from_ptr, from_length, to_char_length);
if (prefix.length() < from_length)
{
- if (current_thd->count_cuted_fields)
+ if (current_thd->count_cuted_fields > CHECK_FIELD_EXPRESSION)
copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN,
WARN_DATA_TRUNCATED, 1);
}
@@ -562,7 +565,8 @@ static void do_varstring2(Copy_field *copy)
if (length > copy->to_length- HA_KEY_BLOB_LENGTH)
{
length=copy->to_length-HA_KEY_BLOB_LENGTH;
- if (copy->from_field->table->in_use->count_cuted_fields &&
+ if (copy->from_field->table->in_use->count_cuted_fields >
+ CHECK_FIELD_EXPRESSION &&
copy->to_field)
copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN,
WARN_DATA_TRUNCATED, 1);
@@ -582,7 +586,7 @@ static void do_varstring2_mb(Copy_field *copy)
Well_formed_prefix prefix(cs, (char*) from_beg, from_length, char_length);
if (prefix.length() < from_length)
{
- if (current_thd->count_cuted_fields)
+ if (current_thd->count_cuted_fields > CHECK_FIELD_EXPRESSION)
copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN,
WARN_DATA_TRUNCATED, 1);
}
@@ -754,7 +758,8 @@ Field::Copy_func *Field_varstring::get_copy_func(const Field *from) const
return do_field_varbinary_pre50;
if (Field_varstring::real_type() != from->real_type() ||
Field_varstring::charset() != from->charset() ||
- length_bytes != ((const Field_varstring*) from)->length_bytes)
+ length_bytes != ((const Field_varstring*) from)->length_bytes ||
+ !compression_method() != !from->compression_method())
return do_field_string;
return length_bytes == 1 ?
(from->charset()->mbmaxlen == 1 ? do_varstring1 : do_varstring1_mb) :
diff --git a/sql/filesort.cc b/sql/filesort.cc
index d76c39c3bd4..823e98b3a8a 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -22,12 +22,9 @@
Sorts a database
*/
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "filesort.h"
-#ifdef HAVE_STDDEF_H
-#include <stddef.h> /* for macro offsetof */
-#endif
#include <m_ctype.h>
#include "sql_sort.h"
#include "probes_mysql.h"
@@ -37,14 +34,8 @@
#include "bounded_queue.h"
#include "filesort_utils.h"
#include "sql_select.h"
-#include "log_slow.h"
#include "debug_sync.h"
-/// How to write record_ref.
-#define WRITE_REF(file,from) \
-if (my_b_write((file),(uchar*) (from),param->ref_length)) \
- DBUG_RETURN(1);
-
/* functions defined in this file */
static uchar *read_buffpek_from_file(IO_CACHE *buffer_file, uint count,
@@ -72,7 +63,7 @@ static void unpack_addon_fields(struct st_sort_addon_field *addon_field,
uchar *buff, uchar *buff_end);
static bool check_if_pq_applicable(Sort_param *param, SORT_INFO *info,
TABLE *table,
- ha_rows records, ulong memory_available);
+ ha_rows records, size_t memory_available);
void Sort_param::init_for_filesort(uint sortlen, TABLE *table,
ulong max_length_for_sort_data,
@@ -93,7 +84,10 @@ void Sort_param::init_for_filesort(uint sortlen, TABLE *table,
table->field, sort_length, &addon_buf);
}
if (addon_field)
- res_length= addon_buf.length;
+ {
+ DBUG_ASSERT(addon_buf.length < UINT_MAX32);
+ res_length= (uint)addon_buf.length;
+ }
else
{
res_length= ref_length;
@@ -103,7 +97,7 @@ void Sort_param::init_for_filesort(uint sortlen, TABLE *table,
*/
sort_length+= ref_length;
}
- rec_length= sort_length + addon_buf.length;
+ rec_length= sort_length + (uint)addon_buf.length;
max_rows= maxrows;
}
@@ -379,7 +373,7 @@ SORT_INFO *filesort(THD *thd, TABLE *table, Filesort *filesort,
}
}
tracker->report_merge_passes_at_end(thd->query_plan_fsort_passes);
- if (error)
+ if (unlikely(error))
{
int kill_errno= thd->killed_errno();
DBUG_ASSERT(thd->is_error() || kill_errno || thd->killed == ABORT_QUERY);
@@ -417,7 +411,7 @@ SORT_INFO *filesort(THD *thd, TABLE *table, Filesort *filesort,
(longlong) sort->found_rows));
MYSQL_FILESORT_DONE(error, num_rows);
- if (error)
+ if (unlikely(error))
{
delete sort;
sort= 0;
@@ -573,7 +567,8 @@ const char* dbug_print_table_row(TABLE *table)
else
output.append(",");
- output.append((*pfield)->field_name? (*pfield)->field_name: "NULL");
+ output.append((*pfield)->field_name.str ?
+ (*pfield)->field_name.str: "NULL");
}
output.append(")=(");
@@ -624,7 +619,8 @@ static void dbug_print_record(TABLE *table, bool print_rowid)
fprintf(DBUG_FILE, "record (");
for (pfield= table->field; *pfield ; pfield++)
- fprintf(DBUG_FILE, "%s%s", (*pfield)->field_name, (pfield[1])? ", ":"");
+ fprintf(DBUG_FILE, "%s%s", (*pfield)->field_name.str,
+ (pfield[1])? ", ":"");
fprintf(DBUG_FILE, ") = ");
fprintf(DBUG_FILE, "(");
@@ -713,10 +709,9 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select,
Bounded_queue<uchar, uchar> *pq,
ha_rows *found_rows)
{
- int error,flag,quick_select;
- uint idx,indexpos,ref_length;
- uchar *ref_pos,*next_pos,ref_buff[MAX_REFLENGTH];
- my_off_t record;
+ int error, quick_select;
+ uint idx, indexpos;
+ uchar *ref_pos, *next_pos, ref_buff[MAX_REFLENGTH];
TABLE *sort_form;
handler *file;
MY_BITMAP *save_read_set, *save_write_set, *save_vcol_set;
@@ -731,14 +726,10 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select,
error=quick_select=0;
sort_form=param->sort_form;
file=sort_form->file;
- ref_length=param->ref_length;
ref_pos= ref_buff;
quick_select=select && select->quick;
- record=0;
*found_rows= 0;
- flag= ((file->ha_table_flags() & HA_REC_NOT_IN_SEQ) || quick_select);
- if (flag)
- ref_pos= &file->ref[0];
+ ref_pos= &file->ref[0];
next_pos=ref_pos;
DBUG_EXECUTE_IF("show_explain_in_find_all_keys",
@@ -750,7 +741,7 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select,
next_pos=(uchar*) 0; /* Find records in sequence */
DBUG_EXECUTE_IF("bug14365043_1",
DBUG_SET("+d,ha_rnd_init_fail"););
- if (file->ha_rnd_init_with_error(1))
+ if (unlikely(file->ha_rnd_init_with_error(1)))
DBUG_RETURN(HA_POS_ERROR);
file->extra_opt(HA_EXTRA_CACHE, thd->variables.read_buff_size);
}
@@ -786,29 +777,15 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select,
for (;;)
{
if (quick_select)
- {
- if ((error= select->quick->get_next()))
- break;
- file->position(sort_form->record[0]);
- DBUG_EXECUTE_IF("debug_filesort", dbug_print_record(sort_form, TRUE););
- }
+ error= select->quick->get_next();
else /* Not quick-select */
- {
- {
- error= file->ha_rnd_next(sort_form->record[0]);
- if (!flag)
- {
- my_store_ptr(ref_pos,ref_length,record); // Position to row
- record+= sort_form->s->db_record_offset;
- }
- else if (!error)
- file->position(sort_form->record[0]);
- }
- if (error && error != HA_ERR_RECORD_DELETED)
- break;
- }
+ error= file->ha_rnd_next(sort_form->record[0]);
+ if (unlikely(error))
+ break;
+ file->position(sort_form->record[0]);
+ DBUG_EXECUTE_IF("debug_filesort", dbug_print_record(sort_form, TRUE););
- if (thd->check_killed())
+ if (unlikely(thd->check_killed()))
{
DBUG_PRINT("info",("Sort killed by user"));
if (!quick_select)
@@ -820,7 +797,7 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select,
}
bool write_record= false;
- if (error == 0)
+ if (likely(error == 0))
{
param->examined_rows++;
if (select && select->cond)
@@ -838,11 +815,11 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select,
MY_BITMAP *tmp_write_set= sort_form->write_set;
MY_BITMAP *tmp_vcol_set= sort_form->vcol_set;
- if (select->cond->with_subselect)
+ if (select->cond->with_subquery())
sort_form->column_bitmaps_set(save_read_set, save_write_set,
save_vcol_set);
write_record= (select->skip_record(thd) > 0);
- if (select->cond->with_subselect)
+ if (select->cond->with_subquery())
sort_form->column_bitmaps_set(tmp_read_set,
tmp_write_set,
tmp_vcol_set);
@@ -873,7 +850,7 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select,
}
/* It does not make sense to read more keys in case of a fatal error */
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
break;
/*
@@ -893,11 +870,11 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select,
/* Signal we should use original column read and write maps */
sort_form->column_bitmaps_set(save_read_set, save_write_set, save_vcol_set);
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
DBUG_RETURN(HA_POS_ERROR);
DBUG_PRINT("test",("error: %d indexpos: %d",error,indexpos));
- if (error != HA_ERR_END_OF_FILE)
+ if (unlikely(error != HA_ERR_END_OF_FILE))
{
file->print_error(error,MYF(ME_ERROR | ME_WAITTANG)); // purecov: inspected
DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */
@@ -1037,8 +1014,10 @@ Type_handler_string_result::make_sort_key(uchar *to, Item *item,
if (use_strnxfrm(cs))
{
- uint tmp_length __attribute__((unused));
- tmp_length= cs->coll->strnxfrm(cs, to, sort_field->length,
+#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(),
@@ -1193,7 +1172,8 @@ static void make_sortkey(Sort_param *param, uchar *to, uchar *ref_pos)
}
else
{ // Item
- sort_field->item->make_sort_key(to, sort_field->item, sort_field, param);
+ sort_field->item->type_handler()->make_sort_key(to, sort_field->item,
+ sort_field, param);
if ((maybe_null= sort_field->item->maybe_null))
to++;
}
@@ -1355,10 +1335,10 @@ static bool save_index(Sort_param *param, uint count,
false - PQ will be slower than merge-sort, or there is not enough memory.
*/
-bool check_if_pq_applicable(Sort_param *param,
+static bool check_if_pq_applicable(Sort_param *param,
SORT_INFO *filesort_info,
TABLE *table, ha_rows num_rows,
- ulong memory_available)
+ size_t memory_available)
{
DBUG_ENTER("check_if_pq_applicable");
@@ -1380,7 +1360,7 @@ bool check_if_pq_applicable(Sort_param *param,
DBUG_RETURN(false);
}
- ulong num_available_keys=
+ size_t num_available_keys=
memory_available / (param->rec_length + sizeof(char*));
// We need 1 extra record in the buffer, when using PQ.
param->max_keys_per_buffer= (uint) param->max_rows + 1;
@@ -1410,7 +1390,7 @@ bool check_if_pq_applicable(Sort_param *param,
// Try to strip off addon fields.
if (param->addon_field)
{
- const ulong row_length=
+ const size_t row_length=
param->sort_length + param->ref_length + sizeof(char*);
num_available_keys= memory_available / row_length;
@@ -1420,7 +1400,7 @@ bool check_if_pq_applicable(Sort_param *param,
const double sort_merge_cost=
get_merge_many_buffs_cost_fast(num_rows,
num_available_keys,
- row_length);
+ (uint)row_length);
/*
PQ has cost:
(insert + qsort) * log(queue size) / TIME_FOR_COMPARE_ROWID +
@@ -1514,27 +1494,28 @@ cleanup:
/**
Read data to buffer.
- @retval
- (uint)-1 if something goes wrong
+ @retval Number of bytes read
+ (ulong)-1 if something goes wrong
*/
-uint read_to_buffer(IO_CACHE *fromfile, BUFFPEK *buffpek,
- uint rec_length)
+ulong read_to_buffer(IO_CACHE *fromfile, BUFFPEK *buffpek,
+ uint rec_length)
{
- uint count;
- uint length;
+ ulong count;
+ ulong length= 0;
- if ((count=(uint) MY_MIN((ha_rows) buffpek->max_keys,buffpek->count)))
+ if ((count= (ulong) MY_MIN((ha_rows) buffpek->max_keys,buffpek->count)))
{
- if (my_b_pread(fromfile, (uchar*) buffpek->base,
- (length= rec_length*count), buffpek->file_pos))
- return ((uint) -1);
+ length= rec_length*count;
+ if (unlikely(my_b_pread(fromfile, (uchar*) buffpek->base, length,
+ buffpek->file_pos)))
+ return ((ulong) -1);
buffpek->key=buffpek->base;
buffpek->file_pos+= length; /* New filepos */
buffpek->count-= count;
buffpek->mem_count= count;
}
- return (count*rec_length);
+ return (length);
} /* read_to_buffer */
@@ -1588,18 +1569,18 @@ void reuse_freed_buff(QUEUE *queue, BUFFPEK *reuse, uint key_length)
@retval
0 OK
@retval
- other error
+ 1 ERROR
*/
-int merge_buffers(Sort_param *param, IO_CACHE *from_file,
- IO_CACHE *to_file, uchar *sort_buffer,
- BUFFPEK *lastbuff, BUFFPEK *Fb, BUFFPEK *Tb,
- int flag)
+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 error;
+ bool error= 0;
uint rec_length,res_length,offset;
size_t sort_length;
- ulong maxcount;
+ ulong maxcount, bytes_read;
ha_rows max_rows,org_max_rows;
my_off_t to_start_filepos;
uchar *strpos;
@@ -1617,7 +1598,6 @@ int merge_buffers(Sort_param *param, IO_CACHE *from_file,
thd->inc_status_sort_merge_passes();
thd->query_plan_fsort_passes++;
- error=0;
rec_length= param->rec_length;
res_length= param->res_length;
sort_length= param->sort_length;
@@ -1645,18 +1625,18 @@ int merge_buffers(Sort_param *param, IO_CACHE *from_file,
cmp= get_ptr_compare(sort_length);
first_cmp_arg= (void*) &sort_length;
}
- if (init_queue(&queue, (uint) (Tb-Fb)+1, offsetof(BUFFPEK,key), 0,
- (queue_compare) cmp, first_cmp_arg, 0, 0))
+ if (unlikely(init_queue(&queue, (uint) (Tb-Fb)+1, offsetof(BUFFPEK,key), 0,
+ (queue_compare) cmp, first_cmp_arg, 0, 0)))
DBUG_RETURN(1); /* purecov: inspected */
for (buffpek= Fb ; buffpek <= Tb ; buffpek++)
{
buffpek->base= strpos;
buffpek->max_keys= maxcount;
- strpos+=
- (uint) (error= (int) read_to_buffer(from_file, buffpek, rec_length));
-
- if (error == -1)
+ bytes_read= read_to_buffer(from_file, buffpek, rec_length);
+ 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
queue_insert(&queue, (uchar*) buffpek);
}
@@ -1676,13 +1656,13 @@ int merge_buffers(Sort_param *param, IO_CACHE *from_file,
buffpek->key+= rec_length;
if (! --buffpek->mem_count)
{
- if (!(error= (int) read_to_buffer(from_file, buffpek,
- rec_length)))
+ if (unlikely(!(bytes_read= read_to_buffer(from_file, buffpek,
+ rec_length))))
{
(void) queue_remove_top(&queue);
reuse_freed_buff(&queue, buffpek, rec_length);
}
- else if (error == -1)
+ else if (unlikely(bytes_read == (ulong) -1))
goto err; /* purecov: inspected */
}
queue_replace_top(&queue); // Top element has been used
@@ -1692,10 +1672,9 @@ int merge_buffers(Sort_param *param, IO_CACHE *from_file,
while (queue.elements > 1)
{
- if (killable && thd->check_killed())
- {
- error= 1; goto err; /* purecov: inspected */
- }
+ if (killable && unlikely(thd->check_killed()))
+ goto err; /* purecov: inspected */
+
for (;;)
{
buffpek= (BUFFPEK*) queue_top(&queue);
@@ -1732,9 +1711,7 @@ int merge_buffers(Sort_param *param, IO_CACHE *from_file,
if (!check_dupl_count || dupl_count >= min_dupl_count)
{
if (my_b_write(to_file, src+wr_offset, wr_len))
- {
- error=1; goto err; /* purecov: inspected */
- }
+ goto err; /* purecov: inspected */
}
if (cmp)
{
@@ -1745,7 +1722,7 @@ int merge_buffers(Sort_param *param, IO_CACHE *from_file,
}
if (!--max_rows)
{
- error= 0; /* purecov: inspected */
+ /* Nothing more to do */
goto end; /* purecov: inspected */
}
@@ -1753,14 +1730,14 @@ int merge_buffers(Sort_param *param, IO_CACHE *from_file,
buffpek->key+= rec_length;
if (! --buffpek->mem_count)
{
- if (!(error= (int) read_to_buffer(from_file, buffpek,
- rec_length)))
+ if (unlikely(!(bytes_read= read_to_buffer(from_file, buffpek,
+ rec_length))))
{
(void) queue_remove_top(&queue);
reuse_freed_buff(&queue, buffpek, rec_length);
break; /* One buffer have been removed */
}
- else if (error == -1)
+ else if (unlikely(bytes_read == (ulong) -1))
goto err; /* purecov: inspected */
}
queue_replace_top(&queue); /* Top element has been replaced */
@@ -1796,14 +1773,9 @@ int merge_buffers(Sort_param *param, IO_CACHE *from_file,
{
src= unique_buff;
if (my_b_write(to_file, src+wr_offset, wr_len))
- {
- error=1; goto err; /* purecov: inspected */
- }
+ goto err; /* purecov: inspected */
if (!--max_rows)
- {
- error= 0;
goto end;
- }
}
}
@@ -1819,9 +1791,7 @@ int merge_buffers(Sort_param *param, IO_CACHE *from_file,
{
if (my_b_write(to_file, (uchar*) buffpek->key,
(size_t)(rec_length*buffpek->mem_count)))
- {
- error= 1; goto err; /* purecov: inspected */
- }
+ goto err; /* purecov: inspected */
}
else
{
@@ -1838,21 +1808,26 @@ int merge_buffers(Sort_param *param, IO_CACHE *from_file,
continue;
}
if (my_b_write(to_file, src, wr_len))
- {
- error=1; goto err;
- }
+ goto err;
}
}
}
- while ((error=(int) read_to_buffer(from_file, buffpek, rec_length))
- != -1 && error != 0);
+ while (likely(!(error=
+ (bytes_read= read_to_buffer(from_file, buffpek,
+ rec_length)) == (ulong) -1)) &&
+ bytes_read != 0);
end:
lastbuff->count= MY_MIN(org_max_rows-max_rows, param->max_rows);
lastbuff->file_pos= to_start_filepos;
-err:
+cleanup:
delete_queue(&queue);
DBUG_RETURN(error);
+
+err:
+ error= 1;
+ goto cleanup;
+
} /* merge_buffers */
@@ -1892,7 +1867,7 @@ Type_handler_string_result::sortlength(THD *thd,
set_if_smaller(sortorder->length, thd->variables.max_sort_length);
if (use_strnxfrm((cs= item->collation.collation)))
{
- sortorder->length= cs->coll->strnxfrmlen(cs, sortorder->length);
+ sortorder->length= (uint)cs->coll->strnxfrmlen(cs, sortorder->length);
}
else if (cs == &my_charset_bin)
{
@@ -1975,14 +1950,15 @@ sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length,
if (use_strnxfrm((cs=sortorder->field->sort_charset())))
{
*multi_byte_charset= true;
- sortorder->length= cs->coll->strnxfrmlen(cs, sortorder->length);
+ sortorder->length= (uint)cs->coll->strnxfrmlen(cs, sortorder->length);
}
if (sortorder->field->maybe_null())
length++; // Place for NULL marker
}
else
{
- sortorder->item->sortlength(thd, sortorder->item, sortorder);
+ sortorder->item->type_handler()->sortlength(thd, sortorder->item,
+ sortorder);
if (use_strnxfrm(sortorder->item->collation.collation))
{
*multi_byte_charset= true;
diff --git a/sql/filesort.h b/sql/filesort.h
index 748e6b2b1ce..6926d92c4eb 100644
--- a/sql/filesort.h
+++ b/sql/filesort.h
@@ -17,7 +17,7 @@
#define FILESORT_INCLUDED
#include "my_base.h" /* ha_rows */
-#include "sql_list.h" /* Sql_alloc */
+#include "sql_alloc.h"
#include "filesort_utils.h"
class SQL_SELECT;
diff --git a/sql/filesort_utils.cc b/sql/filesort_utils.cc
index c36e355b091..849e6ee761a 100644
--- a/sql/filesort_utils.cc
+++ b/sql/filesort_utils.cc
@@ -14,11 +14,11 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
+#include "mariadb.h"
#include "filesort_utils.h"
#include "sql_const.h"
#include "sql_sort.h"
#include "table.h"
-#include "my_sys.h"
namespace {
diff --git a/sql/filesort_utils.h b/sql/filesort_utils.h
index c463e1858ca..1ab1ba2daa8 100644
--- a/sql/filesort_utils.h
+++ b/sql/filesort_utils.h
@@ -16,7 +16,6 @@
#ifndef FILESORT_UTILS_INCLUDED
#define FILESORT_UTILS_INCLUDED
-#include "my_global.h"
#include "my_base.h"
#include "sql_array.h"
diff --git a/sql/gcalc_slicescan.cc b/sql/gcalc_slicescan.cc
index f63a3e717b4..b3752420316 100644
--- a/sql/gcalc_slicescan.cc
+++ b/sql/gcalc_slicescan.cc
@@ -15,7 +15,7 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include <my_sys.h>
#include <m_string.h>
@@ -137,13 +137,13 @@ static void GCALC_DBUG_PRINT_PI(const Gcalc_heap::Info *pi)
static void GCALC_DBUG_PRINT_SLICE(const char *header,
const Gcalc_scan_iterator::point *slice)
{
- int nbuf;
+ size_t nbuf;
char buf[1024];
nbuf= strlen(header);
strcpy(buf, header);
for (; slice; slice= slice->get_next())
{
- int lnbuf= nbuf;
+ size_t lnbuf= nbuf;
lnbuf+= sprintf(buf + lnbuf, "%d\t", slice->thread);
lnbuf+= sprintf(buf + lnbuf, "%s\t", gcalc_ev_name(slice->event));
@@ -170,7 +170,7 @@ static void GCALC_DBUG_PRINT_SLICE(const char *header,
Gcalc_dyn_list::Gcalc_dyn_list(size_t blk_size, size_t sizeof_item):
m_blk_size(blk_size - ALLOC_ROOT_MIN_BLOCK_SIZE),
m_sizeof_item(ALIGN_SIZE(sizeof_item)),
- m_points_per_blk((m_blk_size - PH_DATA_OFFSET) / m_sizeof_item),
+ m_points_per_blk((uint)((m_blk_size - PH_DATA_OFFSET) / m_sizeof_item)),
m_blk_hook(&m_first_blk),
m_free(NULL),
m_keep(NULL)
diff --git a/sql/gcalc_tools.cc b/sql/gcalc_tools.cc
index 134032b0ffe..307f063fb43 100644
--- a/sql/gcalc_tools.cc
+++ b/sql/gcalc_tools.cc
@@ -15,7 +15,7 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#ifdef HAVE_SPATIAL
diff --git a/sql/gen_lex_hash.cc b/sql/gen_lex_hash.cc
index e0e0cadb742..98e6205ee0b 100644
--- a/sql/gen_lex_hash.cc
+++ b/sql/gen_lex_hash.cc
@@ -78,11 +78,10 @@ So, we can read full search-structure as 32-bit word
*/
#define NO_YACC_SYMBOLS
-#include <my_global.h>
+#undef CHECK_UNLIKELY
+#include "mariadb.h"
#include "mysql_version.h"
#include "lex.h"
-#include <stdlib.h>
-#include <stdio.h>
#include <string.h>
#include <welcome_copyright_notice.h> /* ORACLE_WELCOME_COPYRIGHT_NOTICE */
@@ -142,7 +141,8 @@ void insert_into_hash(hash_lex_struct *root, const char *name,
if (root->first_char>(*name))
{
size_t new_size= root->last_char-(*name)+1;
- if (new_size<real_size) printf("error!!!!\n");
+ if (unlikely(new_size<real_size))
+ printf("error!!!!\n");
tails= root->char_tails;
tails= (hash_lex_struct*)realloc((char*)tails,
sizeof(hash_lex_struct)*new_size);
@@ -157,7 +157,8 @@ void insert_into_hash(hash_lex_struct *root, const char *name,
if (root->last_char<(*name))
{
size_t new_size= (*name)-root->first_char+1;
- if (new_size<real_size) printf("error!!!!\n");
+ if (unlikely(new_size<real_size))
+ printf("error!!!!\n");
tails= root->char_tails;
tails= (hash_lex_struct*)realloc((char*)tails,
sizeof(hash_lex_struct)*new_size);
diff --git a/sql/gen_lex_token.cc b/sql/gen_lex_token.cc
index 631c8ecfe52..0ca03b0bf7b 100644
--- a/sql/gen_lex_token.cc
+++ b/sql/gen_lex_token.cc
@@ -14,14 +14,12 @@
along with this program; if not, write to the Free Software Foundation,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
-#include <stdlib.h>
-#include <stdio.h>
+#include "mariadb.h"
#include <string.h>
/* We only need the tokens here */
#define YYSTYPE_IS_DECLARED
-#include <sql_yacc.h>
+#include <sql_yacc.hh>
#include <lex.h>
#include <welcome_copyright_notice.h> /* ORACLE_WELCOME_COPYRIGHT_NOTICE */
@@ -35,7 +33,7 @@
from bison.
See also YYMAXUTOK.
*/
-#define MY_MAX_TOKEN 1000
+#define MY_MAX_TOKEN 1100
/** Generated token. */
struct gen_lex_token_string
{
@@ -132,6 +130,10 @@ void compute_tokens()
set_token(WITH_CUBE_SYM, "WITH CUBE");
set_token(WITH_ROLLUP_SYM, "WITH ROLLUP");
+ set_token(WITH_SYSTEM_SYM, "WITH SYSTEM");
+ set_token(FOR_SYSTEM_TIME_SYM, "FOR SYSTEM_TIME");
+ set_token(VALUES_IN_SYM, "VALUES IN");
+ set_token(VALUES_LESS_SYM, "VALUES LESS");
set_token(NOT2_SYM, "!");
set_token(OR2_SYM, "|");
set_token(PARAM_MARKER, "?");
@@ -253,9 +255,11 @@ void compute_tokens()
set_start_expr_token(STARTS_SYM);
set_start_expr_token(ENDS_SYM);
set_start_expr_token(DEFAULT);
- set_start_expr_token(RETURN_SYM);
+ set_start_expr_token(RETURN_MARIADB_SYM);
+ set_start_expr_token(RETURN_ORACLE_SYM);
set_start_expr_token(IF_SYM);
- set_start_expr_token(ELSEIF_SYM);
+ set_start_expr_token(ELSEIF_MARIADB_SYM);
+ set_start_expr_token(ELSEIF_ORACLE_SYM);
set_start_expr_token(CASE_SYM);
set_start_expr_token(WHEN_SYM);
set_start_expr_token(WHILE_SYM);
diff --git a/sql/group_by_handler.cc b/sql/group_by_handler.cc
index 197ada0d4c3..326aad439ef 100644
--- a/sql/group_by_handler.cc
+++ b/sql/group_by_handler.cc
@@ -21,6 +21,7 @@
upper level.
*/
+#include "mariadb.h"
#include "sql_priv.h"
#include "sql_select.h"
@@ -62,9 +63,8 @@ int Pushdown_query::execute(JOIN *join)
while (!(err= handler->next_row()))
{
- if (thd->check_killed())
+ if (unlikely(thd->check_killed()))
{
- thd->send_kill_message();
handler->end_scan();
DBUG_RETURN(-1);
}
@@ -77,7 +77,7 @@ int Pushdown_query::execute(JOIN *join)
if ((err= table->file->ha_write_tmp_row(table->record[0])))
{
bool is_duplicate;
- if (!table->file->is_fatal_error(err, HA_CHECK_DUP))
+ if (likely(!table->file->is_fatal_error(err, HA_CHECK_DUP)))
continue; // Distinct elimination
if (create_internal_tmp_table_from_heap(thd, table,
@@ -97,7 +97,7 @@ int Pushdown_query::execute(JOIN *join)
{
int error;
/* result < 0 if row was not accepted and should not be counted */
- if ((error= join->result->send_data(*join->fields)))
+ if (unlikely((error= join->result->send_data(*join->fields))))
{
handler->end_scan();
DBUG_RETURN(error < 0 ? 0 : -1);
diff --git a/sql/gstream.cc b/sql/gstream.cc
index 8a3e35ac0c2..4678e85019e 100644
--- a/sql/gstream.cc
+++ b/sql/gstream.cc
@@ -18,7 +18,7 @@
NOTE: These functions assumes that the string is end \0 terminated!
*/
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "gstream.h"
#include "m_string.h" // LEX_STRING
diff --git a/sql/gstream.h b/sql/gstream.h
index 687627aa5f2..c5c715393ac 100644
--- a/sql/gstream.h
+++ b/sql/gstream.h
@@ -17,8 +17,7 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include "my_global.h" /* NULL, NullS */
-#include "my_sys.h" /* MY_ALLOW_ZERO_PTR */
+#include <my_sys.h> /* MY_ALLOW_ZERO_PTR */
#include "m_ctype.h" /* my_charset_latin1, my_charset_bin */
class Gis_read_stream
diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc
index daac3f6a86d..d694fedb831 100644
--- a/sql/ha_partition.cc
+++ b/sql/ha_partition.cc
@@ -35,7 +35,7 @@
Partitioning lays the foundation for more manageable databases that are
extremely large. It does also lay the foundation for more parallelism
in the execution of queries. This functionality will grow with later
- versions of MySQL.
+ versions of MySQL/MariaDB.
The partition is setup to use table locks. It implements an partition "SHARE"
that is inserted into a hash by table name. You can use this to store
@@ -46,7 +46,7 @@
if this file.
*/
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "sql_parse.h" // append_file_to_dir
#include "create_options.h"
@@ -58,6 +58,7 @@
#include "sql_plugin.h"
#include "sql_show.h" // append_identifier
#include "sql_admin.h" // SQL_ADMIN_MSG_TEXT_SIZE
+#include "sql_select.h"
#include "debug_sync.h"
@@ -73,11 +74,11 @@
HA_REC_NOT_IN_SEQ | \
HA_CAN_REPAIR)
#define PARTITION_DISABLED_TABLE_FLAGS (HA_CAN_GEOMETRY | \
- HA_CAN_FULLTEXT | \
HA_DUPLICATE_POS | \
- HA_CAN_SQL_HANDLER | \
HA_CAN_INSERT_DELAYED | \
- HA_READ_BEFORE_WRITE_REMOVAL)
+ HA_READ_BEFORE_WRITE_REMOVAL |\
+ HA_CAN_TABLES_WITHOUT_ROLLBACK)
+
static const char *ha_par_ext= ".par";
/****************************************************************************
@@ -88,10 +89,7 @@ static handler *partition_create_handler(handlerton *hton,
TABLE_SHARE *share,
MEM_ROOT *mem_root);
static uint partition_flags();
-static uint alter_table_flags(uint flags);
-
-extern "C" int cmp_key_part_id(void *key_p, uchar *ref1, uchar *ref2);
-extern "C" int cmp_key_rowid_part_id(void *ptr, uchar *ref1, uchar *ref2);
+static alter_table_operations alter_table_flags(alter_table_operations flags);
/*
If frm_error() is called then we will use this to to find out what file
@@ -124,7 +122,6 @@ static void init_partition_psi_keys(void)
static int partition_initialize(void *p)
{
-
handlerton *partition_hton;
partition_hton= (handlerton *)p;
@@ -158,18 +155,11 @@ static int partition_initialize(void *p)
bool Partition_share::init(uint num_parts)
{
DBUG_ENTER("Partition_share::init");
- mysql_mutex_init(key_partition_auto_inc_mutex,
- &auto_inc_mutex,
- MY_MUTEX_INIT_FAST);
auto_inc_initialized= false;
partition_name_hash_initialized= false;
next_auto_inc_val= 0;
- partitions_share_refs= new Parts_share_refs;
- if (!partitions_share_refs)
- DBUG_RETURN(true);
- if (partitions_share_refs->init(num_parts))
+ if (partitions_share_refs.init(num_parts))
{
- delete partitions_share_refs;
DBUG_RETURN(true);
}
DBUG_RETURN(false);
@@ -187,7 +177,7 @@ bool Partition_share::init(uint num_parts)
New partition object
*/
-static handler *partition_create_handler(handlerton *hton,
+static handler *partition_create_handler(handlerton *hton,
TABLE_SHARE *share,
MEM_ROOT *mem_root)
{
@@ -224,14 +214,12 @@ static uint partition_flags()
return HA_CAN_PARTITION;
}
-static uint alter_table_flags(uint flags __attribute__((unused)))
+static alter_table_operations alter_table_flags(alter_table_operations flags __attribute__((unused)))
{
return (HA_PARTITION_FUNCTION_SUPPORTED |
HA_FAST_CHANGE_PARTITION);
}
-const uint32 ha_partition::NO_CURRENT_PART_ID= NOT_A_PARTITION_ID;
-
/*
Constructor method
@@ -247,12 +235,19 @@ ha_partition::ha_partition(handlerton *hton, TABLE_SHARE *share)
:handler(hton, share)
{
DBUG_ENTER("ha_partition::ha_partition(table)");
- init_alloc_root(&m_mem_root, 512, 512, MYF(0));
- init_handler_variables();
+ ha_partition_init();
DBUG_VOID_RETURN;
}
+/* Initialize all partition variables */
+
+void ha_partition::ha_partition_init()
+{
+ init_alloc_root(&m_mem_root, "ha_partition", 512, 512, MYF(0));
+ init_handler_variables();
+}
+
/*
Constructor method
@@ -269,8 +264,7 @@ ha_partition::ha_partition(handlerton *hton, partition_info *part_info)
{
DBUG_ENTER("ha_partition::ha_partition(part_info)");
DBUG_ASSERT(part_info);
- init_alloc_root(&m_mem_root, 512, 512, MYF(0));
- init_handler_variables();
+ ha_partition_init();
m_part_info= part_info;
m_create_handler= TRUE;
m_is_sub_partitioned= m_part_info->is_sub_partitioned();
@@ -296,8 +290,7 @@ ha_partition::ha_partition(handlerton *hton, TABLE_SHARE *share,
:handler(hton, share)
{
DBUG_ENTER("ha_partition::ha_partition(clone)");
- init_alloc_root(&m_mem_root, 512, 512, MYF(0));
- init_handler_variables();
+ ha_partition_init();
m_part_info= part_info_arg;
m_create_handler= TRUE;
m_is_sub_partitioned= m_part_info->is_sub_partitioned();
@@ -360,6 +353,7 @@ void ha_partition::init_handler_variables()
m_curr_key_info[0]= NULL;
m_curr_key_info[1]= NULL;
m_part_func_monotonicity_info= NON_MONOTONIC;
+ m_key_not_found= FALSE;
auto_increment_lock= FALSE;
auto_increment_safe_stmt_log_lock= FALSE;
/*
@@ -374,6 +368,30 @@ void ha_partition::init_handler_variables()
part_share= NULL;
m_new_partitions_share_refs.empty();
m_part_ids_sorted_by_num_of_records= NULL;
+ m_partitions_to_open= NULL;
+
+ m_range_info= NULL;
+ m_mrr_full_buffer_size= 0;
+ m_mrr_new_full_buffer_size= 0;
+ m_mrr_full_buffer= NULL;
+ m_mrr_range_first= NULL;
+
+ m_pre_calling= FALSE;
+ m_pre_call_use_parallel= FALSE;
+
+ ft_first= ft_current= NULL;
+ bulk_access_executing= FALSE; // For future
+
+ /*
+ Clear bitmaps to allow on one to call my_bitmap_free() on them at any time
+ */
+ my_bitmap_clear(&m_bulk_insert_started);
+ my_bitmap_clear(&m_locked_partitions);
+ my_bitmap_clear(&m_partitions_to_reset);
+ my_bitmap_clear(&m_key_not_found_partitions);
+ my_bitmap_clear(&m_mrr_used_partitions);
+ my_bitmap_clear(&m_opened_partitions);
+ m_file_sample= NULL;
#ifdef DONT_HAVE_TO_BE_INITALIZED
m_start_key.flag= 0;
@@ -383,9 +401,9 @@ void ha_partition::init_handler_variables()
const char *ha_partition::table_type() const
-{
+{
// we can do this since we only support a single engine type
- return m_file[0]->table_type();
+ return m_file[0]->table_type();
}
@@ -686,8 +704,9 @@ int ha_partition::create(const char *name, TABLE *table_arg,
partition_element *part_elem;
handler **file, **abort_file;
DBUG_ENTER("ha_partition::create");
+ DBUG_PRINT("enter", ("name: '%s'", name));
- DBUG_ASSERT(*fn_rext((char*)name) == '\0');
+ DBUG_ASSERT(!fn_frm_ext(name));
/* Not allowed to create temporary partitioned tables */
if (create_info && create_info->tmp_table())
@@ -699,7 +718,6 @@ int ha_partition::create(const char *name, TABLE *table_arg,
if (get_from_handler_file(name, ha_thd()->mem_root, false))
DBUG_RETURN(TRUE);
DBUG_ASSERT(m_file_buffer);
- DBUG_PRINT("enter", ("name: (%s)", name));
name_buffer_ptr= m_name_buffer_ptr;
file= m_file;
/*
@@ -720,12 +738,16 @@ int ha_partition::create(const char *name, TABLE *table_arg,
for (j= 0; j < m_part_info->num_subparts; j++)
{
part_elem= sub_it++;
- if ((error= create_partition_name(name_buff, sizeof(name_buff), path,
- name_buffer_ptr, NORMAL_PART_NAME, FALSE)))
+ if (unlikely((error= create_partition_name(name_buff,
+ sizeof(name_buff), path,
+ name_buffer_ptr,
+ NORMAL_PART_NAME, FALSE))))
goto create_error;
- if ((error= set_up_table_before_create(table_arg, name_buff,
- create_info, part_elem)) ||
- ((error= (*file)->ha_create(name_buff, table_arg, create_info))))
+ if (unlikely((error= set_up_table_before_create(table_arg, name_buff,
+ create_info,
+ part_elem)) ||
+ ((error= (*file)->ha_create(name_buff, table_arg,
+ create_info)))))
goto create_error;
name_buffer_ptr= strend(name_buffer_ptr) + 1;
@@ -734,12 +756,15 @@ int ha_partition::create(const char *name, TABLE *table_arg,
}
else
{
- if ((error= create_partition_name(name_buff, sizeof(name_buff), path,
- name_buffer_ptr, NORMAL_PART_NAME, FALSE)))
+ if (unlikely((error= create_partition_name(name_buff, sizeof(name_buff),
+ path, name_buffer_ptr,
+ NORMAL_PART_NAME, FALSE))))
goto create_error;
- if ((error= set_up_table_before_create(table_arg, name_buff,
- create_info, part_elem)) ||
- ((error= (*file)->ha_create(name_buff, table_arg, create_info))))
+ if (unlikely((error= set_up_table_before_create(table_arg, name_buff,
+ create_info,
+ part_elem)) ||
+ ((error= (*file)->ha_create(name_buff, table_arg,
+ create_info)))))
goto create_error;
name_buffer_ptr= strend(name_buffer_ptr) + 1;
@@ -814,16 +839,19 @@ int ha_partition::drop_partitions(const char *path)
{
partition_element *sub_elem= sub_it++;
part= i * num_subparts + j;
- if ((ret_error= create_subpartition_name(part_name_buff,
- sizeof(part_name_buff), path,
- part_elem->partition_name,
- sub_elem->partition_name, name_variant)))
+ if (unlikely((ret_error=
+ create_subpartition_name(part_name_buff,
+ sizeof(part_name_buff), path,
+ part_elem->partition_name,
+ sub_elem->partition_name,
+ name_variant))))
error= ret_error;
file= m_file[part];
DBUG_PRINT("info", ("Drop subpartition %s", part_name_buff));
- if ((ret_error= file->ha_delete_table(part_name_buff)))
+ if (unlikely((ret_error= file->ha_delete_table(part_name_buff))))
error= ret_error;
- if (deactivate_ddl_log_entry(sub_elem->log_entry->entry_pos))
+ if (unlikely(deactivate_ddl_log_entry(sub_elem->log_entry->
+ entry_pos)))
error= 1;
} while (++j < num_subparts);
}
@@ -837,9 +865,10 @@ int ha_partition::drop_partitions(const char *path)
{
file= m_file[i];
DBUG_PRINT("info", ("Drop partition %s", part_name_buff));
- if ((ret_error= file->ha_delete_table(part_name_buff)))
+ if (unlikely((ret_error= file->ha_delete_table(part_name_buff))))
error= ret_error;
- if (deactivate_ddl_log_entry(part_elem->log_entry->entry_pos))
+ if (unlikely(deactivate_ddl_log_entry(part_elem->log_entry->
+ entry_pos)))
error= 1;
}
}
@@ -920,15 +949,18 @@ int ha_partition::rename_partitions(const char *path)
{
sub_elem= sub_it++;
file= m_reorged_file[part_count++];
- if ((ret_error= create_subpartition_name(norm_name_buff,
- sizeof(norm_name_buff), path,
- part_elem->partition_name,
- sub_elem->partition_name, NORMAL_PART_NAME)))
+ if (unlikely((ret_error=
+ create_subpartition_name(norm_name_buff,
+ sizeof(norm_name_buff), path,
+ part_elem->partition_name,
+ sub_elem->partition_name,
+ NORMAL_PART_NAME))))
error= ret_error;
DBUG_PRINT("info", ("Delete subpartition %s", norm_name_buff));
- if ((ret_error= file->ha_delete_table(norm_name_buff)))
+ if (unlikely((ret_error= file->ha_delete_table(norm_name_buff))))
error= ret_error;
- else if (deactivate_ddl_log_entry(sub_elem->log_entry->entry_pos))
+ else if (unlikely(deactivate_ddl_log_entry(sub_elem->log_entry->
+ entry_pos)))
error= 1;
else
sub_elem->log_entry= NULL; /* Indicate success */
@@ -937,16 +969,19 @@ int ha_partition::rename_partitions(const char *path)
else
{
file= m_reorged_file[part_count++];
- if ((ret_error= create_partition_name(norm_name_buff,
- sizeof(norm_name_buff), path,
- part_elem->partition_name, NORMAL_PART_NAME, TRUE)))
+ if (unlikely((ret_error=
+ create_partition_name(norm_name_buff,
+ sizeof(norm_name_buff), path,
+ part_elem->partition_name,
+ NORMAL_PART_NAME, TRUE))))
error= ret_error;
else
{
DBUG_PRINT("info", ("Delete partition %s", norm_name_buff));
- if ((ret_error= file->ha_delete_table(norm_name_buff)))
+ if (unlikely((ret_error= file->ha_delete_table(norm_name_buff))))
error= ret_error;
- else if (deactivate_ddl_log_entry(part_elem->log_entry->entry_pos))
+ else if (unlikely(deactivate_ddl_log_entry(part_elem->log_entry->
+ entry_pos)))
error= 1;
else
part_elem->log_entry= NULL; /* Indicate success */
@@ -962,7 +997,7 @@ int ha_partition::rename_partitions(const char *path)
When state is PART_IS_CHANGED it means that we have created a new
TEMP partition that is to be renamed to normal partition name and
we are to delete the old partition with currently the normal name.
-
+
We perform this operation by
1) Delete old partition with normal partition name
2) Signal this in table log entry
@@ -993,33 +1028,39 @@ int ha_partition::rename_partitions(const char *path)
{
sub_elem= sub_it++;
part= i * num_subparts + j;
- if ((ret_error= create_subpartition_name(norm_name_buff,
- sizeof(norm_name_buff), path,
- part_elem->partition_name,
- sub_elem->partition_name, NORMAL_PART_NAME)))
+ if (unlikely((ret_error=
+ create_subpartition_name(norm_name_buff,
+ sizeof(norm_name_buff), path,
+ part_elem->partition_name,
+ sub_elem->partition_name,
+ NORMAL_PART_NAME))))
error= ret_error;
if (part_elem->part_state == PART_IS_CHANGED)
{
file= m_reorged_file[part_count++];
DBUG_PRINT("info", ("Delete subpartition %s", norm_name_buff));
- if ((ret_error= file->ha_delete_table(norm_name_buff)))
+ if (unlikely((ret_error= file->ha_delete_table(norm_name_buff))))
error= ret_error;
- else if (deactivate_ddl_log_entry(sub_elem->log_entry->entry_pos))
+ else if (unlikely(deactivate_ddl_log_entry(sub_elem->log_entry->
+ entry_pos)))
error= 1;
(void) sync_ddl_log();
}
file= m_new_file[part];
- if ((ret_error= create_subpartition_name(part_name_buff,
- sizeof(part_name_buff), path,
- part_elem->partition_name,
- sub_elem->partition_name, TEMP_PART_NAME)))
+ if (unlikely((ret_error=
+ create_subpartition_name(part_name_buff,
+ sizeof(part_name_buff), path,
+ part_elem->partition_name,
+ sub_elem->partition_name,
+ TEMP_PART_NAME))))
error= ret_error;
DBUG_PRINT("info", ("Rename subpartition from %s to %s",
part_name_buff, norm_name_buff));
- if ((ret_error= file->ha_rename_table(part_name_buff,
- norm_name_buff)))
+ if (unlikely((ret_error= file->ha_rename_table(part_name_buff,
+ norm_name_buff))))
error= ret_error;
- else if (deactivate_ddl_log_entry(sub_elem->log_entry->entry_pos))
+ else if (unlikely(deactivate_ddl_log_entry(sub_elem->log_entry->
+ entry_pos)))
error= 1;
else
sub_elem->log_entry= NULL;
@@ -1027,12 +1068,17 @@ int ha_partition::rename_partitions(const char *path)
}
else
{
- if ((ret_error= create_partition_name(norm_name_buff,
- sizeof(norm_name_buff), path,
- part_elem->partition_name, NORMAL_PART_NAME, TRUE)) ||
- (ret_error= create_partition_name(part_name_buff,
- sizeof(part_name_buff), path,
- part_elem->partition_name, TEMP_PART_NAME, TRUE)))
+ if (unlikely((ret_error=
+ create_partition_name(norm_name_buff,
+ sizeof(norm_name_buff), path,
+ part_elem->partition_name,
+ NORMAL_PART_NAME, TRUE)) ||
+ (ret_error= create_partition_name(part_name_buff,
+ sizeof(part_name_buff),
+ path,
+ part_elem->
+ partition_name,
+ TEMP_PART_NAME, TRUE))))
error= ret_error;
else
{
@@ -1040,19 +1086,21 @@ int ha_partition::rename_partitions(const char *path)
{
file= m_reorged_file[part_count++];
DBUG_PRINT("info", ("Delete partition %s", norm_name_buff));
- if ((ret_error= file->ha_delete_table(norm_name_buff)))
+ if (unlikely((ret_error= file->ha_delete_table(norm_name_buff))))
error= ret_error;
- else if (deactivate_ddl_log_entry(part_elem->log_entry->entry_pos))
+ else if (unlikely(deactivate_ddl_log_entry(part_elem->log_entry->
+ entry_pos)))
error= 1;
(void) sync_ddl_log();
}
file= m_new_file[i];
DBUG_PRINT("info", ("Rename partition from %s to %s",
part_name_buff, norm_name_buff));
- if ((ret_error= file->ha_rename_table(part_name_buff,
- norm_name_buff)))
+ if (unlikely((ret_error= file->ha_rename_table(part_name_buff,
+ norm_name_buff))))
error= ret_error;
- else if (deactivate_ddl_log_entry(part_elem->log_entry->entry_pos))
+ else if (unlikely(deactivate_ddl_log_entry(part_elem->log_entry->
+ entry_pos)))
error= 1;
else
part_elem->log_entry= NULL;
@@ -1197,7 +1245,7 @@ int ha_partition::preload_keys(THD *thd, HA_CHECK_OPT *check_opt)
DBUG_RETURN(handle_opt_partitions(thd, check_opt, PRELOAD_KEYS_PARTS));
}
-
+
/*
Handle optimize/analyze/check/repair of one partition
@@ -1219,7 +1267,7 @@ int ha_partition::handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt,
int error;
handler *file= m_file[part_id];
DBUG_ENTER("handle_opt_part");
- DBUG_PRINT("enter", ("flag = %u", flag));
+ DBUG_PRINT("enter", ("flag: %u", flag));
if (flag == OPTIMIZE_PARTS)
error= file->ha_optimize(thd, check_opt);
@@ -1263,24 +1311,24 @@ int ha_partition::handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt,
/*
- print a message row formatted for ANALYZE/CHECK/OPTIMIZE/REPAIR TABLE
+ print a message row formatted for ANALYZE/CHECK/OPTIMIZE/REPAIR TABLE
(modelled after mi_check_print_msg)
TODO: move this into the handler, or rewrite mysql_admin_table.
*/
-static bool print_admin_msg(THD* thd, uint len,
+bool print_admin_msg(THD* thd, uint len,
const char* msg_type,
const char* db_name, String &table_name,
const char* op_name, const char *fmt, ...)
ATTRIBUTE_FORMAT(printf, 7, 8);
-static bool print_admin_msg(THD* thd, uint len,
+bool print_admin_msg(THD* thd, uint len,
const char* msg_type,
const char* db_name, String &table_name,
const char* op_name, const char *fmt, ...)
{
va_list args;
Protocol *protocol= thd->protocol;
- uint length;
- uint msg_length;
+ size_t length;
+ size_t msg_length;
char name[NAME_LEN*2+2];
char *msgbuf;
bool error= true;
@@ -1292,7 +1340,7 @@ static bool print_admin_msg(THD* thd, uint len,
va_end(args);
if (msg_length >= (len - 1))
goto err;
- msgbuf[len - 1] = 0; // healthy paranoia
+ msgbuf[len - 1]= 0; // healthy paranoia
if (!thd->vio_ok())
@@ -1301,7 +1349,7 @@ static bool print_admin_msg(THD* thd, uint len,
goto err;
}
- length=(uint) (strxmov(name, db_name, ".", table_name.c_ptr_safe(), NullS) - name);
+ length=(size_t)(strxmov(name, db_name, ".", table_name.c_ptr_safe(), NullS) - name);
/*
TODO: switch from protocol to push_warning here. The main reason we didn't
it yet is parallel repair, which threads have no THD object accessible via
@@ -1362,7 +1410,7 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt,
when ALTER TABLE <CMD> PARTITION ...
it should only do named partitions, otherwise all partitions
*/
- if (!(thd->lex->alter_info.flags & Alter_info::ALTER_ADMIN_PARTITION) ||
+ if (!(thd->lex->alter_info.partition_flags & ALTER_PARTITION_ADMIN) ||
part_elem->part_state == PART_ADMIN)
{
if (m_is_sub_partitioned)
@@ -1376,7 +1424,7 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt,
part= i * num_subparts + j;
DBUG_PRINT("info", ("Optimize subpartition %u (%s)",
part, sub_elem->partition_name));
- if ((error= handle_opt_part(thd, check_opt, part, flag)))
+ if (unlikely((error= handle_opt_part(thd, check_opt, part, flag))))
{
/* print a line which partition the error belongs to */
if (error != HA_ADMIN_NOT_IMPLEMENTED &&
@@ -1386,7 +1434,7 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt,
print_admin_msg(thd, MYSQL_ERRMSG_SIZE, "error",
table_share->db.str, table->alias,
opt_op_name[flag],
- "Subpartition %s returned error",
+ "Subpartition %s returned error",
sub_elem->partition_name);
}
/* reset part_state for the remaining partitions */
@@ -1403,7 +1451,7 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt,
{
DBUG_PRINT("info", ("Optimize partition %u (%s)", i,
part_elem->partition_name));
- if ((error= handle_opt_part(thd, check_opt, i, flag)))
+ if (unlikely((error= handle_opt_part(thd, check_opt, i, flag))))
{
/* print a line which partition the error belongs to */
if (error != HA_ADMIN_NOT_IMPLEMENTED &&
@@ -1412,7 +1460,7 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt,
{
print_admin_msg(thd, MYSQL_ERRMSG_SIZE, "error",
table_share->db.str, table->alias,
- opt_op_name[flag], "Partition %s returned error",
+ opt_op_name[flag], "Partition %s returned error",
part_elem->partition_name);
}
/* reset part_state for the remaining partitions */
@@ -1454,7 +1502,7 @@ bool ha_partition::check_and_repair(THD *thd)
} while (*(++file));
DBUG_RETURN(FALSE);
}
-
+
/**
@breif Check if the table can be automatically repaired
@@ -1494,7 +1542,7 @@ bool ha_partition::is_crashed() const
} while (*(++file));
DBUG_RETURN(FALSE);
}
-
+
/*
Prepare by creating a new partition
@@ -1533,13 +1581,15 @@ int ha_partition::prepare_new_partition(TABLE *tbl,
That file name may be different from part_name, which will be
attached in append_file_to_dir().
*/
- truncate_partition_filename(p_elem->data_file_name);
- truncate_partition_filename(p_elem->index_file_name);
+ truncate_partition_filename((char*) p_elem->data_file_name);
+ truncate_partition_filename((char*) p_elem->index_file_name);
- if ((error= set_up_table_before_create(tbl, part_name, create_info, p_elem)))
+ if (unlikely((error= set_up_table_before_create(tbl, part_name, create_info,
+ p_elem))))
goto error_create;
- tbl->s->connect_string = p_elem->connect_string;
+ if (!(file->ht->flags & HTON_CAN_READ_CONNECT_STRING_IN_PARTITION))
+ tbl->s->connect_string= p_elem->connect_string;
if ((error= file->ha_create(part_name, tbl, create_info)))
{
/*
@@ -1554,8 +1604,8 @@ int ha_partition::prepare_new_partition(TABLE *tbl,
goto error_create;
}
DBUG_PRINT("info", ("partition %s created", part_name));
- if ((error= file->ha_open(tbl, part_name, m_mode,
- m_open_test_lock | HA_OPEN_NO_PSI_CALL)))
+ if (unlikely((error= file->ha_open(tbl, part_name, m_mode,
+ m_open_test_lock | HA_OPEN_NO_PSI_CALL))))
goto error_open;
DBUG_PRINT("info", ("partition %s opened", part_name));
@@ -1565,7 +1615,7 @@ int ha_partition::prepare_new_partition(TABLE *tbl,
assumes that external_lock() is last call that may fail here.
Otherwise see description for cleanup_new_partition().
*/
- if ((error= file->ha_external_lock(ha_thd(), F_WRLCK)))
+ if (unlikely((error= file->ha_external_lock(ha_thd(), F_WRLCK))))
goto error_external_lock;
DBUG_PRINT("info", ("partition %s external locked", part_name));
@@ -1719,7 +1769,6 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info,
!(m_reorged_file= (handler**) thd->calloc(sizeof(handler*)*
(m_reorged_parts + 1))))
{
- mem_alloc_error(sizeof(handler*)*(m_reorged_parts+1));
DBUG_RETURN(HA_ERR_OUT_OF_MEM);
}
@@ -1752,7 +1801,6 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info,
thd->calloc(sizeof(handler*)*
(2*(num_remain_partitions + 1))))))
{
- mem_alloc_error(sizeof(handler*)*2*(num_remain_partitions+1));
DBUG_RETURN(HA_ERR_OUT_OF_MEM);
}
m_added_file= &new_file_array[num_remain_partitions + 1];
@@ -1844,7 +1892,6 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info,
thd->mem_root,
part_elem->engine_type)))
{
- mem_alloc_error(sizeof(handler));
DBUG_RETURN(HA_ERR_OUT_OF_MEM);
}
if ((*new_file)->set_ha_share_ref(&p_share_refs->ha_shares[j]))
@@ -1874,7 +1921,7 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info,
in the partitions.
*/
- uint disable_non_uniq_indexes = indexes_are_disabled();
+ uint disable_non_uniq_indexes= indexes_are_disabled();
i= 0;
part_count= 0;
@@ -1901,21 +1948,24 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info,
do
{
partition_element *sub_elem= sub_it++;
- if ((error= create_subpartition_name(part_name_buff,
- sizeof(part_name_buff), path,
- part_elem->partition_name, sub_elem->partition_name,
- name_variant)))
+ if (unlikely((error=
+ create_subpartition_name(part_name_buff,
+ sizeof(part_name_buff), path,
+ part_elem->partition_name,
+ sub_elem->partition_name,
+ name_variant))))
{
cleanup_new_partition(part_count);
DBUG_RETURN(error);
}
part= i * num_subparts + j;
DBUG_PRINT("info", ("Add subpartition %s", part_name_buff));
- if ((error= prepare_new_partition(table, create_info,
- new_file_array[part],
- (const char *)part_name_buff,
- sub_elem,
- disable_non_uniq_indexes)))
+ if (unlikely((error=
+ prepare_new_partition(table, create_info,
+ new_file_array[part],
+ (const char *)part_name_buff,
+ sub_elem,
+ disable_non_uniq_indexes))))
{
cleanup_new_partition(part_count);
DBUG_RETURN(error);
@@ -1926,20 +1976,23 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info,
}
else
{
- if ((error= create_partition_name(part_name_buff,
- sizeof(part_name_buff), path, part_elem->partition_name,
- name_variant, TRUE)))
+ if (unlikely((error=
+ create_partition_name(part_name_buff,
+ sizeof(part_name_buff), path,
+ part_elem->partition_name,
+ name_variant, TRUE))))
{
cleanup_new_partition(part_count);
DBUG_RETURN(error);
}
DBUG_PRINT("info", ("Add partition %s", part_name_buff));
- if ((error= prepare_new_partition(table, create_info,
- new_file_array[i],
- (const char *)part_name_buff,
- part_elem,
- disable_non_uniq_indexes)))
+ if (unlikely((error=
+ prepare_new_partition(table, create_info,
+ new_file_array[i],
+ (const char *)part_name_buff,
+ part_elem,
+ disable_non_uniq_indexes))))
{
cleanup_new_partition(part_count);
DBUG_RETURN(error);
@@ -1973,7 +2026,7 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info,
part_elem->part_state= PART_TO_BE_DROPPED;
}
m_new_file= new_file_array;
- if ((error= copy_partitions(copied, deleted)))
+ if (unlikely((error= copy_partitions(copied, deleted))))
{
/*
Close and unlock the new temporary partitions.
@@ -2018,6 +2071,11 @@ int ha_partition::copy_partitions(ulonglong * const copied,
else
set_linear_hash_mask(m_part_info, m_part_info->num_subparts);
}
+ else if (m_part_info->part_type == VERSIONING_PARTITION)
+ {
+ if (m_part_info->check_constants(ha_thd(), m_part_info))
+ goto init_error;
+ }
while (reorg_part < m_reorged_parts)
{
@@ -2025,14 +2083,12 @@ int ha_partition::copy_partitions(ulonglong * const copied,
uint32 new_part;
late_extra_cache(reorg_part);
- if ((result= file->ha_rnd_init_with_error(1)))
+ if (unlikely((result= file->ha_rnd_init_with_error(1))))
goto init_error;
while (TRUE)
{
if ((result= file->ha_rnd_next(m_rec0)))
{
- if (result == HA_ERR_RECORD_DELETED)
- continue; //Probably MyISAM
if (result != HA_ERR_END_OF_FILE)
goto error;
/*
@@ -2102,7 +2158,7 @@ void ha_partition::update_create_info(HA_CREATE_INFO *create_info)
HA_STATUS_AUTO is optimized so it will not always be forwarded
to all partitions, but HA_STATUS_VARIABLE will.
*/
- info(HA_STATUS_VARIABLE);
+ info(HA_STATUS_VARIABLE | HA_STATUS_OPEN);
info(HA_STATUS_AUTO);
@@ -2113,10 +2169,11 @@ void ha_partition::update_create_info(HA_CREATE_INFO *create_info)
DATA DIRECTORY and INDEX DIRECTORY are never applied to the whole
partitioned table, only its parts.
*/
- my_bool from_alter = (create_info->data_file_name == (const char*) -1);
- create_info->data_file_name= create_info->index_file_name = NULL;
+ my_bool from_alter= (create_info->data_file_name == (const char*) -1);
+ create_info->data_file_name= create_info->index_file_name= NULL;
- create_info->connect_string= null_lex_str;
+ if (!(m_file[0]->ht->flags & HTON_CAN_READ_CONNECT_STRING_IN_PARTITION))
+ create_info->connect_string= null_clex_str;
/*
We do not need to update the individual partition DATA DIRECTORY settings
@@ -2134,10 +2191,10 @@ void ha_partition::update_create_info(HA_CREATE_INFO *create_info)
List_iterator<partition_element> part_it(m_part_info->partitions);
partition_element *part_elem, *sub_elem;
uint num_subparts= m_part_info->num_subparts;
- uint num_parts = num_subparts ? m_file_tot_parts / num_subparts
- : m_file_tot_parts;
+ uint num_parts= (num_subparts ? m_file_tot_parts / num_subparts :
+ m_file_tot_parts);
HA_CREATE_INFO dummy_info;
- memset(&dummy_info, 0, sizeof(dummy_info));
+ dummy_info.init();
/*
Since update_create_info() can be called from mysql_prepare_alter_table()
@@ -2252,7 +2309,7 @@ void ha_partition::change_table_ptr(TABLE *table_arg, TABLE_SHARE *share)
comment Original comment
RETURN VALUE
- new comment
+ new comment
DESCRIPTION
No comment changes so far
@@ -2307,7 +2364,7 @@ uint ha_partition::del_ren_table(const char *from, const char *to)
Delete table, start by delete the .par file. If error, break, otherwise
delete as much as possible.
*/
- if ((error= handler::delete_table(from)))
+ if (unlikely((error= handler::delete_table(from))))
DBUG_RETURN(error);
}
/*
@@ -2323,17 +2380,19 @@ uint ha_partition::del_ren_table(const char *from, const char *to)
i= 0;
do
{
- if ((error= create_partition_name(from_buff, sizeof(from_buff), from_path,
- name_buffer_ptr, NORMAL_PART_NAME, FALSE)))
+ if (unlikely((error= create_partition_name(from_buff, sizeof(from_buff),
+ from_path, name_buffer_ptr,
+ NORMAL_PART_NAME, FALSE))))
goto rename_error;
if (to != NULL)
{ // Rename branch
- if ((error= create_partition_name(to_buff, sizeof(to_buff), to_path,
- name_buffer_ptr, NORMAL_PART_NAME, FALSE)))
+ if (unlikely((error= create_partition_name(to_buff, sizeof(to_buff),
+ to_path, name_buffer_ptr,
+ NORMAL_PART_NAME, FALSE))))
goto rename_error;
error= (*file)->ha_rename_table(from_buff, to_buff);
- if (error)
+ if (unlikely(error))
goto rename_error;
}
else // delete branch
@@ -2341,13 +2400,13 @@ uint ha_partition::del_ren_table(const char *from, const char *to)
error= (*file)->ha_delete_table(from_buff);
}
name_buffer_ptr= strend(name_buffer_ptr) + 1;
- if (error)
+ if (unlikely(error))
save_error= error;
i++;
} while (*(++file));
if (to != NULL)
{
- if ((error= handler::rename_table(from, to)))
+ if (unlikely((error= handler::rename_table(from, to))))
{
/* Try to revert everything, ignore errors */
(void) handler::rename_table(to, from);
@@ -2379,7 +2438,7 @@ uint ha_partition::count_query_cache_dependant_tables(uint8 *tables_type)
/* Here we rely on the fact that all tables are of the same type */
uint8 type= m_file[0]->table_cache_type();
(*tables_type)|= type;
- DBUG_PRINT("info", ("cnt: %u", (uint)m_tot_parts));
+ DBUG_PRINT("enter", ("cnt: %u", (uint) m_tot_parts));
/*
We need save underlying tables only for HA_CACHE_TBL_ASKTRANSACT:
HA_CACHE_TBL_NONTRANSACT - because all changes goes through partition table
@@ -2420,7 +2479,7 @@ reg_query_cache_dependant_table(THD *thd,
(++(*block_table))->n= ++(*n);
if (!cache->insert_table(thd, cache_key_len,
cache_key, (*block_table),
- table_share->db.length,
+ (uint32) table_share->db.length,
(uint8) (cache_key_len -
table_share->table_cache_key.length),
type,
@@ -2489,7 +2548,7 @@ register_query_cache_dependant_tables(THD *thd,
sub_elem= subpart_it++;
part= i * num_subparts + j;
/* we store the end \0 as part of the key */
- end= strmov(engine_pos, sub_elem->partition_name);
+ end= strmov(engine_pos, sub_elem->partition_name) + 1;
length= (uint)(end - engine_key);
/* Copy the suffix also to query cache key */
memcpy(query_cache_key_end, engine_key_end, (end - engine_key_end));
@@ -2534,7 +2593,7 @@ register_query_cache_dependant_tables(THD *thd,
@return status
@retval TRUE Error
@retval FALSE Success
-
+
@details
Set up
1) Comment on partition
@@ -2544,12 +2603,12 @@ register_query_cache_dependant_tables(THD *thd,
*/
int ha_partition::set_up_table_before_create(TABLE *tbl,
- const char *partition_name_with_path,
+ const char *partition_name_with_path,
HA_CREATE_INFO *info,
partition_element *part_elem)
{
int error= 0;
- const char *partition_name;
+ LEX_CSTRING part_name;
THD *thd= ha_thd();
DBUG_ENTER("set_up_table_before_create");
@@ -2559,15 +2618,16 @@ int ha_partition::set_up_table_before_create(TABLE *tbl,
DBUG_RETURN(1);
tbl->s->max_rows= part_elem->part_max_rows;
tbl->s->min_rows= part_elem->part_min_rows;
- partition_name= strrchr(partition_name_with_path, FN_LIBCHAR);
+ part_name.str= strrchr(partition_name_with_path, FN_LIBCHAR)+1;
+ part_name.length= strlen(part_name.str);
if ((part_elem->index_file_name &&
(error= append_file_to_dir(thd,
(const char**)&part_elem->index_file_name,
- partition_name+1))) ||
+ &part_name))) ||
(part_elem->data_file_name &&
(error= append_file_to_dir(thd,
(const char**)&part_elem->data_file_name,
- partition_name+1))))
+ &part_name))))
{
DBUG_RETURN(error);
}
@@ -2624,10 +2684,10 @@ static uint name_add(char *dest, const char *first_name, const char *sec_name)
bool ha_partition::create_handler_file(const char *name)
{
partition_element *part_elem, *subpart_elem;
- uint i, j, part_name_len, subpart_name_len;
- uint tot_partition_words, tot_name_len, num_parts;
- uint tot_parts= 0;
- uint tot_len_words, tot_len_byte, chksum, tot_name_words;
+ size_t i, j, part_name_len, subpart_name_len;
+ size_t tot_partition_words, tot_name_len, num_parts;
+ size_t tot_parts= 0;
+ size_t tot_len_words, tot_len_byte, chksum, tot_name_words;
char *name_buffer_ptr;
uchar *file_buffer, *engine_array;
bool result= TRUE;
@@ -2639,8 +2699,7 @@ bool ha_partition::create_handler_file(const char *name)
DBUG_ENTER("create_handler_file");
num_parts= m_part_info->partitions.elements;
- DBUG_PRINT("info", ("table name = %s, num_parts = %u", name,
- num_parts));
+ DBUG_PRINT("enter", ("table name: %s num_parts: %zu", name, num_parts));
tot_name_len= 0;
for (i= 0; i < num_parts; i++)
{
@@ -2759,7 +2818,7 @@ bool ha_partition::create_handler_file(const char *name)
{
uchar buffer[4];
part_elem= part_it++;
- uint length = part_elem->connect_string.length;
+ size_t length= part_elem->connect_string.length;
int4store(buffer, length);
if (my_write(file, buffer, 4, MYF(MY_WME | MY_NABP)) ||
my_write(file, (uchar *) part_elem->connect_string.str, length,
@@ -2859,10 +2918,8 @@ bool ha_partition::new_handlers_from_part_info(MEM_ROOT *mem_root)
DBUG_ENTER("ha_partition::new_handlers_from_part_info");
if (!(m_file= (handler **) alloc_root(mem_root, alloc_len)))
- {
- mem_alloc_error(alloc_len);
- goto error_end;
- }
+ goto error;
+
m_file_tot_parts= m_tot_parts;
bzero((char*) m_file, alloc_len);
DBUG_ASSERT(m_part_info->num_parts > 0);
@@ -2903,8 +2960,6 @@ bool ha_partition::new_handlers_from_part_info(MEM_ROOT *mem_root)
}
DBUG_RETURN(FALSE);
error:
- mem_alloc_error(sizeof(handler));
-error_end:
DBUG_RETURN(TRUE);
}
@@ -2957,7 +3012,7 @@ bool ha_partition::read_par_file(const char *name)
if (chksum)
goto err2;
m_tot_parts= uint4korr((file_buffer) + PAR_NUM_PARTS_OFFSET);
- DBUG_PRINT("info", ("No of parts = %u", m_tot_parts));
+ DBUG_PRINT("info", ("No of parts: %u", m_tot_parts));
tot_partition_words= (m_tot_parts + PAR_WORD_SIZE - 1) / PAR_WORD_SIZE;
tot_name_len_offset= file_buffer + PAR_ENGINES_OFFSET +
@@ -2973,27 +3028,29 @@ bool ha_partition::read_par_file(const char *name)
m_file_buffer= file_buffer; // Will be freed in clear_handler_file()
m_name_buffer_ptr= (char*) (tot_name_len_offset + PAR_WORD_SIZE);
- if (!(m_connect_string= (LEX_STRING*)
- alloc_root(&m_mem_root, m_tot_parts * sizeof(LEX_STRING))))
+ if (!(m_connect_string= (LEX_CSTRING*)
+ alloc_root(&m_mem_root, m_tot_parts * sizeof(LEX_CSTRING))))
goto err2;
- bzero(m_connect_string, m_tot_parts * sizeof(LEX_STRING));
+ bzero(m_connect_string, m_tot_parts * sizeof(LEX_CSTRING));
/* Read connection arguments (for federated X engine) */
for (i= 0; i < m_tot_parts; i++)
{
- LEX_STRING connect_string;
+ LEX_CSTRING connect_string;
uchar buffer[4];
+ char *tmp;
if (my_read(file, buffer, 4, MYF(MY_NABP)))
{
/* No extra options; Probably not a federatedx engine */
break;
}
connect_string.length= uint4korr(buffer);
- connect_string.str= (char*) alloc_root(&m_mem_root, connect_string.length+1);
+ connect_string.str= tmp= (char*) alloc_root(&m_mem_root,
+ connect_string.length+1);
if (my_read(file, (uchar*) connect_string.str, connect_string.length,
MYF(MY_NABP)))
break;
- connect_string.str[connect_string.length]= 0;
+ tmp[connect_string.length]= 0;
m_connect_string[i]= connect_string;
}
@@ -3061,7 +3118,7 @@ bool ha_partition::setup_engine_array(MEM_ROOT *mem_root)
}
my_afree(engine_array);
-
+
if (create_handlers(mem_root))
{
clear_handler_file();
@@ -3148,7 +3205,7 @@ bool ha_partition::insert_partition_name_in_hash(const char *name, uint part_id,
{
PART_NAME_DEF *part_def;
uchar *part_name;
- uint part_name_length;
+ size_t part_name_length;
DBUG_ENTER("ha_partition::insert_partition_name_in_hash");
/*
Calculate and store the length here, to avoid doing it when
@@ -3168,7 +3225,7 @@ bool ha_partition::insert_partition_name_in_hash(const char *name, uint part_id,
DBUG_RETURN(true);
memcpy(part_name, name, part_name_length + 1);
part_def->partition_name= part_name;
- part_def->length= part_name_length;
+ part_def->length= (uint)part_name_length;
part_def->part_id= part_id;
part_def->is_subpart= is_subpart;
if (my_hash_insert(&part_share->partition_name_hash, (uchar *) part_def))
@@ -3283,9 +3340,8 @@ bool ha_partition::set_ha_share_ref(Handler_share **ha_share_arg)
DBUG_RETURN(true);
if (!(part_share= get_share()))
DBUG_RETURN(true);
- DBUG_ASSERT(part_share->partitions_share_refs);
- DBUG_ASSERT(part_share->partitions_share_refs->num_parts >= m_tot_parts);
- ha_shares= part_share->partitions_share_refs->ha_shares;
+ DBUG_ASSERT(part_share->partitions_share_refs.num_parts >= m_tot_parts);
+ ha_shares= part_share->partitions_share_refs.ha_shares;
for (i= 0; i < m_tot_parts; i++)
{
if (m_file[i]->set_ha_share_ref(&ha_shares[i]))
@@ -3344,63 +3400,59 @@ void ha_partition::free_partition_bitmaps()
my_bitmap_free(&m_locked_partitions);
my_bitmap_free(&m_partitions_to_reset);
my_bitmap_free(&m_key_not_found_partitions);
+ my_bitmap_free(&m_opened_partitions);
+ my_bitmap_free(&m_mrr_used_partitions);
}
/**
Helper function for initializing all internal bitmaps.
+
+ Note:
+ All bitmaps, including partially allocated, are freed in
+ free_partion_bitmaps()
*/
bool ha_partition::init_partition_bitmaps()
{
DBUG_ENTER("ha_partition::init_partition_bitmaps");
+
/* Initialize the bitmap we use to minimize ha_start_bulk_insert calls */
if (my_bitmap_init(&m_bulk_insert_started, NULL, m_tot_parts + 1, FALSE))
DBUG_RETURN(true);
- bitmap_clear_all(&m_bulk_insert_started);
/* Initialize the bitmap we use to keep track of locked partitions */
if (my_bitmap_init(&m_locked_partitions, NULL, m_tot_parts, FALSE))
- {
- my_bitmap_free(&m_bulk_insert_started);
DBUG_RETURN(true);
- }
- bitmap_clear_all(&m_locked_partitions);
/*
Initialize the bitmap we use to keep track of partitions which may have
something to reset in ha_reset().
*/
if (my_bitmap_init(&m_partitions_to_reset, NULL, m_tot_parts, FALSE))
- {
- my_bitmap_free(&m_bulk_insert_started);
- my_bitmap_free(&m_locked_partitions);
DBUG_RETURN(true);
- }
- bitmap_clear_all(&m_partitions_to_reset);
/*
Initialize the bitmap we use to keep track of partitions which returned
HA_ERR_KEY_NOT_FOUND from index_read_map.
*/
if (my_bitmap_init(&m_key_not_found_partitions, NULL, m_tot_parts, FALSE))
- {
- my_bitmap_free(&m_bulk_insert_started);
- my_bitmap_free(&m_locked_partitions);
- my_bitmap_free(&m_partitions_to_reset);
DBUG_RETURN(true);
- }
- bitmap_clear_all(&m_key_not_found_partitions);
- m_key_not_found= false;
+
+ if (bitmap_init(&m_mrr_used_partitions, NULL, m_tot_parts, TRUE))
+ DBUG_RETURN(true);
+
+ if (my_bitmap_init(&m_opened_partitions, NULL, m_tot_parts, FALSE))
+ DBUG_RETURN(true);
+
+ m_file_sample= NULL;
+
/* Initialize the bitmap for read/lock_partitions */
if (!m_is_clone_of)
{
DBUG_ASSERT(!m_clone_mem_root);
if (m_part_info->set_partition_bitmaps(NULL))
- {
- free_partition_bitmaps();
DBUG_RETURN(true);
- }
}
DBUG_RETURN(false);
}
@@ -3408,8 +3460,7 @@ bool ha_partition::init_partition_bitmaps()
/*
Open handler object
-
- SYNOPSIS
+SYNOPSIS
open()
name Full path of table name
mode Open mode flags
@@ -3431,7 +3482,6 @@ bool ha_partition::init_partition_bitmaps()
int ha_partition::open(const char *name, int mode, uint test_if_locked)
{
- char *name_buffer_ptr;
int error= HA_ERR_INITIALIZATION;
handler **file;
char name_buff[FN_REFLEN + 1];
@@ -3445,7 +3495,6 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked)
m_part_field_array= m_part_info->full_part_field_array;
if (get_from_handler_file(name, &table->mem_root, MY_TEST(m_is_clone_of)))
DBUG_RETURN(error);
- name_buffer_ptr= m_name_buffer_ptr;
if (populate_partition_name_hash())
{
DBUG_RETURN(HA_ERR_INITIALIZATION);
@@ -3465,13 +3514,39 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked)
}
if (init_partition_bitmaps())
- DBUG_RETURN(error);
-
- DBUG_ASSERT(m_part_info);
+ goto err_alloc;
+
+ if (!MY_TEST(m_is_clone_of) &&
+ unlikely((error=
+ m_part_info->set_partition_bitmaps(m_partitions_to_open))))
+ goto err_alloc;
+
+ /* Allocate memory used with MMR */
+ if (!(m_range_info= (void **)
+ my_multi_malloc(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,
+ &m_mrr_buffer_size, sizeof(uint) * m_tot_parts,
+ &m_part_mrr_range_length, sizeof(uint) * m_tot_parts,
+ &m_part_mrr_range_first,
+ sizeof(PARTITION_PART_KEY_MULTI_RANGE *) * m_tot_parts,
+ &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,
+ NullS)))
+ goto err_alloc;
+
+ bzero(m_mrr_buffer, m_tot_parts * sizeof(HANDLER_BUFFER));
+ bzero(m_part_mrr_range_first,
+ sizeof(PARTITION_PART_KEY_MULTI_RANGE *) * m_tot_parts);
if (m_is_clone_of)
{
uint i, alloc_len;
+ char *name_buffer_ptr;
DBUG_ASSERT(m_clone_mem_root);
/* Allocate an array of handler pointers for the partitions handlers. */
alloc_len= (m_tot_parts + 1) * sizeof(handler*);
@@ -3481,6 +3556,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked)
goto err_alloc;
}
memset(m_file, 0, alloc_len);
+ name_buffer_ptr= m_name_buffer_ptr;
/*
Populate them by cloning the original partitions. This also opens them.
Note that file->ref is allocated too.
@@ -3488,8 +3564,12 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked)
file= m_is_clone_of->m_file;
for (i= 0; i < m_tot_parts; i++)
{
- if ((error= create_partition_name(name_buff, sizeof(name_buff), name,
- name_buffer_ptr, NORMAL_PART_NAME, FALSE)))
+ if (!bitmap_is_set(&m_is_clone_of->m_opened_partitions, i))
+ continue;
+
+ if (unlikely((error= create_partition_name(name_buff, sizeof(name_buff),
+ name, name_buffer_ptr,
+ NORMAL_PART_NAME, FALSE))))
goto err_handler;
/* ::clone() will also set ha_share from the original. */
if (!(m_file[i]= file[i]->clone(name_buff, m_clone_mem_root)))
@@ -3498,36 +3578,36 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked)
file= &m_file[i];
goto err_handler;
}
+ if (!m_file_sample)
+ m_file_sample= m_file[i];
name_buffer_ptr+= strlen(name_buffer_ptr) + 1;
+ bitmap_set_bit(&m_opened_partitions, i);
}
}
else
{
- file= m_file;
- do
- {
- if ((error= create_partition_name(name_buff, sizeof(name_buff), name,
- name_buffer_ptr, NORMAL_PART_NAME, FALSE)))
- goto err_handler;
- table->s->connect_string = m_connect_string[(uint)(file-m_file)];
- if ((error= (*file)->ha_open(table, name_buff, mode,
- test_if_locked | HA_OPEN_NO_PSI_CALL)))
- goto err_handler;
- bzero(&table->s->connect_string, sizeof(LEX_STRING));
- if (m_file == file)
- m_num_locks= (*file)->lock_count();
- DBUG_ASSERT(m_num_locks == (*file)->lock_count());
- name_buffer_ptr+= strlen(name_buffer_ptr) + 1;
- } while (*(++file));
+ check_insert_autoincrement();
+ if (unlikely((error= open_read_partitions(name_buff, sizeof(name_buff)))))
+ goto err_handler;
+ m_num_locks= m_file_sample->lock_count();
}
-
+ /*
+ We want to know the upper bound for locks, to allocate enough memory.
+ There is no performance lost if we simply return in lock_count() the
+ maximum number locks needed, only some minor over allocation of memory
+ in get_lock_data().
+ */
+ m_num_locks*= m_tot_parts;
+
file= m_file;
- ref_length= (*file)->ref_length;
- check_table_flags= (((*file)->ha_table_flags() &
+ ref_length= get_open_file_sample()->ref_length;
+ check_table_flags= ((get_open_file_sample()->ha_table_flags() &
~(PARTITION_DISABLED_TABLE_FLAGS)) |
(PARTITION_ENABLED_TABLE_FLAGS));
while (*(++file))
{
+ if (!bitmap_is_set(&m_opened_partitions, (uint)(file - m_file)))
+ continue;
/* MyISAM can have smaller ref_length for partitions with MAX_ROWS set */
set_if_bigger(ref_length, ((*file)->ref_length));
/*
@@ -3540,12 +3620,12 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked)
{
error= HA_ERR_INITIALIZATION;
/* set file to last handler, so all of them are closed */
- file = &m_file[m_tot_parts - 1];
+ file= &m_file[m_tot_parts - 1];
goto err_handler;
}
}
- key_used_on_scan= m_file[0]->key_used_on_scan;
- implicit_emptied= m_file[0]->implicit_emptied;
+ key_used_on_scan= get_open_file_sample()->key_used_on_scan;
+ implicit_emptied= get_open_file_sample()->implicit_emptied;
/*
Add 2 bytes for partition id in position ref length.
ref_length=max_in_all_partitions(ref_length) + PARTITION_BYTES_IN_POS
@@ -3571,15 +3651,21 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked)
m_part_info->part_expr->get_monotonicity_info();
else if (m_part_info->list_of_part_fields)
m_part_func_monotonicity_info= MONOTONIC_STRICT_INCREASING;
- info(HA_STATUS_VARIABLE | HA_STATUS_CONST);
+ info(HA_STATUS_VARIABLE | HA_STATUS_CONST | HA_STATUS_OPEN);
DBUG_RETURN(0);
err_handler:
DEBUG_SYNC(ha_thd(), "partition_open_error");
+ file= &m_file[m_tot_parts - 1];
while (file-- != m_file)
- (*file)->ha_close();
+ {
+ if (bitmap_is_set(&m_opened_partitions, (uint)(file - m_file)))
+ (*file)->ha_close();
+ }
err_alloc:
free_partition_bitmaps();
+ my_free(m_range_info);
+ m_range_info= 0;
DBUG_RETURN(error);
}
@@ -3657,7 +3743,7 @@ handler *ha_partition::clone(const char *name, MEM_ROOT *mem_root)
/*
Allocate new_handler->ref here because otherwise ha_open will allocate it
- on this->table->mem_root and we will not be able to reclaim that memory
+ on this->table->mem_root and we will not be able to reclaim that memory
when the clone handler object is destroyed.
*/
if (!(new_handler->ref= (uchar*) alloc_root(mem_root,
@@ -3699,20 +3785,69 @@ int ha_partition::close(void)
{
bool first= TRUE;
handler **file;
+ uint i;
+ st_partition_ft_info *tmp_ft_info;
DBUG_ENTER("ha_partition::close");
-
DBUG_ASSERT(table->s == table_share);
- destroy_record_priority_queue();
- free_partition_bitmaps();
DBUG_ASSERT(m_part_info);
+
+ destroy_record_priority_queue();
+
+ for (; ft_first ; ft_first= tmp_ft_info)
+ {
+ tmp_ft_info= ft_first->next;
+ my_free(ft_first);
+ }
+
+ /* Free active mrr_ranges */
+ for (i= 0; i < m_tot_parts; i++)
+ {
+ if (m_part_mrr_range_first[i])
+ {
+ PARTITION_PART_KEY_MULTI_RANGE *tmp_mrr_range_first=
+ m_part_mrr_range_first[i];
+ do
+ {
+ PARTITION_PART_KEY_MULTI_RANGE *tmp_mrr_range_current;
+ tmp_mrr_range_current= tmp_mrr_range_first;
+ tmp_mrr_range_first= tmp_mrr_range_first->next;
+ my_free(tmp_mrr_range_current);
+ } while (tmp_mrr_range_first);
+ }
+ }
+ if (m_mrr_range_first)
+ {
+ do
+ {
+ m_mrr_range_current= m_mrr_range_first;
+ m_mrr_range_first= m_mrr_range_first->next;
+ if (m_mrr_range_current->key[0])
+ my_free(m_mrr_range_current->key[0]);
+ if (m_mrr_range_current->key[1])
+ my_free(m_mrr_range_current->key[1]);
+ my_free(m_mrr_range_current);
+ } while (m_mrr_range_first);
+ }
+ my_free(m_range_info);
+ m_range_info= NULL; // Safety
+
+ if (m_mrr_full_buffer)
+ {
+ my_free(m_mrr_full_buffer);
+ m_mrr_full_buffer= NULL;
+ m_mrr_full_buffer_size= 0;
+ }
file= m_file;
repeat:
do
{
- (*file)->ha_close();
+ if (!first || bitmap_is_set(&m_opened_partitions, (uint)(file - m_file)))
+ (*file)->ha_close();
} while (*(++file));
+ free_partition_bitmaps();
+
if (first && m_added_file && m_added_file[0])
{
file= m_added_file;
@@ -3766,7 +3901,7 @@ repeat:
int ha_partition::external_lock(THD *thd, int lock_type)
{
- uint error;
+ int error;
uint i, first_used_partition;
MY_BITMAP *used_partitions;
DBUG_ENTER("ha_partition::external_lock");
@@ -3785,8 +3920,8 @@ int ha_partition::external_lock(THD *thd, int lock_type)
i < m_tot_parts;
i= bitmap_get_next_set(used_partitions, i))
{
- DBUG_PRINT("info", ("external_lock(thd, %d) part %d", lock_type, i));
- if ((error= m_file[i]->ha_external_lock(thd, lock_type)))
+ DBUG_PRINT("info", ("external_lock(thd, %d) part %u", lock_type, i));
+ if (unlikely((error= m_file[i]->ha_external_lock(thd, lock_type))))
{
if (lock_type != F_UNLCK)
goto err_handler;
@@ -3814,8 +3949,18 @@ int ha_partition::external_lock(THD *thd, int lock_type)
(void) (*file)->ha_external_lock(thd, lock_type);
} while (*(++file));
}
- if (lock_type == F_WRLCK && m_part_info->part_expr)
- m_part_info->part_expr->walk(&Item::register_field_in_read_map, 1, 0);
+ if (lock_type == F_WRLCK)
+ {
+ if (m_part_info->part_expr)
+ m_part_info->part_expr->walk(&Item::register_field_in_read_map, 1, 0);
+ if (m_part_info->part_type == VERSIONING_PARTITION &&
+ /* TODO: MDEV-20345 exclude more inapproriate commands like INSERT
+ These commands may be excluded because working history partition is needed
+ only for versioned DML. */
+ thd->lex->sql_command != SQLCOM_SELECT &&
+ thd->lex->sql_command != SQLCOM_INSERT_SELECT)
+ m_part_info->vers_set_hist_part(thd);
+ }
DBUG_RETURN(0);
err_handler:
@@ -3906,7 +4051,7 @@ THR_LOCK_DATA **ha_partition::store_lock(THD *thd,
i < m_tot_parts;
i= bitmap_get_next_set(used_partitions, i))
{
- DBUG_PRINT("info", ("store lock %d iteration", i));
+ DBUG_PRINT("info", ("store lock %u iteration", i));
to= m_file[i]->store_lock(thd, to, lock_type);
}
}
@@ -3950,13 +4095,29 @@ int ha_partition::start_stmt(THD *thd, thr_lock_type lock_type)
i < m_tot_parts;
i= bitmap_get_next_set(&m_part_info->lock_partitions, i))
{
- if ((error= m_file[i]->start_stmt(thd, lock_type)))
+ if (unlikely((error= m_file[i]->start_stmt(thd, lock_type))))
break;
/* Add partition to be called in reset(). */
bitmap_set_bit(&m_partitions_to_reset, i);
}
- if (lock_type == F_WRLCK && m_part_info->part_expr)
- m_part_info->part_expr->walk(&Item::register_field_in_read_map, 1, 0);
+ switch (lock_type)
+ {
+ case TL_WRITE_ALLOW_WRITE:
+ case TL_WRITE_CONCURRENT_INSERT:
+ case TL_WRITE_DELAYED:
+ case TL_WRITE_DEFAULT:
+ case TL_WRITE_LOW_PRIORITY:
+ case TL_WRITE:
+ case TL_WRITE_ONLY:
+ if (m_part_info->part_expr)
+ m_part_info->part_expr->walk(&Item::register_field_in_read_map, 1, 0);
+ if (m_part_info->part_type == VERSIONING_PARTITION &&
+ // TODO: MDEV-20345 (see above)
+ thd->lex->sql_command != SQLCOM_SELECT &&
+ thd->lex->sql_command != SQLCOM_INSERT_SELECT)
+ m_part_info->vers_set_hist_part(thd);
+ default:;
+ }
DBUG_RETURN(error);
}
@@ -3967,25 +4128,14 @@ int ha_partition::start_stmt(THD *thd, thr_lock_type lock_type)
@returns Number of locks returned in call to store_lock
@desc
- Returns the number of store locks needed in call to store lock.
- We return number of partitions we will lock multiplied with number of
- locks needed by each partition. Assists the above functions in allocating
- sufficient space for lock structures.
+ Returns the maxinum possible number of store locks needed in call to
+ store lock.
*/
uint ha_partition::lock_count() const
{
DBUG_ENTER("ha_partition::lock_count");
- /*
- The caller want to know the upper bound, to allocate enough memory.
- There is no performance lost if we simply return maximum number locks
- needed, only some minor over allocation of memory in get_lock_data().
-
- Also notice that this may be called for another thread != table->in_use,
- when mysql_lock_abort_for_thread() is called. So this is more safe, then
- using number of partitions after pruning.
- */
- DBUG_RETURN(m_tot_parts * m_num_locks);
+ DBUG_RETURN(m_num_locks);
}
@@ -4064,7 +4214,7 @@ void ha_partition::try_semi_consistent_read(bool yes)
{
uint i;
DBUG_ENTER("ha_partition::try_semi_consistent_read");
-
+
i= bitmap_get_first_set(&(m_part_info->read_partitions));
DBUG_ASSERT(i != MY_BIT_NONE);
for (;
@@ -4129,7 +4279,7 @@ int ha_partition::write_row(uchar * buf)
sql_mode_t saved_sql_mode= thd->variables.sql_mode;
bool saved_auto_inc_field_not_null= table->auto_increment_field_not_null;
DBUG_ENTER("ha_partition::write_row");
- DBUG_ASSERT(buf == m_rec0);
+ DBUG_PRINT("enter", ("partition this: %p", this));
/*
If we have an auto_increment column and we are writing a changed row
@@ -4137,15 +4287,8 @@ int ha_partition::write_row(uchar * buf)
*/
if (have_auto_increment)
{
- if (!part_share->auto_inc_initialized &&
- !table_share->next_number_keypart)
- {
- /*
- If auto_increment in table_share is not initialized, start by
- initializing it.
- */
- info(HA_STATUS_AUTO);
- }
+ if (!table_share->next_number_keypart)
+ update_next_auto_inc_val();
error= update_auto_increment();
/*
@@ -4153,7 +4296,7 @@ int ha_partition::write_row(uchar * buf)
it is highly likely that we will not be able to insert it into
the correct partition. We must check and fail if necessary.
*/
- if (error)
+ if (unlikely(error))
goto exit;
/*
@@ -4189,7 +4332,7 @@ int ha_partition::write_row(uchar * buf)
goto exit;
}
m_last_part= part_id;
- DBUG_PRINT("info", ("Insert in partition %d", 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. */
@@ -4197,6 +4340,7 @@ int ha_partition::write_row(uchar * 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;
@@ -4228,32 +4372,18 @@ exit:
old_data is always record[1]
*/
-int ha_partition::update_row(const uchar *old_data, uchar *new_data)
+int ha_partition::update_row(const uchar *old_data, const uchar *new_data)
{
THD *thd= ha_thd();
- uint32 new_part_id, old_part_id;
+ uint32 new_part_id, old_part_id= m_last_part;
int error= 0;
- longlong func_value;
DBUG_ENTER("ha_partition::update_row");
m_err_rec= NULL;
// Need to read partition-related columns, to locate the row's partition:
DBUG_ASSERT(bitmap_is_subset(&m_part_info->full_part_field_set,
table->read_set));
- if ((error= get_parts_for_update(old_data, new_data, table->record[0],
- m_part_info, &old_part_id, &new_part_id,
- &func_value)))
- {
- m_part_info->err_value= func_value;
- goto exit;
- }
- DBUG_ASSERT(bitmap_is_set(&(m_part_info->read_partitions), old_part_id));
- if (!bitmap_is_set(&(m_part_info->lock_partitions), new_part_id))
- {
- error= HA_ERR_NOT_IN_LOCK_PARTITIONS;
- goto exit;
- }
-
+#ifndef DBUG_OFF
/*
The protocol for updating a row is:
1) position the handler (cursor) on the row to be updated,
@@ -4271,17 +4401,27 @@ int ha_partition::update_row(const uchar *old_data, uchar *new_data)
Notice that HA_READ_BEFORE_WRITE_REMOVAL does not require this protocol,
so this is not supported for this engine.
*/
- if (old_part_id != m_last_part)
+ error= get_part_for_buf(old_data, m_rec0, m_part_info, &old_part_id);
+ DBUG_ASSERT(!error);
+ DBUG_ASSERT(old_part_id == m_last_part);
+ DBUG_ASSERT(bitmap_is_set(&(m_part_info->read_partitions), old_part_id));
+#endif
+
+ if (unlikely((error= get_part_for_buf(new_data, m_rec0, m_part_info,
+ &new_part_id))))
+ goto exit;
+ if (unlikely(!bitmap_is_set(&(m_part_info->lock_partitions), new_part_id)))
{
- m_err_rec= old_data;
- DBUG_RETURN(HA_ERR_ROW_IN_WRONG_PARTITION);
+ error= HA_ERR_NOT_IN_LOCK_PARTITIONS;
+ goto exit;
}
+
m_last_part= new_part_id;
start_part_bulk_insert(thd, new_part_id);
if (new_part_id == old_part_id)
{
- DBUG_PRINT("info", ("Update in partition %d", new_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);
@@ -4301,25 +4441,20 @@ int ha_partition::update_row(const uchar *old_data, uchar *new_data)
This gives the same behavior for partitioned vs non partitioned tables.
*/
table->next_number_field= NULL;
- DBUG_PRINT("info", ("Update from partition %d to partition %d",
- old_part_id, new_part_id));
+ 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(new_data);
+ error= m_file[new_part_id]->ha_write_row((uchar*) new_data);
reenable_binlog(thd);
table->next_number_field= saved_next_number_field;
- if (error)
+ 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 (error)
- {
-#ifdef IN_THE_FUTURE
- (void) m_file[new_part_id]->delete_last_inserted_row(new_data);
-#endif
+ if (unlikely(error))
goto exit;
- }
}
exit:
@@ -4338,9 +4473,9 @@ exit:
bitmap_is_set(table->write_set,
table->found_next_number_field->field_index))
{
- if (!part_share->auto_inc_initialized)
- info(HA_STATUS_AUTO);
- set_auto_increment_if_higher(table->found_next_number_field);
+ update_next_auto_inc_val();
+ if (part_share->auto_inc_initialized)
+ set_auto_increment_if_higher(table->found_next_number_field);
}
DBUG_RETURN(error);
}
@@ -4376,7 +4511,6 @@ exit:
int ha_partition::delete_row(const uchar *buf)
{
- uint32 part_id;
int error;
THD *thd= ha_thd();
DBUG_ENTER("ha_partition::delete_row");
@@ -4384,16 +4518,7 @@ int ha_partition::delete_row(const uchar *buf)
DBUG_ASSERT(bitmap_is_subset(&m_part_info->full_part_field_set,
table->read_set));
- if ((error= get_part_for_delete(buf, m_rec0, m_part_info, &part_id)))
- {
- DBUG_RETURN(error);
- }
- /* Should never call delete_row on a partition which is not read */
- DBUG_ASSERT(bitmap_is_set(&(m_part_info->read_partitions), part_id));
- DBUG_ASSERT(bitmap_is_set(&(m_part_info->lock_partitions), part_id));
- if (!bitmap_is_set(&(m_part_info->lock_partitions), part_id))
- DBUG_RETURN(HA_ERR_NOT_IN_LOCK_PARTITIONS);
-
+#ifndef DBUG_OFF
/*
The protocol for deleting a row is:
1) position the handler (cursor) on the row to be deleted,
@@ -4411,18 +4536,26 @@ int ha_partition::delete_row(const uchar *buf)
Notice that HA_READ_BEFORE_WRITE_REMOVAL does not require this protocol,
so this is not supported for this engine.
- TODO: change the assert in InnoDB into an error instead and make this one
- an assert instead and remove the get_part_for_delete()!
+ For partitions by system_time, get_part_for_buf() is always either current
+ or last historical partition, but DELETE HISTORY can delete from any
+ historical partition. So, skip the check in this case.
*/
- if (part_id != m_last_part)
+ if (!thd->lex->vers_conditions.delete_history)
{
- m_err_rec= buf;
- DBUG_RETURN(HA_ERR_ROW_IN_WRONG_PARTITION);
+ uint32 part_id;
+ error= get_part_for_buf(buf, m_rec0, m_part_info, &part_id);
+ DBUG_ASSERT(!error);
+ DBUG_ASSERT(part_id == m_last_part);
}
+ DBUG_ASSERT(bitmap_is_set(&(m_part_info->read_partitions), m_last_part));
+ DBUG_ASSERT(bitmap_is_set(&(m_part_info->lock_partitions), m_last_part));
+#endif
+
+ if (!bitmap_is_set(&(m_part_info->lock_partitions), m_last_part))
+ DBUG_RETURN(HA_ERR_NOT_IN_LOCK_PARTITIONS);
- m_last_part= part_id;
tmp_disable_binlog(thd);
- error= m_file[part_id]->ha_delete_row(buf);
+ error= m_file[m_last_part]->ha_delete_row(buf);
reenable_binlog(thd);
DBUG_RETURN(error);
}
@@ -4461,7 +4594,7 @@ int ha_partition::delete_all_rows()
i= bitmap_get_next_set(&m_part_info->read_partitions, i))
{
/* Can be pruned, like DELETE FROM t PARTITION (pX) */
- if ((error= m_file[i]->ha_delete_all_rows()))
+ if (unlikely((error= m_file[i]->ha_delete_all_rows())))
DBUG_RETURN(error);
}
DBUG_RETURN(0);
@@ -4493,7 +4626,7 @@ int ha_partition::truncate()
file= m_file;
do
{
- if ((error= (*file)->ha_truncate()))
+ if (unlikely((error= (*file)->ha_truncate())))
DBUG_RETURN(error);
} while (*(++file));
DBUG_RETURN(0);
@@ -4551,7 +4684,7 @@ int ha_partition::truncate_partition(Alter_info *alter_info, bool *binlog_stmt)
part= i * num_subparts + j;
DBUG_PRINT("info", ("truncate subpartition %u (%s)",
part, sub_elem->partition_name));
- if ((error= m_file[part]->ha_truncate()))
+ if (unlikely((error= m_file[part]->ha_truncate())))
break;
sub_elem->part_state= PART_NORMAL;
} while (++j < num_subparts);
@@ -4626,9 +4759,9 @@ void ha_partition::start_part_bulk_insert(THD *thd, uint part_id)
DESCRIPTION
If the estimated number of rows to insert is less than 10 (but not 0)
the new buffer size is same as original buffer size.
- In case of first partition of when partition function is monotonic
+ In case of first partition of when partition function is monotonic
new buffer size is same as the original buffer size.
- For rest of the partition total buffer of 10*original_size is divided
+ For rest of the partition total buffer of 10*original_size is divided
equally if number of partition is more than 10 other wise each partition
will be allowed to use original buffer size.
*/
@@ -4666,7 +4799,7 @@ long ha_partition::estimate_read_buffer_size(long original_size)
If monotonic partitioning function was used
guess that 50 % of the inserts goes to the first partition
For all other cases, guess on equal distribution between the partitions
-*/
+*/
ha_rows ha_partition::guess_bulk_insert_rows()
{
DBUG_ENTER("guess_bulk_insert_rows");
@@ -4675,7 +4808,7 @@ ha_rows ha_partition::guess_bulk_insert_rows()
DBUG_RETURN(estimation_rows_to_insert);
/* If first insert/partition and monotonic partition function, guess 50%. */
- if (!m_bulk_inserted_rows &&
+ if (!m_bulk_inserted_rows &&
m_part_func_monotonicity_info != NON_MONOTONIC &&
m_tot_parts > 1)
DBUG_RETURN(estimation_rows_to_insert / 2);
@@ -4741,7 +4874,7 @@ int ha_partition::end_bulk_insert()
>0 Error code
0 Success
- DESCRIPTION
+ DESCRIPTION
rnd_init() is called when the server wants the storage engine to do a
table scan or when the server wants to access data through rnd_pos.
@@ -4777,7 +4910,10 @@ int ha_partition::rnd_init(bool scan)
*/
if (bitmap_is_overlapping(&m_part_info->full_part_field_set,
table->write_set))
+ {
+ DBUG_PRINT("info", ("partition set full bitmap"));
bitmap_set_all(table->read_set);
+ }
else
{
/*
@@ -4786,6 +4922,7 @@ int ha_partition::rnd_init(bool scan)
fields of the partition functions are read such that we can
calculate the partition id to place updated and deleted records.
*/
+ DBUG_PRINT("info", ("partition set part_field bitmap"));
bitmap_union(table->read_set, &m_part_info->full_part_field_set);
}
}
@@ -4794,9 +4931,9 @@ int ha_partition::rnd_init(bool scan)
DBUG_PRINT("info", ("m_part_info->read_partitions: %p",
m_part_info->read_partitions.bitmap));
part_id= bitmap_get_first_set(&(m_part_info->read_partitions));
- DBUG_PRINT("info", ("m_part_spec.start_part %d", part_id));
+ DBUG_PRINT("info", ("m_part_spec.start_part: %u", (uint) part_id));
- if (MY_BIT_NONE == part_id)
+ if (part_id == MY_BIT_NONE)
{
error= 0;
goto err1;
@@ -4806,7 +4943,7 @@ int ha_partition::rnd_init(bool scan)
We have a partition and we are scanning with rnd_next
so we bump our cache
*/
- DBUG_PRINT("info", ("rnd_init on partition %d", part_id));
+ DBUG_PRINT("info", ("rnd_init on partition: %u", (uint) part_id));
if (scan)
{
/*
@@ -4815,26 +4952,29 @@ int ha_partition::rnd_init(bool scan)
*/
rnd_end();
late_extra_cache(part_id);
- if ((error= m_file[part_id]->ha_rnd_init(scan)))
- goto err;
+
+ m_index_scan_type= partition_no_index_scan;
}
- else
+
+ for (i= part_id;
+ i < m_tot_parts;
+ i= bitmap_get_next_set(&m_part_info->read_partitions, i))
{
- for (i= part_id;
- i < m_tot_parts;
- i= bitmap_get_next_set(&m_part_info->read_partitions, i))
- {
- if ((error= m_file[i]->ha_rnd_init(scan)))
- goto err;
- }
+ if (unlikely((error= m_file[i]->ha_rnd_init(scan))))
+ goto err;
}
+
m_scan_value= scan;
m_part_spec.start_part= part_id;
m_part_spec.end_part= m_tot_parts - 1;
- DBUG_PRINT("info", ("m_scan_value=%d", m_scan_value));
+ m_rnd_init_and_first= TRUE;
+ DBUG_PRINT("info", ("m_scan_value: %u", m_scan_value));
DBUG_RETURN(0);
err:
+ if (scan)
+ late_extra_no_cache(part_id);
+
/* Call rnd_end for all previously inited partitions. */
for (;
part_id < i;
@@ -4866,13 +5006,10 @@ int ha_partition::rnd_end()
switch (m_scan_value) {
case 2: // Error
break;
- case 1:
- if (NO_CURRENT_PART_ID != m_part_spec.start_part) // Table scan
- {
+ case 1: // Table scan
+ if (m_part_spec.start_part != NO_CURRENT_PART_ID)
late_extra_no_cache(m_part_spec.start_part);
- m_file[m_part_spec.start_part]->ha_rnd_end();
- }
- break;
+ /* fall through */
case 0:
uint i;
for (i= bitmap_get_first_set(&m_part_info->read_partitions);
@@ -4888,6 +5025,7 @@ int ha_partition::rnd_end()
DBUG_RETURN(0);
}
+
/*
read next row during full table scan (scan in random row order)
@@ -4912,14 +5050,15 @@ int ha_partition::rnd_end()
int ha_partition::rnd_next(uchar *buf)
{
handler *file;
- int result= HA_ERR_END_OF_FILE;
+ int result= HA_ERR_END_OF_FILE, error;
uint part_id= m_part_spec.start_part;
DBUG_ENTER("ha_partition::rnd_next");
+ DBUG_PRINT("enter", ("partition this: %p", this));
/* upper level will increment this once again at end of call */
decrement_statistics(&SSV::ha_read_rnd_next_count);
- if (NO_CURRENT_PART_ID == part_id)
+ if (part_id == NO_CURRENT_PART_ID)
{
/*
The original set of partitions to scan was empty and thus we report
@@ -4927,16 +5066,26 @@ int ha_partition::rnd_next(uchar *buf)
*/
goto end;
}
-
+
DBUG_ASSERT(m_scan_value == 1);
+
+ if (m_rnd_init_and_first)
+ {
+ m_rnd_init_and_first= FALSE;
+ error= handle_pre_scan(FALSE, check_parallel_search());
+ if (m_pre_calling || error)
+ DBUG_RETURN(error);
+ }
+
file= m_file[part_id];
-
+
while (TRUE)
{
result= file->ha_rnd_next(buf);
if (!result)
{
m_last_part= part_id;
+ DBUG_PRINT("info", ("partition m_last_part: %u", (uint) m_last_part));
m_part_spec.start_part= part_id;
table->status= 0;
DBUG_RETURN(0);
@@ -4945,18 +5094,11 @@ int ha_partition::rnd_next(uchar *buf)
/*
if we get here, then the current partition ha_rnd_next returned failure
*/
- if (result == HA_ERR_RECORD_DELETED)
- continue; // Probably MyISAM
-
if (result != HA_ERR_END_OF_FILE)
goto end_dont_reset_start_part; // Return error
/* End current partition */
late_extra_no_cache(part_id);
- DBUG_PRINT("info", ("rnd_end on partition %d", part_id));
- if ((result= file->ha_rnd_end()))
- break;
-
/* Shift to next partition */
part_id= bitmap_get_next_set(&m_part_info->read_partitions, part_id);
if (part_id >= m_tot_parts)
@@ -4965,15 +5107,14 @@ int ha_partition::rnd_next(uchar *buf)
break;
}
m_last_part= part_id;
+ DBUG_PRINT("info", ("partition m_last_part: %u", (uint) m_last_part));
m_part_spec.start_part= part_id;
file= m_file[part_id];
- DBUG_PRINT("info", ("rnd_init on partition %d", part_id));
- if ((result= file->ha_rnd_init(1)))
- break;
late_extra_cache(part_id);
}
end:
+ DBUG_PRINT("exit", ("reset start_part"));
m_part_spec.start_part= NO_CURRENT_PART_ID;
end_dont_reset_start_part:
DBUG_RETURN(result);
@@ -5008,7 +5149,7 @@ end_dont_reset_start_part:
void ha_partition::position(const uchar *record)
{
handler *file= m_file[m_last_part];
- uint pad_length;
+ size_t pad_length;
DBUG_ASSERT(bitmap_is_set(&(m_part_info->read_partitions), m_last_part));
DBUG_ENTER("ha_partition::position");
@@ -5083,7 +5224,7 @@ int ha_partition::rnd_pos_by_record(uchar *record)
{
DBUG_ENTER("ha_partition::rnd_pos_by_record");
- if (unlikely(get_part_for_delete(record, m_rec0, m_part_info, &m_last_part)))
+ if (unlikely(get_part_for_buf(record, m_rec0, m_part_info, &m_last_part)))
DBUG_RETURN(1);
int err= m_file[m_last_part]->rnd_pos_by_record(record);
@@ -5124,12 +5265,16 @@ bool ha_partition::init_record_priority_queue()
*/
if (!m_ordered_rec_buffer)
{
- uint alloc_len;
+ size_t alloc_len;
uint used_parts= bitmap_bits_set(&m_part_info->read_partitions);
+
+ if (used_parts == 0) /* Do nothing since no records expected. */
+ DBUG_RETURN(false);
+
/* Allocate record buffer for each used partition. */
m_priority_queue_rec_len= m_rec_length + PARTITION_BYTES_IN_POS;
if (!m_using_extended_keys)
- m_priority_queue_rec_len += m_file[0]->ref_length;
+ m_priority_queue_rec_len += get_open_file_sample()->ref_length;
alloc_len= used_parts * m_priority_queue_rec_len;
/* Allocate a key for temporary use when setting up the scan. */
alloc_len+= table_share->max_key_length;
@@ -5155,20 +5300,15 @@ bool ha_partition::init_record_priority_queue()
ptr+= m_priority_queue_rec_len;
}
m_start_key.key= (const uchar*)ptr;
-
+
/* Initialize priority queue, initialized to reading forward. */
int (*cmp_func)(void *, uchar *, uchar *);
- void *cmp_arg;
- if (!m_using_extended_keys)
- {
+ void *cmp_arg= (void*) this;
+ if (!m_using_extended_keys && !(table_flags() & HA_CMP_REF_IS_EXPENSIVE))
cmp_func= cmp_key_rowid_part_id;
- cmp_arg= (void*)this;
- }
else
- {
cmp_func= cmp_key_part_id;
- cmp_arg= (void*)m_curr_key_info;
- }
+ DBUG_PRINT("info", ("partition queue_init(1) used_parts: %u", used_parts));
if (init_queue(&m_queue, used_parts, 0, 0, cmp_func, cmp_arg, 0, 0))
{
my_free(m_ordered_rec_buffer);
@@ -5219,8 +5359,8 @@ int ha_partition::index_init(uint inx, bool sorted)
int error= 0;
uint i;
DBUG_ENTER("ha_partition::index_init");
+ DBUG_PRINT("enter", ("partition this: %p inx: %u sorted: %u", this, inx, sorted));
- DBUG_PRINT("info", ("inx %u sorted %u", inx, sorted));
active_index= inx;
m_part_spec.start_part= NO_CURRENT_PART_ID;
m_start_key.length= 0;
@@ -5255,11 +5395,14 @@ int ha_partition::index_init(uint inx, bool sorted)
But this is required for operations that may need to change data only.
*/
if (get_lock_type() == F_WRLCK)
+ {
+ DBUG_PRINT("info", ("partition set part_field bitmap"));
bitmap_union(table->read_set, &m_part_info->full_part_field_set);
+ }
if (sorted)
{
/*
- An ordered scan is requested. We must make sure all fields of the
+ An ordered scan is requested. We must make sure all fields of the
used index are in the read set, as partitioning requires them for
sorting (see ha_partition::handle_ordered_index_scan).
@@ -5282,7 +5425,7 @@ int ha_partition::index_init(uint inx, bool sorted)
i < m_tot_parts;
i= bitmap_get_next_set(&m_part_info->read_partitions, i))
{
- if ((error= m_file[i]->ha_index_init(inx, sorted)))
+ if (unlikely((error= m_file[i]->ha_index_init(inx, sorted))))
goto err;
DBUG_EXECUTE_IF("ha_partition_fail_index_init", {
@@ -5292,7 +5435,7 @@ int ha_partition::index_init(uint inx, bool sorted)
});
}
err:
- if (error)
+ if (unlikely(error))
{
/* End the previously initialized indexes. */
uint j;
@@ -5326,19 +5469,28 @@ err:
int ha_partition::index_end()
{
int error= 0;
- uint i;
+ handler **file;
DBUG_ENTER("ha_partition::index_end");
active_index= MAX_KEY;
m_part_spec.start_part= NO_CURRENT_PART_ID;
- for (i= bitmap_get_first_set(&m_part_info->read_partitions);
- i < m_tot_parts;
- i= bitmap_get_next_set(&m_part_info->read_partitions, i))
+ file= m_file;
+ do
{
- int tmp;
- if ((tmp= m_file[i]->ha_index_end()))
- error= tmp;
- }
+ if ((*file)->inited == INDEX)
+ {
+ int tmp;
+ if ((tmp= (*file)->ha_index_end()))
+ error= tmp;
+ }
+ else if ((*file)->inited == RND)
+ {
+ // Possible due to MRR
+ int tmp;
+ if ((tmp= (*file)->ha_rnd_end()))
+ error= tmp;
+ }
+ } while (*(++file));
destroy_record_priority_queue();
DBUG_RETURN(error);
}
@@ -5387,34 +5539,26 @@ int ha_partition::index_read_map(uchar *buf, const uchar *key,
/* Compare two part_no partition numbers */
static int cmp_part_ids(uchar *ref1, uchar *ref2)
{
- /* The following was taken from ha_partition::cmp_ref */
- my_ptrdiff_t diff1= ref2[1] - ref1[1];
- my_ptrdiff_t diff2= ref2[0] - ref1[0];
- if (!diff1 && !diff2)
- return 0;
-
- if (diff1 > 0)
- return(-1);
-
- if (diff1 < 0)
- return(+1);
-
- if (diff2 > 0)
- return(-1);
-
- return(+1);
+ uint32 diff2= uint2korr(ref2);
+ uint32 diff1= uint2korr(ref1);
+ if (diff2 > diff1)
+ return -1;
+ if (diff2 < diff1)
+ return 1;
+ return 0;
}
/*
@brief
- Provide ordering by (key_value, part_no).
+ Provide ordering by (key_value, part_no).
*/
-extern "C" int cmp_key_part_id(void *key_p, uchar *ref1, uchar *ref2)
+extern "C" int cmp_key_part_id(void *ptr, uchar *ref1, uchar *ref2)
{
+ ha_partition *file= (ha_partition*)ptr;
int res;
- if ((res= key_rec_cmp(key_p, ref1 + PARTITION_BYTES_IN_POS,
+ if ((res= key_rec_cmp(file->m_curr_key_info, ref1 + PARTITION_BYTES_IN_POS,
ref2 + PARTITION_BYTES_IN_POS)))
{
return res;
@@ -5424,7 +5568,7 @@ extern "C" int cmp_key_part_id(void *key_p, uchar *ref1, uchar *ref2)
/*
@brief
- Provide ordering by (key_value, underying_table_rowid, part_no).
+ Provide ordering by (key_value, underying_table_rowid, part_no).
*/
extern "C" int cmp_key_rowid_part_id(void *ptr, uchar *ref1, uchar *ref2)
{
@@ -5436,8 +5580,9 @@ extern "C" int cmp_key_rowid_part_id(void *ptr, uchar *ref1, uchar *ref2)
{
return res;
}
- if ((res= file->m_file[0]->cmp_ref(ref1 + PARTITION_BYTES_IN_POS + file->m_rec_length,
- ref2 + PARTITION_BYTES_IN_POS + file->m_rec_length)))
+ if ((res= file->get_open_file_sample()->cmp_ref(ref1 +
+ PARTITION_BYTES_IN_POS + file->m_rec_length,
+ ref2 + PARTITION_BYTES_IN_POS + file->m_rec_length)))
{
return res;
}
@@ -5449,26 +5594,26 @@ extern "C" int cmp_key_rowid_part_id(void *ptr, uchar *ref1, uchar *ref2)
Common routine for a number of index_read variants
@param buf Buffer where the record should be returned.
- @param have_start_key TRUE <=> the left endpoint is available, i.e.
+ @param have_start_key TRUE <=> the left endpoint is available, i.e.
we're in index_read call or in read_range_first
call and the range has left endpoint.
FALSE <=> there is no left endpoint (we're in
read_range_first() call and the range has no left
endpoint).
-
+
@return Operation status
- @retval 0 OK
+ @retval 0 OK
@retval HA_ERR_END_OF_FILE Whole index scanned, without finding the record.
@retval HA_ERR_KEY_NOT_FOUND Record not found, but index cursor positioned.
@retval other error code.
@details
- Start scanning the range (when invoked from read_range_first()) or doing
+ Start scanning the range (when invoked from read_range_first()) or doing
an index lookup (when invoked from index_read_XXX):
- If possible, perform partition selection
- Find the set of partitions we're going to use
- Depending on whether we need ordering:
- NO: Get the first record from first used partition (see
+ NO: Get the first record from first used partition (see
handle_unordered_scan_next_partition)
YES: Fill the priority queue and get the record that is the first in
the ordering
@@ -5486,19 +5631,19 @@ int ha_partition::common_index_read(uchar *buf, bool have_start_key)
if (have_start_key)
{
- m_start_key.length= key_len= calculate_key_len(table, active_index,
+ m_start_key.length= key_len= calculate_key_len(table, active_index,
m_start_key.key,
m_start_key.keypart_map);
DBUG_PRINT("info", ("have_start_key map %lu find_flag %u len %u",
m_start_key.keypart_map, m_start_key.flag, key_len));
DBUG_ASSERT(key_len);
}
- if ((error= partition_scan_set_up(buf, have_start_key)))
+ if (unlikely((error= partition_scan_set_up(buf, have_start_key))))
{
DBUG_RETURN(error);
}
- if (have_start_key &&
+ if (have_start_key &&
(m_start_key.flag == HA_READ_PREFIX_LAST ||
m_start_key.flag == HA_READ_PREFIX_LAST_OR_PREV ||
m_start_key.flag == HA_READ_BEFORE_KEY))
@@ -5518,7 +5663,9 @@ int ha_partition::common_index_read(uchar *buf, bool have_start_key)
The unordered index scan will use the partition set created.
*/
DBUG_PRINT("info", ("doing unordered scan"));
- error= handle_unordered_scan_next_partition(buf);
+ error= handle_pre_scan(FALSE, FALSE);
+ if (likely(!error))
+ error= handle_unordered_scan_next_partition(buf);
}
else
{
@@ -5566,7 +5713,7 @@ int ha_partition::index_first(uchar * buf)
/*
Start an index scan from rightmost record and return first record
-
+
SYNOPSIS
index_last()
buf Read row in MySQL Row Format
@@ -5599,7 +5746,7 @@ int ha_partition::index_last(uchar * buf)
SYNOPSIS
ha_partition::common_first_last()
-
+
see index_first for rest
*/
@@ -5607,11 +5754,15 @@ int ha_partition::common_first_last(uchar *buf)
{
int error;
- if ((error= partition_scan_set_up(buf, FALSE)))
+ if (unlikely((error= partition_scan_set_up(buf, FALSE))))
return error;
if (!m_ordered_scan_ongoing &&
m_index_scan_type != partition_index_last)
- return handle_unordered_scan_next_partition(buf);
+ {
+ if (unlikely((error= handle_pre_scan(FALSE, check_parallel_search()))))
+ return error;
+ return handle_unordered_scan_next_partition(buf);
+ }
return handle_ordered_index_scan(buf, FALSE);
}
@@ -5639,7 +5790,7 @@ int ha_partition::index_read_idx_map(uchar *buf, uint index,
get_partition_set(table, buf, index, &m_start_key, &m_part_spec);
- /*
+ /*
We have either found exactly 1 partition
(in which case start_part == end_part)
or no matching partitions (start_part > end_part)
@@ -5656,8 +5807,8 @@ int ha_partition::index_read_idx_map(uchar *buf, uint index,
{
error= m_file[part]->ha_index_read_idx_map(buf, index, key,
keypart_map, find_flag);
- if (error != HA_ERR_KEY_NOT_FOUND &&
- error != HA_ERR_END_OF_FILE)
+ if (likely(error != HA_ERR_KEY_NOT_FOUND &&
+ error != HA_ERR_END_OF_FILE))
break;
}
if (part <= m_part_spec.end_part)
@@ -5704,7 +5855,8 @@ int ha_partition::index_next(uchar * buf)
and if direction changes, we must step back those partitions in
the record queue so we don't return a value from the wrong direction.
*/
- DBUG_ASSERT(m_index_scan_type != partition_index_last);
+ if (m_index_scan_type == partition_index_last)
+ DBUG_RETURN(HA_ERR_WRONG_COMMAND);
if (!m_ordered_scan_ongoing)
{
DBUG_RETURN(handle_unordered_next(buf, FALSE));
@@ -5737,13 +5889,30 @@ int ha_partition::index_next_same(uchar *buf, const uchar *key, uint keylen)
decrement_statistics(&SSV::ha_read_next_count);
DBUG_ASSERT(keylen == m_start_key.length);
- DBUG_ASSERT(m_index_scan_type != partition_index_last);
+ if (m_index_scan_type == partition_index_last)
+ DBUG_RETURN(HA_ERR_WRONG_COMMAND);
if (!m_ordered_scan_ongoing)
DBUG_RETURN(handle_unordered_next(buf, TRUE));
DBUG_RETURN(handle_ordered_next(buf, TRUE));
}
+int ha_partition::index_read_last_map(uchar *buf,
+ const uchar *key,
+ key_part_map keypart_map)
+{
+ DBUG_ENTER("ha_partition::index_read_last_map");
+
+ m_ordered= true; // Safety measure
+ end_range= NULL;
+ m_index_scan_type= partition_index_read_last;
+ m_start_key.key= key;
+ m_start_key.keypart_map= keypart_map;
+ m_start_key.flag= HA_READ_PREFIX_LAST;
+ DBUG_RETURN(common_index_read(buf, true));
+}
+
+
/*
Read next record when performing index scan backwards
@@ -5765,7 +5934,8 @@ int ha_partition::index_prev(uchar * buf)
decrement_statistics(&SSV::ha_read_prev_count);
/* TODO: read comment in index_next */
- DBUG_ASSERT(m_index_scan_type != partition_index_first);
+ if (m_index_scan_type == partition_index_first)
+ DBUG_RETURN(HA_ERR_WRONG_COMMAND);
DBUG_RETURN(handle_ordered_prev(buf));
}
@@ -5836,6 +6006,994 @@ int ha_partition::read_range_next()
DBUG_RETURN(handle_unordered_next(table->record[0], eq_range));
}
+/**
+ Create a copy of all keys used by multi_range_read()
+
+ @retval 0 ok
+ @retval HA_ERR_END_OF_FILE no keys in range
+ @retval other value: error
+
+ TODO to save memory:
+ - If (mrr_mode & HA_MRR_MATERIALIZED_KEYS) is set then the keys data is
+ stable and we don't have to copy the keys, only store a pointer to the
+ key.
+ - When allocating key data, store things in a MEM_ROOT buffer instead of
+ a malloc() per key. This will simplify and speed up the current code
+ and use less memory.
+*/
+
+int ha_partition::multi_range_key_create_key(RANGE_SEQ_IF *seq,
+ range_seq_t seq_it)
+{
+ uint i, length;
+ key_range *start_key, *end_key;
+ KEY_MULTI_RANGE *range;
+ DBUG_ENTER("ha_partition::multi_range_key_create_key");
+
+ bitmap_clear_all(&m_mrr_used_partitions);
+ m_mrr_range_length= 0;
+ bzero(m_part_mrr_range_length,
+ sizeof(*m_part_mrr_range_length) * m_tot_parts);
+ 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),
+ NullS)))
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+
+ m_mrr_range_first->id= 1;
+ m_mrr_range_first->key[0]= NULL;
+ m_mrr_range_first->key[1]= NULL;
+ m_mrr_range_first->next= NULL;
+ }
+ else
+ m_mrr_range_current= m_mrr_range_first;
+
+ for (i= 0; i < m_tot_parts; i++)
+ {
+ 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),
+ NullS)))
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+ }
+ else
+ {
+ m_part_mrr_range_current[i]= m_part_mrr_range_first[i];
+ m_part_mrr_range_current[i]->partition_key_multi_range= NULL;
+ }
+ }
+ m_mrr_range_current->key_multi_range.start_key.key= NULL;
+ m_mrr_range_current->key_multi_range.end_key.key= NULL;
+
+ while (!seq->next(seq_it, &m_mrr_range_current->key_multi_range))
+ {
+ m_mrr_range_length++;
+ range= &m_mrr_range_current->key_multi_range;
+
+ /* Copy start key */
+ start_key= &range->start_key;
+ DBUG_PRINT("info",("partition range->range_flag: %u", range->range_flag));
+ DBUG_PRINT("info",("partition start_key->key: %p", start_key->key));
+ DBUG_PRINT("info",("partition start_key->length: %u", start_key->length));
+ DBUG_PRINT("info",("partition start_key->keypart_map: %lu",
+ start_key->keypart_map));
+ DBUG_PRINT("info",("partition start_key->flag: %u", start_key->flag));
+
+ if (start_key->key)
+ {
+ length= start_key->length;
+ if (!m_mrr_range_current->key[0] ||
+ m_mrr_range_current->length[0] < length)
+ {
+ 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))))
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+ m_mrr_range_current->length[0]= length;
+ }
+ memcpy(m_mrr_range_current->key[0], start_key->key, length);
+ start_key->key= m_mrr_range_current->key[0];
+ }
+
+ /* Copy end key */
+ end_key= &range->end_key;
+ DBUG_PRINT("info",("partition end_key->key: %p", end_key->key));
+ DBUG_PRINT("info",("partition end_key->length: %u", end_key->length));
+ DBUG_PRINT("info",("partition end_key->keypart_map: %lu",
+ end_key->keypart_map));
+ DBUG_PRINT("info",("partition end_key->flag: %u", end_key->flag));
+ if (end_key->key)
+ {
+ length= end_key->length;
+ if (!m_mrr_range_current->key[1] ||
+ m_mrr_range_current->length[1] < length)
+ {
+ 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))))
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+ m_mrr_range_current->length[1]= length;
+ }
+ memcpy(m_mrr_range_current->key[1], end_key->key, length);
+ end_key->key= m_mrr_range_current->key[1];
+ }
+
+ m_mrr_range_current->ptr= m_mrr_range_current->key_multi_range.ptr;
+ m_mrr_range_current->key_multi_range.ptr= m_mrr_range_current;
+
+ if (start_key->key && (start_key->flag & HA_READ_KEY_EXACT))
+ get_partition_set(table, table->record[0], active_index,
+ start_key, &m_part_spec);
+ else
+ {
+ m_part_spec.start_part= 0;
+ m_part_spec.end_part= m_tot_parts - 1;
+ }
+
+ /* Copy key to those partitions that needs it */
+ for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++)
+ {
+ if (bitmap_is_set(&(m_part_info->read_partitions), i))
+ {
+ bitmap_set_bit(&m_mrr_used_partitions, i);
+ m_part_mrr_range_length[i]++;
+ m_part_mrr_range_current[i]->partition_key_multi_range=
+ m_mrr_range_current;
+
+ if (!m_part_mrr_range_current[i]->next)
+ {
+ 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),
+ MYF(MY_WME | MY_ZEROFILL))))
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+
+ m_part_mrr_range_current[i]->next= tmp_part_mrr_range;
+ m_part_mrr_range_current[i]= tmp_part_mrr_range;
+ }
+ else
+ {
+ m_part_mrr_range_current[i]= m_part_mrr_range_current[i]->next;
+ m_part_mrr_range_current[i]->partition_key_multi_range= NULL;
+ }
+ }
+ }
+
+ if (!m_mrr_range_current->next)
+ {
+ /* 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))))
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+
+ tmp_mrr_range->id= m_mrr_range_current->id + 1;
+ tmp_mrr_range->key[0]= NULL;
+ tmp_mrr_range->key[1]= NULL;
+ tmp_mrr_range->next= NULL;
+ m_mrr_range_current->next= tmp_mrr_range;
+ }
+ m_mrr_range_current= m_mrr_range_current->next;
+ }
+
+ if (!m_mrr_range_length)
+ {
+ DBUG_PRINT("Warning",("No keys to use for mrr"));
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+ }
+
+ /* set start and end part */
+ m_part_spec.start_part= bitmap_get_first_set(&m_mrr_used_partitions);
+
+ for (i= m_tot_parts; i-- > 0;)
+ {
+ if (bitmap_is_set(&m_mrr_used_partitions, i))
+ {
+ m_part_spec.end_part= i;
+ break;
+ }
+ }
+ for (i= 0; i < m_tot_parts; i++)
+ {
+ m_partition_part_key_multi_range_hld[i].partition= this;
+ m_partition_part_key_multi_range_hld[i].part_id= i;
+ m_partition_part_key_multi_range_hld[i].partition_part_key_multi_range=
+ m_part_mrr_range_first[i];
+ }
+ DBUG_PRINT("return",("OK"));
+ DBUG_RETURN(0);
+}
+
+
+static void partition_multi_range_key_get_key_info(void *init_params,
+ uint *length,
+ key_part_map *map)
+{
+ PARTITION_PART_KEY_MULTI_RANGE_HLD *hld=
+ (PARTITION_PART_KEY_MULTI_RANGE_HLD *)init_params;
+ ha_partition *partition= hld->partition;
+ key_range *start_key= (&partition->m_mrr_range_first->
+ key_multi_range.start_key);
+ DBUG_ENTER("partition_multi_range_key_get_key_info");
+ *length= start_key->length;
+ *map= start_key->keypart_map;
+ DBUG_VOID_RETURN;
+}
+
+
+static range_seq_t partition_multi_range_key_init(void *init_params,
+ uint n_ranges,
+ uint flags)
+{
+ PARTITION_PART_KEY_MULTI_RANGE_HLD *hld=
+ (PARTITION_PART_KEY_MULTI_RANGE_HLD *)init_params;
+ ha_partition *partition= hld->partition;
+ uint i= hld->part_id;
+ DBUG_ENTER("partition_multi_range_key_init");
+ // not used: partition->m_mrr_range_init_flags= flags;
+ hld->partition_part_key_multi_range= partition->m_part_mrr_range_first[i];
+ DBUG_RETURN(init_params);
+}
+
+
+static bool partition_multi_range_key_next(range_seq_t seq,
+ KEY_MULTI_RANGE *range)
+{
+ PARTITION_PART_KEY_MULTI_RANGE_HLD *hld=
+ (PARTITION_PART_KEY_MULTI_RANGE_HLD *)seq;
+ PARTITION_KEY_MULTI_RANGE *partition_key_multi_range=
+ hld->partition_part_key_multi_range->partition_key_multi_range;
+ DBUG_ENTER("partition_multi_range_key_next");
+ if (!partition_key_multi_range)
+ DBUG_RETURN(TRUE);
+ *range= partition_key_multi_range->key_multi_range;
+ hld->partition_part_key_multi_range=
+ hld->partition_part_key_multi_range->next;
+ DBUG_RETURN(FALSE);
+}
+
+
+static bool partition_multi_range_key_skip_record(range_seq_t seq,
+ range_id_t range_info,
+ uchar *rowid)
+{
+ PARTITION_PART_KEY_MULTI_RANGE_HLD *hld=
+ (PARTITION_PART_KEY_MULTI_RANGE_HLD *)seq;
+ PARTITION_KEY_MULTI_RANGE *pkmr= (PARTITION_KEY_MULTI_RANGE *)range_info;
+ DBUG_ENTER("partition_multi_range_key_skip_record");
+ DBUG_RETURN(hld->partition->m_seq_if->skip_record(hld->partition->m_seq,
+ pkmr->ptr, rowid));
+}
+
+
+static bool partition_multi_range_key_skip_index_tuple(range_seq_t seq,
+ range_id_t range_info)
+{
+ PARTITION_PART_KEY_MULTI_RANGE_HLD *hld=
+ (PARTITION_PART_KEY_MULTI_RANGE_HLD *)seq;
+ PARTITION_KEY_MULTI_RANGE *pkmr= (PARTITION_KEY_MULTI_RANGE *)range_info;
+ DBUG_ENTER("partition_multi_range_key_skip_index_tuple");
+ DBUG_RETURN(hld->partition->m_seq_if->skip_index_tuple(hld->partition->m_seq,
+ pkmr->ptr));
+}
+
+ha_rows ha_partition::multi_range_read_info_const(uint keyno,
+ RANGE_SEQ_IF *seq,
+ void *seq_init_param,
+ uint n_ranges, uint *bufsz,
+ uint *mrr_mode,
+ Cost_estimate *cost)
+{
+ int error;
+ uint i;
+ handler **file;
+ ha_rows rows= 0;
+ uint ret_mrr_mode= 0;
+ range_seq_t seq_it;
+ part_id_range save_part_spec;
+ DBUG_ENTER("ha_partition::multi_range_read_info_const");
+ DBUG_PRINT("enter", ("partition this: %p", this));
+
+ m_mrr_new_full_buffer_size= 0;
+ save_part_spec= m_part_spec;
+
+ seq_it= seq->init(seq_init_param, n_ranges, *mrr_mode);
+ if (unlikely((error= multi_range_key_create_key(seq, seq_it))))
+ {
+ if (likely(error == HA_ERR_END_OF_FILE)) // No keys in range
+ {
+ rows= 0;
+ goto calc_cost;
+ }
+ /*
+ This error means that we can't do multi_range_read for the moment
+ (probably running out of memory) and we need to fallback to
+ normal reads
+ */
+ m_part_spec= save_part_spec;
+ DBUG_RETURN(HA_POS_ERROR);
+ }
+ m_part_seq_if.get_key_info=
+ seq->get_key_info ? partition_multi_range_key_get_key_info : NULL;
+ m_part_seq_if.init= partition_multi_range_key_init;
+ m_part_seq_if.next= partition_multi_range_key_next;
+ m_part_seq_if.skip_record= (seq->skip_record ?
+ partition_multi_range_key_skip_record : NULL);
+ m_part_seq_if.skip_index_tuple= (seq->skip_index_tuple ?
+ partition_multi_range_key_skip_index_tuple :
+ NULL);
+ file= m_file;
+ do
+ {
+ i= (uint)(file - m_file);
+ DBUG_PRINT("info",("partition part_id: %u", i));
+ if (bitmap_is_set(&m_mrr_used_partitions, i))
+ {
+ ha_rows tmp_rows;
+ uint tmp_mrr_mode;
+ m_mrr_buffer_size[i]= 0;
+ tmp_mrr_mode= *mrr_mode;
+ tmp_rows= (*file)->
+ multi_range_read_info_const(keyno, &m_part_seq_if,
+ &m_partition_part_key_multi_range_hld[i],
+ m_part_mrr_range_length[i],
+ &m_mrr_buffer_size[i],
+ &tmp_mrr_mode, cost);
+ if (tmp_rows == HA_POS_ERROR)
+ {
+ m_part_spec= save_part_spec;
+ DBUG_RETURN(HA_POS_ERROR);
+ }
+ rows+= tmp_rows;
+ ret_mrr_mode|= tmp_mrr_mode;
+ m_mrr_new_full_buffer_size+= m_mrr_buffer_size[i];
+ }
+ } while (*(++file));
+ *mrr_mode= ret_mrr_mode;
+
+calc_cost:
+ m_part_spec= save_part_spec;
+ cost->reset();
+ cost->avg_io_cost= 1;
+ if ((*mrr_mode & HA_MRR_INDEX_ONLY) && rows > 2)
+ cost->io_count= keyread_time(keyno, n_ranges, (uint) rows);
+ else
+ cost->io_count= read_time(keyno, n_ranges, rows);
+ cost->cpu_cost= (double) rows / TIME_FOR_COMPARE + 0.01;
+ DBUG_RETURN(rows);
+}
+
+
+ha_rows ha_partition::multi_range_read_info(uint keyno, uint n_ranges,
+ uint keys,
+ uint key_parts, uint *bufsz,
+ uint *mrr_mode,
+ Cost_estimate *cost)
+{
+ uint i;
+ handler **file;
+ ha_rows rows;
+ DBUG_ENTER("ha_partition::multi_range_read_info");
+ DBUG_PRINT("enter", ("partition this: %p", this));
+
+ m_mrr_new_full_buffer_size= 0;
+ file= m_file;
+ do
+ {
+ i= (uint)(file - m_file);
+ if (bitmap_is_set(&(m_part_info->read_partitions), (i)))
+ {
+ m_mrr_buffer_size[i]= 0;
+ if ((rows= (*file)->multi_range_read_info(keyno, n_ranges, keys,
+ key_parts,
+ &m_mrr_buffer_size[i],
+ mrr_mode, cost)))
+ DBUG_RETURN(rows);
+ m_mrr_new_full_buffer_size+= m_mrr_buffer_size[i];
+ }
+ } while (*(++file));
+
+ cost->reset();
+ cost->avg_io_cost= 1;
+ if (*mrr_mode & HA_MRR_INDEX_ONLY)
+ cost->io_count= keyread_time(keyno, n_ranges, (uint) rows);
+ else
+ cost->io_count= read_time(keyno, n_ranges, rows);
+ DBUG_RETURN(0);
+}
+
+
+int ha_partition::multi_range_read_init(RANGE_SEQ_IF *seq,
+ void *seq_init_param,
+ uint n_ranges, uint mrr_mode,
+ HANDLER_BUFFER *buf)
+{
+ int error;
+ uint i;
+ handler **file;
+ uchar *tmp_buffer;
+ DBUG_ENTER("ha_partition::multi_range_read_init");
+ DBUG_PRINT("enter", ("partition this: %p", this));
+
+ m_seq_if= seq;
+ m_seq= seq->init(seq_init_param, n_ranges, mrr_mode);
+ if (unlikely((error= multi_range_key_create_key(seq, m_seq))))
+ DBUG_RETURN(0);
+
+ m_part_seq_if.get_key_info= (seq->get_key_info ?
+ partition_multi_range_key_get_key_info :
+ NULL);
+ m_part_seq_if.init= partition_multi_range_key_init;
+ m_part_seq_if.next= partition_multi_range_key_next;
+ m_part_seq_if.skip_record= (seq->skip_record ?
+ partition_multi_range_key_skip_record :
+ NULL);
+ m_part_seq_if.skip_index_tuple= (seq->skip_index_tuple ?
+ partition_multi_range_key_skip_index_tuple :
+ NULL);
+
+ /* m_mrr_new_full_buffer_size was calculated in multi_range_read_info */
+ if (m_mrr_full_buffer_size < m_mrr_new_full_buffer_size)
+ {
+ 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))))
+ {
+ m_mrr_full_buffer_size= 0;
+ error= HA_ERR_OUT_OF_MEM;
+ goto error;
+ }
+ m_mrr_full_buffer_size= m_mrr_new_full_buffer_size;
+ }
+
+ tmp_buffer= m_mrr_full_buffer;
+ file= m_file;
+ do
+ {
+ i= (uint)(file - m_file);
+ DBUG_PRINT("info",("partition part_id: %u", i));
+ if (bitmap_is_set(&m_mrr_used_partitions, i))
+ {
+ if (m_mrr_new_full_buffer_size)
+ {
+ if (m_mrr_buffer_size[i])
+ {
+ m_mrr_buffer[i].buffer= tmp_buffer;
+ m_mrr_buffer[i].end_of_used_area= tmp_buffer;
+ tmp_buffer+= m_mrr_buffer_size[i];
+ m_mrr_buffer[i].buffer_end= tmp_buffer;
+ }
+ }
+ else
+ m_mrr_buffer[i]= *buf;
+
+ if (unlikely((error= (*file)->
+ multi_range_read_init(&m_part_seq_if,
+ &m_partition_part_key_multi_range_hld[i],
+ m_part_mrr_range_length[i],
+ mrr_mode,
+ &m_mrr_buffer[i]))))
+ goto error;
+ m_stock_range_seq[i]= 0;
+ }
+ } while (*(++file));
+
+ m_multi_range_read_first= TRUE;
+ m_mrr_range_current= m_mrr_range_first;
+ m_index_scan_type= partition_read_multi_range;
+ m_mrr_mode= mrr_mode;
+ m_mrr_n_ranges= n_ranges;
+ DBUG_RETURN(0);
+
+error:
+ DBUG_RETURN(error);
+}
+
+
+int ha_partition::multi_range_read_next(range_id_t *range_info)
+{
+ int error;
+ DBUG_ENTER("ha_partition::multi_range_read_next");
+ DBUG_PRINT("enter", ("partition this: %p partition m_mrr_mode: %u",
+ this, m_mrr_mode));
+
+ if ((m_mrr_mode & HA_MRR_SORTED))
+ {
+ if (m_multi_range_read_first)
+ {
+ if (unlikely((error= handle_ordered_index_scan(table->record[0],
+ FALSE))))
+ DBUG_RETURN(error);
+ if (!m_pre_calling)
+ m_multi_range_read_first= FALSE;
+ }
+ else if (unlikely((error= handle_ordered_next(table->record[0],
+ eq_range))))
+ DBUG_RETURN(error);
+ *range_info= m_mrr_range_current->ptr;
+ }
+ else
+ {
+ if (unlikely(m_multi_range_read_first))
+ {
+ if (unlikely((error=
+ handle_unordered_scan_next_partition(table->record[0]))))
+ DBUG_RETURN(error);
+ if (!m_pre_calling)
+ m_multi_range_read_first= FALSE;
+ }
+ else if (unlikely((error= handle_unordered_next(table->record[0], FALSE))))
+ DBUG_RETURN(error);
+
+ if (!(m_mrr_mode & HA_MRR_NO_ASSOCIATION))
+ {
+ *range_info=
+ ((PARTITION_KEY_MULTI_RANGE *) m_range_info[m_last_part])->ptr;
+ }
+ }
+ DBUG_RETURN(0);
+}
+
+
+int ha_partition::multi_range_read_explain_info(uint mrr_mode, char *str,
+ size_t size)
+{
+ DBUG_ENTER("ha_partition::multi_range_read_explain_info");
+ DBUG_RETURN(get_open_file_sample()->
+ multi_range_read_explain_info(mrr_mode, str, size));
+}
+
+
+/**
+ Find and retrieve the Full Text Search relevance ranking for a search string
+ in a full text index.
+
+ @param handler Full Text Search handler
+ @param record Search string
+ @param length Length of the search string
+
+ @retval Relevance value
+*/
+
+float partition_ft_find_relevance(FT_INFO *handler,
+ uchar *record, uint length)
+{
+ st_partition_ft_info *info= (st_partition_ft_info *)handler;
+ uint m_last_part= ((ha_partition*) info->file)->last_part();
+ FT_INFO *m_handler= info->part_ft_info[m_last_part];
+ DBUG_ENTER("partition_ft_find_relevance");
+ if (!m_handler)
+ DBUG_RETURN((float)-1.0);
+ DBUG_RETURN(m_handler->please->find_relevance(m_handler, record, length));
+}
+
+
+/**
+ Retrieve the Full Text Search relevance ranking for the current
+ full text search.
+
+ @param handler Full Text Search handler
+
+ @retval Relevance value
+*/
+
+float partition_ft_get_relevance(FT_INFO *handler)
+{
+ st_partition_ft_info *info= (st_partition_ft_info *)handler;
+ uint m_last_part= ((ha_partition*) info->file)->last_part();
+ FT_INFO *m_handler= info->part_ft_info[m_last_part];
+ DBUG_ENTER("partition_ft_get_relevance");
+ if (!m_handler)
+ DBUG_RETURN((float)-1.0);
+ DBUG_RETURN(m_handler->please->get_relevance(m_handler));
+}
+
+
+/**
+ Free the memory for a full text search handler.
+
+ @param handler Full Text Search handler
+*/
+
+void partition_ft_close_search(FT_INFO *handler)
+{
+ st_partition_ft_info *info= (st_partition_ft_info *)handler;
+ info->file->ft_close_search(handler);
+}
+
+
+/**
+ Free the memory for a full text search handler.
+
+ @param handler Full Text Search handler
+*/
+
+void ha_partition::ft_close_search(FT_INFO *handler)
+{
+ uint i;
+ st_partition_ft_info *info= (st_partition_ft_info *)handler;
+ DBUG_ENTER("ha_partition::ft_close_search");
+
+ for (i= 0; i < m_tot_parts; i++)
+ {
+ FT_INFO *m_handler= info->part_ft_info[i];
+ DBUG_ASSERT(!m_handler ||
+ (m_handler->please && m_handler->please->close_search));
+ if (m_handler &&
+ m_handler->please &&
+ m_handler->please->close_search)
+ m_handler->please->close_search(m_handler);
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/* Partition Full Text search function table */
+_ft_vft partition_ft_vft =
+{
+ NULL, // partition_ft_read_next
+ partition_ft_find_relevance,
+ partition_ft_close_search,
+ partition_ft_get_relevance,
+ NULL // partition_ft_reinit_search
+};
+
+
+/**
+ Initialize a full text search.
+*/
+
+int ha_partition::ft_init()
+{
+ int error;
+ uint i= 0;
+ uint32 part_id;
+ DBUG_ENTER("ha_partition::ft_init");
+ DBUG_PRINT("info", ("partition this: %p", this));
+
+ /*
+ For operations that may need to change data, we may need to extend
+ read_set.
+ */
+ if (get_lock_type() == F_WRLCK)
+ {
+ /*
+ If write_set contains any of the fields used in partition and
+ subpartition expression, we need to set all bits in read_set because
+ the row may need to be inserted in a different [sub]partition. In
+ other words update_row() can be converted into write_row(), which
+ requires a complete record.
+ */
+ if (bitmap_is_overlapping(&m_part_info->full_part_field_set,
+ table->write_set))
+ bitmap_set_all(table->read_set);
+ else
+ {
+ /*
+ Some handlers only read fields as specified by the bitmap for the
+ read set. For partitioned handlers we always require that the
+ fields of the partition functions are read such that we can
+ calculate the partition id to place updated and deleted records.
+ */
+ bitmap_union(table->read_set, &m_part_info->full_part_field_set);
+ }
+ }
+
+ /* Now we see what the index of our first important partition is */
+ DBUG_PRINT("info", ("m_part_info->read_partitions: %p",
+ (void *) m_part_info->read_partitions.bitmap));
+ part_id= bitmap_get_first_set(&(m_part_info->read_partitions));
+ DBUG_PRINT("info", ("m_part_spec.start_part %u", (uint) part_id));
+
+ if (part_id == MY_BIT_NONE)
+ {
+ error= 0;
+ goto err1;
+ }
+
+ DBUG_PRINT("info", ("ft_init on partition %u", (uint) part_id));
+ /*
+ ft_end() is needed for partitioning to reset internal data if scan
+ is already in use
+ */
+ if (m_pre_calling)
+ {
+ if (unlikely((error= pre_ft_end())))
+ goto err1;
+ }
+ else
+ ft_end();
+ m_index_scan_type= partition_ft_read;
+ for (i= part_id; i < m_tot_parts; i++)
+ {
+ if (bitmap_is_set(&(m_part_info->read_partitions), i))
+ {
+ error= m_pre_calling ? m_file[i]->pre_ft_init() : m_file[i]->ft_init();
+ if (unlikely(error))
+ goto err2;
+ }
+ }
+ m_scan_value= 1;
+ m_part_spec.start_part= part_id;
+ m_part_spec.end_part= m_tot_parts - 1;
+ m_ft_init_and_first= TRUE;
+ DBUG_PRINT("info", ("m_scan_value: %u", m_scan_value));
+ DBUG_RETURN(0);
+
+err2:
+ late_extra_no_cache(part_id);
+ while ((int)--i >= (int)part_id)
+ {
+ if (bitmap_is_set(&(m_part_info->read_partitions), i))
+ {
+ if (m_pre_calling)
+ m_file[i]->pre_ft_end();
+ else
+ m_file[i]->ft_end();
+ }
+ }
+err1:
+ m_scan_value= 2;
+ m_part_spec.start_part= NO_CURRENT_PART_ID;
+ DBUG_RETURN(error);
+}
+
+
+/**
+ Initialize a full text search during a bulk access request.
+*/
+
+int ha_partition::pre_ft_init()
+{
+ bool save_m_pre_calling;
+ int error;
+ DBUG_ENTER("ha_partition::pre_ft_init");
+ save_m_pre_calling= m_pre_calling;
+ m_pre_calling= TRUE;
+ error= ft_init();
+ m_pre_calling= save_m_pre_calling;
+ DBUG_RETURN(error);
+}
+
+
+/**
+ Terminate a full text search.
+*/
+
+void ha_partition::ft_end()
+{
+ handler **file;
+ DBUG_ENTER("ha_partition::ft_end");
+ DBUG_PRINT("info", ("partition this: %p", this));
+
+ switch (m_scan_value) {
+ case 2: // Error
+ break;
+ case 1: // Table scan
+ if (NO_CURRENT_PART_ID != m_part_spec.start_part)
+ late_extra_no_cache(m_part_spec.start_part);
+ file= m_file;
+ do
+ {
+ if (bitmap_is_set(&(m_part_info->read_partitions), (uint)(file - m_file)))
+ {
+ if (m_pre_calling)
+ (*file)->pre_ft_end();
+ else
+ (*file)->ft_end();
+ }
+ } while (*(++file));
+ break;
+ }
+ m_scan_value= 2;
+ m_part_spec.start_part= NO_CURRENT_PART_ID;
+ ft_current= 0;
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Terminate a full text search during a bulk access request.
+*/
+
+int ha_partition::pre_ft_end()
+{
+ bool save_m_pre_calling;
+ DBUG_ENTER("ha_partition::pre_ft_end");
+ save_m_pre_calling= m_pre_calling;
+ m_pre_calling= TRUE;
+ ft_end();
+ m_pre_calling= save_m_pre_calling;
+ DBUG_RETURN(0);
+}
+
+
+/**
+ Initialize a full text search using the extended API.
+
+ @param flags Search flags
+ @param inx Key number
+ @param key Key value
+
+ @return FT_INFO structure if successful
+ NULL otherwise
+*/
+
+FT_INFO *ha_partition::ft_init_ext(uint flags, uint inx, String *key)
+{
+ FT_INFO *ft_handler;
+ handler **file;
+ st_partition_ft_info *ft_target, **parent;
+ DBUG_ENTER("ha_partition::ft_init_ext");
+
+ if (ft_current)
+ parent= &ft_current->next;
+ else
+ parent= &ft_first;
+
+ if (!(ft_target= *parent))
+ {
+ 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,
+ NullS)))
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
+ DBUG_RETURN(NULL);
+ }
+ ft_target->part_ft_info= tmp_ft_info;
+ (*parent)= ft_target;
+ }
+
+ ft_current= ft_target;
+ file= m_file;
+ do
+ {
+ if (bitmap_is_set(&(m_part_info->read_partitions), (uint)(file - m_file)))
+ {
+ if ((ft_handler= (*file)->ft_init_ext(flags, inx, key)))
+ (*file)->ft_handler= ft_handler;
+ else
+ (*file)->ft_handler= NULL;
+ ft_target->part_ft_info[file - m_file]= ft_handler;
+ }
+ else
+ {
+ (*file)->ft_handler= NULL;
+ ft_target->part_ft_info[file - m_file]= NULL;
+ }
+ } while (*(++file));
+
+ ft_target->please= &partition_ft_vft;
+ ft_target->file= this;
+ DBUG_RETURN((FT_INFO*)ft_target);
+}
+
+
+/**
+ Return the next record from the FT result set during an ordered index
+ pre-scan
+
+ @param use_parallel Is it a parallel search
+
+ @return >0 Error code
+ 0 Success
+*/
+
+int ha_partition::pre_ft_read(bool use_parallel)
+{
+ bool save_m_pre_calling;
+ int error;
+ DBUG_ENTER("ha_partition::pre_ft_read");
+ DBUG_PRINT("info", ("partition this: %p", this));
+ save_m_pre_calling= m_pre_calling;
+ m_pre_calling= TRUE;
+ m_pre_call_use_parallel= use_parallel;
+ error= ft_read(table->record[0]);
+ m_pre_calling= save_m_pre_calling;
+ DBUG_RETURN(error);
+}
+
+
+/**
+ Return the first or next record in a full text search.
+
+ @param buf Buffer where the record should be returned
+
+ @return >0 Error code
+ 0 Success
+*/
+
+int ha_partition::ft_read(uchar *buf)
+{
+ handler *file;
+ int result= HA_ERR_END_OF_FILE, error;
+ uint part_id= m_part_spec.start_part;
+ DBUG_ENTER("ha_partition::ft_read");
+ DBUG_PRINT("info", ("partition this: %p", this));
+ DBUG_PRINT("info", ("part_id: %u", part_id));
+
+ if (part_id == NO_CURRENT_PART_ID)
+ {
+ /*
+ The original set of partitions to scan was empty and thus we report
+ the result here.
+ */
+ DBUG_PRINT("info", ("NO_CURRENT_PART_ID"));
+ goto end;
+ }
+
+ DBUG_ASSERT(m_scan_value == 1);
+
+ if (m_ft_init_and_first) // First call to ft_read()
+ {
+ m_ft_init_and_first= FALSE;
+ if (!bulk_access_executing)
+ {
+ error= handle_pre_scan(FALSE, check_parallel_search());
+ if (m_pre_calling || error)
+ DBUG_RETURN(error);
+ }
+ late_extra_cache(part_id);
+ }
+
+ file= m_file[part_id];
+
+ while (TRUE)
+ {
+ if (!(result= file->ft_read(buf)))
+ {
+ /* Found row: remember position and return it. */
+ m_part_spec.start_part= m_last_part= part_id;
+ table->status= 0;
+ DBUG_RETURN(0);
+ }
+
+ /*
+ if we get here, then the current partition ft_next returned failure
+ */
+ if (result != HA_ERR_END_OF_FILE)
+ goto end_dont_reset_start_part; // Return error
+
+ /* End current partition */
+ late_extra_no_cache(part_id);
+ DBUG_PRINT("info", ("stopping using partition %u", (uint) part_id));
+
+ /* Shift to next partition */
+ while (++part_id < m_tot_parts &&
+ !bitmap_is_set(&(m_part_info->read_partitions), part_id))
+ ;
+ if (part_id >= m_tot_parts)
+ {
+ result= HA_ERR_END_OF_FILE;
+ break;
+ }
+ m_part_spec.start_part= m_last_part= part_id;
+ file= m_file[part_id];
+ DBUG_PRINT("info", ("now using partition %u", (uint) part_id));
+ late_extra_cache(part_id);
+ }
+
+end:
+ m_part_spec.start_part= NO_CURRENT_PART_ID;
+end_dont_reset_start_part:
+ table->status= STATUS_NOT_FOUND;
+ DBUG_RETURN(result);
+}
+
/*
Common routine to set up index scans
@@ -5843,10 +7001,10 @@ int ha_partition::read_range_next()
SYNOPSIS
ha_partition::partition_scan_set_up()
buf Buffer to later return record in (this function
- needs it to calculate partitioning function
+ needs it to calculcate partitioning function
values)
- idx_read_flag TRUE <=> m_start_key has range start endpoint which
+ idx_read_flag TRUE <=> m_start_key has range start endpoint which
probably can be used to determine the set of partitions
to scan.
FALSE <=> there is no start endpoint.
@@ -5868,7 +7026,7 @@ int ha_partition::partition_scan_set_up(uchar * buf, bool idx_read_flag)
DBUG_ENTER("ha_partition::partition_scan_set_up");
if (idx_read_flag)
- get_partition_set(table,buf,active_index,&m_start_key,&m_part_spec);
+ get_partition_set(table, buf, active_index, &m_start_key, &m_part_spec);
else
{
m_part_spec.start_part= 0;
@@ -5889,8 +7047,8 @@ int ha_partition::partition_scan_set_up(uchar * buf, bool idx_read_flag)
We discovered a single partition to scan, this never needs to be
performed using the ordered index scan.
*/
- DBUG_PRINT("info", ("index scan using the single partition %d",
- m_part_spec.start_part));
+ DBUG_PRINT("info", ("index scan using the single partition %u",
+ (uint) m_part_spec.start_part));
m_ordered_scan_ongoing= FALSE;
}
else
@@ -5917,6 +7075,206 @@ int ha_partition::partition_scan_set_up(uchar * buf, bool idx_read_flag)
DBUG_RETURN(0);
}
+/**
+ Check if we can search partitions in parallel
+
+ @retval TRUE yes
+ @retval FALSE no
+*/
+
+bool ha_partition::check_parallel_search()
+{
+ TABLE_LIST *table_list= table->pos_in_table_list;
+ st_select_lex *select_lex;
+ JOIN *join;
+ DBUG_ENTER("ha_partition::check_parallel_search");
+ if (!table_list)
+ goto not_parallel;
+
+ while (table_list->parent_l)
+ table_list= table_list->parent_l;
+
+ select_lex= table_list->select_lex;
+ DBUG_PRINT("info",("partition select_lex: %p", select_lex));
+ if (!select_lex)
+ goto not_parallel;
+ if (!select_lex->explicit_limit)
+ {
+ DBUG_PRINT("info",("partition not using explicit_limit"));
+ goto parallel;
+ }
+
+ join= select_lex->join;
+ DBUG_PRINT("info",("partition join: %p", join));
+ if (join && join->skip_sort_order)
+ {
+ DBUG_PRINT("info",("partition order_list.elements: %u",
+ select_lex->order_list.elements));
+ if (select_lex->order_list.elements)
+ {
+ Item *item= *select_lex->order_list.first->item;
+ DBUG_PRINT("info",("partition item: %p", item));
+ DBUG_PRINT("info",("partition item->type(): %u", item->type()));
+ DBUG_PRINT("info",("partition m_part_info->part_type: %u",
+ m_part_info->part_type));
+ DBUG_PRINT("info",("partition m_is_sub_partitioned: %s",
+ m_is_sub_partitioned ? "TRUE" : "FALSE"));
+ DBUG_PRINT("info",("partition m_part_info->part_expr: %p",
+ m_part_info->part_expr));
+ if (item->type() == Item::FIELD_ITEM &&
+ m_part_info->part_type == RANGE_PARTITION &&
+ !m_is_sub_partitioned &&
+ (!m_part_info->part_expr ||
+ m_part_info->part_expr->type() == Item::FIELD_ITEM))
+ {
+ Field *order_field= ((Item_field *)item)->field;
+ DBUG_PRINT("info",("partition order_field: %p", order_field));
+ 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)
+ {
+ /*
+ We are using ORDER BY partition_field LIMIT #
+ In this case, let's not do things in parallel as it's
+ likely that the query can be satisfied from the first
+ partition
+ */
+ DBUG_PRINT("info",("partition with ORDER on partition field"));
+ goto not_parallel;
+ }
+ }
+ }
+ DBUG_PRINT("info",("partition have order"));
+ goto parallel;
+ }
+
+ DBUG_PRINT("info",("partition group_list.elements: %u",
+ select_lex->group_list.elements));
+ if (select_lex->group_list.elements)
+ {
+ Item *item= *select_lex->group_list.first->item;
+ DBUG_PRINT("info",("partition item: %p", item));
+ DBUG_PRINT("info",("partition item->type(): %u", item->type()));
+ DBUG_PRINT("info",("partition m_part_info->part_type: %u",
+ m_part_info->part_type));
+ DBUG_PRINT("info",("partition m_is_sub_partitioned: %s",
+ m_is_sub_partitioned ? "TRUE" : "FALSE"));
+ DBUG_PRINT("info",("partition m_part_info->part_expr: %p",
+ m_part_info->part_expr));
+ if (item->type() == Item::FIELD_ITEM &&
+ m_part_info->part_type == RANGE_PARTITION &&
+ !m_is_sub_partitioned &&
+ (!m_part_info->part_expr ||
+ m_part_info->part_expr->type() == Item::FIELD_ITEM))
+ {
+ Field *group_field= ((Item_field *)item)->field;
+ DBUG_PRINT("info",("partition group_field: %p", group_field));
+ 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)
+ {
+ DBUG_PRINT("info",("partition with GROUP BY on partition field"));
+ goto not_parallel;
+ }
+ }
+ }
+ DBUG_PRINT("info",("partition with GROUP BY"));
+ goto parallel;
+ }
+ }
+ else if (select_lex->order_list.elements ||
+ select_lex->group_list.elements)
+ {
+ DBUG_PRINT("info",("partition is not skip_order"));
+ DBUG_PRINT("info",("partition order_list.elements: %u",
+ select_lex->order_list.elements));
+ DBUG_PRINT("info",("partition group_list.elements: %u",
+ select_lex->group_list.elements));
+ goto parallel;
+ }
+ DBUG_PRINT("info",("partition is not skip_order"));
+
+not_parallel:
+ DBUG_PRINT("return",("partition FALSE"));
+ DBUG_RETURN(FALSE);
+
+parallel:
+ DBUG_PRINT("return",("partition TRUE"));
+ DBUG_RETURN(TRUE);
+}
+
+
+int ha_partition::handle_pre_scan(bool reverse_order, bool use_parallel)
+{
+ uint i;
+ DBUG_ENTER("ha_partition::handle_pre_scan");
+ DBUG_PRINT("enter",
+ ("m_part_spec.start_part: %u m_part_spec.end_part: %u",
+ (uint) m_part_spec.start_part, (uint) m_part_spec.end_part));
+
+ for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++)
+ {
+ if (!(bitmap_is_set(&(m_part_info->read_partitions), i)))
+ continue;
+ int error;
+ handler *file= m_file[i];
+
+ switch (m_index_scan_type) {
+ case partition_index_read:
+ error= file->pre_index_read_map(m_start_key.key,
+ m_start_key.keypart_map,
+ m_start_key.flag,
+ use_parallel);
+ break;
+ case partition_index_first:
+ error= file->pre_index_first(use_parallel);
+ break;
+ case partition_index_last:
+ error= file->pre_index_last(use_parallel);
+ break;
+ case partition_index_read_last:
+ error= file->pre_index_read_last_map(m_start_key.key,
+ m_start_key.keypart_map,
+ use_parallel);
+ break;
+ case partition_read_range:
+ error= file->pre_read_range_first(m_start_key.key? &m_start_key: NULL,
+ end_range, eq_range, TRUE, use_parallel);
+ break;
+ case partition_read_multi_range:
+ if (!bitmap_is_set(&m_mrr_used_partitions, i))
+ continue;
+ error= file->pre_multi_range_read_next(use_parallel);
+ break;
+ case partition_ft_read:
+ error= file->pre_ft_read(use_parallel);
+ break;
+ case partition_no_index_scan:
+ error= file->pre_rnd_next(use_parallel);
+ break;
+ default:
+ DBUG_ASSERT(FALSE);
+ DBUG_RETURN(0);
+ }
+ if (error == HA_ERR_END_OF_FILE)
+ error= 0;
+ if (unlikely(error))
+ DBUG_RETURN(error);
+ }
+ table->status= 0;
+ DBUG_RETURN(0);
+}
+
/****************************************************************************
Unordered Index Scan Routines
@@ -5963,9 +7321,18 @@ int ha_partition::handle_unordered_next(uchar *buf, bool is_next_same)
partition_read_range is_next_same are always local constants
*/
- if (m_index_scan_type == partition_read_range)
+ if (m_index_scan_type == partition_read_multi_range)
{
- if (!(error= file->read_range_next()))
+ if (likely(!(error= file->
+ multi_range_read_next(&m_range_info[m_part_spec.start_part]))))
+ {
+ m_last_part= m_part_spec.start_part;
+ DBUG_RETURN(0);
+ }
+ }
+ else if (m_index_scan_type == partition_read_range)
+ {
+ if (likely(!(error= file->read_range_next())))
{
m_last_part= m_part_spec.start_part;
DBUG_RETURN(0);
@@ -5973,23 +7340,23 @@ int ha_partition::handle_unordered_next(uchar *buf, bool is_next_same)
}
else if (is_next_same)
{
- if (!(error= file->ha_index_next_same(buf, m_start_key.key,
- m_start_key.length)))
+ if (likely(!(error= file->ha_index_next_same(buf, m_start_key.key,
+ m_start_key.length))))
{
m_last_part= m_part_spec.start_part;
DBUG_RETURN(0);
}
}
- else
+ else
{
- if (!(error= file->ha_index_next(buf)))
+ if (likely(!(error= file->ha_index_next(buf))))
{
m_last_part= m_part_spec.start_part;
DBUG_RETURN(0); // Row was in range
}
}
- if (error == HA_ERR_END_OF_FILE)
+ if (unlikely(error == HA_ERR_END_OF_FILE))
{
m_part_spec.start_part++; // Start using next part
error= handle_unordered_scan_next_partition(buf);
@@ -6003,7 +7370,7 @@ int ha_partition::handle_unordered_next(uchar *buf, bool is_next_same)
SYNOPSIS
handle_unordered_scan_next_partition()
- buf Read row in MySQL Row Format
+ buf Read row in MariaDB Row Format
RETURN VALUE
HA_ERR_END_OF_FILE End of scan
@@ -6021,6 +7388,7 @@ int ha_partition::handle_unordered_scan_next_partition(uchar * buf)
int saved_error= HA_ERR_END_OF_FILE;
DBUG_ENTER("ha_partition::handle_unordered_scan_next_partition");
+ /* Read next partition that includes start_part */
if (i)
i= bitmap_get_next_set(&m_part_info->read_partitions, i - 1);
else
@@ -6033,53 +7401,49 @@ int ha_partition::handle_unordered_scan_next_partition(uchar * buf)
int error;
handler *file= m_file[i];
m_part_spec.start_part= i;
+
switch (m_index_scan_type) {
+ case partition_read_multi_range:
+ if (!bitmap_is_set(&m_mrr_used_partitions, i))
+ continue;
+ DBUG_PRINT("info", ("read_multi_range on partition %u", i));
+ error= file->multi_range_read_next(&m_range_info[i]);
+ break;
case partition_read_range:
- DBUG_PRINT("info", ("read_range_first on partition %d", i));
+ DBUG_PRINT("info", ("read_range_first on partition %u", i));
error= file->read_range_first(m_start_key.key? &m_start_key: NULL,
end_range, eq_range, FALSE);
break;
case partition_index_read:
- DBUG_PRINT("info", ("index_read on partition %d", i));
+ DBUG_PRINT("info", ("index_read on partition %u", i));
error= file->ha_index_read_map(buf, m_start_key.key,
m_start_key.keypart_map,
m_start_key.flag);
break;
case partition_index_first:
- DBUG_PRINT("info", ("index_first on partition %d", i));
+ DBUG_PRINT("info", ("index_first on partition %u", i));
error= file->ha_index_first(buf);
break;
- case partition_index_first_unordered:
- /*
- We perform a scan without sorting and this means that we
- should not use the index_first since not all handlers
- support it and it is also unnecessary to restrict sort
- order.
- */
- DBUG_PRINT("info", ("read_range_first on partition %d", i));
- table->record[0]= buf;
- error= file->read_range_first(0, end_range, eq_range, 0);
- table->record[0]= m_rec0;
- break;
default:
DBUG_ASSERT(FALSE);
DBUG_RETURN(1);
}
- if (!error)
+ if (likely(!error))
{
m_last_part= i;
DBUG_RETURN(0);
}
- if ((error != HA_ERR_END_OF_FILE) && (error != HA_ERR_KEY_NOT_FOUND))
+ if (likely((error != HA_ERR_END_OF_FILE) &&
+ (error != HA_ERR_KEY_NOT_FOUND)))
DBUG_RETURN(error);
/*
- If HA_ERR_KEY_NOT_FOUND, we must return that error instead of
+ If HA_ERR_KEY_NOT_FOUND, we must return that error instead of
HA_ERR_END_OF_FILE, to be able to continue search.
*/
if (saved_error != HA_ERR_KEY_NOT_FOUND)
saved_error= error;
- DBUG_PRINT("info", ("END_OF_FILE/KEY_NOT_FOUND on partition %d", i));
+ DBUG_PRINT("info", ("END_OF_FILE/KEY_NOT_FOUND on partition %u", i));
}
if (saved_error == HA_ERR_END_OF_FILE)
m_part_spec.start_part= NO_CURRENT_PART_ID;
@@ -6090,7 +7454,7 @@ int ha_partition::handle_unordered_scan_next_partition(uchar * buf)
/**
Common routine to start index scan with ordered results.
- @param[out] buf Read row in MySQL Row Format
+ @param[out] buf Read row in MariaDB Row Format
@return Operation status
@retval HA_ERR_END_OF_FILE End of scan
@@ -6116,19 +7480,31 @@ int ha_partition::handle_unordered_scan_next_partition(uchar * buf)
int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order)
{
+ int error;
uint i;
uint j= queue_first_element(&m_queue);
+ uint smallest_range_seq= 0;
bool found= FALSE;
uchar *part_rec_buf_ptr= m_ordered_rec_buffer;
int saved_error= HA_ERR_END_OF_FILE;
DBUG_ENTER("ha_partition::handle_ordered_index_scan");
+ DBUG_PRINT("enter", ("partition this: %p", this));
+
+ if (m_pre_calling)
+ error= handle_pre_scan(reverse_order, m_pre_call_use_parallel);
+ else
+ error= handle_pre_scan(reverse_order, check_parallel_search());
+ if (unlikely(error))
+ DBUG_RETURN(error);
if (m_key_not_found)
{
+ /* m_key_not_found was set in the previous call to this function */
m_key_not_found= false;
bitmap_clear_all(&m_key_not_found_partitions);
}
m_top_entry= NO_CURRENT_PART_ID;
+ DBUG_PRINT("info", ("partition queue_remove_all(1)"));
queue_remove_all(&m_queue);
DBUG_ASSERT(bitmap_is_set(&m_part_info->read_partitions,
m_part_spec.start_part));
@@ -6148,14 +7524,14 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order)
DBUG_PRINT("info", ("m_part_spec.start_part %u first_used_part %u",
m_part_spec.start_part, i));
for (/* continue from above */ ;
- i <= m_part_spec.end_part;
- i= bitmap_get_next_set(&m_part_info->read_partitions, i))
+ i <= m_part_spec.end_part ;
+ i= bitmap_get_next_set(&m_part_info->read_partitions, i),
+ part_rec_buf_ptr+= m_priority_queue_rec_len)
{
DBUG_PRINT("info", ("reading from part %u (scan_type: %u)",
i, m_index_scan_type));
DBUG_ASSERT(i == uint2korr(part_rec_buf_ptr));
uchar *rec_buf_ptr= part_rec_buf_ptr + PARTITION_BYTES_IN_POS;
- int error;
handler *file= m_file[i];
switch (m_index_scan_type) {
@@ -6164,6 +7540,7 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order)
m_start_key.key,
m_start_key.keypart_map,
m_start_key.flag);
+ /* Caller has specified reverse_order */
break;
case partition_index_first:
error= file->ha_index_first(rec_buf_ptr);
@@ -6175,21 +7552,54 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order)
break;
case partition_read_range:
{
- /*
+ /*
This can only read record to table->record[0], as it was set when
the table was being opened. We have to memcpy data ourselves.
*/
error= file->read_range_first(m_start_key.key? &m_start_key: NULL,
end_range, eq_range, TRUE);
- memcpy(rec_buf_ptr, table->record[0], m_rec_length);
+ if (likely(!error))
+ memcpy(rec_buf_ptr, table->record[0], m_rec_length);
reverse_order= FALSE;
break;
}
+ case partition_read_multi_range:
+ {
+ if (!bitmap_is_set(&m_mrr_used_partitions, i))
+ continue;
+ DBUG_PRINT("info", ("partition %u", i));
+ error= file->multi_range_read_next(&m_range_info[i]);
+ DBUG_PRINT("info", ("error: %d", error));
+ if (error == HA_ERR_KEY_NOT_FOUND || error == HA_ERR_END_OF_FILE)
+ {
+ bitmap_clear_bit(&m_mrr_used_partitions, i);
+ continue;
+ }
+ if (likely(!error))
+ {
+ memcpy(rec_buf_ptr, table->record[0], m_rec_length);
+ reverse_order= FALSE;
+ m_stock_range_seq[i]= (((PARTITION_KEY_MULTI_RANGE *)
+ m_range_info[i])->id);
+ /* Test if the key is in the first key range */
+ if (m_stock_range_seq[i] != m_mrr_range_current->id)
+ {
+ /*
+ smallest_range_seq contains the smallest key range we have seen
+ so far
+ */
+ if (!smallest_range_seq || smallest_range_seq > m_stock_range_seq[i])
+ smallest_range_seq= m_stock_range_seq[i];
+ continue;
+ }
+ }
+ break;
+ }
default:
DBUG_ASSERT(FALSE);
DBUG_RETURN(HA_ERR_END_OF_FILE);
}
- if (!error)
+ if (likely(!error))
{
found= TRUE;
if (!m_using_extended_keys)
@@ -6202,10 +7612,6 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order)
*/
queue_element(&m_queue, j++)= part_rec_buf_ptr;
}
- else if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
- {
- DBUG_RETURN(error);
- }
else if (error == HA_ERR_KEY_NOT_FOUND)
{
DBUG_PRINT("info", ("HA_ERR_KEY_NOT_FOUND from partition %u", i));
@@ -6213,7 +7619,53 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order)
m_key_not_found= true;
saved_error= error;
}
- part_rec_buf_ptr+= m_priority_queue_rec_len;
+ else if (error != HA_ERR_END_OF_FILE)
+ {
+ DBUG_RETURN(error);
+ }
+ }
+
+ if (!found && smallest_range_seq)
+ {
+ /* We know that there is an existing row based on code above */
+ found= TRUE;
+ part_rec_buf_ptr= m_ordered_rec_buffer;
+
+ /*
+ No key found in the first key range
+ Collect all partitions that has a key in smallest_range_seq
+ */
+ DBUG_PRINT("info", ("partition !found && smallest_range_seq"));
+ for (i= bitmap_get_first_set(&m_part_info->read_partitions);
+ i <= m_part_spec.end_part;
+ i= bitmap_get_next_set(&m_part_info->read_partitions, i))
+ {
+ DBUG_PRINT("info", ("partition current_part: %u", i));
+ if (i < m_part_spec.start_part)
+ {
+ part_rec_buf_ptr+= m_priority_queue_rec_len;
+ DBUG_PRINT("info", ("partition i < m_part_spec.start_part"));
+ continue;
+ }
+ if (!bitmap_is_set(&m_mrr_used_partitions, i))
+ {
+ part_rec_buf_ptr+= m_priority_queue_rec_len;
+ DBUG_PRINT("info", ("partition !bitmap_is_set(&m_mrr_used_partitions, i)"));
+ continue;
+ }
+ DBUG_ASSERT(i == uint2korr(part_rec_buf_ptr));
+ if (smallest_range_seq == m_stock_range_seq[i])
+ {
+ m_stock_range_seq[i]= 0;
+ queue_element(&m_queue, j++)= (uchar *) part_rec_buf_ptr;
+ DBUG_PRINT("info", ("partition smallest_range_seq == m_stock_range_seq[i]"));
+ }
+ part_rec_buf_ptr+= m_priority_queue_rec_len;
+ }
+
+ /* Update global m_mrr_range_current to the current range */
+ while (m_mrr_range_current->id < smallest_range_seq)
+ m_mrr_range_current= m_mrr_range_current->next;
}
if (found)
{
@@ -6222,12 +7674,11 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order)
after that read the first entry and copy it to the buffer to return in.
*/
queue_set_max_at_top(&m_queue, reverse_order);
- queue_set_cmp_arg(&m_queue, m_using_extended_keys? m_curr_key_info : (void*)this);
+ queue_set_cmp_arg(&m_queue, (void*) this);
m_queue.elements= j - queue_first_element(&m_queue);
queue_fix(&m_queue);
return_top_record(buf);
- table->status= 0;
- DBUG_PRINT("info", ("Record returned from partition %d", m_top_entry));
+ DBUG_PRINT("info", ("Record returned from partition %u", m_top_entry));
DBUG_RETURN(0);
}
DBUG_RETURN(saved_error);
@@ -6250,11 +7701,28 @@ void ha_partition::return_top_record(uchar *buf)
uint part_id;
uchar *key_buffer= queue_top(&m_queue);
uchar *rec_buffer= key_buffer + PARTITION_BYTES_IN_POS;
+ DBUG_ENTER("ha_partition::return_top_record");
+ DBUG_PRINT("enter", ("partition this: %p", this));
part_id= uint2korr(key_buffer);
memcpy(buf, rec_buffer, m_rec_length);
m_last_part= part_id;
+ DBUG_PRINT("info", ("partition m_last_part: %u", m_last_part));
m_top_entry= part_id;
+ table->status= 0; // Found an existing row
+ m_file[part_id]->return_record_by_parent();
+ DBUG_VOID_RETURN;
+}
+
+/*
+ This function is only used if the partitioned table has own partitions.
+ This can happen if the partitioned VP engine is used (part of spider).
+*/
+
+void ha_partition::return_record_by_parent()
+{
+ m_file[m_last_part]->return_record_by_parent();
+ DBUG_ASSERT(0);
}
@@ -6273,6 +7741,7 @@ int ha_partition::handle_ordered_index_scan_key_not_found()
uchar *part_buf= m_ordered_rec_buffer;
uchar *curr_rec_buf= NULL;
DBUG_ENTER("ha_partition::handle_ordered_index_scan_key_not_found");
+ DBUG_PRINT("enter", ("partition this: %p", this));
DBUG_ASSERT(m_key_not_found);
/*
Loop over all used partitions to get the correct offset
@@ -6292,8 +7761,11 @@ int ha_partition::handle_ordered_index_scan_key_not_found()
error= m_file[i]->ha_index_next(curr_rec_buf);
/* HA_ERR_KEY_NOT_FOUND is not allowed from index_next! */
DBUG_ASSERT(error != HA_ERR_KEY_NOT_FOUND);
- if (!error)
+ if (likely(!error))
+ {
+ DBUG_PRINT("info", ("partition queue_insert(1)"));
queue_insert(&m_queue, part_buf);
+ }
else if (error != HA_ERR_END_OF_FILE && error != HA_ERR_KEY_NOT_FOUND)
DBUG_RETURN(error);
}
@@ -6330,11 +7802,15 @@ int ha_partition::handle_ordered_index_scan_key_not_found()
int ha_partition::handle_ordered_next(uchar *buf, bool is_next_same)
{
int error;
+ DBUG_ENTER("ha_partition::handle_ordered_next");
+
+ if (m_top_entry == NO_CURRENT_PART_ID)
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+
uint part_id= m_top_entry;
uchar *rec_buf= queue_top(&m_queue) + PARTITION_BYTES_IN_POS;
handler *file;
- DBUG_ENTER("ha_partition::handle_ordered_next");
-
+
if (m_key_not_found)
{
if (is_next_same)
@@ -6347,7 +7823,7 @@ int ha_partition::handle_ordered_next(uchar *buf, bool is_next_same)
{
/* There are partitions not included in the index record queue. */
uint old_elements= m_queue.elements;
- if ((error= handle_ordered_index_scan_key_not_found()))
+ if (unlikely((error= handle_ordered_index_scan_key_not_found())))
DBUG_RETURN(error);
/*
If the queue top changed, i.e. one of the partitions that gave
@@ -6376,24 +7852,140 @@ int ha_partition::handle_ordered_next(uchar *buf, bool is_next_same)
error= file->read_range_next();
memcpy(rec_buf, table->record[0], m_rec_length);
}
+ else if (m_index_scan_type == partition_read_multi_range)
+ {
+ DBUG_PRINT("info", ("partition_read_multi_range route"));
+ DBUG_PRINT("info", ("part_id: %u", part_id));
+ bool get_next= FALSE;
+ error= file->multi_range_read_next(&m_range_info[part_id]);
+ DBUG_PRINT("info", ("error: %d", error));
+ if (unlikely(error == HA_ERR_KEY_NOT_FOUND))
+ error= HA_ERR_END_OF_FILE;
+ if (unlikely(error == HA_ERR_END_OF_FILE))
+ {
+ bitmap_clear_bit(&m_mrr_used_partitions, part_id);
+ DBUG_PRINT("info", ("partition m_queue.elements: %u", m_queue.elements));
+ if (m_queue.elements)
+ {
+ DBUG_PRINT("info", ("partition queue_remove_top(1)"));
+ queue_remove_top(&m_queue);
+ if (m_queue.elements)
+ {
+ return_top_record(buf);
+ DBUG_PRINT("info", ("Record returned from partition %u (3)",
+ m_top_entry));
+ DBUG_RETURN(0);
+ }
+ }
+ get_next= TRUE;
+ }
+ else if (likely(!error))
+ {
+ DBUG_PRINT("info", ("m_range_info[%u])->id: %u", part_id,
+ ((PARTITION_KEY_MULTI_RANGE *)
+ m_range_info[part_id])->id));
+ DBUG_PRINT("info", ("m_mrr_range_current->id: %u",
+ m_mrr_range_current->id));
+ memcpy(rec_buf, table->record[0], m_rec_length);
+ if (((PARTITION_KEY_MULTI_RANGE *) m_range_info[part_id])->id !=
+ m_mrr_range_current->id)
+ {
+ m_stock_range_seq[part_id]=
+ ((PARTITION_KEY_MULTI_RANGE *) m_range_info[part_id])->id;
+ DBUG_PRINT("info", ("partition queue_remove_top(2)"));
+ queue_remove_top(&m_queue);
+ if (!m_queue.elements)
+ get_next= TRUE;
+ }
+ }
+ if (get_next)
+ {
+ DBUG_PRINT("info", ("get_next route"));
+ uint i, j= 0, smallest_range_seq= UINT_MAX32;
+ for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++)
+ {
+ if (!(bitmap_is_set(&(m_part_info->read_partitions), i)))
+ continue;
+ if (!bitmap_is_set(&m_mrr_used_partitions, i))
+ continue;
+ if (smallest_range_seq > m_stock_range_seq[i])
+ smallest_range_seq= m_stock_range_seq[i];
+ }
+
+ DBUG_PRINT("info", ("smallest_range_seq: %u", smallest_range_seq));
+ if (smallest_range_seq != UINT_MAX32)
+ {
+ uchar *part_rec_buf_ptr= m_ordered_rec_buffer;
+ DBUG_PRINT("info", ("partition queue_remove_all(2)"));
+ queue_remove_all(&m_queue);
+ DBUG_PRINT("info", ("m_part_spec.start_part: %u",
+ m_part_spec.start_part));
+
+ for (i= bitmap_get_first_set(&m_part_info->read_partitions);
+ i <= m_part_spec.end_part;
+ i= bitmap_get_next_set(&m_part_info->read_partitions, i),
+ part_rec_buf_ptr+= m_priority_queue_rec_len)
+ {
+ DBUG_PRINT("info",("partition part_id: %u", i));
+ if (i < m_part_spec.start_part)
+ {
+ DBUG_PRINT("info",("partition i < m_part_spec.start_part"));
+ continue;
+ }
+ if (!bitmap_is_set(&m_mrr_used_partitions, i))
+ {
+ DBUG_PRINT("info",("partition !bitmap_is_set(&m_mrr_used_partitions, i)"));
+ continue;
+ }
+ DBUG_PRINT("info",("partition uint2korr: %u",
+ uint2korr(part_rec_buf_ptr)));
+ DBUG_ASSERT(i == uint2korr(part_rec_buf_ptr));
+ DBUG_PRINT("info", ("partition m_stock_range_seq[%u]: %u",
+ i, m_stock_range_seq[i]));
+ if (smallest_range_seq == m_stock_range_seq[i])
+ {
+ m_stock_range_seq[i]= 0;
+ DBUG_PRINT("info", ("partition queue_insert(2)"));
+ queue_insert(&m_queue, part_rec_buf_ptr);
+ j++;
+ }
+ }
+ while (m_mrr_range_current->id < smallest_range_seq)
+ m_mrr_range_current= m_mrr_range_current->next;
+
+ DBUG_PRINT("info",("partition m_mrr_range_current: %p",
+ m_mrr_range_current));
+ DBUG_PRINT("info",("partition m_mrr_range_current->id: %u",
+ m_mrr_range_current ? m_mrr_range_current->id : 0));
+ queue_set_max_at_top(&m_queue, FALSE);
+ queue_set_cmp_arg(&m_queue, (void*) this);
+ m_queue.elements= j;
+ queue_fix(&m_queue);
+ return_top_record(buf);
+ DBUG_PRINT("info", ("Record returned from partition %u (4)",
+ m_top_entry));
+ DBUG_RETURN(0);
+ }
+ }
+ }
else if (!is_next_same)
error= file->ha_index_next(rec_buf);
else
error= file->ha_index_next_same(rec_buf, m_start_key.key,
m_start_key.length);
- if (error)
+ if (unlikely(error))
{
- if (error == HA_ERR_END_OF_FILE)
+ if (error == HA_ERR_END_OF_FILE && m_queue.elements)
{
/* Return next buffered row */
+ DBUG_PRINT("info", ("partition queue_remove_top(3)"));
queue_remove_top(&m_queue);
if (m_queue.elements)
{
+ return_top_record(buf);
DBUG_PRINT("info", ("Record returned from partition %u (2)",
m_top_entry));
- return_top_record(buf);
- table->status= 0;
error= 0;
}
}
@@ -6429,30 +8021,35 @@ int ha_partition::handle_ordered_next(uchar *buf, bool is_next_same)
int ha_partition::handle_ordered_prev(uchar *buf)
{
int error;
+ DBUG_ENTER("ha_partition::handle_ordered_prev");
+ DBUG_PRINT("enter", ("partition: %p", this));
+
+ if (m_top_entry == NO_CURRENT_PART_ID)
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+
uint part_id= m_top_entry;
uchar *rec_buf= queue_top(&m_queue) + PARTITION_BYTES_IN_POS;
handler *file= m_file[part_id];
- DBUG_ENTER("ha_partition::handle_ordered_prev");
- if ((error= file->ha_index_prev(rec_buf)))
+ if (unlikely((error= file->ha_index_prev(rec_buf))))
{
- if (error == HA_ERR_END_OF_FILE)
+ if (error == HA_ERR_END_OF_FILE && m_queue.elements)
{
+ DBUG_PRINT("info", ("partition queue_remove_top(4)"));
queue_remove_top(&m_queue);
if (m_queue.elements)
{
return_top_record(buf);
- DBUG_PRINT("info", ("Record returned from partition %d (2)",
+ DBUG_PRINT("info", ("Record returned from partition %u (2)",
m_top_entry));
error= 0;
- table->status= 0;
}
}
DBUG_RETURN(error);
}
queue_replace_top(&m_queue);
return_top_record(buf);
- DBUG_PRINT("info", ("Record returned from partition %d", m_top_entry));
+ DBUG_PRINT("info", ("Record returned from partition %u", m_top_entry));
DBUG_RETURN(0);
}
@@ -6498,7 +8095,7 @@ int ha_partition::compare_number_of_records(ha_partition *me,
::info() is used to return information to the optimizer.
Currently this table handler doesn't implement most of the fields
really needed. SHOW also makes use of this data
- Another note, if your handler doesn't proved exact record count,
+ Another note, if your handler doesn't provide exact record count,
you will probably want to have the following in your code:
if (records < 2)
records = 2;
@@ -6562,6 +8159,7 @@ int ha_partition::info(uint flag)
if (flag & HA_STATUS_AUTO)
{
bool auto_inc_is_first_in_idx= (table_share->next_number_keypart == 0);
+ bool all_parts_opened= true;
DBUG_PRINT("info", ("HA_STATUS_AUTO"));
if (!table->found_next_number_field)
stats.auto_increment_value= 0;
@@ -6592,6 +8190,15 @@ int ha_partition::info(uint flag)
("checking all partitions for auto_increment_value"));
do
{
+ if (!bitmap_is_set(&m_opened_partitions, (uint)(file_array - m_file)))
+ {
+ /*
+ Some partitions aren't opened.
+ So we can't calculate the autoincrement.
+ */
+ all_parts_opened= false;
+ break;
+ }
file= *file_array;
file->info(HA_STATUS_AUTO | no_lock_flag);
set_if_bigger(auto_increment_value,
@@ -6600,11 +8207,12 @@ int ha_partition::info(uint flag)
DBUG_ASSERT(auto_increment_value);
stats.auto_increment_value= auto_increment_value;
- if (auto_inc_is_first_in_idx)
+ if (all_parts_opened && auto_inc_is_first_in_idx)
{
set_if_bigger(part_share->next_auto_inc_val,
auto_increment_value);
- part_share->auto_inc_initialized= true;
+ if (can_use_for_auto_inc_init())
+ part_share->auto_inc_initialized= true;
DBUG_PRINT("info", ("initializing next_auto_inc_val to %lu",
(ulong) part_share->next_auto_inc_val));
}
@@ -6640,8 +8248,9 @@ int ha_partition::info(uint flag)
stats.deleted= 0;
stats.data_file_length= 0;
stats.index_file_length= 0;
- stats.check_time= 0;
stats.delete_length= 0;
+ stats.check_time= 0;
+ stats.checksum= 0;
for (i= bitmap_get_first_set(&m_part_info->read_partitions);
i < m_tot_parts;
i= bitmap_get_next_set(&m_part_info->read_partitions, i))
@@ -6655,6 +8264,7 @@ int ha_partition::info(uint flag)
stats.delete_length+= file->stats.delete_length;
if (file->stats.check_time > stats.check_time)
stats.check_time= file->stats.check_time;
+ stats.checksum+= file->stats.checksum;
}
if (stats.records && stats.records < 2 &&
!(m_file[0]->ha_table_flags() & HA_STATS_RECORDS_IS_EXACT))
@@ -6719,20 +8329,25 @@ int ha_partition::info(uint flag)
ulonglong max_records= 0;
uint32 i= 0;
uint32 handler_instance= 0;
+ bool handler_instance_set= 0;
file_array= m_file;
do
{
file= *file_array;
- /* Get variables if not already done */
- if (!(flag & HA_STATUS_VARIABLE) ||
- !bitmap_is_set(&(m_part_info->read_partitions),
- (uint)(file_array - m_file)))
- file->info(HA_STATUS_VARIABLE | no_lock_flag | extra_var_flag);
- if (file->stats.records > max_records)
+ if (bitmap_is_set(&(m_opened_partitions), (uint)(file_array - m_file)))
{
- max_records= file->stats.records;
- handler_instance= i;
+ /* Get variables if not already done */
+ if (!(flag & HA_STATUS_VARIABLE) ||
+ !bitmap_is_set(&(m_part_info->read_partitions),
+ (uint) (file_array - m_file)))
+ file->info(HA_STATUS_VARIABLE | no_lock_flag | extra_var_flag);
+ if (file->stats.records > max_records || !handler_instance_set)
+ {
+ handler_instance_set= 1;
+ max_records= file->stats.records;
+ handler_instance= i;
+ }
}
i++;
} while (*(++file_array));
@@ -6802,14 +8417,111 @@ void ha_partition::get_dynamic_partition_info(PARTITION_STATS *stat_info,
stat_info->data_file_length= file->stats.data_file_length;
stat_info->max_data_file_length= file->stats.max_data_file_length;
stat_info->index_file_length= file->stats.index_file_length;
+ stat_info->max_index_file_length= file->stats.max_index_file_length;
stat_info->delete_length= file->stats.delete_length;
stat_info->create_time= file->stats.create_time;
stat_info->update_time= file->stats.update_time;
stat_info->check_time= file->stats.check_time;
- stat_info->check_sum= 0;
- if (file->ha_table_flags() & (HA_HAS_OLD_CHECKSUM | HA_HAS_NEW_CHECKSUM))
- stat_info->check_sum= file->checksum();
- return;
+ stat_info->check_sum= file->stats.checksum;
+}
+
+
+void ha_partition::set_partitions_to_open(List<String> *partition_names)
+{
+ m_partitions_to_open= partition_names;
+}
+
+
+int ha_partition::open_read_partitions(char *name_buff, size_t name_buff_size)
+{
+ handler **file;
+ char *name_buffer_ptr;
+ int error= 0;
+
+ name_buffer_ptr= m_name_buffer_ptr;
+ file= m_file;
+ m_file_sample= NULL;
+ do
+ {
+ int n_file= (int)(file-m_file);
+ int is_open= bitmap_is_set(&m_opened_partitions, n_file);
+ int should_be_open= bitmap_is_set(&m_part_info->read_partitions, n_file);
+
+ /*
+ TODO: we can close some opened partitions if they're not
+ used in the query. It probably should be syncronized with the
+ table_open_cache value.
+
+ if (is_open && !should_be_open)
+ {
+ if (unlikely((error= (*file)->ha_close())))
+ goto err_handler;
+ bitmap_clear_bit(&m_opened_partitions, n_file);
+ }
+ else
+ */
+ if (!is_open && should_be_open)
+ {
+ LEX_CSTRING save_connect_string= table->s->connect_string;
+ if (unlikely((error=
+ create_partition_name(name_buff, name_buff_size,
+ table->s->normalized_path.str,
+ name_buffer_ptr, NORMAL_PART_NAME,
+ FALSE))))
+ goto err_handler;
+ if (!((*file)->ht->flags & HTON_CAN_READ_CONNECT_STRING_IN_PARTITION))
+ table->s->connect_string= m_connect_string[(uint)(file-m_file)];
+ error= (*file)->ha_open(table, name_buff, m_mode,
+ m_open_test_lock | HA_OPEN_NO_PSI_CALL);
+ table->s->connect_string= save_connect_string;
+ if (error)
+ goto err_handler;
+ bitmap_set_bit(&m_opened_partitions, n_file);
+ m_last_part= n_file;
+ }
+ if (!m_file_sample && should_be_open)
+ m_file_sample= *file;
+ name_buffer_ptr+= strlen(name_buffer_ptr) + 1;
+ } while (*(++file));
+
+err_handler:
+ return error;
+}
+
+
+int ha_partition::change_partitions_to_open(List<String> *partition_names)
+{
+ char name_buff[FN_REFLEN+1];
+ int error= 0;
+
+ if (m_is_clone_of)
+ return 0;
+
+ m_partitions_to_open= partition_names;
+ if (unlikely((error= m_part_info->set_partition_bitmaps(partition_names))))
+ goto err_handler;
+
+ if (m_lock_type != F_UNLCK)
+ {
+ /*
+ That happens after the LOCK TABLE statement.
+ Do nothing in this case.
+ */
+ return 0;
+ }
+
+ check_insert_autoincrement();
+ if (bitmap_cmp(&m_opened_partitions, &m_part_info->read_partitions) != 0)
+ return 0;
+
+ if (unlikely((error= read_par_file(table->s->normalized_path.str)) ||
+ (error= open_read_partitions(name_buff, sizeof(name_buff)))))
+ goto err_handler;
+
+ clear_handler_file();
+
+err_handler:
+ return error;
}
@@ -7048,6 +8760,16 @@ static int end_keyread_cb(handler* h, void *unused)
HA_EXTRA_NO_READCHECK=5 No readcheck on update
HA_EXTRA_READCHECK=6 Use readcheck (def)
+ HA_EXTRA_REMEMBER_POS:
+ HA_EXTRA_RESTORE_POS:
+ System versioning needs this for MyISAM and Aria tables.
+ On DELETE using PRIMARY KEY:
+ 1) handler::ha_index_read_map() saves rowid used for row delete/update
+ 2) handler::ha_update_row() can rewrite saved rowid
+ 3) handler::ha_delete_row()/handler::ha_update_row() expects saved but got
+ different rowid and operation fails
+ Using those flags prevents harmful side effect of 2)
+
4) Operations only used by temporary tables for query processing
----------------------------------------------------------------
HA_EXTRA_RESET_STATE:
@@ -7107,8 +8829,6 @@ static int end_keyread_cb(handler* h, void *unused)
Only used MyISAM, only used internally in MyISAM handler, never called
from server level.
HA_EXTRA_KEYREAD_CHANGE_POS:
- HA_EXTRA_REMEMBER_POS:
- HA_EXTRA_RESTORE_POS:
HA_EXTRA_PRELOAD_BUFFER_SIZE:
HA_EXTRA_CHANGE_KEY_TO_DUP:
HA_EXTRA_CHANGE_KEY_TO_UNIQUE:
@@ -7131,7 +8851,7 @@ static int end_keyread_cb(handler* h, void *unused)
HA_EXTRA_DELETE_CANNOT_BATCH:
HA_EXTRA_UPDATE_CANNOT_BATCH:
Inform handler that delete_row()/update_row() cannot batch deletes/updates
- and should perform them immediately. This may be needed when table has
+ and should perform them immediately. This may be needed when table has
AFTER DELETE/UPDATE triggers which access to subject table.
These flags are reset by the handler::extra(HA_EXTRA_RESET) call.
@@ -7147,7 +8867,7 @@ static int end_keyread_cb(handler* h, void *unused)
int ha_partition::extra(enum ha_extra_function operation)
{
DBUG_ENTER("ha_partition:extra");
- DBUG_PRINT("info", ("operation: %d", (int) operation));
+ DBUG_PRINT("enter", ("operation: %d", (int) operation));
switch (operation) {
/* Category 1), used by most handlers */
@@ -7169,13 +8889,13 @@ int ha_partition::extra(enum ha_extra_function operation)
{
if (!m_myisam)
DBUG_RETURN(loop_partitions(extra_cb, &operation));
- break;
}
+ break;
/* Category 3), used by MyISAM handlers */
case HA_EXTRA_PREPARE_FOR_UPDATE:
/*
- Needs to be run on the first partition in the range now, and
+ Needs to be run on the first partition in the range now, and
later in late_extra_cache, when switching to a new partition to scan.
*/
m_extra_prepare_for_update= TRUE;
@@ -7191,6 +8911,9 @@ int ha_partition::extra(enum ha_extra_function operation)
case HA_EXTRA_QUICK:
case HA_EXTRA_PREPARE_FOR_DROP:
case HA_EXTRA_FLUSH_CACHE:
+ case HA_EXTRA_PREPARE_FOR_ALTER_TABLE:
+ case HA_EXTRA_REMEMBER_POS:
+ case HA_EXTRA_RESTORE_POS:
{
DBUG_RETURN(loop_partitions(extra_cb, &operation));
}
@@ -7243,18 +8966,9 @@ int ha_partition::extra(enum ha_extra_function operation)
with row being inserted by PK/unique key without reporting error
to the SQL-layer.
- This optimization is not safe for partitioned table in general case
- since we may have to put new version of row into partition which is
- different from partition in which old version resides (for example
- when we partition by non-PK column or by some column which is not
- part of unique key which were violated).
- And since NDB which is the only engine at the moment that supports
- this optimization handles partitioning on its own we simple disable
- it here. (BTW for NDB this optimization is safe since it supports
- only KEY partitioning and won't use this optimization for tables
- which have additional unique constraints).
+ At this time, this is safe by limitation of ha_partition
*/
- break;
+ DBUG_RETURN(loop_partitions(extra_cb, &operation));
}
/* Category 7), used by federated handlers */
case HA_EXTRA_INSERT_WITH_UPDATE:
@@ -7268,20 +8982,38 @@ int ha_partition::extra(enum ha_extra_function operation)
}
/* Category 9) Operations only used by MERGE */
case HA_EXTRA_ADD_CHILDREN_LIST:
+ DBUG_RETURN(loop_partitions(extra_cb, &operation));
case HA_EXTRA_ATTACH_CHILDREN:
- case HA_EXTRA_IS_ATTACHED_CHILDREN:
- case HA_EXTRA_DETACH_CHILDREN:
{
- /* Special actions for MERGE tables. Ignore. */
+ int result;
+ uint num_locks;
+ handler **file;
+ if ((result= loop_partitions(extra_cb, &operation)))
+ DBUG_RETURN(result);
+
+ /* Recalculate lock count as each child may have different set of locks */
+ num_locks= 0;
+ file= m_file;
+ do
+ {
+ num_locks+= (*file)->lock_count();
+ } while (*(++file));
+
+ m_num_locks= num_locks;
break;
}
+ case HA_EXTRA_IS_ATTACHED_CHILDREN:
+ DBUG_RETURN(loop_partitions(extra_cb, &operation));
+ case HA_EXTRA_DETACH_CHILDREN:
+ DBUG_RETURN(loop_partitions(extra_cb, &operation));
+ case HA_EXTRA_MARK_AS_LOG_TABLE:
/*
http://dev.mysql.com/doc/refman/5.1/en/partitioning-limitations.html
says we no longer support logging to partitioned tables, so we fail
here.
*/
- case HA_EXTRA_MARK_AS_LOG_TABLE:
DBUG_RETURN(ER_UNSUPORTED_LOG_ENGINE);
+ case HA_EXTRA_STARTING_ORDERED_INDEX_SCAN:
case HA_EXTRA_BEGIN_ALTER_COPY:
case HA_EXTRA_END_ALTER_COPY:
case HA_EXTRA_FAKE_START_STMT:
@@ -7293,7 +9025,7 @@ int ha_partition::extra(enum ha_extra_function operation)
break;
}
}
- DBUG_RETURN(0);
+ DBUG_RETURN(1);
}
@@ -7321,7 +9053,8 @@ int ha_partition::reset(void)
i < m_tot_parts;
i= bitmap_get_next_set(&m_partitions_to_reset, i))
{
- if ((tmp= m_file[i]->ha_reset()))
+ if (bitmap_is_set(&m_opened_partitions, i) &&
+ (tmp= m_file[i]->ha_reset()))
result= tmp;
}
bitmap_clear_all(&m_partitions_to_reset);
@@ -7329,7 +9062,7 @@ int ha_partition::reset(void)
DBUG_RETURN(result);
}
-/*
+/**
Special extra method with additional parameter
See @ref ha_partition::extra
@@ -7364,7 +9097,7 @@ int ha_partition::extra_opt(enum ha_extra_function operation, ulong arg)
default:
DBUG_ASSERT(0);
}
- DBUG_RETURN(0);
+ DBUG_RETURN(1);
}
@@ -7382,7 +9115,7 @@ int ha_partition::extra_opt(enum ha_extra_function operation, ulong arg)
void ha_partition::prepare_extra_cache(uint cachesize)
{
DBUG_ENTER("ha_partition::prepare_extra_cache");
- DBUG_PRINT("info", ("cachesize %u", cachesize));
+ DBUG_PRINT("enter", ("cachesize %u", cachesize));
m_extra_cache= TRUE;
m_extra_cache_size= cachesize;
@@ -7432,6 +9165,7 @@ int ha_partition::loop_extra_alter(enum ha_extra_function operation)
DBUG_RETURN(result);
}
+
/**
Call callback(part, param) on all partitions
@@ -7448,12 +9182,17 @@ int ha_partition::loop_partitions(handler_callback callback, void *param)
int result= 0, tmp;
uint i;
DBUG_ENTER("ha_partition::loop_partitions");
-
+
for (i= bitmap_get_first_set(&m_part_info->lock_partitions);
i < m_tot_parts;
i= bitmap_get_next_set(&m_part_info->lock_partitions, i))
{
- if ((tmp= callback(m_file[i], param)))
+ /*
+ This can be called after an error in ha_open.
+ In this case calling 'extra' can crash.
+ */
+ if (bitmap_is_set(&m_opened_partitions, i) &&
+ (tmp= callback(m_file[i], param)))
result= tmp;
}
/* Add all used partitions to be called in reset(). */
@@ -7477,9 +9216,9 @@ void ha_partition::late_extra_cache(uint partition_id)
{
handler *file;
DBUG_ENTER("ha_partition::late_extra_cache");
- DBUG_PRINT("info", ("extra_cache %u prepare %u partid %u size %u",
- m_extra_cache, m_extra_prepare_for_update,
- partition_id, m_extra_cache_size));
+ DBUG_PRINT("enter", ("extra_cache %u prepare %u partid %u size %u",
+ m_extra_cache, m_extra_prepare_for_update,
+ partition_id, m_extra_cache_size));
if (!m_extra_cache && !m_extra_prepare_for_update)
DBUG_VOID_RETURN;
@@ -7493,7 +9232,6 @@ void ha_partition::late_extra_cache(uint partition_id)
}
if (m_extra_prepare_for_update)
{
- DBUG_ASSERT(m_extra_cache);
(void) file->extra(HA_EXTRA_PREPARE_FOR_UPDATE);
}
m_extra_cache_part_id= partition_id;
@@ -7543,7 +9281,7 @@ void ha_partition::late_extra_no_cache(uint partition_id)
const key_map *ha_partition::keys_to_use_for_scanning()
{
DBUG_ENTER("ha_partition::keys_to_use_for_scanning");
- DBUG_RETURN(m_file[0]->keys_to_use_for_scanning());
+ DBUG_RETURN(get_open_file_sample()->keys_to_use_for_scanning());
}
@@ -7676,7 +9414,7 @@ ha_rows ha_partition::records_in_range(uint inx, key_range *min_key,
!= NO_CURRENT_PART_ID)
{
rows= m_file[part_id]->records_in_range(inx, min_key, max_key);
-
+
DBUG_PRINT("info", ("part %u match %lu rows of %lu", part_id, (ulong) rows,
(ulong) m_file[part_id]->stats.records));
@@ -7766,7 +9504,7 @@ double ha_partition::read_time(uint index, uint ranges, ha_rows rows)
{
DBUG_ENTER("ha_partition::read_time");
- DBUG_RETURN(m_file[0]->read_time(index, ranges, rows));
+ DBUG_RETURN(get_open_file_sample()->read_time(index, ranges, rows));
}
@@ -7778,7 +9516,8 @@ double ha_partition::read_time(uint index, uint ranges, ha_rows rows)
ha_rows ha_partition::records()
{
- ha_rows rows, tot_rows= 0;
+ int error;
+ ha_rows tot_rows= 0;
uint i;
DBUG_ENTER("ha_partition::records");
@@ -7786,11 +9525,13 @@ ha_rows ha_partition::records()
i < m_tot_parts;
i= bitmap_get_next_set(&m_part_info->read_partitions, i))
{
- rows= m_file[i]->records();
- if (rows == HA_POS_ERROR)
+ ha_rows rows;
+ if (unlikely((error= m_file[i]->pre_records()) ||
+ (rows= m_file[i]->records()) == HA_POS_ERROR))
DBUG_RETURN(HA_POS_ERROR);
tot_rows+= rows;
}
+ DBUG_PRINT("exit", ("records: %lld", (longlong) tot_rows));
DBUG_RETURN(tot_rows);
}
@@ -7814,7 +9555,7 @@ bool ha_partition::can_switch_engines()
{
handler **file;
DBUG_ENTER("ha_partition::can_switch_engines");
-
+
file= m_file;
do
{
@@ -7837,7 +9578,7 @@ uint8 ha_partition::table_cache_type()
{
DBUG_ENTER("ha_partition::table_cache_type");
- DBUG_RETURN(m_file[0]->table_cache_type());
+ DBUG_RETURN(get_open_file_sample()->table_cache_type());
}
@@ -8014,7 +9755,7 @@ void ha_partition::append_row_to_str(String &str)
{
Field *field= key_part->field;
str.append(" ");
- str.append(field->field_name);
+ str.append(&field->field_name);
str.append(":");
field_unpack(&str, field, rec, 0, false);
}
@@ -8034,7 +9775,7 @@ void ha_partition::append_row_to_str(String &str)
{
Field *field= *field_ptr;
str.append(" ");
- str.append(field->field_name);
+ str.append(&field->field_name);
str.append(":");
field_unpack(&str, field, rec, 0, false);
}
@@ -8049,12 +9790,11 @@ void ha_partition::print_error(int error, myf errflag)
{
THD *thd= ha_thd();
DBUG_ENTER("ha_partition::print_error");
-
- /* Should probably look for my own errors first */
DBUG_PRINT("enter", ("error: %d", error));
+ /* Should probably look for my own errors first */
if ((error == HA_ERR_NO_PARTITION_FOUND) &&
- ! (thd->lex->alter_info.flags & Alter_info::ALTER_TRUNCATE_PARTITION))
+ ! (thd->lex->alter_info.partition_flags & ALTER_PARTITION_TRUNCATE))
{
m_part_info->print_no_partition_found(table, errflag);
DBUG_VOID_RETURN;
@@ -8077,7 +9817,7 @@ void ha_partition::print_error(int error, myf errflag)
str.append("(");
str.append_ulonglong(m_last_part);
str.append(" != ");
- if (get_part_for_delete(m_err_rec, m_rec0, m_part_info, &part_id))
+ if (get_part_for_buf(m_err_rec, m_rec0, m_part_info, &part_id))
str.append("?");
else
str.append_ulonglong(part_id);
@@ -8169,9 +9909,10 @@ handler::Table_flags ha_partition::table_flags() const
alter_table_flags must be on handler/table level, not on hton level
due to the ha_partition hton does not know what the underlying hton is.
*/
-uint ha_partition::alter_table_flags(uint flags)
+
+alter_table_operations ha_partition::alter_table_flags(alter_table_operations flags)
{
- uint flags_to_return;
+ alter_table_operations flags_to_return;
DBUG_ENTER("ha_partition::alter_table_flags");
flags_to_return= ht->alter_table_flags(flags);
@@ -8258,7 +9999,8 @@ ha_partition::check_if_supported_inplace_alter(TABLE *altered_table,
Alter_inplace_info *ha_alter_info)
{
uint index= 0;
- enum_alter_inplace_result result= HA_ALTER_INPLACE_NO_LOCK;
+ enum_alter_inplace_result result;
+ alter_table_operations orig_ops;
ha_partition_inplace_ctx *part_inplace_ctx;
bool first_is_set= false;
THD *thd= ha_thd();
@@ -8269,8 +10011,11 @@ ha_partition::check_if_supported_inplace_alter(TABLE *altered_table,
Any other change would set partition_changed in
prep_alter_part_table() in mysql_alter_table().
*/
- if (ha_alter_info->alter_info->flags == Alter_info::ALTER_PARTITION)
+ if (ha_alter_info->alter_info->partition_flags == ALTER_PARTITION_INFO)
+ {
+ DBUG_ASSERT(ha_alter_info->alter_info->flags == 0);
DBUG_RETURN(HA_ALTER_INPLACE_NO_LOCK);
+ }
part_inplace_ctx=
new (thd->mem_root) ha_partition_inplace_ctx(thd, m_tot_parts);
@@ -8282,33 +10027,35 @@ ha_partition::check_if_supported_inplace_alter(TABLE *altered_table,
if (!part_inplace_ctx->handler_ctx_array)
DBUG_RETURN(HA_ALTER_ERROR);
- /* Set all to NULL, including the terminating one. */
- for (index= 0; index <= m_tot_parts; index++)
- part_inplace_ctx->handler_ctx_array[index]= NULL;
+ do {
+ result= HA_ALTER_INPLACE_NO_LOCK;
+ /* Set all to NULL, including the terminating one. */
+ for (index= 0; index <= m_tot_parts; index++)
+ part_inplace_ctx->handler_ctx_array[index]= NULL;
- ha_alter_info->handler_flags |= Alter_inplace_info::ALTER_PARTITIONED;
- for (index= 0; index < m_tot_parts; index++)
- {
- enum_alter_inplace_result p_result=
- m_file[index]->check_if_supported_inplace_alter(altered_table,
- ha_alter_info);
- part_inplace_ctx->handler_ctx_array[index]= ha_alter_info->handler_ctx;
-
- if (index == 0)
+ ha_alter_info->handler_flags |= ALTER_PARTITIONED;
+ orig_ops= ha_alter_info->handler_flags;
+ for (index= 0; index < m_tot_parts; index++)
{
- first_is_set= (ha_alter_info->handler_ctx != NULL);
- }
- else if (first_is_set != (ha_alter_info->handler_ctx != NULL))
- {
- /* Either none or all partitions must set handler_ctx! */
- DBUG_ASSERT(0);
- DBUG_RETURN(HA_ALTER_ERROR);
+ enum_alter_inplace_result p_result=
+ m_file[index]->check_if_supported_inplace_alter(altered_table,
+ ha_alter_info);
+ part_inplace_ctx->handler_ctx_array[index]= ha_alter_info->handler_ctx;
+
+ if (index == 0)
+ first_is_set= (ha_alter_info->handler_ctx != NULL);
+ else if (first_is_set != (ha_alter_info->handler_ctx != NULL))
+ {
+ /* Either none or all partitions must set handler_ctx! */
+ DBUG_ASSERT(0);
+ DBUG_RETURN(HA_ALTER_ERROR);
+ }
+ if (p_result < result)
+ result= p_result;
+ if (result == HA_ALTER_ERROR)
+ break;
}
- if (p_result < result)
- result= p_result;
- if (result == HA_ALTER_ERROR)
- break;
- }
+ } while (orig_ops != ha_alter_info->handler_flags);
ha_alter_info->handler_ctx= part_inplace_ctx;
/*
@@ -8334,10 +10081,13 @@ bool ha_partition::prepare_inplace_alter_table(TABLE *altered_table,
/*
Changing to similar partitioning, only update metadata.
- Non allowed changes would be caught in prep_alter_part_table().
+ Non allowed changes would be caought in prep_alter_part_table().
*/
- if (ha_alter_info->alter_info->flags == Alter_info::ALTER_PARTITION)
+ if (ha_alter_info->alter_info->partition_flags == ALTER_PARTITION_INFO)
+ {
+ DBUG_ASSERT(ha_alter_info->alter_info->flags == 0);
DBUG_RETURN(false);
+ }
part_inplace_ctx=
static_cast<class ha_partition_inplace_ctx*>(ha_alter_info->handler_ctx);
@@ -8369,8 +10119,11 @@ bool ha_partition::inplace_alter_table(TABLE *altered_table,
Changing to similar partitioning, only update metadata.
Non allowed changes would be caught in prep_alter_part_table().
*/
- if (ha_alter_info->alter_info->flags == Alter_info::ALTER_PARTITION)
+ if (ha_alter_info->alter_info->partition_flags == ALTER_PARTITION_INFO)
+ {
+ DBUG_ASSERT(ha_alter_info->alter_info->flags == 0);
DBUG_RETURN(false);
+ }
part_inplace_ctx=
static_cast<class ha_partition_inplace_ctx*>(ha_alter_info->handler_ctx);
@@ -8414,8 +10167,11 @@ bool ha_partition::commit_inplace_alter_table(TABLE *altered_table,
Changing to similar partitioning, only update metadata.
Non allowed changes would be caught in prep_alter_part_table().
*/
- if (ha_alter_info->alter_info->flags == Alter_info::ALTER_PARTITION)
+ if (ha_alter_info->alter_info->partition_flags == ALTER_PARTITION_INFO)
+ {
+ DBUG_ASSERT(ha_alter_info->alter_info->flags == 0);
DBUG_RETURN(false);
+ }
part_inplace_ctx=
static_cast<class ha_partition_inplace_ctx*>(ha_alter_info->handler_ctx);
@@ -8427,7 +10183,7 @@ bool ha_partition::commit_inplace_alter_table(TABLE *altered_table,
ha_alter_info->handler_ctx= part_inplace_ctx->handler_ctx_array[0];
error= m_file[0]->ha_commit_inplace_alter_table(altered_table,
ha_alter_info, commit);
- if (error)
+ if (unlikely(error))
goto end;
if (ha_alter_info->group_commit_ctx)
{
@@ -8560,7 +10316,7 @@ uint ha_partition::min_record_length(uint options) const
the same record. Otherwise we use the particular handler to decide if
they are the same. Sort in partition id order if not equal.
- MariaDB note:
+ MariaDB note:
Please don't merge the code from MySQL that does this:
We get two references and need to check if those records are the same.
@@ -8574,15 +10330,18 @@ uint ha_partition::min_record_length(uint options) const
int ha_partition::cmp_ref(const uchar *ref1, const uchar *ref2)
{
int cmp;
- my_ptrdiff_t diff1, diff2;
+ uint32 diff1, diff2;
DBUG_ENTER("ha_partition::cmp_ref");
- cmp = m_file[0]->cmp_ref((ref1 + PARTITION_BYTES_IN_POS),
- (ref2 + PARTITION_BYTES_IN_POS));
+ cmp= get_open_file_sample()->cmp_ref((ref1 + PARTITION_BYTES_IN_POS),
+ (ref2 + PARTITION_BYTES_IN_POS));
if (cmp)
DBUG_RETURN(cmp);
- if ((ref1[0] == ref2[0]) && (ref1[1] == ref2[1]))
+ diff2= uint2korr(ref2);
+ diff1= uint2korr(ref1);
+
+ if (diff1 == diff2)
{
/* This means that the references are same and are in same partition.*/
DBUG_RETURN(0);
@@ -8595,22 +10354,7 @@ int ha_partition::cmp_ref(const uchar *ref1, const uchar *ref2)
Remove this assert if DB_ROW_ID is changed to be per partition.
*/
DBUG_ASSERT(!m_innodb);
-
- diff1= ref2[1] - ref1[1];
- diff2= ref2[0] - ref1[0];
- if (diff1 > 0)
- {
- DBUG_RETURN(-1);
- }
- if (diff1 < 0)
- {
- DBUG_RETURN(+1);
- }
- if (diff2 > 0)
- {
- DBUG_RETURN(-1);
- }
- DBUG_RETURN(+1);
+ DBUG_RETURN(diff2 > diff1 ? -1 : 1);
}
@@ -8619,6 +10363,82 @@ int ha_partition::cmp_ref(const uchar *ref1, const uchar *ref2)
****************************************************************************/
+/**
+ Retreive new values for part_share->next_auto_inc_val if needed
+
+ This is needed if the value has not been initialized or if one of
+ the underlying partitions require that the value should be re-calculated
+*/
+
+void ha_partition::update_next_auto_inc_val()
+{
+ if (!part_share->auto_inc_initialized ||
+ need_info_for_auto_inc())
+ info(HA_STATUS_AUTO);
+}
+
+
+/**
+ Determine whether a partition needs auto-increment initialization.
+
+ @return
+ TRUE A partition needs auto-increment initialization
+ FALSE No partition needs auto-increment initialization
+
+ Resets part_share->auto_inc_initialized if next auto_increment needs to be
+ recalculated.
+*/
+
+bool ha_partition::need_info_for_auto_inc()
+{
+ handler **file= m_file;
+ DBUG_ENTER("ha_partition::need_info_for_auto_inc");
+
+ do
+ {
+ if ((*file)->need_info_for_auto_inc())
+ {
+ /* We have to get new auto_increment values from handler */
+ part_share->auto_inc_initialized= FALSE;
+ DBUG_RETURN(TRUE);
+ }
+ } while (*(++file));
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ Determine if all partitions can use the current auto-increment value for
+ auto-increment initialization.
+
+ @return
+ TRUE All partitions can use the current auto-increment
+ value for auto-increment initialization
+ FALSE All partitions cannot use the current
+ auto-increment value for auto-increment
+ initialization
+
+ Notes
+ This function is only called for ::info(HA_STATUS_AUTO) and is
+ mainly used by the Spider engine, which returns false
+ except in the case of DROP TABLE or ALTER TABLE when it returns TRUE.
+ Other engines always returns TRUE for this call.
+*/
+
+bool ha_partition::can_use_for_auto_inc_init()
+{
+ handler **file= m_file;
+ DBUG_ENTER("ha_partition::can_use_for_auto_inc_init");
+
+ do
+ {
+ if (!(*file)->can_use_for_auto_inc_init())
+ DBUG_RETURN(FALSE);
+ } while (*(++file));
+ DBUG_RETURN(TRUE);
+}
+
+
int ha_partition::reset_auto_increment(ulonglong value)
{
handler **file= m_file;
@@ -8652,8 +10472,8 @@ void ha_partition::get_auto_increment(ulonglong offset, ulonglong increment,
ulonglong *nb_reserved_values)
{
DBUG_ENTER("ha_partition::get_auto_increment");
- DBUG_PRINT("info", ("offset: %lu inc: %lu desired_values: %lu "
- "first_value: %lu", (ulong) offset, (ulong) increment,
+ DBUG_PRINT("enter", ("offset: %lu inc: %lu desired_values: %lu "
+ "first_value: %lu", (ulong) offset, (ulong) increment,
(ulong) nb_desired_values, (ulong) *first_value));
DBUG_ASSERT(increment);
DBUG_ASSERT(nb_desired_values);
@@ -8675,7 +10495,7 @@ void ha_partition::get_auto_increment(ulonglong offset, ulonglong increment,
/* Only nb_desired_values = 1 makes sense */
(*file)->get_auto_increment(offset, increment, 1,
&first_value_part, &nb_reserved_values_part);
- if (first_value_part == ULONGLONG_MAX) // error in one partition
+ if (unlikely(first_value_part == ULONGLONG_MAX)) // error in one partition
{
*first_value= first_value_part;
/* log that the error was between table/partition handler */
@@ -8698,7 +10518,7 @@ void ha_partition::get_auto_increment(ulonglong offset, ulonglong increment,
/*
Get a lock for handling the auto_increment in part_share
for avoiding two concurrent statements getting the same number.
- */
+ */
lock_auto_increment();
@@ -8709,8 +10529,8 @@ void ha_partition::get_auto_increment(ulonglong offset, ulonglong increment,
based replication. Because the statement-based binary log contains
only the first generated value used by the statement, and slaves assumes
all other generated values used by this statement were consecutive to
- this first one, we must exclusively lock the generator until the statement
- is done.
+ this first one, we must exclusively lock the generator until the
+ statement is done.
*/
if (!auto_increment_safe_stmt_log_lock &&
thd->lex->sql_command != SQLCOM_INSERT &&
@@ -8799,27 +10619,6 @@ void ha_partition::init_table_handle_for_HANDLER()
}
-/**
- Return the checksum of the table (all partitions)
-*/
-
-uint ha_partition::checksum() const
-{
- ha_checksum sum= 0;
-
- DBUG_ENTER("ha_partition::checksum");
- if ((table_flags() & (HA_HAS_OLD_CHECKSUM | HA_HAS_NEW_CHECKSUM)))
- {
- handler **file= m_file;
- do
- {
- sum+= (*file)->checksum();
- } while (*(++file));
- }
- DBUG_RETURN(sum);
-}
-
-
/****************************************************************************
MODULE enable/disable indexes
****************************************************************************/
@@ -8842,7 +10641,7 @@ int ha_partition::disable_indexes(uint mode)
DBUG_ASSERT(bitmap_is_set_all(&(m_part_info->lock_partitions)));
for (file= m_file; *file; file++)
{
- if ((error= (*file)->ha_disable_indexes(mode)))
+ if (unlikely((error= (*file)->ha_disable_indexes(mode))))
break;
}
return error;
@@ -8867,7 +10666,7 @@ int ha_partition::enable_indexes(uint mode)
DBUG_ASSERT(bitmap_is_set_all(&(m_part_info->lock_partitions)));
for (file= m_file; *file; file++)
{
- if ((error= (*file)->ha_enable_indexes(mode)))
+ if (unlikely((error= (*file)->ha_enable_indexes(mode))))
break;
}
return error;
@@ -8892,7 +10691,7 @@ int ha_partition::indexes_are_disabled(void)
DBUG_ASSERT(bitmap_is_set_all(&(m_part_info->lock_partitions)));
for (file= m_file; *file; file++)
{
- if ((error= (*file)->indexes_are_disabled()))
+ if (unlikely((error= (*file)->indexes_are_disabled())))
break;
}
return error;
@@ -8906,8 +10705,8 @@ int ha_partition::indexes_are_disabled(void)
@param repair If true, move misplaced rows to correct partition.
@return Operation status.
- @retval 0 Success
- @retval != 0 Error
+ @retval HA_ADMIN_OK Success
+ @retval != HA_ADMIN_OK Error
*/
int ha_partition::check_misplaced_rows(uint read_part_id, bool do_repair)
@@ -8921,6 +10720,17 @@ int ha_partition::check_misplaced_rows(uint read_part_id, bool do_repair)
DBUG_ASSERT(m_file);
+ if (m_part_info->vers_info &&
+ read_part_id != m_part_info->vers_info->now_part->id &&
+ !m_part_info->vers_info->interval.is_set())
+ {
+ print_admin_msg(ha_thd(), MYSQL_ERRMSG_SIZE, "note",
+ table_share->db.str, table->alias,
+ opt_op_name[CHECK_PARTS],
+ "Not supported for non-INTERVAL history partitions");
+ DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
+ }
+
if (do_repair)
{
/* We must read the full row, if we need to move it! */
@@ -8942,8 +10752,6 @@ int ha_partition::check_misplaced_rows(uint read_part_id, bool do_repair)
{
if ((result= m_file[read_part_id]->ha_rnd_next(m_rec0)))
{
- if (result == HA_ERR_RECORD_DELETED)
- continue;
if (result != HA_ERR_END_OF_FILE)
break;
@@ -8981,8 +10789,8 @@ int ha_partition::check_misplaced_rows(uint read_part_id, bool do_repair)
}
else
{
- DBUG_PRINT("info", ("Moving row from partition %d to %d",
- read_part_id, correct_part_id));
+ DBUG_PRINT("info", ("Moving row from partition %u to %u",
+ (uint) read_part_id, (uint) correct_part_id));
/*
Insert row into correct partition. Notice that there are no commit
@@ -9013,19 +10821,19 @@ int ha_partition::check_misplaced_rows(uint read_part_id, bool do_repair)
{
/* Log this error, so the DBA can notice it and fix it! */
sql_print_error("Table '%-192s' failed to move/insert a row"
- " from part %d into part %d:\n%s",
+ " from part %u into part %u:\n%s",
table->s->table_name.str,
- read_part_id,
- correct_part_id,
+ (uint) read_part_id,
+ (uint) correct_part_id,
str.c_ptr_safe());
}
print_admin_msg(ha_thd(), MYSQL_ERRMSG_SIZE, "error",
table_share->db.str, table->alias,
opt_op_name[REPAIR_PARTS],
"Failed to move/insert a row"
- " from part %d into part %d:\n%s",
- read_part_id,
- correct_part_id,
+ " from part %u into part %u:\n%s",
+ (uint) read_part_id,
+ (uint) correct_part_id,
str.c_ptr_safe());
break;
}
@@ -9046,14 +10854,14 @@ int ha_partition::check_misplaced_rows(uint read_part_id, bool do_repair)
append_row_to_str(str);
/* Log this error, so the DBA can notice it and fix it! */
- sql_print_error("Table '%-192s': Delete from part %d failed with"
+ sql_print_error("Table '%-192s': Delete from part %u failed with"
" error %d. But it was already inserted into"
- " part %d, when moving the misplaced row!"
+ " part %u, when moving the misplaced row!"
"\nPlease manually fix the duplicate row:\n%s",
table->s->table_name.str,
- read_part_id,
+ (uint) read_part_id,
result,
- correct_part_id,
+ (uint) correct_part_id,
str.c_ptr_safe());
break;
}
@@ -9131,10 +10939,8 @@ int ha_partition::check_for_upgrade(HA_CHECK_OPT *check_opt)
partition_info::enum_key_algorithm old_algorithm;
old_algorithm= m_part_info->key_algorithm;
error= HA_ADMIN_FAILED;
- append_identifier(ha_thd(), &db_name, table_share->db.str,
- table_share->db.length);
- append_identifier(ha_thd(), &table_name, table_share->table_name.str,
- table_share->table_name.length);
+ append_identifier(ha_thd(), &db_name, &table_share->db);
+ append_identifier(ha_thd(), &table_name, &table_share->table_name);
if (m_part_info->key_algorithm != partition_info::KEY_ALGORITHM_NONE)
{
/*
@@ -9146,8 +10952,8 @@ int ha_partition::check_for_upgrade(HA_CHECK_OPT *check_opt)
m_part_info->key_algorithm= partition_info::KEY_ALGORITHM_51;
if (skip_generation ||
!(part_buf= generate_partition_syntax_for_frm(thd, m_part_info,
- &part_buf_len,
- NULL, NULL)) ||
+ &part_buf_len,
+ NULL, NULL)) ||
print_admin_msg(thd, SQL_ADMIN_MSG_TEXT_SIZE + 1, "error",
table_share->db.str,
table->alias,
@@ -9181,6 +10987,20 @@ int ha_partition::check_for_upgrade(HA_CHECK_OPT *check_opt)
}
+TABLE_LIST *ha_partition::get_next_global_for_child()
+{
+ handler **file;
+ DBUG_ENTER("ha_partition::get_next_global_for_child");
+ for (file= m_file; *file; file++)
+ {
+ TABLE_LIST *table_list;
+ if ((table_list= (*file)->get_next_global_for_child()))
+ DBUG_RETURN(table_list);
+ }
+ DBUG_RETURN(0);
+}
+
+
/**
Push an engine condition to the condition stack of the storage engine
for each partition.
@@ -9199,6 +11019,22 @@ 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
+ */
+ do
+ {
+ if (((*file)->set_top_table_and_fields(top_table,
+ top_table_field,
+ top_table_fields)))
+ DBUG_RETURN(cond); // Abort cond push, no error
+ } while (*(++file));
+ file= m_file;
+ }
+
do
{
if ((*file)->pushed_cond != cond)
@@ -9231,6 +11067,609 @@ void ha_partition::cond_pop()
}
+/**
+ Perform bulk update preparation on each partition.
+
+ SYNOPSIS
+ start_bulk_update()
+
+ RETURN VALUE
+ TRUE Error
+ FALSE Success
+*/
+
+bool ha_partition::start_bulk_update()
+{
+ handler **file= m_file;
+ DBUG_ENTER("ha_partition::start_bulk_update");
+
+ if (bitmap_is_overlapping(&m_part_info->full_part_field_set,
+ table->write_set))
+ DBUG_RETURN(TRUE);
+
+ do
+ {
+ if ((*file)->start_bulk_update())
+ DBUG_RETURN(TRUE);
+ } while (*(++file));
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ Perform bulk update execution on each partition. A bulk update allows
+ a handler to batch the updated rows instead of performing the updates
+ one row at a time.
+
+ SYNOPSIS
+ exec_bulk_update()
+
+ RETURN VALUE
+ TRUE Error
+ FALSE Success
+*/
+
+int ha_partition::exec_bulk_update(ha_rows *dup_key_found)
+{
+ int error;
+ handler **file= m_file;
+ DBUG_ENTER("ha_partition::exec_bulk_update");
+
+ do
+ {
+ if (unlikely((error= (*file)->exec_bulk_update(dup_key_found))))
+ DBUG_RETURN(error);
+ } while (*(++file));
+ DBUG_RETURN(0);
+}
+
+
+/**
+ Perform bulk update cleanup on each partition.
+
+ SYNOPSIS
+ end_bulk_update()
+
+ RETURN VALUE
+ NONE
+*/
+
+int ha_partition::end_bulk_update()
+{
+ int error= 0;
+ handler **file= m_file;
+ DBUG_ENTER("ha_partition::end_bulk_update");
+
+ do
+ {
+ int tmp;
+ if ((tmp= (*file)->end_bulk_update()))
+ error= tmp;
+ } while (*(++file));
+ DBUG_RETURN(error);
+}
+
+
+/**
+ Add the row to the bulk update on the partition on which the row is stored.
+ A bulk update allows a handler to batch the updated rows instead of
+ performing the updates one row at a time.
+
+ SYNOPSIS
+ bulk_update_row()
+ old_data Old record
+ new_data New record
+ dup_key_found Number of duplicate keys found
+
+ RETURN VALUE
+ >1 Error
+ 1 Bulk update not used, normal operation used
+ 0 Bulk update used by handler
+*/
+
+int ha_partition::bulk_update_row(const uchar *old_data, const uchar *new_data,
+ ha_rows *dup_key_found)
+{
+ int error= 0;
+ uint32 part_id;
+ longlong func_value;
+ my_bitmap_map *old_map;
+ DBUG_ENTER("ha_partition::bulk_update_row");
+
+ 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);
+ if (unlikely(error))
+ {
+ m_part_info->err_value= func_value;
+ goto end;
+ }
+
+ error= m_file[part_id]->ha_bulk_update_row(old_data, new_data,
+ dup_key_found);
+
+end:
+ DBUG_RETURN(error);
+}
+
+
+/**
+ Perform bulk delete preparation on each partition.
+
+ SYNOPSIS
+ start_bulk_delete()
+
+ RETURN VALUE
+ TRUE Error
+ FALSE Success
+*/
+
+bool ha_partition::start_bulk_delete()
+{
+ handler **file= m_file;
+ DBUG_ENTER("ha_partition::start_bulk_delete");
+
+ do
+ {
+ if ((*file)->start_bulk_delete())
+ DBUG_RETURN(TRUE);
+ } while (*(++file));
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ Perform bulk delete cleanup on each partition.
+
+ SYNOPSIS
+ end_bulk_delete()
+
+ RETURN VALUE
+ >0 Error
+ 0 Success
+*/
+
+int ha_partition::end_bulk_delete()
+{
+ int error= 0;
+ handler **file= m_file;
+ DBUG_ENTER("ha_partition::end_bulk_delete");
+
+ do
+ {
+ int tmp;
+ if ((tmp= (*file)->end_bulk_delete()))
+ error= tmp;
+ } while (*(++file));
+ DBUG_RETURN(error);
+}
+
+
+/**
+ Perform initialization for a direct update request.
+
+ SYNOPSIS
+ direct_update_rows_init()
+ update fields Pointer to the list of fields to update
+
+ RETURN VALUE
+ >0 Error
+ 0 Success
+*/
+
+int ha_partition::direct_update_rows_init(List<Item> *update_fields)
+{
+ int error;
+ uint i, found;
+ handler *file;
+ DBUG_ENTER("ha_partition::direct_update_rows_init");
+
+ if (bitmap_is_overlapping(&m_part_info->full_part_field_set,
+ table->write_set))
+ {
+ DBUG_PRINT("info", ("partition FALSE by updating part_key"));
+ DBUG_RETURN(HA_ERR_WRONG_COMMAND);
+ }
+
+ m_part_spec.start_part= 0;
+ m_part_spec.end_part= m_tot_parts - 1;
+ m_direct_update_part_spec= m_part_spec;
+
+ found= 0;
+ for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++)
+ {
+ if (bitmap_is_set(&(m_part_info->read_partitions), i) &&
+ bitmap_is_set(&(m_part_info->lock_partitions), i))
+ {
+ file= m_file[i];
+ if (unlikely((error= (m_pre_calling ?
+ file->pre_direct_update_rows_init(update_fields) :
+ file->direct_update_rows_init(update_fields)))))
+ {
+ DBUG_PRINT("info", ("partition FALSE by storage engine"));
+ DBUG_RETURN(error);
+ }
+ found++;
+ }
+ }
+
+ TABLE_LIST *table_list= table->pos_in_table_list;
+ if (found != 1 && table_list)
+ {
+ while (table_list->parent_l)
+ table_list= table_list->parent_l;
+ st_select_lex *select_lex= table_list->select_lex;
+ DBUG_PRINT("info", ("partition select_lex: %p", select_lex));
+ if (select_lex && select_lex->explicit_limit)
+ {
+ DBUG_PRINT("info", ("partition explicit_limit=TRUE"));
+ DBUG_PRINT("info", ("partition offset_limit: %p",
+ select_lex->offset_limit));
+ DBUG_PRINT("info", ("partition select_limit: %p",
+ select_lex->select_limit));
+ DBUG_PRINT("info", ("partition FALSE by select_lex"));
+ DBUG_RETURN(HA_ERR_WRONG_COMMAND);
+ }
+ }
+ DBUG_PRINT("info", ("partition OK"));
+ DBUG_RETURN(0);
+}
+
+
+/**
+ Do initialization for performing parallel direct update
+ for a handlersocket update request.
+
+ SYNOPSIS
+ pre_direct_update_rows_init()
+ update fields Pointer to the list of fields to update
+
+ RETURN VALUE
+ >0 Error
+ 0 Success
+*/
+
+int ha_partition::pre_direct_update_rows_init(List<Item> *update_fields)
+{
+ bool save_m_pre_calling;
+ int error;
+ DBUG_ENTER("ha_partition::pre_direct_update_rows_init");
+ save_m_pre_calling= m_pre_calling;
+ m_pre_calling= TRUE;
+ error= direct_update_rows_init(update_fields);
+ m_pre_calling= save_m_pre_calling;
+ DBUG_RETURN(error);
+}
+
+
+/**
+ Execute a direct update request. A direct update request updates all
+ qualified rows in a single operation, rather than one row at a time.
+ The direct update operation is pushed down to each individual
+ partition.
+
+ SYNOPSIS
+ direct_update_rows()
+ update_rows Number of updated rows
+
+ RETURN VALUE
+ >0 Error
+ 0 Success
+*/
+
+int ha_partition::direct_update_rows(ha_rows *update_rows_result)
+{
+ int error;
+ bool rnd_seq= FALSE;
+ ha_rows update_rows= 0;
+ uint32 i;
+ DBUG_ENTER("ha_partition::direct_update_rows");
+
+ /* If first call to direct_update_rows with RND scan */
+ if ((m_pre_calling ? pre_inited : inited) == RND && m_scan_value == 1)
+ {
+ rnd_seq= TRUE;
+ m_scan_value= 2;
+ }
+
+ *update_rows_result= 0;
+ for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++)
+ {
+ handler *file= m_file[i];
+ if (bitmap_is_set(&(m_part_info->read_partitions), i) &&
+ bitmap_is_set(&(m_part_info->lock_partitions), i))
+ {
+ if (rnd_seq && (m_pre_calling ? file->pre_inited : file->inited) == NONE)
+ {
+ if (unlikely((error= (m_pre_calling ?
+ file->ha_pre_rnd_init(TRUE) :
+ file->ha_rnd_init(TRUE)))))
+ DBUG_RETURN(error);
+ }
+ if (unlikely((error= (m_pre_calling ?
+ (file)->pre_direct_update_rows() :
+ (file)->ha_direct_update_rows(&update_rows)))))
+ {
+ if (rnd_seq)
+ {
+ if (m_pre_calling)
+ file->ha_pre_rnd_end();
+ else
+ file->ha_rnd_end();
+ }
+ DBUG_RETURN(error);
+ }
+ *update_rows_result+= update_rows;
+ }
+ if (rnd_seq)
+ {
+ if (unlikely((error= (m_pre_calling ?
+ file->ha_pre_index_or_rnd_end() :
+ file->ha_index_or_rnd_end()))))
+ DBUG_RETURN(error);
+ }
+ }
+ DBUG_RETURN(0);
+}
+
+
+/**
+ Start parallel execution of a direct update for a handlersocket update
+ request. A direct update request updates all qualified rows in a single
+ operation, rather than one row at a time. The direct update operation
+ is pushed down to each individual partition.
+
+ SYNOPSIS
+ pre_direct_update_rows()
+
+ RETURN VALUE
+ >0 Error
+ 0 Success
+*/
+
+int ha_partition::pre_direct_update_rows()
+{
+ bool save_m_pre_calling;
+ int error;
+ ha_rows not_used= 0;
+ DBUG_ENTER("ha_partition::pre_direct_update_rows");
+ save_m_pre_calling= m_pre_calling;
+ m_pre_calling= TRUE;
+ error= direct_update_rows(&not_used);
+ m_pre_calling= save_m_pre_calling;
+ DBUG_RETURN(error);
+}
+
+
+/**
+ Perform initialization for a direct delete request.
+
+ SYNOPSIS
+ direct_delete_rows_init()
+
+ RETURN VALUE
+ >0 Error
+ 0 Success
+*/
+
+int ha_partition::direct_delete_rows_init()
+{
+ int error;
+ uint i, found;
+ DBUG_ENTER("ha_partition::direct_delete_rows_init");
+
+ m_part_spec.start_part= 0;
+ m_part_spec.end_part= m_tot_parts - 1;
+ m_direct_update_part_spec= m_part_spec;
+
+ found= 0;
+ for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++)
+ {
+ if (bitmap_is_set(&(m_part_info->read_partitions), i) &&
+ bitmap_is_set(&(m_part_info->lock_partitions), i))
+ {
+ handler *file= m_file[i];
+ if (unlikely((error= (m_pre_calling ?
+ file->pre_direct_delete_rows_init() :
+ file->direct_delete_rows_init()))))
+ {
+ DBUG_PRINT("exit", ("error in direct_delete_rows_init"));
+ DBUG_RETURN(error);
+ }
+ found++;
+ }
+ }
+
+ TABLE_LIST *table_list= table->pos_in_table_list;
+ if (found != 1 && table_list)
+ {
+ while (table_list->parent_l)
+ table_list= table_list->parent_l;
+ st_select_lex *select_lex= table_list->select_lex;
+ DBUG_PRINT("info", ("partition select_lex: %p", select_lex));
+ if (select_lex && select_lex->explicit_limit)
+ {
+ DBUG_PRINT("info", ("partition explicit_limit: TRUE"));
+ DBUG_PRINT("info", ("partition offset_limit: %p",
+ select_lex->offset_limit));
+ DBUG_PRINT("info", ("partition select_limit: %p",
+ select_lex->select_limit));
+ DBUG_PRINT("info", ("partition FALSE by select_lex"));
+ DBUG_RETURN(HA_ERR_WRONG_COMMAND);
+ }
+ }
+ DBUG_PRINT("exit", ("OK"));
+ DBUG_RETURN(0);
+}
+
+
+/**
+ Do initialization for performing parallel direct delete
+ for a handlersocket delete request.
+
+ SYNOPSIS
+ pre_direct_delete_rows_init()
+
+ RETURN VALUE
+ >0 Error
+ 0 Success
+*/
+
+int ha_partition::pre_direct_delete_rows_init()
+{
+ bool save_m_pre_calling;
+ int error;
+ DBUG_ENTER("ha_partition::pre_direct_delete_rows_init");
+ save_m_pre_calling= m_pre_calling;
+ m_pre_calling= TRUE;
+ error= direct_delete_rows_init();
+ m_pre_calling= save_m_pre_calling;
+ DBUG_RETURN(error);
+}
+
+
+/**
+ Execute a direct delete request. A direct delete request deletes all
+ qualified rows in a single operation, rather than one row at a time.
+ The direct delete operation is pushed down to each individual
+ partition.
+
+ SYNOPSIS
+ direct_delete_rows()
+ delete_rows Number of deleted rows
+
+ RETURN VALUE
+ >0 Error
+ 0 Success
+*/
+
+int ha_partition::direct_delete_rows(ha_rows *delete_rows_result)
+{
+ int error;
+ bool rnd_seq= FALSE;
+ ha_rows delete_rows= 0;
+ uint32 i;
+ handler *file;
+ DBUG_ENTER("ha_partition::direct_delete_rows");
+
+ if ((m_pre_calling ? pre_inited : inited) == RND && m_scan_value == 1)
+ {
+ rnd_seq= TRUE;
+ m_scan_value= 2;
+ }
+
+ *delete_rows_result= 0;
+ m_part_spec= m_direct_update_part_spec;
+ for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++)
+ {
+ file= m_file[i];
+ if (bitmap_is_set(&(m_part_info->read_partitions), i) &&
+ bitmap_is_set(&(m_part_info->lock_partitions), i))
+ {
+ if (rnd_seq && (m_pre_calling ? file->pre_inited : file->inited) == NONE)
+ {
+ if (unlikely((error= (m_pre_calling ?
+ file->ha_pre_rnd_init(TRUE) :
+ file->ha_rnd_init(TRUE)))))
+ DBUG_RETURN(error);
+ }
+ if ((error= (m_pre_calling ?
+ file->pre_direct_delete_rows() :
+ file->ha_direct_delete_rows(&delete_rows))))
+ {
+ if (m_pre_calling)
+ file->ha_pre_rnd_end();
+ else
+ file->ha_rnd_end();
+ DBUG_RETURN(error);
+ }
+ delete_rows_result+= delete_rows;
+ }
+ if (rnd_seq)
+ {
+ if (unlikely((error= (m_pre_calling ?
+ file->ha_pre_index_or_rnd_end() :
+ file->ha_index_or_rnd_end()))))
+ DBUG_RETURN(error);
+ }
+ }
+ DBUG_RETURN(0);
+}
+
+
+/**
+ Start parallel execution of a direct delete for a handlersocket delete
+ request. A direct delete request deletes all qualified rows in a single
+ operation, rather than one row at a time. The direct delete operation
+ is pushed down to each individual partition.
+
+ SYNOPSIS
+ pre_direct_delete_rows()
+
+ RETURN VALUE
+ >0 Error
+ 0 Success
+*/
+
+int ha_partition::pre_direct_delete_rows()
+{
+ bool save_m_pre_calling;
+ int error;
+ ha_rows not_used;
+ DBUG_ENTER("ha_partition::pre_direct_delete_rows");
+ save_m_pre_calling= m_pre_calling;
+ m_pre_calling= TRUE;
+ error= direct_delete_rows(&not_used);
+ m_pre_calling= save_m_pre_calling;
+ DBUG_RETURN(error);
+}
+
+/**
+ Push metadata for the current operation down to each partition.
+
+ SYNOPSIS
+ info_push()
+
+ RETURN VALUE
+ >0 Error
+ 0 Success
+*/
+
+int ha_partition::info_push(uint info_type, void *info)
+{
+ int error= 0;
+ handler **file= m_file;
+ DBUG_ENTER("ha_partition::info_push");
+
+ do
+ {
+ int tmp;
+ if ((tmp= (*file)->info_push(info_type, info)))
+ error= tmp;
+ } while (*(++file));
+ DBUG_RETURN(error);
+}
+
+
+void ha_partition::clear_top_table_fields()
+{
+ handler **file;
+ 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 (file= m_file; *file; file++)
+ (*file)->clear_top_table_fields();
+ }
+ DBUG_VOID_RETURN;
+}
+
+
struct st_mysql_storage_engine partition_storage_engine=
{ MYSQL_HANDLERTON_INTERFACE_VERSION };
diff --git a/sql/ha_partition.h b/sql/ha_partition.h
index a48aa639237..6a12c8a4800 100644
--- a/sql/ha_partition.h
+++ b/sql/ha_partition.h
@@ -48,10 +48,8 @@ public:
{
uint i;
for (i= 0; i < num_parts; i++)
- if (ha_shares[i])
- delete ha_shares[i];
- if (ha_shares)
- delete [] ha_shares;
+ delete ha_shares[i];
+ delete[] ha_shares;
}
bool init(uint arg_num_parts)
{
@@ -69,6 +67,21 @@ public:
}
};
+class ha_partition;
+
+/* Partition Full Text Search info */
+struct st_partition_ft_info
+{
+ struct _ft_vft *please;
+ st_partition_ft_info *next;
+ ha_partition *file;
+ FT_INFO **part_ft_info;
+};
+
+
+#ifdef HAVE_PSI_MUTEX_INTERFACE
+extern PSI_mutex_key key_partition_auto_inc_mutex;
+#endif
/**
Partition specific Handler_share.
@@ -79,6 +92,7 @@ public:
bool auto_inc_initialized;
mysql_mutex_t auto_inc_mutex; /**< protecting auto_inc val */
ulonglong next_auto_inc_val; /**< first non reserved value */
+ ulonglong prev_auto_inc_val; /**< stored next_auto_inc_val */
/**
Hash of partition names. Initialized in the first ha_partition::open()
for the table_share. After that it is read-only, i.e. no locking required.
@@ -86,30 +100,175 @@ public:
bool partition_name_hash_initialized;
HASH partition_name_hash;
/** Storage for each partitions Handler_share */
- Parts_share_refs *partitions_share_refs;
- Partition_share() {}
+ Parts_share_refs partitions_share_refs;
+ Partition_share()
+ : auto_inc_initialized(false),
+ next_auto_inc_val(0),
+ prev_auto_inc_val(0),
+ partition_name_hash_initialized(false),
+ partition_names(NULL)
+ {
+ mysql_mutex_init(key_partition_auto_inc_mutex,
+ &auto_inc_mutex,
+ MY_MUTEX_INIT_FAST);
+ }
+
~Partition_share()
{
- DBUG_ENTER("Partition_share::~Partition_share");
mysql_mutex_destroy(&auto_inc_mutex);
+ if (partition_names)
+ {
+ my_free(partition_names);
+ }
if (partition_name_hash_initialized)
+ {
my_hash_free(&partition_name_hash);
- if (partitions_share_refs)
- delete partitions_share_refs;
- DBUG_VOID_RETURN;
+ }
}
+
bool init(uint num_parts);
- void lock_auto_inc()
+
+ /**
+ Release reserved auto increment values not used.
+ @param thd Thread.
+ @param table_share Table Share
+ @param next_insert_id Next insert id (first non used auto inc value).
+ @param max_reserved End of reserved auto inc range.
+ */
+ void release_auto_inc_if_possible(THD *thd, TABLE_SHARE *table_share,
+ const ulonglong next_insert_id,
+ const ulonglong max_reserved);
+
+ /** lock mutex protecting auto increment value next_auto_inc_val. */
+ inline void lock_auto_inc()
{
mysql_mutex_lock(&auto_inc_mutex);
}
- void unlock_auto_inc()
+ /** unlock mutex protecting auto increment value next_auto_inc_val. */
+ inline void unlock_auto_inc()
{
mysql_mutex_unlock(&auto_inc_mutex);
}
+ /**
+ Populate partition_name_hash with partition and subpartition names
+ from part_info.
+ @param part_info Partition info containing all partitions metadata.
+
+ @return Operation status.
+ @retval false Success.
+ @retval true Failure.
+ */
+ bool populate_partition_name_hash(partition_info *part_info);
+ /** Get partition name.
+
+ @param part_id Partition id (for subpartitioned table only subpartition
+ names will be returned.)
+
+ @return partition name or NULL if error.
+ */
+ const char *get_partition_name(size_t part_id) const;
+private:
+ const uchar **partition_names;
+ /**
+ Insert [sub]partition name into partition_name_hash
+ @param name Partition name.
+ @param part_id Partition id.
+ @param is_subpart True if subpartition else partition.
+
+ @return Operation status.
+ @retval false Success.
+ @retval true Failure.
+ */
+ bool insert_partition_name_in_hash(const char *name,
+ uint part_id,
+ bool is_subpart);
};
+/*
+ List of ranges to be scanned by ha_partition's MRR implementation
+
+ This object is
+ - A KEY_MULTI_RANGE structure (the MRR range)
+ - Storage for the range endpoints that the KEY_MULTI_RANGE has pointers to
+ - list of such ranges (connected through the "next" pointer).
+*/
+
+typedef struct st_partition_key_multi_range
+{
+ /*
+ Number of the range. The ranges are numbered in the order RANGE_SEQ_IF has
+ emitted them, starting from 1. The numbering in used by ordered MRR scans.
+ */
+ uint id;
+ uchar *key[2];
+ /*
+ Sizes of allocated memory in key[]. These may be larger then the actual
+ values as this structure is reused across MRR scans
+ */
+ uint length[2];
+
+ /*
+ The range.
+ key_multi_range.ptr is a pointer to the this PARTITION_KEY_MULTI_RANGE
+ object
+ */
+ KEY_MULTI_RANGE key_multi_range;
+
+ // Range id from the SQL layer
+ range_id_t ptr;
+
+ // The next element in the list of MRR ranges.
+ st_partition_key_multi_range *next;
+} PARTITION_KEY_MULTI_RANGE;
+
+
+/*
+ List of ranges to be scanned in a certain [sub]partition
+
+ The idea is that there's a list of ranges to be scanned in the table
+ (formed by PARTITION_KEY_MULTI_RANGE structures),
+ and for each [sub]partition, we only need to scan a subset of that list.
+
+ PKMR1 --> PKMR2 --> PKMR3 -->... // list of PARTITION_KEY_MULTI_RANGE
+ ^ ^
+ | |
+ PPKMR1 ----------> PPKMR2 -->... // list of PARTITION_PART_KEY_MULTI_RANGE
+
+ This way, per-partition lists of PARTITION_PART_KEY_MULTI_RANGE have pointers
+ to the elements of the global list of PARTITION_KEY_MULTI_RANGE.
+*/
+
+typedef struct st_partition_part_key_multi_range
+{
+ PARTITION_KEY_MULTI_RANGE *partition_key_multi_range;
+ st_partition_part_key_multi_range *next;
+} PARTITION_PART_KEY_MULTI_RANGE;
+
+
+class ha_partition;
+
+/*
+ The structure holding information about range sequence to be used with one
+ partition.
+ (pointer to this is used as seq_init_param for RANGE_SEQ_IF structure when
+ invoking MRR for an individual partition)
+*/
+
+typedef struct st_partition_part_key_multi_range_hld
+{
+ /* Owner object */
+ ha_partition *partition;
+
+ /* id of the the partition this structure is for */
+ uint32 part_id;
+
+ /* Current range we're iterating through */
+ PARTITION_PART_KEY_MULTI_RANGE *partition_part_key_multi_range;
+} PARTITION_PART_KEY_MULTI_RANGE_HLD;
+
+
+extern "C" int cmp_key_part_id(void *key_p, uchar *ref1, uchar *ref2);
extern "C" int cmp_key_rowid_part_id(void *ptr, uchar *ref1, uchar *ref2);
class ha_partition :public handler
@@ -119,16 +278,17 @@ private:
{
partition_index_read= 0,
partition_index_first= 1,
- partition_index_first_unordered= 2,
partition_index_last= 3,
partition_index_read_last= 4,
partition_read_range = 5,
- partition_no_index_scan= 6
+ partition_no_index_scan= 6,
+ partition_read_multi_range = 7,
+ partition_ft_read= 8
};
/* Data for the partition handler */
int m_mode; // Open mode
uint m_open_test_lock; // Open test_if_locked
- uchar *m_file_buffer; // Content of the .par file
+ uchar *m_file_buffer; // Content of the .par file
char *m_name_buffer_ptr; // Pointer to first partition name
MEM_ROOT m_mem_root;
plugin_ref *m_engine_array; // Array of types of the handlers
@@ -137,10 +297,12 @@ private:
handler **m_new_file; // Array of references to new handlers
handler **m_reorged_file; // Reorganised partitions
handler **m_added_file; // Added parts kept for errors
- LEX_STRING *m_connect_string;
+ LEX_CSTRING *m_connect_string;
partition_info *m_part_info; // local reference to partition
Field **m_part_field_array; // Part field array locally to save acc
uchar *m_ordered_rec_buffer; // Row and key buffer for ord. idx scan
+ st_partition_ft_info *ft_first;
+ st_partition_ft_info *ft_current;
/*
Current index.
When used in key_rec_cmp: If clustered pk, index compare
@@ -157,10 +319,10 @@ private:
Length of an element in m_ordered_rec_buffer. The elements are composed of
[part_no] [table->record copy] [underlying_table_rowid]
-
+
underlying_table_rowid is only stored when the table has no extended keys.
*/
- uint m_priority_queue_rec_len;
+ size_t m_priority_queue_rec_len;
/*
If true, then sorting records by key value also sorts them by their
@@ -205,14 +367,16 @@ private:
bool m_create_handler; // Handler used to create table
bool m_is_sub_partitioned; // Is subpartitioned
bool m_ordered_scan_ongoing;
+ bool m_rnd_init_and_first;
+ bool m_ft_init_and_first;
- /*
+ /*
If set, this object was created with ha_partition::clone and doesn't
"own" the m_part_info structure.
*/
ha_partition *m_is_clone_of;
MEM_ROOT *m_clone_mem_root;
-
+
/*
We keep track if all underlying handlers are MyISAM since MyISAM has a
great number of extra flags not needed by other handlers.
@@ -256,10 +420,34 @@ private:
ha_rows m_bulk_inserted_rows;
/** used for prediction of start_bulk_insert rows */
enum_monotonicity_info m_part_func_monotonicity_info;
+ part_id_range m_direct_update_part_spec;
+ bool m_pre_calling;
+ bool m_pre_call_use_parallel;
+ /* Keep track of bulk access requests */
+ bool bulk_access_executing;
+
/** keep track of locked partitions */
MY_BITMAP m_locked_partitions;
/** Stores shared auto_increment etc. */
Partition_share *part_share;
+ /** Fix spurious -Werror=overloaded-virtual in GCC 9 */
+ virtual void restore_auto_increment(ulonglong prev_insert_id)
+ {
+ handler::restore_auto_increment(prev_insert_id);
+ }
+ /** Store and restore next_auto_inc_val over duplicate key errors. */
+ virtual void store_auto_increment()
+ {
+ DBUG_ASSERT(part_share);
+ part_share->prev_auto_inc_val= part_share->next_auto_inc_val;
+ handler::store_auto_increment();
+ }
+ virtual void restore_auto_increment()
+ {
+ DBUG_ASSERT(part_share);
+ part_share->next_auto_inc_val= part_share->prev_auto_inc_val;
+ handler::restore_auto_increment();
+ }
/** Temporary storage for new partitions Handler_shares during ALTER */
List<Parts_share_refs> m_new_partitions_share_refs;
/** Sorted array of partition ids in descending order of number of rows. */
@@ -273,7 +461,24 @@ private:
/** partitions that returned HA_ERR_KEY_NOT_FOUND. */
MY_BITMAP m_key_not_found_partitions;
bool m_key_not_found;
+ List<String> *m_partitions_to_open;
+ MY_BITMAP m_opened_partitions;
+ /** This is one of the m_file-s that it guaranteed to be opened. */
+ /** It is set in open_read_partitions() */
+ handler *m_file_sample;
public:
+ handler **get_child_handlers()
+ {
+ return m_file;
+ }
+ virtual part_id_range *get_part_spec()
+ {
+ return &m_part_spec;
+ }
+ virtual uint get_no_current_part_id()
+ {
+ return NO_CURRENT_PART_ID;
+ }
Partition_share *get_part_share() { return part_share; }
handler *clone(const char *name, MEM_ROOT *mem_root);
virtual void set_part_info(partition_info *part_info)
@@ -281,6 +486,25 @@ public:
m_part_info= part_info;
m_is_sub_partitioned= part_info->is_sub_partitioned();
}
+
+ virtual void return_record_by_parent();
+
+ virtual bool vers_can_native(THD *thd)
+ {
+ if (thd->lex->part_info)
+ {
+ // PARTITION BY SYSTEM_TIME is not supported for now
+ return thd->lex->part_info->part_type != VERSIONING_PARTITION;
+ }
+ else
+ {
+ bool can= true;
+ for (uint i= 0; i < m_tot_parts && can; i++)
+ can= can && m_file[i]->vers_can_native(thd);
+ return can;
+ }
+ }
+
/*
-------------------------------------------------------------------------
MODULE create/delete handler object
@@ -299,6 +523,7 @@ public:
ha_partition *clone_arg,
MEM_ROOT *clone_mem_root_arg);
~ha_partition();
+ void ha_partition_init();
/*
A partition handler has no characteristics in itself. It only inherits
those from the underlying handlers. Here we set-up those constants to
@@ -425,6 +650,7 @@ public:
virtual THR_LOCK_DATA **store_lock(THD * thd, THR_LOCK_DATA ** to,
enum thr_lock_type lock_type);
virtual int external_lock(THD * thd, int lock_type);
+ LEX_CSTRING *engine_name() { return hton_name(partition_ht()); }
/*
When table is locked a statement is started by calling start_stmt
instead of external_lock
@@ -480,8 +706,23 @@ public:
number of calls to write_row.
*/
virtual int write_row(uchar * buf);
- virtual int update_row(const uchar * old_data, uchar * new_data);
+ virtual bool start_bulk_update();
+ virtual int exec_bulk_update(ha_rows *dup_key_found);
+ virtual int end_bulk_update();
+ virtual int bulk_update_row(const uchar *old_data, const uchar *new_data,
+ ha_rows *dup_key_found);
+ virtual int update_row(const uchar * old_data, const uchar * new_data);
+ virtual int direct_update_rows_init(List<Item> *update_fields);
+ virtual int pre_direct_update_rows_init(List<Item> *update_fields);
+ virtual int direct_update_rows(ha_rows *update_rows);
+ virtual int pre_direct_update_rows();
+ virtual bool start_bulk_delete();
+ virtual int end_bulk_delete();
virtual int delete_row(const uchar * buf);
+ virtual int direct_delete_rows_init();
+ virtual int pre_direct_delete_rows_init();
+ virtual int direct_delete_rows(ha_rows *delete_rows);
+ virtual int pre_direct_delete_rows();
virtual int delete_all_rows(void);
virtual int truncate();
virtual void start_bulk_insert(ha_rows rows, uint flags);
@@ -600,20 +841,16 @@ public:
virtual int index_last(uchar * buf);
virtual int index_next_same(uchar * buf, const uchar * key, uint keylen);
+ int index_read_last_map(uchar *buf,
+ const uchar *key,
+ key_part_map keypart_map);
+
/*
read_first_row is virtual method but is only implemented by
handler.cc, no storage engine has implemented it so neither
will the partition handler.
-
- virtual int read_first_row(uchar *buf, uint primary_key);
- */
- /*
- We don't implement multi read range yet, will do later.
- virtual int read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
- KEY_MULTI_RANGE *ranges, uint range_count,
- bool sorted, HANDLER_BUFFER *buffer);
- virtual int read_multi_range_next(KEY_MULTI_RANGE **found_range_p);
+ virtual int read_first_row(uchar *buf, uint primary_key);
*/
@@ -622,12 +859,90 @@ public:
bool eq_range, bool sorted);
virtual int read_range_next();
+
+ HANDLER_BUFFER *m_mrr_buffer;
+ uint *m_mrr_buffer_size;
+ uchar *m_mrr_full_buffer;
+ uint m_mrr_full_buffer_size;
+ uint m_mrr_new_full_buffer_size;
+ MY_BITMAP m_mrr_used_partitions;
+ uint *m_stock_range_seq;
+ /* not used: uint m_current_range_seq; */
+
+ /* Value of mrr_mode passed to ha_partition::multi_range_read_init */
+ uint m_mrr_mode;
+
+ /* Value of n_ranges passed to ha_partition::multi_range_read_init */
+ uint m_mrr_n_ranges;
+
+ /*
+ Ordered MRR mode: m_range_info[N] has the range_id of the last record that
+ we've got from partition N
+ */
+ range_id_t *m_range_info;
+
+ /*
+ TRUE <=> This ha_partition::multi_range_read_next() call is the first one
+ */
+ bool m_multi_range_read_first;
+
+ /* not used: uint m_mrr_range_init_flags; */
+
+ /* Number of elements in the list pointed by m_mrr_range_first. Not used */
+ uint m_mrr_range_length;
+
+ /* Linked list of ranges to scan */
+ PARTITION_KEY_MULTI_RANGE *m_mrr_range_first;
+ PARTITION_KEY_MULTI_RANGE *m_mrr_range_current;
+
+ /*
+ For each partition: number of ranges MRR scan will scan in the partition
+ */
+ uint *m_part_mrr_range_length;
+
+ /* For each partition: List of ranges to scan in this partition */
+ PARTITION_PART_KEY_MULTI_RANGE **m_part_mrr_range_first;
+ PARTITION_PART_KEY_MULTI_RANGE **m_part_mrr_range_current;
+ PARTITION_PART_KEY_MULTI_RANGE_HLD *m_partition_part_key_multi_range_hld;
+
+ /*
+ Sequence of ranges to be scanned (TODO: why not store this in
+ handler::mrr_{iter,funcs}?)
+ */
+ range_seq_t m_seq;
+ RANGE_SEQ_IF *m_seq_if;
+
+ /* Range iterator structure to be supplied to partitions */
+ RANGE_SEQ_IF m_part_seq_if;
+
+ virtual int multi_range_key_create_key(
+ RANGE_SEQ_IF *seq,
+ range_seq_t seq_it
+ );
+ virtual ha_rows multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
+ void *seq_init_param,
+ uint n_ranges, uint *bufsz,
+ uint *mrr_mode,
+ Cost_estimate *cost);
+ virtual ha_rows multi_range_read_info(uint keyno, uint n_ranges, uint keys,
+ uint key_parts, uint *bufsz,
+ uint *mrr_mode, Cost_estimate *cost);
+ virtual int multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param,
+ uint n_ranges, uint mrr_mode,
+ HANDLER_BUFFER *buf);
+ virtual int multi_range_read_next(range_id_t *range_info);
+ virtual int multi_range_read_explain_info(uint mrr_mode, char *str,
+ size_t size);
+ uint last_part() { return m_last_part; }
+
private:
bool init_record_priority_queue();
void destroy_record_priority_queue();
int common_index_read(uchar * buf, bool have_start_key);
int common_first_last(uchar * buf);
int partition_scan_set_up(uchar * buf, bool idx_read_flag);
+ bool check_parallel_search();
+ int handle_pre_scan(bool reverse_order, bool use_parallel);
int handle_unordered_next(uchar * buf, bool next_same);
int handle_unordered_scan_next_partition(uchar * buf);
int handle_ordered_index_scan(uchar * buf, bool reverse_order);
@@ -648,6 +963,9 @@ public:
virtual int info(uint);
void get_dynamic_partition_info(PARTITION_STATS *stat_info,
uint part_id);
+ void set_partitions_to_open(List<String> *partition_names);
+ int change_partitions_to_open(List<String> *partition_names);
+ int open_read_partitions(char *name_buff, size_t name_buff_size);
virtual int extra(enum ha_extra_function operation);
virtual int extra_opt(enum ha_extra_function operation, ulong arg);
virtual int reset(void);
@@ -670,12 +988,13 @@ private:
Query_cache_block_table
**block_table,
handler *file, uint *n);
- static const uint NO_CURRENT_PART_ID;
+ static const uint NO_CURRENT_PART_ID= NOT_A_PARTITION_ID;
int loop_partitions(handler_callback callback, void *param);
int loop_extra_alter(enum ha_extra_function operations);
void late_extra_cache(uint partition_id);
void late_extra_no_cache(uint partition_id);
void prepare_extra_cache(uint cachesize);
+ handler *get_open_file_sample() const { return m_file_sample; }
public:
/*
@@ -845,6 +1164,10 @@ public:
with hidden primary key)
(No handler has this limitation currently)
+ HA_WANTS_PRIMARY_KEY:
+ Can't define a table without primary key except sequences
+ (Only InnoDB has this when using innodb_force_primary_key == ON)
+
HA_STATS_RECORDS_IS_EXACT:
Does the counter of records after the info call specify an exact
value or not. If it does this flag is set.
@@ -919,7 +1242,7 @@ public:
special file for handling names of partitions, engine types.
HA_REC_NOT_IN_SEQ is always set for partition handler since we cannot
guarantee that the records will be returned in sequence.
- HA_CAN_GEOMETRY, HA_CAN_FULLTEXT, HA_CAN_SQL_HANDLER, HA_DUPLICATE_POS,
+ HA_DUPLICATE_POS,
HA_CAN_INSERT_DELAYED, HA_PRIMARY_KEY_REQUIRED_FOR_POSITION is disabled
until further investigated.
*/
@@ -994,14 +1317,14 @@ public:
wrapper function for handlerton alter_table_flags, since
the ha_partition_hton cannot know all its capabilities
*/
- virtual uint alter_table_flags(uint flags);
+ virtual alter_table_operations alter_table_flags(alter_table_operations flags);
/*
unireg.cc will call the following to make sure that the storage engine
can handle the data it is about to send.
The maximum supported values is the minimum of all handlers in the table
*/
- uint min_of_the_max_uint(uint (handler::*operator_func)(void) const) const;
+ uint min_of_the_max_uint(uint (handler::*operator_func)(void) const) const;
virtual uint max_supported_record_length() const;
virtual uint max_supported_keys() const;
virtual uint max_supported_key_parts() const;
@@ -1046,6 +1369,8 @@ public:
auto_increment_column_changed
-------------------------------------------------------------------------
*/
+ virtual bool need_info_for_auto_inc();
+ virtual bool can_use_for_auto_inc_init();
virtual void get_auto_increment(ulonglong offset, ulonglong increment,
ulonglong nb_desired_values,
ulonglong *first_value,
@@ -1053,16 +1378,17 @@ public:
virtual void release_auto_increment();
private:
virtual int reset_auto_increment(ulonglong value);
+ void update_next_auto_inc_val();
virtual void lock_auto_increment()
{
/* lock already taken */
if (auto_increment_safe_stmt_log_lock)
return;
- DBUG_ASSERT(!auto_increment_lock);
- if(table_share->tmp_table == NO_TMP_TABLE)
+ if (table_share->tmp_table == NO_TMP_TABLE)
{
- auto_increment_lock= TRUE;
part_share->lock_auto_inc();
+ DBUG_ASSERT(!auto_increment_lock);
+ auto_increment_lock= TRUE;
}
}
virtual void unlock_auto_increment()
@@ -1072,10 +1398,10 @@ private:
It will be set to false and thus unlocked at the end of the statement by
ha_partition::release_auto_increment.
*/
- if(auto_increment_lock && !auto_increment_safe_stmt_log_lock)
+ if (auto_increment_lock && !auto_increment_safe_stmt_log_lock)
{
- part_share->unlock_auto_inc();
auto_increment_lock= FALSE;
+ part_share->unlock_auto_inc();
}
}
virtual void set_auto_increment_if_higher(Field *field)
@@ -1083,13 +1409,27 @@ private:
ulonglong nr= (((Field_num*) field)->unsigned_flag ||
field->val_int() > 0) ? field->val_int() : 0;
lock_auto_increment();
- DBUG_ASSERT(part_share->auto_inc_initialized);
+ DBUG_ASSERT(part_share->auto_inc_initialized ||
+ !can_use_for_auto_inc_init());
/* must check when the mutex is taken */
if (nr >= part_share->next_auto_inc_val)
part_share->next_auto_inc_val= nr + 1;
unlock_auto_increment();
}
+ void check_insert_autoincrement()
+ {
+ /*
+ If we INSERT into the table having the AUTO_INCREMENT column,
+ we have to read all partitions for the next autoincrement value
+ unless we already did it.
+ */
+ if (!part_share->auto_inc_initialized &&
+ ha_thd()->lex->sql_command == SQLCOM_INSERT &&
+ table->found_next_number_field)
+ bitmap_set_all(&m_part_info->read_partitions);
+ }
+
public:
/*
@@ -1132,14 +1472,15 @@ public:
-------------------------------------------------------------------------
MODULE fulltext index
-------------------------------------------------------------------------
- Fulltext stuff not yet.
- -------------------------------------------------------------------------
- virtual int ft_init() { return HA_ERR_WRONG_COMMAND; }
- virtual FT_INFO *ft_init_ext(uint flags,uint inx,const uchar *key,
- uint keylen)
- { return NULL; }
- virtual int ft_read(uchar *buf) { return HA_ERR_WRONG_COMMAND; }
*/
+ void ft_close_search(FT_INFO *handler);
+ virtual int ft_init();
+ virtual int pre_ft_init();
+ virtual void ft_end();
+ virtual int pre_ft_end();
+ virtual FT_INFO *ft_init_ext(uint flags, uint inx, String *key);
+ virtual int ft_read(uchar *buf);
+ virtual int pre_ft_read(bool use_parallel);
/*
-------------------------------------------------------------------------
@@ -1201,13 +1542,15 @@ public:
virtual bool is_crashed() const;
virtual int check_for_upgrade(HA_CHECK_OPT *check_opt);
- /*
- -----------------------------------------------------------------------
- MODULE condition pushdown
- -----------------------------------------------------------------------
- */
+ /*
+ -------------------------------------------------------------------------
+ MODULE condition pushdown
+ -------------------------------------------------------------------------
+ */
virtual const COND *cond_push(const COND *cond);
virtual void cond_pop();
+ virtual void clear_top_table_fields();
+ virtual int info_push(uint info_type, void *info);
private:
int handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt, uint flags);
@@ -1221,21 +1564,10 @@ public:
void append_row_to_str(String &str);
public:
- /*
- -------------------------------------------------------------------------
- Admin commands not supported currently (almost purely MyISAM routines)
- This means that the following methods are not implemented:
- -------------------------------------------------------------------------
-
- virtual int backup(TD* thd, HA_CHECK_OPT *check_opt);
- virtual int restore(THD* thd, HA_CHECK_OPT *check_opt);
- virtual int dump(THD* thd, int fd = -1);
- virtual int net_read_dump(NET* net);
- */
- virtual uint checksum() const;
/* Enabled keycache for performance reasons, WL#4571 */
virtual int assign_to_keycache(THD* thd, HA_CHECK_OPT *check_opt);
virtual int preload_keys(THD* thd, HA_CHECK_OPT* check_opt);
+ virtual TABLE_LIST *get_next_global_for_child();
/*
-------------------------------------------------------------------------
@@ -1282,7 +1614,26 @@ public:
return h;
}
+ ha_rows part_records(void *_part_elem)
+ {
+ partition_element *part_elem= reinterpret_cast<partition_element *>(_part_elem);
+ DBUG_ASSERT(m_part_info);
+ uint32 sub_factor= m_part_info->num_subparts ? m_part_info->num_subparts : 1;
+ uint32 part_id= part_elem->id * sub_factor;
+ uint32 part_id_end= part_id + sub_factor;
+ DBUG_ASSERT(part_id_end <= m_tot_parts);
+ ha_rows part_recs= 0;
+ for (; part_id < part_id_end; ++part_id)
+ {
+ handler *file= m_file[part_id];
+ DBUG_ASSERT(bitmap_is_set(&(m_part_info->read_partitions), part_id));
+ file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK | HA_STATUS_OPEN);
+ part_recs+= file->stats.records;
+ }
+ return part_recs;
+ }
+
friend int cmp_key_rowid_part_id(void *ptr, uchar *ref1, uchar *ref2);
+ friend int cmp_key_part_id(void *key_p, uchar *ref1, uchar *ref2);
};
-
#endif /* HA_PARTITION_INCLUDED */
diff --git a/sql/ha_sequence.cc b/sql/ha_sequence.cc
new file mode 100644
index 00000000000..a0959f692cf
--- /dev/null
+++ b/sql/ha_sequence.cc
@@ -0,0 +1,448 @@
+/*
+ Copyright (c) 2017, Aliyun and/or its affiliates.
+ 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
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "mariadb.h"
+#include "sql_list.h"
+#include "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"
+
+/*
+ Table flags we should inherit and disable from the original engine.
+ We add HA_STATS_RECORDS_IS_EXACT as ha_sequence::info() will ensure
+ that records is always 1
+*/
+
+#define SEQUENCE_ENABLED_TABLE_FLAGS (HA_STATS_RECORDS_IS_EXACT | \
+ HA_PERSISTENT_TABLE)
+#define SEQUENCE_DISABLED_TABLE_FLAGS (HA_CAN_SQL_HANDLER | \
+ HA_CAN_INSERT_DELAYED | \
+ HA_BINLOG_STMT_CAPABLE)
+handlerton *sql_sequence_hton;
+
+/*
+ Create a sequence handler
+*/
+
+ha_sequence::ha_sequence(handlerton *hton, TABLE_SHARE *share)
+ :handler(hton, share), write_locked(0)
+{
+ sequence= share->sequence;
+ DBUG_ASSERT(share->sequence);
+}
+
+/**
+ Destructor method must remove the underlying handler
+*/
+ha_sequence::~ha_sequence()
+{
+ delete file;
+}
+
+/**
+ Sequence table open method
+
+ @param name Path to file (dbname and tablename)
+ @param mode mode
+ @param flags Flags how to open file
+
+ RETURN VALUES
+ @retval 0 Success
+ @retval != 0 Failure
+*/
+
+int ha_sequence::open(const char *name, int mode, uint flags)
+{
+ int error;
+ DBUG_ENTER("ha_sequence::open");
+ DBUG_ASSERT(table->s == table_share && file);
+
+ file->table= table;
+ if (likely(!(error= file->open(name, mode, flags))))
+ {
+ /*
+ Allocate ref in table's mem_root. We can't use table's ref
+ as it's allocated by ha_ caller that allocates this.
+ */
+ ref_length= file->ref_length;
+ if (!(ref= (uchar*) alloc_root(&table->mem_root,ALIGN_SIZE(ref_length)*2)))
+ {
+ file->ha_close();
+ error=HA_ERR_OUT_OF_MEM;
+ DBUG_RETURN(error);
+ }
+ file->ref= ref;
+ file->dup_ref= dup_ref= ref+ALIGN_SIZE(file->ref_length);
+
+ /*
+ ha_open() sets the following for us. We have to set this for the
+ underlying handler
+ */
+ file->cached_table_flags= file->table_flags();
+
+ 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)))
+ {
+ if (unlikely((error= table->s->sequence->read_initial_values(table))))
+ file->ha_close();
+ }
+ else if (!table->s->tmp_table)
+ table->internal_set_needs_reopen(true);
+
+ /*
+ 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);
+ }
+ DBUG_RETURN(error);
+}
+
+/*
+ Clone the sequence. Needed if table is used by range optimization
+ (Very, very unlikely)
+*/
+
+handler *ha_sequence::clone(const char *name, MEM_ROOT *mem_root)
+{
+ ha_sequence *new_handler;
+ DBUG_ENTER("ha_sequence::clone");
+ if (!(new_handler= new (mem_root) ha_sequence(ht, table_share)))
+ DBUG_RETURN(NULL);
+
+ /*
+ Allocate new_handler->ref here because otherwise ha_open will allocate it
+ on this->table->mem_root and we will not be able to reclaim that memory
+ when the clone handler object is destroyed.
+ */
+
+ if (!(new_handler->ref= (uchar*) alloc_root(mem_root,
+ ALIGN_SIZE(ref_length)*2)))
+ goto err;
+
+ if (new_handler->ha_open(table, name,
+ table->db_stat,
+ HA_OPEN_IGNORE_IF_LOCKED | HA_OPEN_NO_PSI_CALL))
+ goto err;
+
+ /* Reuse original storage engine data for duplicate key reference */
+ new_handler->ref= file->ref;
+ new_handler->ref_length= file->ref_length;
+ new_handler->dup_ref= file->dup_ref;
+
+ DBUG_RETURN((handler*) new_handler);
+
+err:
+ delete new_handler;
+ DBUG_RETURN(NULL);
+}
+
+
+/*
+ Map the create table to the original storage engine
+*/
+
+int ha_sequence::create(const char *name, TABLE *form,
+ HA_CREATE_INFO *create_info)
+{
+ DBUG_ASSERT(create_info->sequence);
+ /* Sequence tables has one and only one row */
+ create_info->max_rows= create_info->min_rows= 1;
+ return (file->create(name, form, create_info));
+}
+
+/**
+ Sequence write row method.
+
+ A sequence table has only one row. Any inserts in the table
+ will update this row.
+
+ @retval 0 Success
+ @retval != 0 Failure
+
+ NOTES:
+ write_locked is set if we are called from SEQUENCE::next_value
+ In this case the mutex is already locked and we should not update
+ the sequence with 'buf' as the sequence object is already up to date.
+*/
+
+int ha_sequence::write_row(uchar *buf)
+{
+ int error;
+ sequence_definition tmp_seq;
+ bool sequence_locked;
+ DBUG_ENTER("ha_sequence::write_row");
+ DBUG_ASSERT(table->record[0] == buf);
+
+ row_already_logged= 0;
+ if (unlikely(sequence->initialized == SEQUENCE::SEQ_IN_PREPARE))
+ {
+ /* This calls is from ha_open() as part of create table */
+ DBUG_RETURN(file->write_row(buf));
+ }
+ if (unlikely(sequence->initialized == SEQUENCE::SEQ_IN_ALTER))
+ {
+ int error= 0;
+ /* This is called from alter table */
+ tmp_seq.read_fields(table);
+ if (tmp_seq.check_and_adjust(0))
+ DBUG_RETURN(HA_ERR_SEQUENCE_INVALID_DATA);
+ sequence->copy(&tmp_seq);
+ if (likely(!(error= file->write_row(buf))))
+ sequence->initialized= SEQUENCE::SEQ_READY_TO_USE;
+ DBUG_RETURN(error);
+ }
+ if (unlikely(sequence->initialized != SEQUENCE::SEQ_READY_TO_USE))
+ DBUG_RETURN(HA_ERR_WRONG_COMMAND);
+
+ sequence_locked= write_locked;
+ if (!write_locked) // If not from next_value()
+ {
+ /*
+ User tries to write a full row directly to the sequence table with
+ INSERT or LOAD DATA.
+
+ - Get an exclusive lock for the table. This is needed to ensure that
+ we excute all full inserts (same as ALTER SEQUENCE) in same order
+ on master and slaves
+ - Check that the new row is an accurate SEQUENCE object
+ */
+
+ THD *thd= table->in_use;
+ if (table->s->tmp_table == NO_TMP_TABLE &&
+ thd->mdl_context.upgrade_shared_lock(table->mdl_ticket,
+ MDL_EXCLUSIVE,
+ thd->variables.
+ lock_wait_timeout))
+ DBUG_RETURN(ER_LOCK_WAIT_TIMEOUT);
+
+ tmp_seq.read_fields(table);
+ if (tmp_seq.check_and_adjust(0))
+ DBUG_RETURN(HA_ERR_SEQUENCE_INVALID_DATA);
+
+ /*
+ Lock sequence to ensure that no one can come in between
+ while sequence, table and binary log are updated.
+ */
+ sequence->write_lock(table);
+ }
+
+ if (likely(!(error= file->update_first_row(buf))))
+ {
+ Log_func *log_func= Write_rows_log_event::binlog_row_logging_function;
+ if (!sequence_locked)
+ 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;
+ }
+
+ sequence->all_values_used= 0;
+ if (!sequence_locked)
+ sequence->write_unlock(table);
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Inherit the sequence base table flags.
+*/
+
+handler::Table_flags ha_sequence::table_flags() const
+{
+ DBUG_ENTER("ha_sequence::table_flags");
+ DBUG_RETURN((file->table_flags() & ~SEQUENCE_DISABLED_TABLE_FLAGS) |
+ SEQUENCE_ENABLED_TABLE_FLAGS);
+}
+
+
+int ha_sequence::info(uint flag)
+{
+ DBUG_ENTER("ha_sequence::info");
+ file->info(flag);
+ /* Inform optimizer that we have always only one record */
+ stats= file->stats;
+ stats.records= 1;
+ DBUG_RETURN(false);
+}
+
+
+int ha_sequence::extra(enum ha_extra_function operation)
+{
+ if (operation == HA_EXTRA_PREPARE_FOR_ALTER_TABLE)
+ {
+ /* In case of ALTER TABLE allow ::write_row() to copy rows */
+ sequence->initialized= SEQUENCE::SEQ_IN_ALTER;
+ }
+ return file->extra(operation);
+}
+
+bool ha_sequence::check_if_incompatible_data(HA_CREATE_INFO *create_info,
+ uint table_changes)
+{
+ /* Table definition is locked for SEQUENCE tables */
+ return(COMPATIBLE_DATA_YES);
+}
+
+
+int ha_sequence::external_lock(THD *thd, int lock_type)
+{
+ int error= file->external_lock(thd, lock_type);
+
+ /*
+ Copy lock flag to satisfy DBUG_ASSERT checks in ha_* functions in
+ handler.cc when we later call it with file->ha_..()
+ */
+ if (!error)
+ file->m_lock_type= lock_type;
+ return error;
+}
+
+/*
+ Squence engine error deal method
+*/
+
+void ha_sequence::print_error(int error, myf errflag)
+{
+ const char *sequence_db= table_share->db.str;
+ const char *sequence_name= table_share->table_name.str;
+ DBUG_ENTER("ha_sequence::print_error");
+
+ switch (error) {
+ case HA_ERR_SEQUENCE_INVALID_DATA:
+ {
+ my_error(ER_SEQUENCE_INVALID_DATA, MYF(errflag), sequence_db,
+ sequence_name);
+ DBUG_VOID_RETURN;
+ }
+ case HA_ERR_SEQUENCE_RUN_OUT:
+ {
+ my_error(ER_SEQUENCE_RUN_OUT, MYF(errflag), sequence_db, sequence_name);
+ DBUG_VOID_RETURN;
+ }
+ case HA_ERR_WRONG_COMMAND:
+ my_error(ER_ILLEGAL_HA, MYF(0), "SEQUENCE", sequence_db, sequence_name);
+ DBUG_VOID_RETURN;
+ case ER_WRONG_INSERT_INTO_SEQUENCE:
+ my_error(error, MYF(0));
+ DBUG_VOID_RETURN;
+ }
+ file->print_error(error, errflag);
+ DBUG_VOID_RETURN;
+}
+
+/*****************************************************************************
+ Sequence plugin interface
+*****************************************************************************/
+
+/*
+ Create an new handler
+*/
+
+static handler *sequence_create_handler(handlerton *hton,
+ TABLE_SHARE *share,
+ MEM_ROOT *mem_root)
+{
+ DBUG_ENTER("sequence_create_handler");
+ DBUG_RETURN(new (mem_root) ha_sequence(hton, share));
+}
+
+
+/*
+ Sequence engine end.
+
+ SYNOPSIS
+ sequence_end()
+ p handlerton.
+ type panic type.
+ RETURN VALUES
+ 0 Success
+ !=0 Failure
+*/
+static int sequence_end(handlerton* hton,
+ ha_panic_function type __attribute__((unused)))
+{
+ DBUG_ENTER("sequence_end");
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Sequence engine init.
+
+ SYNOPSIS
+ sequence_initialize()
+
+ @param p handlerton.
+
+ retval 0 Success
+ retval !=0 Failure
+*/
+
+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;
+ local_sequence_hton->flags= (HTON_NOT_USER_SELECTABLE |
+ HTON_HIDDEN |
+ HTON_TEMPORARY_NOT_SUPPORTED |
+ HTON_ALTER_NOT_SUPPORTED |
+ HTON_NO_PARTITION);
+ DBUG_RETURN(0);
+}
+
+
+static struct st_mysql_storage_engine sequence_storage_engine=
+{ MYSQL_HANDLERTON_INTERFACE_VERSION };
+
+maria_declare_plugin(sql_sequence)
+{
+ MYSQL_STORAGE_ENGINE_PLUGIN,
+ &sequence_storage_engine,
+ "SQL_SEQUENCE",
+ "jianwei.zhao @ Aliyun & Monty @ MariaDB corp",
+ "Sequence Storage Engine for CREATE SEQUENCE",
+ PLUGIN_LICENSE_GPL,
+ sequence_initialize, /* Plugin Init */
+ NULL, /* Plugin Deinit */
+ 0x0100, /* 1.0 */
+ NULL, /* status variables */
+ NULL, /* system variables */
+ "1.0", /* string version */
+ MariaDB_PLUGIN_MATURITY_STABLE /* maturity */
+}
+maria_declare_plugin_end;
diff --git a/sql/ha_sequence.h b/sql/ha_sequence.h
new file mode 100644
index 00000000000..fd9da05b591
--- /dev/null
+++ b/sql/ha_sequence.h
@@ -0,0 +1,165 @@
+#ifndef HA_SEQUENCE_INCLUDED
+#define HA_SEQUENCE_INCLUDED
+/*
+ Copyright (c) 2017 Aliyun and/or its affiliates.
+ Copyright (c) 2017 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-1301 USA
+*/
+
+#include "sql_sequence.h"
+#include "table.h"
+#include "handler.h"
+
+extern handlerton *sql_sequence_hton;
+
+/*
+ Sequence engine handler.
+
+ The sequence engine is a logic engine. It doesn't store any data.
+ All the sequence data stored into the base table which must support
+ non rollback writes (HA_CAN_TABLES_WITHOUT_ROLLBACK)
+
+ The sequence data (SEQUENCE class) is stored in TABLE_SHARE->sequence
+
+ TABLE RULES:
+ 1. When table is created, one row is automaticlly inserted into
+ the table. The table will always have one and only one row.
+ 2. Any inserts or updates to the table will be validated.
+ 3. Inserts will overwrite the original row.
+ 4. DELETE and TRUNCATE will not affect the table.
+ Instead a warning will be given.
+ 5. Cache will be reset for any updates.
+
+ CACHE RULES:
+ SEQUENCE class is used to cache values that sequence defined.
+ 1. If hit cache, we can query back the sequence nextval directly
+ instead of reading the underlying table.
+
+ 2. When run out of values, the sequence engine will reserve new values
+ in update the base table.
+
+ 3. The cache is invalidated if any update on based table.
+*/
+
+class ha_sequence :public handler
+{
+private:
+ handler *file;
+ SEQUENCE *sequence; /* From table_share->sequence */
+
+public:
+ /* Set when handler is write locked */
+ bool write_locked;
+
+ ha_sequence(handlerton *hton, TABLE_SHARE *share);
+ ~ha_sequence();
+
+ /* virtual function that are re-implemented for sequence */
+ int open(const char *name, int mode, uint test_if_locked);
+ int create(const char *name, TABLE *form,
+ HA_CREATE_INFO *create_info);
+ handler *clone(const char *name, MEM_ROOT *mem_root);
+ int write_row(uchar *buf);
+ Table_flags table_flags() const;
+ /* One can't update or delete from sequence engine */
+ int update_row(const uchar *old_data, const uchar *new_data)
+ { return HA_ERR_WRONG_COMMAND; }
+ int delete_row(const uchar *buf)
+ { return HA_ERR_WRONG_COMMAND; }
+ /* One can't delete from sequence engine */
+ int truncate()
+ { return HA_ERR_WRONG_COMMAND; }
+ /* Can't use query cache */
+ uint8 table_cache_type()
+ { return HA_CACHE_TBL_NOCACHE; }
+ void print_error(int error, myf errflag);
+ int info(uint);
+ LEX_CSTRING *engine_name() { return hton_name(file->ht); }
+ int external_lock(THD *thd, int lock_type);
+ int extra(enum ha_extra_function operation);
+ /* For ALTER ONLINE TABLE */
+ bool check_if_incompatible_data(HA_CREATE_INFO *create_info,
+ uint table_changes);
+ void write_lock() { write_locked= 1;}
+ void unlock() { write_locked= 0; }
+ bool is_locked() { return write_locked; }
+
+ /* Functions that are directly mapped to the underlying handler */
+ int rnd_init(bool scan)
+ { return file->rnd_init(scan); }
+ /*
+ We need to have a lock here to protect engines like MyISAM from
+ simultaneous read and write. For sequence's this is not critical
+ as this function is used extremely seldom.
+ */
+ int rnd_next(uchar *buf)
+ {
+ int error;
+ table->s->sequence->read_lock(table);
+ error= file->rnd_next(buf);
+ table->s->sequence->read_unlock(table);
+ return error;
+ }
+ int rnd_end()
+ { return file->rnd_end(); }
+ int rnd_pos(uchar *buf, uchar *pos)
+ {
+ int error;
+ table->s->sequence->read_lock(table);
+ error= file->rnd_pos(buf, pos);
+ table->s->sequence->read_unlock(table);
+ return error;
+ }
+ void position(const uchar *record)
+ { return file->position(record); }
+ const char *table_type() const
+ { return file->table_type(); }
+ ulong index_flags(uint inx, uint part, bool all_parts) const
+ { return file->index_flags(inx, part, all_parts); }
+ THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
+ enum thr_lock_type lock_type)
+ { return file->store_lock(thd, to, lock_type); }
+ int close(void)
+ { return file->close(); }
+ const char **bas_ext() const
+ { return file->bas_ext(); }
+ int delete_table(const char*name)
+ { return file->delete_table(name); }
+ int rename_table(const char *from, const char *to)
+ { return file->rename_table(from, to); }
+ void unbind_psi()
+ { return file->unbind_psi(); }
+ void rebind_psi()
+ { return file->rebind_psi(); }
+
+ bool auto_repair(int error) const
+ { return file->auto_repair(error); }
+ int repair(THD* thd, HA_CHECK_OPT* check_opt)
+ { return file->repair(thd, check_opt); }
+ bool check_and_repair(THD *thd)
+ { return file->check_and_repair(thd); }
+ bool is_crashed() const
+ { return file->is_crashed(); }
+ void column_bitmaps_signal()
+ { return file->column_bitmaps_signal(); }
+
+ /* New methods */
+ void register_original_handler(handler *file_arg)
+ {
+ file= file_arg;
+ init(); /* Update cached_table_flags */
+ }
+};
+#endif
diff --git a/sql/handler.cc b/sql/handler.cc
index 0a5ed2dffbb..dcc130c340e 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2016, Oracle and/or its affiliates.
- Copyright (c) 2009, 2018, MariaDB Corporation.
+ Copyright (c) 2009, 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
@@ -20,11 +20,11 @@
Handler-calling-functions
*/
-#include <my_global.h>
+#include "mariadb.h"
#include <inttypes.h>
#include "sql_priv.h"
#include "unireg.h"
-#include "rpl_handler.h"
+#include "rpl_rli.h"
#include "sql_cache.h" // query_cache, query_cache_*
#include "sql_connect.h" // global_table_stats
#include "key.h" // key_copy, key_unpack, key_cmp_if_same, key_cmp
@@ -42,6 +42,7 @@
#include <mysql/psi/mysql_table.h>
#include "debug_sync.h" // DEBUG_SYNC
#include "sql_audit.h"
+#include "ha_sequence.h"
#ifdef WITH_PARTITION_STORAGE_ENGINE
#include "ha_partition.h"
@@ -50,6 +51,7 @@
#ifdef WITH_ARIA_STORAGE_ENGINE
#include "../storage/maria/ha_maria.h"
#endif
+#include "semisync_master.h"
#include "wsrep_mysqld.h"
#include "wsrep.h"
@@ -67,13 +69,13 @@ static handlerton *installed_htons[128];
#define BITMAP_STACKBUF_SIZE (128/8)
KEY_CREATE_INFO default_key_create_info=
-{ HA_KEY_ALG_UNDEF, 0, {NullS, 0}, {NullS, 0}, true };
+{ HA_KEY_ALG_UNDEF, 0, 0, {NullS, 0}, {NullS, 0}, true };
/* number of entries in handlertons[] */
ulong total_ha= 0;
/* number of storage engines (from handlertons[]) that support 2pc */
ulong total_ha_2pc= 0;
-#ifndef DBUG_OFF
+#ifdef DBUG_ASSERT_EXISTS
/*
Number of non-mandatory 2pc handlertons whose initialization failed
to estimate total_ha_2pc value under supposition of the failures
@@ -84,12 +86,12 @@ ulong failed_ha_2pc= 0;
/* size of savepoint storage area (see ha_init) */
ulong savepoint_alloc_size= 0;
-static const LEX_STRING sys_table_aliases[]=
+static const LEX_CSTRING sys_table_aliases[]=
{
- { C_STRING_WITH_LEN("INNOBASE") }, { C_STRING_WITH_LEN("INNODB") },
- { C_STRING_WITH_LEN("HEAP") }, { C_STRING_WITH_LEN("MEMORY") },
- { C_STRING_WITH_LEN("MERGE") }, { C_STRING_WITH_LEN("MRG_MYISAM") },
- { C_STRING_WITH_LEN("Maria") }, { C_STRING_WITH_LEN("Aria") },
+ { STRING_WITH_LEN("INNOBASE") }, { STRING_WITH_LEN("INNODB") },
+ { STRING_WITH_LEN("HEAP") }, { STRING_WITH_LEN("MEMORY") },
+ { STRING_WITH_LEN("MERGE") }, { STRING_WITH_LEN("MRG_MYISAM") },
+ { STRING_WITH_LEN("Maria") }, { STRING_WITH_LEN("Aria") },
{NullS, 0}
};
@@ -168,9 +170,10 @@ handlerton *ha_default_tmp_handlerton(THD *thd)
RETURN
pointer to storage engine plugin handle
*/
-plugin_ref ha_resolve_by_name(THD *thd, const LEX_STRING *name, bool tmp_table)
+plugin_ref ha_resolve_by_name(THD *thd, const LEX_CSTRING *name,
+ bool tmp_table)
{
- const LEX_STRING *table_alias;
+ const LEX_CSTRING *table_alias;
plugin_ref plugin;
redo:
@@ -215,16 +218,8 @@ Storage_engine_name::resolve_storage_engine_with_error(THD *thd,
handlerton **ha,
bool tmp_table)
{
-#if MYSQL_VERSION_ID < 100300
- /*
- Please remove tmp_name when merging to 10.3 and pass m_storage_engine_name
- directly to ha_resolve_by_name().
- */
- LEX_STRING tmp_name;
- tmp_name.str= const_cast<char*>(m_storage_engine_name.str);
- tmp_name.length= m_storage_engine_name.length;
-#endif
- if (plugin_ref plugin= ha_resolve_by_name(thd, &tmp_name, tmp_table))
+ if (plugin_ref plugin= ha_resolve_by_name(thd, &m_storage_engine_name,
+ tmp_table))
{
*ha= plugin_hton(plugin);
return false;
@@ -334,7 +329,6 @@ handler *get_ha_partition(partition_info *part_info)
}
#endif
-
static const char **handler_errmsgs;
C_MODE_START
@@ -448,7 +442,7 @@ static int full_discover_for_existence(handlerton *, const char *, const char *)
static int ext_based_existence(handlerton *, const char *, const char *)
{ return 0; }
-static int hton_ext_based_table_discovery(handlerton *hton, LEX_STRING *db,
+static int hton_ext_based_table_discovery(handlerton *hton, LEX_CSTRING *db,
MY_DIR *dir, handlerton::discovered_list *result)
{
/*
@@ -661,7 +655,7 @@ int ha_initialize_handlerton(st_plugin_int *plugin)
/*
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
- remove partition and myisammrg.
+ remove partition.
*/
switch (hton->db_type) {
case DB_TYPE_HEAP:
@@ -673,6 +667,9 @@ int ha_initialize_handlerton(st_plugin_int *plugin)
case DB_TYPE_PARTITION_DB:
partition_hton= hton;
break;
+ case DB_TYPE_SEQUENCE:
+ sql_sequence_hton= hton;
+ break;
default:
break;
};
@@ -691,7 +688,7 @@ err_deinit:
(void) plugin->plugin->deinit(NULL);
err:
-#ifndef DBUG_OFF
+#ifdef DBUG_ASSERT_EXISTS
if (hton->prepare && hton->state == SHOW_OPTION_YES)
failed_ha_2pc++;
#endif
@@ -728,7 +725,7 @@ int ha_end()
So if flag is equal to HA_PANIC_CLOSE, the deallocate
the errors.
*/
- if (ha_finish_errors())
+ if (unlikely(ha_finish_errors()))
error= 1;
DBUG_RETURN(error);
@@ -1227,7 +1224,7 @@ int ha_prepare(THD *thd)
handlerton *ht= ha_info->ht();
if (ht->prepare)
{
- if (prepare_or_error(ht, thd, all))
+ if (unlikely(prepare_or_error(ht, thd, all)))
{
ha_rollback_trans(thd, all);
error=1;
@@ -1447,6 +1444,41 @@ int ha_commit_trans(THD *thd, bool all)
goto err;
}
+#if 1 // FIXME: This should be done in ha_prepare().
+ if (rw_trans || (thd->lex->sql_command == SQLCOM_ALTER_TABLE &&
+ thd->lex->alter_info.flags & ALTER_ADD_SYSTEM_VERSIONING &&
+ is_real_trans))
+ {
+ ulonglong trx_start_id= 0, trx_end_id= 0;
+ for (Ha_trx_info *ha_info= trans->ha_list; ha_info; ha_info= ha_info->next())
+ {
+ if (ha_info->ht()->prepare_commit_versioned)
+ {
+ trx_end_id= ha_info->ht()->prepare_commit_versioned(thd, &trx_start_id);
+ if (trx_end_id)
+ break; // FIXME: use a common ID for cross-engine transactions
+ }
+ }
+
+ if (trx_end_id)
+ {
+ if (!TR_table::use_transaction_registry)
+ {
+ my_error(ER_VERS_TRT_IS_DISABLED, MYF(0));
+ goto err;
+ }
+ DBUG_ASSERT(trx_start_id);
+ TR_table trt(thd, true);
+ if (trt.update(trx_start_id, trx_end_id))
+ goto err;
+ // 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);
+ }
+ }
+#endif
+
if (trans->no_2pc || (rw_ha_count <= 1))
{
error= ha_commit_one_phase(thd, all);
@@ -1471,7 +1503,7 @@ int ha_commit_trans(THD *thd, bool all)
Sic: we know that prepare() is not NULL since otherwise
trans->no_2pc would have been set.
*/
- if (prepare_or_error(ht, thd, all))
+ if (unlikely(prepare_or_error(ht, thd, all)))
goto err;
need_prepare_ordered|= (ht->prepare_ordered != NULL);
@@ -1480,11 +1512,13 @@ 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(););
+#ifdef WITH_WSREP
if (!error && WSREP_ON && wsrep_is_wsrep_xid(&thd->transaction.xid_state.xid))
{
// xid was rewritten by wsrep
xid= wsrep_xid_seqno(thd->transaction.xid_state.xid);
}
+#endif /* WITH_WSREP */
if (!is_real_trans)
{
@@ -1517,7 +1551,10 @@ done:
mysql_mutex_assert_not_owner(mysql_bin_log.get_log_lock());
mysql_mutex_assert_not_owner(&LOCK_after_binlog_sync);
mysql_mutex_assert_not_owner(&LOCK_commit_ordered);
- RUN_HOOK(transaction, after_commit, (thd, FALSE));
+#ifdef HAVE_REPLICATION
+ repl_semisync_master.wait_after_commit(thd, all);
+ DEBUG_SYNC(thd, "after_group_after_commit");
+#endif
goto end;
/* Come here if error and we need to rollback. */
@@ -1591,6 +1628,7 @@ static int
commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans)
{
int error= 0;
+ uint count= 0;
Ha_trx_info *ha_info= trans->ha_list, *ha_info_next;
DBUG_ENTER("commit_one_phase_2");
if (is_real_trans)
@@ -1608,6 +1646,8 @@ commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans)
}
/* Should this be done only if is_real_trans is set ? */
status_var_increment(thd->status_var.ha_commit_count);
+ if (is_real_trans && ht != binlog_hton && ha_info->is_trx_read_write())
+ ++count;
ha_info_next= ha_info->next();
ha_info->reset(); /* keep it conveniently zero-filled */
}
@@ -1626,6 +1666,8 @@ commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans)
{
thd->has_waiter= false;
thd->transaction.cleanup();
+ if (count >= 2)
+ statistic_increment(transactions_multi_engine, LOCK_status);
}
DBUG_RETURN(error);
@@ -1757,7 +1799,9 @@ int ha_rollback_trans(THD *thd, bool all)
push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARNING_NOT_COMPLETE_ROLLBACK,
ER_THD(thd, ER_WARNING_NOT_COMPLETE_ROLLBACK));
- (void) RUN_HOOK(transaction, after_rollback, (thd, FALSE));
+#ifdef HAVE_REPLICATION
+ repl_semisync_master.wait_after_rollback(thd, all);
+#endif
DBUG_RETURN(error);
}
@@ -1946,9 +1990,10 @@ static my_bool xarecover_handlerton(THD *unused, plugin_ref plugin,
for (int i=0; i < got; i ++)
{
- my_xid x= WSREP_ON && wsrep_is_wsrep_xid(&info->list[i]) ?
- wsrep_xid_seqno(info->list[i]) :
- info->list[i].get_my_xid();
+ my_xid x= IF_WSREP(WSREP_ON && wsrep_is_wsrep_xid(&info->list[i]) ?
+ wsrep_xid_seqno(info->list[i]) :
+ info->list[i].get_my_xid(),
+ info->list[i].get_my_xid());
if (!x) // not "mine" - that is generated by external TM
{
#ifndef DBUG_OFF
@@ -2070,6 +2115,97 @@ int ha_recover(HASH *commit_list)
}
/**
+ return the XID as it appears in the SQL function's arguments.
+ So this string can be passed to XA START, XA PREPARE etc...
+
+ @note
+ the 'buf' has to have space for at least SQL_XIDSIZE bytes.
+*/
+
+
+/*
+ 'a'..'z' 'A'..'Z', '0'..'9'
+ and '-' '_' ' ' symbols don't have to be
+ converted.
+*/
+
+static const char xid_needs_conv[128]=
+{
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,
+ 0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1
+};
+
+uint get_sql_xid(XID *xid, char *buf)
+{
+ int tot_len= xid->gtrid_length + xid->bqual_length;
+ int i;
+ const char *orig_buf= buf;
+
+ for (i=0; i<tot_len; i++)
+ {
+ uchar c= ((uchar *) xid->data)[i];
+ if (c >= 128 || xid_needs_conv[c])
+ break;
+ }
+
+ if (i >= tot_len)
+ {
+ /* No need to convert characters to hexadecimals. */
+ *buf++= '\'';
+ memcpy(buf, xid->data, xid->gtrid_length);
+ buf+= xid->gtrid_length;
+ *buf++= '\'';
+ if (xid->bqual_length > 0 || xid->formatID != 1)
+ {
+ *buf++= ',';
+ *buf++= '\'';
+ memcpy(buf, xid->data+xid->gtrid_length, xid->bqual_length);
+ buf+= xid->bqual_length;
+ *buf++= '\'';
+ }
+ }
+ else
+ {
+ *buf++= 'X';
+ *buf++= '\'';
+ for (i= 0; i < xid->gtrid_length; i++)
+ {
+ *buf++=_dig_vec_lower[((uchar*) xid->data)[i] >> 4];
+ *buf++=_dig_vec_lower[((uchar*) xid->data)[i] & 0x0f];
+ }
+ *buf++= '\'';
+ if (xid->bqual_length > 0 || xid->formatID != 1)
+ {
+ *buf++= ',';
+ *buf++= 'X';
+ *buf++= '\'';
+ for (; i < tot_len; i++)
+ {
+ *buf++=_dig_vec_lower[((uchar*) xid->data)[i] >> 4];
+ *buf++=_dig_vec_lower[((uchar*) xid->data)[i] & 0x0f];
+ }
+ *buf++= '\'';
+ }
+ }
+
+ if (xid->formatID != 1)
+ {
+ *buf++= ',';
+ buf+= my_longlong10_to_str_8bit(&my_charset_bin, buf,
+ MY_INT64_NUM_DECIMAL_DIGITS, -10, xid->formatID);
+ }
+
+ return (uint)(buf - orig_buf);
+}
+
+
+/**
return the list of XID's to a client, the same way SHOW commands do.
@note
@@ -2078,7 +2214,8 @@ int ha_recover(HASH *commit_list)
It can be easily fixed later, if necessary.
*/
-static my_bool xa_recover_callback(XID_STATE *xs, Protocol *protocol)
+static my_bool xa_recover_callback(XID_STATE *xs, Protocol *protocol,
+ char *data, uint data_len, CHARSET_INFO *data_cs)
{
if (xs->xa_state == XA_PREPARED)
{
@@ -2086,8 +2223,7 @@ static my_bool xa_recover_callback(XID_STATE *xs, Protocol *protocol)
protocol->store_longlong((longlong) xs->xid.formatID, FALSE);
protocol->store_longlong((longlong) xs->xid.gtrid_length, FALSE);
protocol->store_longlong((longlong) xs->xid.bqual_length, FALSE);
- protocol->store(xs->xid.data, xs->xid.gtrid_length + xs->xid.bqual_length,
- &my_charset_bin);
+ protocol->store(data, data_len, data_cs);
if (protocol->write())
return TRUE;
}
@@ -2095,11 +2231,28 @@ static my_bool xa_recover_callback(XID_STATE *xs, Protocol *protocol)
}
+static my_bool xa_recover_callback_short(XID_STATE *xs, Protocol *protocol)
+{
+ return xa_recover_callback(xs, protocol, xs->xid.data,
+ xs->xid.gtrid_length + xs->xid.bqual_length, &my_charset_bin);
+}
+
+
+static my_bool xa_recover_callback_verbose(XID_STATE *xs, Protocol *protocol)
+{
+ 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);
+}
+
+
bool mysql_xa_recover(THD *thd)
{
List<Item> field_list;
Protocol *protocol= thd->protocol;
MEM_ROOT *mem_root= thd->mem_root;
+ my_hash_walk_action action;
DBUG_ENTER("mysql_xa_recover");
field_list.push_back(new (mem_root)
@@ -2111,16 +2264,32 @@ bool mysql_xa_recover(THD *thd)
field_list.push_back(new (mem_root)
Item_int(thd, "bqual_length", 0,
MY_INT32_NUM_DECIMAL_DIGITS), mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "data",
- XIDDATASIZE), mem_root);
+ {
+ uint len;
+ CHARSET_INFO *cs;
+
+ if (thd->lex->verbose)
+ {
+ len= SQL_XIDSIZE;
+ cs= &my_charset_utf8_general_ci;
+ action= (my_hash_walk_action) xa_recover_callback_verbose;
+ }
+ else
+ {
+ len= XIDDATASIZE;
+ cs= &my_charset_bin;
+ action= (my_hash_walk_action) xa_recover_callback_short;
+ }
+
+ field_list.push_back(new (mem_root)
+ Item_empty_string(thd, "data", len, cs), mem_root);
+ }
if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
- if (xid_cache_iterate(thd, (my_hash_walk_action) xa_recover_callback,
- protocol))
+ if (xid_cache_iterate(thd, action, protocol))
DBUG_RETURN(1);
my_eof(thd);
DBUG_RETURN(0);
@@ -2330,7 +2499,7 @@ int ha_start_consistent_snapshot(THD *thd)
*/
if (warn)
push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
- "This MySQL server does not support any "
+ "This MariaDB server does not support any "
"consistent-read capable storage engine");
return 0;
}
@@ -2420,7 +2589,7 @@ const char *get_canonical_filename(handler *file, const char *path,
The .frm file will be deleted only if we return 0.
*/
int ha_delete_table(THD *thd, handlerton *table_type, const char *path,
- const char *db, const char *alias, bool generate_warning)
+ const LEX_CSTRING *db, const LEX_CSTRING *alias, bool generate_warning)
{
handler *file;
char tmp_path[FN_REFLEN];
@@ -2439,7 +2608,7 @@ int ha_delete_table(THD *thd, handlerton *table_type, const char *path,
dummy_table.s= &dummy_share;
path= get_canonical_filename(file, path, tmp_path);
- if ((error= file->ha_delete_table(path)))
+ if (unlikely((error= file->ha_delete_table(path))))
{
/*
it's not an error if the table doesn't exist in the engine.
@@ -2453,12 +2622,9 @@ int ha_delete_table(THD *thd, handlerton *table_type, const char *path,
dummy_share.path.str= (char*) path;
dummy_share.path.length= strlen(path);
dummy_share.normalized_path= dummy_share.path;
- dummy_share.db.str= (char*) db;
- dummy_share.db.length= strlen(db);
- dummy_share.table_name.str= (char*) alias;
- dummy_share.table_name.length= strlen(alias);
- dummy_table.alias.set(alias, dummy_share.table_name.length,
- table_alias_charset);
+ 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_JUST_WARNING : 0));
}
@@ -2473,6 +2639,18 @@ int ha_delete_table(THD *thd, handlerton *table_type, const char *path,
/****************************************************************************
** General handler functions
****************************************************************************/
+
+
+/**
+ Clone a handler
+
+ @param name name of new table instance
+ @param mem_root Where 'this->ref' should be allocated. It can't be
+ in this->table->mem_root as otherwise we will not be
+ able to reclaim that memory when the clone handler
+ object is destroyed.
+*/
+
handler *handler::clone(const char *name, MEM_ROOT *mem_root)
{
handler *new_handler= get_new_handler(table->s, mem_root, ht);
@@ -2483,16 +2661,6 @@ handler *handler::clone(const char *name, MEM_ROOT *mem_root)
goto err;
/*
- Allocate handler->ref here because otherwise ha_open will allocate it
- on this->table->mem_root and we will not be able to reclaim that memory
- when the clone handler object is destroyed.
- */
-
- if (!(new_handler->ref= (uchar*) alloc_root(mem_root,
- ALIGN_SIZE(ref_length)*2)))
- goto err;
-
- /*
TODO: Implement a more efficient way to have more than one index open for
the same table instance. The ha_open call is not cacheable for clone.
@@ -2500,7 +2668,7 @@ handler *handler::clone(const char *name, MEM_ROOT *mem_root)
and should be able to use the original instance of the table.
*/
if (new_handler->ha_open(table, name, table->db_stat,
- HA_OPEN_IGNORE_IF_LOCKED))
+ HA_OPEN_IGNORE_IF_LOCKED, mem_root))
goto err;
return new_handler;
@@ -2510,6 +2678,11 @@ err:
return NULL;
}
+LEX_CSTRING *handler::engine_name()
+{
+ return hton_name(ht);
+}
+
double handler::keyread_time(uint index, uint ranges, ha_rows rows)
{
@@ -2522,7 +2695,7 @@ double handler::keyread_time(uint index, uint ranges, ha_rows rows)
engines that support that (e.g. InnoDB) may want to overwrite this method.
The model counts in the time to read index entries from cache.
*/
- ulong len= table->key_info[index].key_length + ref_length;
+ size_t len= table->key_info[index].key_length + ref_length;
if (index == table->s->primary_key && table->file->primary_key_is_clustered())
len= table->s->stored_rec_length;
double keys_per_block= (stats.block_size/2.0/len+1);
@@ -2573,7 +2746,8 @@ PSI_table_share *handler::ha_table_share_psi() const
Don't wait for locks if not HA_OPEN_WAIT_IF_LOCKED is set
*/
int handler::ha_open(TABLE *table_arg, const char *name, int mode,
- uint test_if_locked)
+ uint test_if_locked, MEM_ROOT *mem_root,
+ List<String> *partitions_to_open)
{
int error;
DBUG_ENTER("handler::ha_open");
@@ -2588,7 +2762,9 @@ int handler::ha_open(TABLE *table_arg, const char *name, int mode,
DBUG_PRINT("info", ("old m_lock_type: %d F_UNLCK %d", m_lock_type, F_UNLCK));
DBUG_ASSERT(alloc_root_inited(&table->mem_root));
- if ((error=open(name,mode,test_if_locked)))
+ set_partitions_to_open(partitions_to_open);
+
+ if (unlikely((error=open(name,mode,test_if_locked))))
{
if ((error == EACCES || error == EROFS) && mode == O_RDWR &&
(table->db_stat & HA_TRY_READ_ONLY))
@@ -2597,7 +2773,7 @@ int handler::ha_open(TABLE *table_arg, const char *name, int mode,
error=open(name,O_RDONLY,test_if_locked);
}
}
- if (error)
+ if (unlikely(error))
{
my_errno= error; /* Safeguard */
DBUG_PRINT("error",("error: %d errno: %d",error,errno));
@@ -2620,9 +2796,9 @@ int handler::ha_open(TABLE *table_arg, const char *name, int mode,
table->db_stat|=HA_READ_ONLY;
(void) extra(HA_EXTRA_NO_READCHECK); // Not needed in SQL
- /* ref is already allocated for us if we're called from handler::clone() */
- if (!ref && !(ref= (uchar*) alloc_root(&table->mem_root,
- ALIGN_SIZE(ref_length)*2)))
+ /* Allocate ref in thd or on the table's mem_root */
+ if (!(ref= (uchar*) alloc_root(mem_root ? mem_root : &table->mem_root,
+ ALIGN_SIZE(ref_length)*2)))
{
ha_close();
error=HA_ERR_OUT_OF_MEM;
@@ -2633,6 +2809,7 @@ 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);
}
@@ -2665,19 +2842,27 @@ int handler::ha_rnd_next(uchar *buf)
m_lock_type != F_UNLCK);
DBUG_ASSERT(inited == RND);
- TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_FETCH_ROW, MAX_KEY, 0,
- { result= rnd_next(buf); })
- if (!result)
+ do
{
- update_rows_read();
- if (table->vfield && buf == table->record[0])
- table->update_virtual_fields(this, VCOL_UPDATE_FOR_READ);
- increment_statistics(&SSV::ha_read_rnd_next_count);
- }
- else if (result == HA_ERR_RECORD_DELETED)
- increment_statistics(&SSV::ha_read_rnd_deleted_count);
+ TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_FETCH_ROW, MAX_KEY, 0,
+ { result= rnd_next(buf); })
+ if (result != HA_ERR_RECORD_DELETED)
+ break;
+ status_var_increment(table->in_use->status_var.ha_read_rnd_deleted_count);
+ } while (!table->in_use->check_killed(1));
+
+ if (result == HA_ERR_RECORD_DELETED)
+ result= HA_ERR_ABORTED_BY_USER;
else
+ {
+ if (!result)
+ {
+ update_rows_read();
+ if (table->vfield && buf == table->record[0])
+ table->update_virtual_fields(this, VCOL_UPDATE_FOR_READ);
+ }
increment_statistics(&SSV::ha_read_rnd_next_count);
+ }
table->status=result ? STATUS_NOT_FOUND: 0;
DBUG_RETURN(result);
@@ -2694,7 +2879,9 @@ int handler::ha_rnd_pos(uchar *buf, uchar *pos)
TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_FETCH_ROW, MAX_KEY, 0,
{ result= rnd_pos(buf, pos); })
increment_statistics(&SSV::ha_read_rnd_count);
- if (!result)
+ if (result == HA_ERR_RECORD_DELETED)
+ result= HA_ERR_KEY_NOT_FOUND;
+ else if (!result)
{
update_rows_read();
if (table->vfield && buf == table->record[0])
@@ -2872,7 +3059,7 @@ bool handler::ha_was_semi_consistent_read()
int handler::ha_rnd_init_with_error(bool scan)
{
int error;
- if (!(error= ha_rnd_init(scan)))
+ if (likely(!(error= ha_rnd_init(scan))))
return 0;
table->file->print_error(error, MYF(0));
return error;
@@ -2880,10 +3067,11 @@ int handler::ha_rnd_init_with_error(bool scan)
/**
- Read first row (only) from a table.
+ Read first row (only) from a table. Used for reading tables with
+ only one row, either based on table statistics or if table is a SEQUENCE.
- This is never called for InnoDB tables, as these table types
- has the HA_STATS_RECORDS_IS_EXACT set.
+ This is never called for normal InnoDB tables, as these table types
+ does not have HA_STATS_RECORDS_IS_EXACT set.
*/
int handler::read_first_row(uchar * buf, uint primary_key)
{
@@ -2898,23 +3086,22 @@ int handler::read_first_row(uchar * buf, uint primary_key)
if (stats.deleted < 10 || primary_key >= MAX_KEY ||
!(index_flags(primary_key, 0, 0) & HA_READ_ORDER))
{
- if (!(error= ha_rnd_init(1)))
+ if (likely(!(error= ha_rnd_init(1))))
{
- while ((error= ha_rnd_next(buf)) == HA_ERR_RECORD_DELETED)
- /* skip deleted row */;
+ error= ha_rnd_next(buf);
const int end_error= ha_rnd_end();
- if (!error)
+ if (likely(!error))
error= end_error;
}
}
else
{
/* Find the first row through the primary key */
- if (!(error= ha_index_init(primary_key, 0)))
+ if (likely(!(error= ha_index_init(primary_key, 0))))
{
error= ha_index_first(buf);
const int end_error= ha_index_end();
- if (!error)
+ if (likely(!error))
error= end_error;
}
}
@@ -3131,6 +3318,25 @@ int handler::update_auto_increment()
DBUG_RETURN(0);
}
+ // ALTER TABLE ... ADD COLUMN ... AUTO_INCREMENT
+ if (thd->lex->sql_command == SQLCOM_ALTER_TABLE)
+ {
+ if (table->versioned())
+ {
+ Field *end= table->vers_end_field();
+ DBUG_ASSERT(end);
+ bitmap_set_bit(table->read_set, end->field_index);
+ if (!end->is_max())
+ {
+ if (!table->next_number_field->real_maybe_null())
+ DBUG_RETURN(HA_ERR_UNSUPPORTED);
+ table->next_number_field->set_null();
+ DBUG_RETURN(0);
+ }
+ }
+ table->next_number_field->set_notnull();
+ }
+
if ((nr= next_insert_id) >= auto_inc_interval_for_cur_row.maximum())
{
/* next_insert_id is beyond what is reserved, so we reserve more. */
@@ -3230,7 +3436,7 @@ int handler::update_auto_increment()
/* Store field without warning (Warning will be printed by insert) */
save_count_cuted_fields= thd->count_cuted_fields;
thd->count_cuted_fields= CHECK_FIELD_IGNORE;
- tmp= table->next_number_field->store((longlong) nr, TRUE);
+ tmp= table->next_number_field->store((longlong)nr, TRUE);
thd->count_cuted_fields= save_count_cuted_fields;
if (unlikely(tmp)) // Out of range value in store
@@ -3396,7 +3602,7 @@ void handler::get_auto_increment(ulonglong offset, ulonglong increment,
*nb_reserved_values= 1;
}
- if (error)
+ if (unlikely(error))
{
if (error == HA_ERR_END_OF_FILE || error == HA_ERR_KEY_NOT_FOUND)
/* No entry found, that's fine */;
@@ -3451,6 +3657,10 @@ void handler::ha_release_auto_increment()
@param msg Error message template to which key value should be
added.
@param errflag Flags for my_error() call.
+
+ @notes
+ The error message is from ER_DUP_ENTRY_WITH_KEY_NAME but to keep things compatibly
+ with old code, the error number is ER_DUP_ENTRY
*/
void print_keydup_error(TABLE *table, KEY *key, const char *msg, myf errflag)
@@ -3477,7 +3687,8 @@ void print_keydup_error(TABLE *table, KEY *key, const char *msg, myf errflag)
str.length(max_length-4);
str.append(STRING_WITH_LEN("..."));
}
- my_printf_error(ER_DUP_ENTRY, msg, errflag, str.c_ptr_safe(), key->name);
+ my_printf_error(ER_DUP_ENTRY, msg, errflag, str.c_ptr_safe(),
+ key->name.str);
}
}
@@ -3649,13 +3860,19 @@ void handler::print_error(int error, myf errflag)
textno=ER_UNSUPPORTED_EXTENSION;
break;
case HA_ERR_RECORD_FILE_FULL:
- case HA_ERR_INDEX_FILE_FULL:
{
textno=ER_RECORD_FILE_FULL;
/* Write the error message to error log */
errflag|= ME_NOREFRESH;
break;
}
+ case HA_ERR_INDEX_FILE_FULL:
+ {
+ textno=ER_INDEX_FILE_FULL;
+ /* Write the error message to error log */
+ errflag|= ME_NOREFRESH;
+ break;
+ }
case HA_ERR_LOCK_WAIT_TIMEOUT:
textno=ER_LOCK_WAIT_TIMEOUT;
break;
@@ -3710,7 +3927,7 @@ void handler::print_error(int error, myf errflag)
const char *ptr= "???";
uint key_nr= get_dup_key(error);
if ((int) key_nr >= 0)
- ptr= table->key_info[key_nr].name;
+ ptr= table->key_info[key_nr].name.str;
my_error(ER_DROP_INDEX_FK, errflag, ptr);
DBUG_VOID_RETURN;
}
@@ -3730,7 +3947,7 @@ void handler::print_error(int error, myf errflag)
break;
case HA_ERR_AUTOINC_ERANGE:
textno= error;
- my_error(textno, errflag, table->next_number_field->field_name,
+ my_error(textno, errflag, table->next_number_field->field_name.str,
table->in_use->get_stmt_da()->current_row_for_warning());
DBUG_VOID_RETURN;
break;
@@ -3777,7 +3994,7 @@ void handler::print_error(int error, myf errflag)
}
}
DBUG_ASSERT(textno > 0);
- if (fatal_error)
+ if (unlikely(fatal_error))
{
/* Ensure this becomes a true error */
errflag&= ~(ME_JUST_WARNING | ME_JUST_INFO);
@@ -3904,7 +4121,7 @@ int handler::ha_check_for_upgrade(HA_CHECK_OPT *check_opt)
if (table->s->frm_version < FRM_VER_TRUE_VARCHAR)
return HA_ADMIN_NEEDS_ALTER;
- if ((error= check_collation_compatibility()))
+ if (unlikely((error= check_collation_compatibility())))
return error;
return check_for_upgrade(check_opt);
@@ -3959,7 +4176,7 @@ static bool update_frm_version(TABLE *table)
int4store(version, MYSQL_VERSION_ID);
- if ((result= mysql_file_pwrite(file, (uchar*) version, 4, 51L, MYF_RW)))
+ if ((result= (int)mysql_file_pwrite(file, (uchar*) version, 4, 51L, MYF_RW)))
goto err;
table->s->mysql_version= MYSQL_VERSION_ID;
@@ -3982,7 +4199,8 @@ uint handler::get_dup_key(int error)
m_lock_type != F_UNLCK);
DBUG_ENTER("handler::get_dup_key");
table->file->errkey = (uint) -1;
- if (error == HA_ERR_FOUND_DUPP_KEY || error == HA_ERR_FOREIGN_DUPLICATE_KEY ||
+ 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);
@@ -4046,14 +4264,14 @@ int handler::rename_table(const char * from, const char * to)
start_ext= bas_ext();
for (ext= start_ext; *ext ; ext++)
{
- if (rename_file_ext(from, to, *ext))
+ if (unlikely(rename_file_ext(from, to, *ext)))
{
if ((error=my_errno) != ENOENT)
break;
error= 0;
}
}
- if (error)
+ if (unlikely(error))
{
/* Try to revert the rename. Ignore errors. */
for (; ext >= start_ext; ext--)
@@ -4097,15 +4315,15 @@ int handler::ha_check(THD *thd, HA_CHECK_OPT *check_opt)
if (table->s->mysql_version < MYSQL_VERSION_ID)
{
- if ((error= check_old_types()))
+ if (unlikely((error= check_old_types())))
return error;
error= ha_check_for_upgrade(check_opt);
- if (error && (error != HA_ADMIN_NEEDS_CHECK))
+ if (unlikely(error && (error != HA_ADMIN_NEEDS_CHECK)))
return error;
- if (!error && (check_opt->sql_flags & TT_FOR_UPGRADE))
+ if (unlikely(!error && (check_opt->sql_flags & TT_FOR_UPGRADE)))
return 0;
}
- if ((error= check(thd, check_opt)))
+ if (unlikely((error= check(thd, check_opt))))
return error;
/* Skip updating frm version if not main handler. */
if (table->file != this)
@@ -4131,7 +4349,7 @@ void handler::mark_trx_read_write_internal()
*/
if (ha_info->is_started())
{
- DBUG_ASSERT(has_transactions());
+ 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.
@@ -4184,8 +4402,8 @@ int handler::ha_end_bulk_insert()
*/
int
-handler::ha_bulk_update_row(const uchar *old_data, uchar *new_data,
- uint *dup_key_found)
+handler::ha_bulk_update_row(const uchar *old_data, const uchar *new_data,
+ ha_rows *dup_key_found)
{
DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
m_lock_type == F_WRLCK);
@@ -4392,21 +4610,29 @@ handler::check_if_supported_inplace_alter(TABLE *altered_table,
HA_CREATE_INFO *create_info= ha_alter_info->create_info;
- Alter_inplace_info::HA_ALTER_FLAGS inplace_offline_operations=
- Alter_inplace_info::ALTER_COLUMN_EQUAL_PACK_LENGTH |
- Alter_inplace_info::ALTER_COLUMN_NAME |
- Alter_inplace_info::ALTER_COLUMN_DEFAULT |
- Alter_inplace_info::ALTER_COLUMN_OPTION |
- Alter_inplace_info::CHANGE_CREATE_OPTION |
- Alter_inplace_info::ALTER_PARTITIONED |
- Alter_inplace_info::ALTER_VIRTUAL_GCOL_EXPR |
- Alter_inplace_info::ALTER_RENAME;
+ if (altered_table->versioned(VERS_TIMESTAMP))
+ DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
+
+ alter_table_operations inplace_offline_operations=
+ ALTER_COLUMN_EQUAL_PACK_LENGTH |
+ ALTER_COLUMN_NAME |
+ ALTER_RENAME_COLUMN |
+ ALTER_CHANGE_COLUMN_DEFAULT |
+ ALTER_COLUMN_DEFAULT |
+ ALTER_COLUMN_OPTION |
+ ALTER_CHANGE_CREATE_OPTION |
+ ALTER_DROP_CHECK_CONSTRAINT |
+ ALTER_PARTITIONED |
+ ALTER_VIRTUAL_GCOL_EXPR |
+ ALTER_RENAME;
/* Is there at least one operation that requires copy algorithm? */
if (ha_alter_info->handler_flags & ~inplace_offline_operations)
DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
/*
+ The following checks for changes related to ALTER_OPTIONS
+
ALTER TABLE tbl_name CONVERT TO CHARACTER SET .. and
ALTER TABLE table_name DEFAULT CHARSET = .. most likely
change column charsets and so not supported in-place through
@@ -4418,12 +4644,13 @@ handler::check_if_supported_inplace_alter(TABLE *altered_table,
if (create_info->used_fields & (HA_CREATE_USED_CHARSET |
HA_CREATE_USED_DEFAULT_CHARSET |
HA_CREATE_USED_PACK_KEYS |
+ HA_CREATE_USED_CHECKSUM |
HA_CREATE_USED_MAX_ROWS) ||
(table->s->row_type != create_info->row_type))
DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
uint table_changes= (ha_alter_info->handler_flags &
- Alter_inplace_info::ALTER_COLUMN_EQUAL_PACK_LENGTH) ?
+ ALTER_COLUMN_EQUAL_PACK_LENGTH) ?
IS_EQUAL_PACK_LENGTH : IS_EQUAL_YES;
if (table->file->check_if_incompatible_data(create_info, table_changes)
== COMPATIBLE_DATA_YES)
@@ -4433,7 +4660,7 @@ handler::check_if_supported_inplace_alter(TABLE *altered_table,
}
void Alter_inplace_info::report_unsupported_error(const char *not_supported,
- const char *try_instead)
+ const char *try_instead) const
{
if (unsupported_reason == NULL)
my_error(ER_ALTER_OPERATION_NOT_SUPPORTED, MYF(0),
@@ -4621,7 +4848,7 @@ int ha_enable_transaction(THD *thd, bool on)
is an optimization hint that storage engine is free to ignore.
So, let's commit an open transaction (if any) now.
*/
- if (!(error= ha_commit_trans(thd, 0)))
+ if (likely(!(error= ha_commit_trans(thd, 0))))
error= trans_commit_implicit(thd);
}
DBUG_RETURN(error);
@@ -4690,14 +4917,12 @@ void handler::get_dynamic_partition_info(PARTITION_STATS *stat_info,
stat_info->data_file_length= stats.data_file_length;
stat_info->max_data_file_length= stats.max_data_file_length;
stat_info->index_file_length= stats.index_file_length;
+ stat_info->max_index_file_length=stats.max_index_file_length;
stat_info->delete_length= stats.delete_length;
stat_info->create_time= stats.create_time;
stat_info->update_time= stats.update_time;
stat_info->check_time= stats.check_time;
- stat_info->check_sum= 0;
- if (table_flags() & (HA_HAS_OLD_CHECKSUM | HA_HAS_NEW_CHECKSUM))
- stat_info->check_sum= checksum();
- return;
+ stat_info->check_sum= stats.checksum;
}
@@ -4721,7 +4946,8 @@ void handler::update_global_table_stats()
if (rows_read + rows_changed == 0)
return; // Nothing to update.
- DBUG_ASSERT(table->s && table->s->table_cache_key.str);
+ DBUG_ASSERT(table->s);
+ DBUG_ASSERT(table->s->table_cache_key.str);
mysql_mutex_lock(&LOCK_global_table_stats);
/* Gets the global table stats, creating one if necessary. */
@@ -4739,7 +4965,7 @@ void handler::update_global_table_stats()
}
memcpy(table_stats->table, table->s->table_cache_key.str,
table->s->table_cache_key.length);
- table_stats->table_name_length= table->s->table_cache_key.length;
+ table_stats->table_name_length= (uint)table->s->table_cache_key.length;
table_stats->engine_type= ht->db_type;
/* No need to set variables to 0, as we use MY_ZEROFILL above */
@@ -4782,13 +5008,13 @@ void handler::update_global_index_stats()
if (index_rows_read[index])
{
INDEX_STATS* index_stats;
- uint key_length;
+ size_t key_length;
KEY *key_info = &table->key_info[index]; // Rows were read using this
DBUG_ASSERT(key_info->cache_name);
if (!key_info->cache_name)
continue;
- key_length= table->s->table_cache_key.length + key_info->name_length + 1;
+ key_length= table->s->table_cache_key.length + key_info->name.length + 1;
mysql_mutex_lock(&LOCK_global_index_stats);
// Gets the global index stats, creating one if necessary.
if (!(index_stats= (INDEX_STATS*) my_hash_search(&global_index_stats,
@@ -4818,6 +5044,98 @@ end:
}
+static void flush_checksum(ha_checksum *row_crc, uchar **checksum_start,
+ size_t *checksum_length)
+{
+ if (*checksum_start)
+ {
+ *row_crc= my_checksum(*row_crc, *checksum_start, *checksum_length);
+ *checksum_start= NULL;
+ *checksum_length= 0;
+ }
+}
+
+
+/* calculating table's checksum */
+int handler::calculate_checksum()
+{
+ int error;
+ THD *thd=ha_thd();
+ DBUG_ASSERT(table->s->last_null_bit_pos < 8);
+ uchar null_mask= table->s->last_null_bit_pos
+ ? 256 - (1 << table->s->last_null_bit_pos) : 0;
+
+ table->use_all_columns();
+ stats.checksum= 0;
+
+ if ((error= ha_rnd_init(1)))
+ return error;
+
+ for (;;)
+ {
+ if (thd->killed)
+ return HA_ERR_ABORTED_BY_USER;
+
+ ha_checksum row_crc= 0;
+ error= table->file->ha_rnd_next(table->record[0]);
+ if (error)
+ break;
+
+ if (table->s->null_bytes)
+ {
+ /* fix undefined null bits */
+ table->record[0][table->s->null_bytes-1] |= null_mask;
+ if (!(table->s->db_create_options & HA_OPTION_PACK_RECORD))
+ table->record[0][0] |= 1;
+
+ row_crc= my_checksum(row_crc, table->record[0], table->s->null_bytes);
+ }
+
+ uchar *checksum_start= NULL;
+ size_t checksum_length= 0;
+ for (uint i= 0; i < table->s->fields; i++ )
+ {
+ Field *f= table->field[i];
+
+ if (! thd->variables.old_mode && f->is_real_null(0))
+ {
+ flush_checksum(&row_crc, &checksum_start, &checksum_length);
+ continue;
+ }
+ /*
+ BLOB and VARCHAR have pointers in their field, we must convert
+ to string; GEOMETRY is implemented on top of BLOB.
+ BIT may store its data among NULL bits, convert as well.
+ */
+ switch (f->type()) {
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_GEOMETRY:
+ case MYSQL_TYPE_BIT:
+ {
+ flush_checksum(&row_crc, &checksum_start, &checksum_length);
+ String tmp;
+ f->val_str(&tmp);
+ row_crc= my_checksum(row_crc, (uchar*) tmp.ptr(), tmp.length());
+ break;
+ }
+ default:
+ if (!checksum_start)
+ checksum_start= f->ptr;
+ DBUG_ASSERT(checksum_start + checksum_length == f->ptr);
+ checksum_length+= f->pack_length();
+ break;
+ }
+ }
+ flush_checksum(&row_crc, &checksum_start, &checksum_length);
+
+ stats.checksum+= row_crc;
+ }
+ table->file->ha_rnd_end();
+ return error == HA_ERR_END_OF_FILE ? 0 : error;
+}
+
+
/****************************************************************************
** Some general functions that isn't in the handler class
****************************************************************************/
@@ -4841,7 +5159,6 @@ int ha_create_table(THD *thd, const char *path,
TABLE_SHARE share;
bool temp_table __attribute__((unused)) =
create_info->options & (HA_LEX_CREATE_TMP_TABLE | HA_CREATE_TMP_ALTER);
-
DBUG_ENTER("ha_create_table");
init_tmp_table_share(thd, &share, db, 0, table_name, path);
@@ -4869,7 +5186,8 @@ int ha_create_table(THD *thd, const char *path,
share.m_psi= PSI_CALL_get_table_share(temp_table, &share);
- if (open_table_from_share(thd, &share, "", 0, READ_ALL, 0, &table, true))
+ if (open_table_from_share(thd, &share, &empty_clex_str, 0, READ_ALL, 0,
+ &table, true))
goto err;
update_create_info_from_table(create_info, &table);
@@ -4878,13 +5196,13 @@ int ha_create_table(THD *thd, const char *path,
error= table.file->ha_create(name, &table, create_info);
- if (error)
+ if (unlikely(error))
{
if (!thd->is_error())
my_error(ER_CANT_CREATE_TABLE, MYF(0), db, table_name, error);
table.file->print_error(error, MYF(ME_JUST_WARNING));
- PSI_CALL_drop_table_share(temp_table, share.db.str, share.db.length,
- share.table_name.str, share.table_name.length);
+ PSI_CALL_drop_table_share(temp_table, share.db.str, (uint)share.db.length,
+ share.table_name.str, (uint)share.table_name.length);
}
(void) closefrm(&table);
@@ -5033,7 +5351,7 @@ static my_bool discover_handlerton(THD *thd, plugin_ref plugin,
int error= hton->discover_table(hton, thd, share);
if (error != HA_ERR_NO_SUCH_TABLE)
{
- if (error)
+ if (unlikely(error))
{
if (!share->error)
{
@@ -5179,30 +5497,36 @@ private:
loaded, frm is invalid), the return value will be true, but
*hton will be NULL.
*/
-bool ha_table_exists(THD *thd, const char *db, const char *table_name,
- handlerton **hton)
+
+bool ha_table_exists(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *table_name,
+ handlerton **hton, bool *is_sequence)
{
handlerton *dummy;
+ bool dummy2;
DBUG_ENTER("ha_table_exists");
if (hton)
*hton= 0;
else if (engines_with_discover)
hton= &dummy;
+ if (!is_sequence)
+ is_sequence= &dummy2;
+ *is_sequence= 0;
- TDC_element *element= tdc_lock_share(thd, db, table_name);
+ TDC_element *element= tdc_lock_share(thd, db->str, table_name->str);
if (element && element != MY_ERRPTR)
{
if (hton)
*hton= element->share->db_type();
+ *is_sequence= element->share->table_type == TABLE_TYPE_SEQUENCE;
tdc_unlock_share(element);
DBUG_RETURN(TRUE);
}
char path[FN_REFLEN + 1];
size_t path_len = build_table_filename(path, sizeof(path) - 1,
- db, table_name, "", 0);
- st_discover_existence_args args= {path, path_len, db, table_name, 0, true};
+ db->str, table_name->str, "", 0);
+ st_discover_existence_args args= {path, path_len, db->str, table_name->str, 0, true};
if (file_ext_exists(path, path_len, reg_ext))
{
@@ -5210,16 +5534,17 @@ bool ha_table_exists(THD *thd, const char *db, const char *table_name,
if (hton)
{
char engine_buf[NAME_CHAR_LEN + 1];
- LEX_STRING engine= { engine_buf, 0 };
- frm_type_enum type;
+ LEX_CSTRING engine= { engine_buf, 0 };
+ Table_type type;
- if ((type= dd_frm_type(thd, path, &engine)) == FRMTYPE_ERROR)
+ if ((type= dd_frm_type(thd, path, &engine, is_sequence)) ==
+ TABLE_TYPE_UNKNOWN)
DBUG_RETURN(0);
-
- if (type != FRMTYPE_VIEW)
+
+ if (type != TABLE_TYPE_VIEW)
{
- plugin_ref p= plugin_lock_by_name(thd, &engine,
- MYSQL_STORAGE_ENGINE_PLUGIN);
+ 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
@@ -5240,19 +5565,16 @@ bool ha_table_exists(THD *thd, const char *db, const char *table_name,
DBUG_RETURN(TRUE);
}
-
if (need_full_discover_for_existence)
{
TABLE_LIST table;
uint flags = GTS_TABLE | GTS_VIEW;
-
if (!hton)
flags|= GTS_NOLOCK;
Table_exists_error_handler no_such_table_handler;
thd->push_internal_handler(&no_such_table_handler);
- table.init_one_table(db, strlen(db), table_name, strlen(table_name),
- table_name, TL_READ);
+ table.init_one_table(db, table_name, 0, TL_READ);
TABLE_SHARE *share= tdc_acquire_share(thd, &table, flags);
thd->pop_internal_handler();
@@ -5282,17 +5604,24 @@ static int cmp_file_names(const void *a, const void *b)
return my_strnncoll(cs, (uchar*)aa, strlen(aa), (uchar*)bb, strlen(bb));
}
-static int cmp_table_names(LEX_STRING * const *a, LEX_STRING * const *b)
+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);
}
+#ifndef DBUG_OFF
+static int cmp_table_names_desc(LEX_CSTRING * const *a, LEX_CSTRING * const *b)
+{
+ return -cmp_table_names(a, b);
+}
+#endif
+
}
Discovered_table_list::Discovered_table_list(THD *thd_arg,
- Dynamic_array<LEX_STRING*> *tables_arg,
- const LEX_STRING *wild_arg) :
+ Dynamic_array<LEX_CSTRING*> *tables_arg,
+ const LEX_CSTRING *wild_arg) :
thd(thd_arg), with_temps(false), tables(tables_arg)
{
if (wild_arg->str && wild_arg->str[0])
@@ -5316,7 +5645,7 @@ bool Discovered_table_list::add_table(const char *tname, size_t tlen)
wild_prefix, wild_one, wild_many))
return 0;
- LEX_STRING *name= thd->make_lex_string(tname, tlen);
+ LEX_CSTRING *name= thd->make_clex_string(tname, tlen);
if (!name || tables->append(name))
return 1;
return 0;
@@ -5340,14 +5669,23 @@ void Discovered_table_list::sort()
tables->sort(cmp_table_names);
}
+
+#ifndef DBUG_OFF
+void Discovered_table_list::sort_desc()
+{
+ tables->sort(cmp_table_names_desc);
+}
+#endif
+
+
void Discovered_table_list::remove_duplicates()
{
- LEX_STRING **src= tables->front();
- LEX_STRING **dst= src;
+ LEX_CSTRING **src= tables->front();
+ LEX_CSTRING **dst= src;
sort();
while (++dst <= tables->back())
{
- LEX_STRING *s= *src, *d= *dst;
+ LEX_CSTRING *s= *src, *d= *dst;
DBUG_ASSERT(strncmp(s->str, d->str, MY_MIN(s->length, d->length)) <= 0);
if ((s->length != d->length || strncmp(s->str, d->str, d->length)))
{
@@ -5361,7 +5699,7 @@ void Discovered_table_list::remove_duplicates()
struct st_discover_names_args
{
- LEX_STRING *db;
+ LEX_CSTRING *db;
MY_DIR *dirp;
Discovered_table_list *result;
uint possible_duplicates;
@@ -5375,7 +5713,7 @@ static my_bool discover_names(THD *thd, plugin_ref plugin,
if (ht->state == SHOW_OPTION_YES && ht->discover_table_names)
{
- uint old_elements= args->result->tables->elements();
+ size_t old_elements= args->result->tables->elements();
if (ht->discover_table_names(ht, args->db, args->dirp, args->result))
return 1;
@@ -5384,7 +5722,7 @@ static my_bool discover_names(THD *thd, plugin_ref plugin,
a corresponding .frm file; but custom engine discover methods might
*/
if (ht->discover_table_names != hton_ext_based_table_discovery)
- args->possible_duplicates+= args->result->tables->elements() - old_elements;
+ args->possible_duplicates+= (uint)(args->result->tables->elements() - old_elements);
}
return 0;
@@ -5406,7 +5744,7 @@ static my_bool discover_names(THD *thd, plugin_ref plugin,
for DROP DATABASE (as it needs to know and delete non-table files).
*/
-int ha_discover_table_names(THD *thd, LEX_STRING *db, MY_DIR *dirp,
+int ha_discover_table_names(THD *thd, LEX_CSTRING *db, MY_DIR *dirp,
Discovered_table_list *result, bool reusable)
{
int error;
@@ -5438,6 +5776,27 @@ int ha_discover_table_names(THD *thd, LEX_STRING *db, MY_DIR *dirp,
}
+/*
+int handler::pre_read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
+ KEY_MULTI_RANGE *ranges,
+ uint range_count,
+ bool sorted, HANDLER_BUFFER *buffer,
+ bool use_parallel)
+{
+ int result;
+ DBUG_ENTER("handler::pre_read_multi_range_first");
+ result = pre_read_range_first(ranges->start_key.keypart_map ?
+ &ranges->start_key : 0,
+ ranges->end_key.keypart_map ?
+ &ranges->end_key : 0,
+ test(ranges->range_flag & EQ_RANGE),
+ sorted,
+ use_parallel);
+ DBUG_RETURN(result);
+}
+*/
+
+
/**
Read first row between two ranges.
Store ranges for future calls to read_range_next.
@@ -5629,12 +5988,12 @@ int handler::index_read_idx_map(uchar * buf, uint index, const uchar * key,
int error, UNINIT_VAR(error1);
error= ha_index_init(index, 0);
- if (!error)
+ if (likely(!error))
{
error= index_read_map(buf, key, keypart_map, find_flag);
error1= ha_index_end();
}
- return error ? error : error1;
+ return error ? error : error1;
}
@@ -5756,7 +6115,7 @@ bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat)
{
if (db_type->state != SHOW_OPTION_YES)
{
- const LEX_STRING *name= hton_name(db_type);
+ const LEX_CSTRING *name= hton_name(db_type);
result= stat_print(thd, name->str, name->length,
"", 0, "DISABLED", 8) ? 1 : 0;
}
@@ -5771,7 +6130,7 @@ bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat)
We also check thd->is_error() as Innodb may return 0 even if
there was an error.
*/
- if (!result && !thd->is_error())
+ if (likely(!result && !thd->is_error()))
my_eof(thd);
else if (!thd->is_error())
my_error(ER_GET_ERRNO, MYF(0), errno, hton_name(db_type)->str);
@@ -5796,8 +6155,10 @@ bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat)
1 Row needs to be logged
*/
-inline bool handler::check_table_binlog_row_based(bool binlog_row)
+bool handler::check_table_binlog_row_based(bool binlog_row)
{
+ if (table->versioned(VERS_TRX_ID))
+ return false;
if (unlikely((table->in_use->variables.sql_log_bin_off)))
return 0; /* Called by partitioning engine */
if (unlikely((!check_table_binlog_row_based_done)))
@@ -5813,7 +6174,7 @@ bool handler::check_table_binlog_row_based_internal(bool binlog_row)
{
THD *thd= table->in_use;
- return (table->s->cached_row_logging_check &&
+ return (table->s->can_do_row_logging &&
thd->is_current_stmt_binlog_format_row() &&
/*
Wsrep partially enables binary logging if it have not been
@@ -5921,8 +6282,6 @@ static int write_locked_table_maps(THD *thd)
}
-typedef bool Log_func(THD*, TABLE*, bool, const uchar*, const uchar*);
-
static int binlog_log_row_internal(TABLE* table,
const uchar *before_record,
const uchar *after_record,
@@ -5954,10 +6313,8 @@ static int binlog_log_row_internal(TABLE* table,
return error ? HA_ERR_RBR_LOGGING_FAILED : 0;
}
-static inline int binlog_log_row(TABLE* table,
- const uchar *before_record,
- const uchar *after_record,
- Log_func *log_func)
+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;
@@ -6036,7 +6393,7 @@ int handler::ha_external_lock(THD *thd, int lock_type)
DBUG_EXECUTE_IF("external_lock_failure", error= HA_ERR_GENERIC;);
- if (error == 0 || lock_type == F_UNLCK)
+ if (likely(error == 0 || lock_type == F_UNLCK))
{
m_lock_type= lock_type;
cached_table_flags= table_flags();
@@ -6088,6 +6445,7 @@ int handler::ha_reset()
/* Reset information about pushed engine conditions */
cancel_pushed_idx_cond();
/* Reset information about pushed index conditions */
+ clear_top_table_fields();
DBUG_RETURN(reset());
}
@@ -6109,7 +6467,7 @@ int handler::ha_write_row(uchar *buf)
{ error= write_row(buf); })
MYSQL_INSERT_ROW_DONE(error);
- if (likely(!error))
+ if (likely(!error) && !row_already_logged)
{
rows_changed++;
error= binlog_log_row(table, 0, buf, log_func);
@@ -6119,7 +6477,7 @@ int handler::ha_write_row(uchar *buf)
}
-int handler::ha_update_row(const uchar *old_data, uchar *new_data)
+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;
@@ -6141,7 +6499,7 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data)
{ error= update_row(old_data, new_data);})
MYSQL_UPDATE_ROW_DONE(error);
- if (likely(!error))
+ if (likely(!error) && !row_already_logged)
{
rows_changed++;
error= binlog_log_row(table, old_data, new_data, log_func);
@@ -6149,6 +6507,34 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data)
return error;
}
+/*
+ Update first row. Only used by sequence tables
+*/
+
+int handler::update_first_row(uchar *new_data)
+{
+ int error;
+ if (likely(!(error= ha_rnd_init(1))))
+ {
+ int end_error;
+ if (likely(!(error= ha_rnd_next(table->record[1]))))
+ {
+ /*
+ We have to do the memcmp as otherwise we may get error 169 from InnoDB
+ */
+ if (memcmp(new_data, table->record[1], table->s->reclength))
+ error= update_row(table->record[1], new_data);
+ }
+ end_error= ha_rnd_end();
+ if (likely(!error))
+ error= end_error;
+ /* Logging would be wrong if update_row works but ha_rnd_end fails */
+ DBUG_ASSERT(!end_error || error != 0);
+ }
+ return error;
+}
+
+
int handler::ha_delete_row(const uchar *buf)
{
int error;
@@ -6177,6 +6563,59 @@ int handler::ha_delete_row(const uchar *buf)
}
+/**
+ Execute a direct update request. A direct update request updates all
+ qualified rows in a single operation, rather than one row at a time.
+ In a Spider cluster the direct update operation is pushed down to the
+ child levels of the cluster.
+
+ Note that this can't be used in case of statment logging
+
+ @param update_rows Number of updated rows.
+
+ @retval 0 Success.
+ @retval != 0 Failure.
+*/
+
+int handler::ha_direct_update_rows(ha_rows *update_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);
+ MYSQL_UPDATE_ROW_DONE(error);
+ return error;
+}
+
+
+/**
+ Execute a direct delete request. A direct delete request deletes all
+ qualified rows in a single operation, rather than one row at a time.
+ In a Spider cluster the direct delete operation is pushed down to the
+ child levels of the cluster.
+
+ @param delete_rows Number of deleted rows.
+
+ @retval 0 Success.
+ @retval != 0 Failure.
+*/
+
+int handler::ha_direct_delete_rows(ha_rows *delete_rows)
+{
+ int error;
+ /* Ensure we are not using binlog row */
+ DBUG_ASSERT(!table->in_use->is_current_stmt_binlog_format_row());
+
+ MYSQL_DELETE_ROW_START(table_share->db.str, table_share->table_name.str);
+ mark_trx_read_write();
+
+ error = direct_delete_rows(delete_rows);
+ MYSQL_DELETE_ROW_DONE(error);
+ return error;
+}
+
/** @brief
use_hidden_primary_key() is called in case of an update/delete when
@@ -6204,7 +6643,8 @@ void handler::use_hidden_primary_key()
Handler_share *handler::get_ha_share_ptr()
{
DBUG_ENTER("handler::get_ha_share_ptr");
- DBUG_ASSERT(ha_share && table_share);
+ DBUG_ASSERT(ha_share);
+ DBUG_ASSERT(table_share);
#ifndef DBUG_OFF
if (table_share->tmp_table == NO_TMP_TABLE)
@@ -6566,7 +7006,7 @@ bool HA_CREATE_INFO::check_conflicting_charset_declarations(CHARSET_INFO *cs)
/* Remove all indexes for a given table from global index statistics */
static
-int del_global_index_stats_for_table(THD *thd, uchar* cache_key, uint cache_key_length)
+int del_global_index_stats_for_table(THD *thd, uchar* cache_key, size_t cache_key_length)
{
int res = 0;
DBUG_ENTER("del_global_index_stats_for_table");
@@ -6602,12 +7042,12 @@ int del_global_index_stats_for_table(THD *thd, uchar* cache_key, uint cache_key_
/* Remove a table from global table statistics */
-int del_global_table_stat(THD *thd, LEX_STRING *db, LEX_STRING *table)
+int del_global_table_stat(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *table)
{
TABLE_STATS *table_stats;
int res = 0;
uchar *cache_key;
- uint cache_key_length;
+ size_t cache_key_length;
DBUG_ENTER("del_global_table_stat");
cache_key_length= db->length + 1 + table->length + 1;
@@ -6644,7 +7084,7 @@ end:
int del_global_index_stat(THD *thd, TABLE* table, KEY* key_info)
{
INDEX_STATS *index_stats;
- uint key_length= table->s->table_cache_key.length + key_info->name_length + 1;
+ size_t key_length= table->s->table_cache_key.length + key_info->name.length + 1;
int res = 0;
DBUG_ENTER("del_global_index_stat");
mysql_mutex_lock(&LOCK_global_index_stats);
@@ -6657,3 +7097,469 @@ int del_global_index_stat(THD *thd, TABLE* table, KEY* key_info)
mysql_mutex_unlock(&LOCK_global_index_stats);
DBUG_RETURN(res);
}
+
+bool Vers_parse_info::is_start(const char *name) const
+{
+ DBUG_ASSERT(name);
+ return as_row.start && as_row.start.streq(name);
+}
+bool Vers_parse_info::is_end(const char *name) const
+{
+ DBUG_ASSERT(name);
+ return as_row.end && as_row.end.streq(name);
+}
+bool Vers_parse_info::is_start(const Create_field &f) const
+{
+ return f.flags & VERS_SYS_START_FLAG;
+}
+bool Vers_parse_info::is_end(const Create_field &f) const
+{
+ return f.flags & VERS_SYS_END_FLAG;
+}
+
+static Create_field *vers_init_sys_field(THD *thd, const char *field_name, int flags, bool integer)
+{
+ Create_field *f= new (thd->mem_root) Create_field();
+ if (!f)
+ return NULL;
+
+ f->field_name.str= field_name;
+ f->field_name.length= strlen(field_name);
+ f->charset= system_charset_info;
+ f->flags= flags | NOT_NULL_FLAG;
+ if (integer)
+ {
+ DBUG_ASSERT(0); // Not implemented yet
+ f->set_handler(&type_handler_vers_trx_id);
+ f->length= MY_INT64_NUM_DECIMAL_DIGITS - 1;
+ f->flags|= UNSIGNED_FLAG;
+ }
+ else
+ {
+ f->set_handler(&type_handler_timestamp2);
+ f->length= MAX_DATETIME_PRECISION;
+ }
+ f->invisible= DBUG_EVALUATE_IF("sysvers_show", VISIBLE, INVISIBLE_SYSTEM);
+
+ if (f->check(thd))
+ return NULL;
+
+ return f;
+}
+
+static bool vers_create_sys_field(THD *thd, const char *field_name,
+ Alter_info *alter_info, int flags)
+{
+ Create_field *f= vers_init_sys_field(thd, field_name, flags, false);
+ if (!f)
+ return true;
+
+ alter_info->flags|= ALTER_PARSER_ADD_COLUMN;
+ alter_info->create_list.push_back(f);
+
+ return false;
+}
+
+const Lex_ident Vers_parse_info::default_start= "row_start";
+const Lex_ident Vers_parse_info::default_end= "row_end";
+
+bool Vers_parse_info::fix_implicit(THD *thd, Alter_info *alter_info)
+{
+ // If user specified some of these he must specify the others too. Do nothing.
+ if (*this)
+ return false;
+
+ alter_info->flags|= ALTER_PARSER_ADD_COLUMN;
+
+ system_time= start_end_t(default_start, default_end);
+ as_row= system_time;
+
+ if (vers_create_sys_field(thd, default_start, alter_info, VERS_SYS_START_FLAG) ||
+ vers_create_sys_field(thd, default_end, alter_info, VERS_SYS_END_FLAG))
+ {
+ return true;
+ }
+ return false;
+}
+
+
+bool Table_scope_and_contents_source_st::vers_fix_system_fields(
+ THD *thd, Alter_info *alter_info, const TABLE_LIST &create_table)
+{
+ DBUG_ASSERT(!(alter_info->flags & ALTER_DROP_SYSTEM_VERSIONING));
+
+ DBUG_EXECUTE_IF("sysvers_force", if (!tmp_table()) {
+ alter_info->flags|= ALTER_ADD_SYSTEM_VERSIONING;
+ options|= HA_VERSIONED_TABLE; });
+
+ if (!vers_info.need_check(alter_info))
+ return false;
+
+ if (!vers_info.versioned_fields && vers_info.unversioned_fields &&
+ !(alter_info->flags & ALTER_ADD_SYSTEM_VERSIONING))
+ {
+ // All is correct but this table is not versioned.
+ options&= ~HA_VERSIONED_TABLE;
+ return false;
+ }
+
+ if (!(alter_info->flags & ALTER_ADD_SYSTEM_VERSIONING) && vers_info)
+ {
+ my_error(ER_MISSING, MYF(0), create_table.table_name.str,
+ "WITH SYSTEM VERSIONING");
+ return true;
+ }
+
+ List_iterator<Create_field> it(alter_info->create_list);
+ while (Create_field *f= it++)
+ {
+ if ((f->versioning == Column_definition::VERSIONING_NOT_SET &&
+ !(alter_info->flags & ALTER_ADD_SYSTEM_VERSIONING)) ||
+ f->versioning == Column_definition::WITHOUT_VERSIONING)
+ {
+ f->flags|= VERS_UPDATE_UNVERSIONED_FLAG;
+ }
+ } // while (Create_field *f= it++)
+
+ if (vers_info.fix_implicit(thd, alter_info))
+ return true;
+
+ return false;
+}
+
+
+bool Table_scope_and_contents_source_st::vers_check_system_fields(
+ THD *thd, Alter_info *alter_info, const Lex_table_name &table_name,
+ const Lex_table_name &db, int select_count)
+{
+ if (!(options & HA_VERSIONED_TABLE))
+ return false;
+
+ if (!(alter_info->flags & ALTER_DROP_SYSTEM_VERSIONING))
+ {
+ uint versioned_fields= 0;
+ uint fieldnr= 0;
+ List_iterator<Create_field> field_it(alter_info->create_list);
+ while (Create_field *f= field_it++)
+ {
+ /*
+ The field from the CREATE part can be duplicated in the SELECT part of
+ CREATE...SELECT. In that case double counts should be avoided.
+ select_create::create_table_from_items just pushes the fields back into
+ the create_list, without additional manipulations, so the fields from
+ SELECT go last there.
+ */
+ bool is_dup= false;
+ if (fieldnr >= alter_info->create_list.elements - select_count)
+ {
+ List_iterator<Create_field> dup_it(alter_info->create_list);
+ for (Create_field *dup= dup_it++; !is_dup && dup != f; dup= dup_it++)
+ is_dup= my_strcasecmp(default_charset_info,
+ dup->field_name.str, f->field_name.str) == 0;
+ }
+
+ if (!(f->flags & VERS_UPDATE_UNVERSIONED_FLAG) && !is_dup)
+ versioned_fields++;
+ fieldnr++;
+ }
+ if (versioned_fields == VERSIONING_FIELDS)
+ {
+ my_error(ER_VERS_TABLE_MUST_HAVE_COLUMNS, MYF(0), table_name.str);
+ return true;
+ }
+ }
+
+ 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);
+}
+
+
+bool Vers_parse_info::fix_alter_info(THD *thd, Alter_info *alter_info,
+ HA_CREATE_INFO *create_info, TABLE *table)
+{
+ TABLE_SHARE *share= table->s;
+ const char *table_name= share->table_name.str;
+
+ if (!need_check(alter_info) && !share->versioned)
+ return false;
+
+ if (DBUG_EVALUATE_IF("sysvers_force", 0, share->tmp_table))
+ {
+ my_error(ER_VERS_TEMPORARY, MYF(0));
+ return true;
+ }
+
+ if (alter_info->flags & ALTER_ADD_SYSTEM_VERSIONING &&
+ table->versioned())
+ {
+ my_error(ER_VERS_ALREADY_VERSIONED, MYF(0), table_name);
+ return true;
+ }
+
+ if (alter_info->flags & ALTER_DROP_SYSTEM_VERSIONING)
+ {
+ if (!share->versioned)
+ {
+ my_error(ER_VERS_NOT_VERSIONED, MYF(0), table_name);
+ return true;
+ }
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (table->part_info &&
+ table->part_info->part_type == VERSIONING_PARTITION)
+ {
+ my_error(ER_DROP_VERSIONING_SYSTEM_TIME_PARTITION, MYF(0), table_name);
+ return true;
+ }
+#endif
+
+ return false;
+ }
+
+ if (!(alter_info->flags & ALTER_ADD_SYSTEM_VERSIONING))
+ {
+ List_iterator_fast<Create_field> it(alter_info->create_list);
+ while (Create_field *f= it++)
+ {
+ if (f->flags & VERS_SYSTEM_FIELD)
+ {
+ my_error(ER_VERS_DUPLICATE_ROW_START_END, MYF(0),
+ f->flags & VERS_SYS_START_FLAG ? "START" : "END", f->field_name.str);
+ return true;
+ }
+ }
+ }
+
+ if ((alter_info->flags & ALTER_DROP_PERIOD ||
+ versioned_fields || unversioned_fields) && !share->versioned)
+ {
+ my_error(ER_VERS_NOT_VERSIONED, MYF(0), table_name);
+ return true;
+ }
+
+ if (share->versioned)
+ {
+ if (alter_info->flags & ALTER_ADD_PERIOD)
+ {
+ my_error(ER_VERS_ALREADY_VERSIONED, MYF(0), table_name);
+ return true;
+ }
+
+ // copy info from existing table
+ create_info->options|= HA_VERSIONED_TABLE;
+
+ DBUG_ASSERT(share->vers_start_field());
+ DBUG_ASSERT(share->vers_end_field());
+ Lex_ident start(share->vers_start_field()->field_name);
+ Lex_ident end(share->vers_end_field()->field_name);
+ DBUG_ASSERT(start.str);
+ DBUG_ASSERT(end.str);
+
+ as_row= start_end_t(start, end);
+ system_time= as_row;
+
+ if (alter_info->create_list.elements)
+ {
+ List_iterator_fast<Create_field> it(alter_info->create_list);
+ while (Create_field *f= it++)
+ {
+ if (f->versioning == Column_definition::WITHOUT_VERSIONING)
+ f->flags|= VERS_UPDATE_UNVERSIONED_FLAG;
+
+ if (f->change.str && (start.streq(f->change) || end.streq(f->change)))
+ {
+ my_error(ER_VERS_ALTER_SYSTEM_FIELD, MYF(0), f->change.str);
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ return fix_implicit(thd, alter_info);
+}
+
+bool
+Vers_parse_info::fix_create_like(Alter_info &alter_info, HA_CREATE_INFO &create_info,
+ TABLE_LIST &src_table, TABLE_LIST &table)
+{
+ List_iterator<Create_field> it(alter_info.create_list);
+ Create_field *f, *f_start=NULL, *f_end= NULL;
+
+ DBUG_ASSERT(alter_info.create_list.elements > 2);
+
+ if (create_info.tmp_table())
+ {
+ int remove= 2;
+ while (remove && (f= it++))
+ {
+ if (f->flags & VERS_SYSTEM_FIELD)
+ {
+ it.remove();
+ remove--;
+ }
+ }
+ DBUG_ASSERT(remove == 0);
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_UNKNOWN_ERROR,
+ "System versioning is stripped from temporary `%s.%s`",
+ table.db.str, table.table_name.str);
+ return false;
+ }
+
+ while ((f= it++))
+ {
+ if (f->flags & VERS_SYS_START_FLAG)
+ {
+ f_start= f;
+ if (f_end)
+ break;
+ }
+ else if (f->flags & VERS_SYS_END_FLAG)
+ {
+ f_end= f;
+ if (f_start)
+ break;
+ }
+ }
+
+ if (!f_start || !f_end)
+ {
+ my_error(ER_MISSING, MYF(0), src_table.table_name.str,
+ f_start ? "AS ROW END" : "AS ROW START");
+ return true;
+ }
+
+ as_row= start_end_t(f_start->field_name, f_end->field_name);
+ system_time= as_row;
+
+ create_info.options|= HA_VERSIONED_TABLE;
+ return false;
+}
+
+bool Vers_parse_info::need_check(const Alter_info *alter_info) const
+{
+ return versioned_fields || unversioned_fields ||
+ alter_info->flags & ALTER_ADD_PERIOD ||
+ alter_info->flags & ALTER_DROP_PERIOD ||
+ alter_info->flags & ALTER_ADD_SYSTEM_VERSIONING ||
+ alter_info->flags & ALTER_DROP_SYSTEM_VERSIONING || *this;
+}
+
+bool Vers_parse_info::check_conditions(const Lex_table_name &table_name,
+ const Lex_table_name &db) const
+{
+ if (!as_row.start || !as_row.end)
+ {
+ my_error(ER_MISSING, MYF(0), table_name.str,
+ as_row.start ? "AS ROW END" : "AS ROW START");
+ return true;
+ }
+
+ if (!system_time.start || !system_time.end)
+ {
+ my_error(ER_MISSING, MYF(0), table_name.str, "PERIOD FOR SYSTEM_TIME");
+ return true;
+ }
+
+ if (!as_row.start.streq(system_time.start) ||
+ !as_row.end.streq(system_time.end))
+ {
+ my_error(ER_VERS_PERIOD_COLUMNS, MYF(0), as_row.start.str, as_row.end.str);
+ return true;
+ }
+
+ if (db.streq(MYSQL_SCHEMA_NAME))
+ {
+ my_error(ER_VERS_DB_NOT_SUPPORTED, MYF(0), MYSQL_SCHEMA_NAME.str);
+ return true;
+ }
+ return false;
+}
+
+static bool is_versioning_timestamp(const Create_field *f)
+{
+ return f->type_handler() == &type_handler_timestamp2 &&
+ f->length == MAX_DATETIME_FULL_WIDTH;
+}
+
+static bool is_some_bigint(const Create_field *f)
+{
+ return f->type_handler() == &type_handler_longlong ||
+ f->type_handler() == &type_handler_vers_trx_id;
+}
+
+static bool is_versioning_bigint(const Create_field *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)
+{
+ my_error(ER_VERS_FIELD_WRONG_TYPE, MYF(0), f->field_name.str, "TIMESTAMP(6)",
+ table_name.str);
+ return true;
+}
+static bool require_bigint(const Create_field *f, Lex_table_name table_name)
+{
+ my_error(ER_VERS_FIELD_WRONG_TYPE, MYF(0), f->field_name.str,
+ "BIGINT(20) UNSIGNED", table_name.str);
+ return true;
+}
+
+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
+{
+ if (check_conditions(table_name, db))
+ return true;
+
+ 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++)
+ {
+ if (!row_start && f->flags & VERS_SYS_START_FLAG)
+ row_start= f;
+ else if (!row_end && f->flags & VERS_SYS_END_FLAG)
+ row_end= f;
+ }
+
+ const bool expect_timestamp=
+ !can_native || !is_some_bigint(row_start) || !is_some_bigint(row_end);
+
+ if (expect_timestamp)
+ {
+ if (!is_versioning_timestamp(row_start))
+ return require_timestamp(row_start, table_name);
+
+ if (!is_versioning_timestamp(row_end))
+ return require_timestamp(row_end, table_name);
+ }
+ else
+ {
+ 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);
+ }
+
+ 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));
+ return true;
+ }
+
+ return false;
+}
diff --git a/sql/handler.h b/sql/handler.h
index 0aa56afe1a5..c3e62cc1997 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -2,7 +2,7 @@
#define HANDLER_INCLUDED
/*
Copyright (c) 2000, 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
@@ -25,7 +25,6 @@
#pragma interface /* gcc class implementation */
#endif
-#include <my_global.h> /* For handlers */
#include "sql_const.h"
#include "sql_basic_types.h"
#include "mysqld.h" /* server_id */
@@ -35,6 +34,7 @@
#include "structs.h" /* SHOW_COMP_OPTION */
#include "sql_array.h" /* Dynamic_array<> */
#include "mdl.h"
+#include "vers_string.h"
#include "sql_analyze_stmt.h" // for Exec_time_tracker
@@ -42,9 +42,11 @@
#include <ft_global.h>
#include <keycache.h>
#include <mysql/psi/mysql_table.h>
+#include "sql_sequence.h"
class Alter_info;
class Virtual_column_info;
+class sequence_definition;
// the following is for checking tables
@@ -71,11 +73,14 @@ class Virtual_column_info;
*/
enum enum_alter_inplace_result {
HA_ALTER_ERROR,
+ HA_ALTER_INPLACE_COPY_NO_LOCK,
+ HA_ALTER_INPLACE_COPY_LOCK,
+ HA_ALTER_INPLACE_NOCOPY_LOCK,
+ HA_ALTER_INPLACE_NOCOPY_NO_LOCK,
+ HA_ALTER_INPLACE_INSTANT,
HA_ALTER_INPLACE_NOT_SUPPORTED,
HA_ALTER_INPLACE_EXCLUSIVE_LOCK,
- HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE,
HA_ALTER_INPLACE_SHARED_LOCK,
- HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE,
HA_ALTER_INPLACE_NO_LOCK
};
@@ -146,7 +151,6 @@ enum enum_alter_inplace_result {
#define HA_HAS_OLD_CHECKSUM (1ULL << 24)
/* Table data are stored in separate files (for lower_case_table_names) */
#define HA_FILE_BASED (1ULL << 26)
-#define HA_NO_VARCHAR (1ULL << 27) /* unused */
#define HA_CAN_BIT_FIELD (1ULL << 28) /* supports bit fields */
#define HA_NEED_READ_RANGE_BUFFER (1ULL << 29) /* for read_multi_range */
#define HA_ANY_INDEX_MAY_BE_UNIQUE (1ULL << 30)
@@ -160,6 +164,7 @@ enum enum_alter_inplace_result {
*/
#define HA_BINLOG_ROW_CAPABLE (1ULL << 34)
#define HA_BINLOG_STMT_CAPABLE (1ULL << 35)
+
/*
When a multiple key conflict happens in a REPLACE command mysql
expects the conflicts to be reported in the ascending order of
@@ -195,7 +200,8 @@ enum enum_alter_inplace_result {
#define HA_HAS_NEW_CHECKSUM (1ULL << 38)
#define HA_CAN_VIRTUAL_COLUMNS (1ULL << 39)
#define HA_MRR_CANT_SORT (1ULL << 40)
-#define HA_RECORD_MUST_BE_CLEAN_ON_WRITE (1ULL << 41) /* unused */
+/* All of VARCHAR is stored, including bytes after real varchar data */
+#define HA_RECORD_MUST_BE_CLEAN_ON_WRITE (1ULL << 41)
/*
This storage engine supports condition pushdown
@@ -262,11 +268,43 @@ enum enum_alter_inplace_result {
#define HA_CONCURRENT_OPTIMIZE (1ULL << 46)
/*
+ If the storage engine support tables that will not roll back on commit
+ In addition the table should not lock rows and support READ and WRITE
+ UNCOMMITTED.
+ This is useful for implementing things like SEQUENCE but can also in
+ the future be useful to do logging that should never roll back.
+*/
+#define HA_CAN_TABLES_WITHOUT_ROLLBACK (1ULL << 47)
+
+/*
+ Mainly for usage by SEQUENCE engine. Setting this flag means
+ that the table will never roll back and that all operations
+ for this table should stored in the non transactional log
+ space that will always be written, even on rollback.
+*/
+
+#define HA_PERSISTENT_TABLE (1ULL << 48)
+
+/*
Set of all binlog flags. Currently only contain the capabilities
flags.
*/
#define HA_BINLOG_FLAGS (HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE)
+/* The following are used by Spider */
+#define HA_CAN_FORCE_BULK_UPDATE (1ULL << 50)
+#define HA_CAN_FORCE_BULK_DELETE (1ULL << 51)
+#define HA_CAN_DIRECT_UPDATE_AND_DELETE (1ULL << 52)
+
+/* The following is for partition handler */
+#define HA_CAN_MULTISTEP_MERGE (1LL << 53)
+
+/* calling cmp_ref() on the engine is expensive */
+#define HA_CMP_REF_IS_EXPENSIVE (1ULL << 54)
+
+/* Engine wants primary keys for everything except sequences */
+#define HA_WANTS_PRIMARY_KEY (1ULL << 55)
+
/* bits in index_flags(index_number) for what you can do with index */
#define HA_READ_NEXT 1 /* TODO really use this flag */
#define HA_READ_PREV 2 /* supports ::index_prev */
@@ -386,6 +424,8 @@ enum enum_alter_inplace_result {
#define HA_LEX_CREATE_TMP_TABLE 1U
#define HA_CREATE_TMP_ALTER 8U
+#define HA_LEX_CREATE_SEQUENCE 16U
+#define HA_VERSIONED_TABLE 32U
#define HA_MAX_REC_LENGTH 65535
@@ -421,6 +461,12 @@ static const uint MYSQL_START_TRANS_OPT_READ_WRITE = 4;
#define HA_CHECK_DUP (HA_CHECK_DUP_KEY + HA_CHECK_DUP_UNIQUE)
#define HA_CHECK_ALL (~0U)
+/* Options for info_push() */
+#define INFO_KIND_UPDATE_FIELDS 101
+#define INFO_KIND_UPDATE_VALUES 102
+#define INFO_KIND_FORCE_LIMIT_BEGIN 103
+#define INFO_KIND_FORCE_LIMIT_END 104
+
enum legacy_db_type
{
/* note these numerical values are fixed and can *not* be changed */
@@ -440,7 +486,8 @@ enum legacy_db_type
DB_TYPE_PERFORMANCE_SCHEMA=28,
DB_TYPE_ARIA=42,
DB_TYPE_TOKUDB=43,
- DB_TYPE_FIRST_DYNAMIC=44,
+ DB_TYPE_SEQUENCE=44,
+ DB_TYPE_FIRST_DYNAMIC=45,
DB_TYPE_DEFAULT=127 // Must be last
};
/*
@@ -528,6 +575,187 @@ given at all. */
*/
#define HA_CREATE_USED_STATS_SAMPLE_PAGES (1UL << 24)
+/* Create a sequence */
+#define HA_CREATE_USED_SEQUENCE (1UL << 25)
+
+typedef ulonglong alter_table_operations;
+
+/*
+ These flags are set by the parser and describes the type of
+ operation(s) specified by the ALTER TABLE statement.
+*/
+
+// Set by parser for ADD [COLUMN]
+#define ALTER_PARSER_ADD_COLUMN (1ULL << 0)
+// Set by parser for DROP [COLUMN]
+#define ALTER_PARSER_DROP_COLUMN (1ULL << 1)
+// Set for CHANGE [COLUMN] | MODIFY [CHANGE] & mysql_recreate_table
+#define ALTER_CHANGE_COLUMN (1ULL << 2)
+// Set for ADD INDEX | ADD KEY | ADD PRIMARY KEY | ADD UNIQUE KEY |
+// ADD UNIQUE INDEX | ALTER ADD [COLUMN]
+#define ALTER_ADD_INDEX (1ULL << 3)
+// Set for DROP PRIMARY KEY | DROP FOREIGN KEY | DROP KEY | DROP INDEX
+#define ALTER_DROP_INDEX (1ULL << 4)
+// Set for RENAME [TO]
+#define ALTER_RENAME (1ULL << 5)
+// Set for ORDER BY
+#define ALTER_ORDER (1ULL << 6)
+// Set for table_options, like table comment
+#define ALTER_OPTIONS (1ULL << 7)
+// Set for ALTER [COLUMN] ... SET DEFAULT ... | DROP DEFAULT
+#define ALTER_CHANGE_COLUMN_DEFAULT (1ULL << 8)
+// Set for DISABLE KEYS | ENABLE KEYS
+#define ALTER_KEYS_ONOFF (1ULL << 9)
+// Set for FORCE, ENGINE(same engine), by mysql_recreate_table()
+#define ALTER_RECREATE (1ULL << 10)
+// Set for ADD FOREIGN KEY
+#define ALTER_ADD_FOREIGN_KEY (1ULL << 21)
+// Set for DROP FOREIGN KEY
+#define ALTER_DROP_FOREIGN_KEY (1ULL << 22)
+// Set for ADD [COLUMN] FIRST | AFTER
+#define ALTER_COLUMN_ORDER (1ULL << 25)
+#define ALTER_ADD_CHECK_CONSTRAINT (1ULL << 27)
+#define ALTER_DROP_CHECK_CONSTRAINT (1ULL << 28)
+#define ALTER_RENAME_COLUMN (1ULL << 29)
+#define ALTER_COLUMN_UNVERSIONED (1ULL << 30)
+#define ALTER_ADD_SYSTEM_VERSIONING (1ULL << 31)
+#define ALTER_DROP_SYSTEM_VERSIONING (1ULL << 32)
+#define ALTER_ADD_PERIOD (1ULL << 33)
+#define ALTER_DROP_PERIOD (1ULL << 34)
+
+/*
+ Following defines are used by ALTER_INPLACE_TABLE
+
+ They do describe in more detail the type operation(s) to be executed
+ by the storage engine. For example, which type of type of index to be
+ added/dropped. These are set by fill_alter_inplace_info().
+*/
+
+#define ALTER_RECREATE_TABLE ALTER_RECREATE
+#define ALTER_CHANGE_CREATE_OPTION ALTER_OPTIONS
+#define ALTER_ADD_COLUMN (ALTER_ADD_VIRTUAL_COLUMN | \
+ ALTER_ADD_STORED_BASE_COLUMN | \
+ ALTER_ADD_STORED_GENERATED_COLUMN)
+#define ALTER_DROP_COLUMN (ALTER_DROP_VIRTUAL_COLUMN | \
+ ALTER_DROP_STORED_COLUMN)
+#define ALTER_COLUMN_DEFAULT ALTER_CHANGE_COLUMN_DEFAULT
+
+// Add non-unique, non-primary index
+#define ALTER_ADD_NON_UNIQUE_NON_PRIM_INDEX (1ULL << 35)
+
+// Drop non-unique, non-primary index
+#define ALTER_DROP_NON_UNIQUE_NON_PRIM_INDEX (1ULL << 36)
+
+// Add unique, non-primary index
+#define ALTER_ADD_UNIQUE_INDEX (1ULL << 37)
+
+// Drop unique, non-primary index
+#define ALTER_DROP_UNIQUE_INDEX (1ULL << 38)
+
+// Add primary index
+#define ALTER_ADD_PK_INDEX (1ULL << 39)
+
+// Drop primary index
+#define ALTER_DROP_PK_INDEX (1ULL << 40)
+
+// Virtual generated column
+#define ALTER_ADD_VIRTUAL_COLUMN (1ULL << 41)
+// Stored base (non-generated) column
+#define ALTER_ADD_STORED_BASE_COLUMN (1ULL << 42)
+// Stored generated column
+#define ALTER_ADD_STORED_GENERATED_COLUMN (1ULL << 43)
+
+// Drop column
+#define ALTER_DROP_VIRTUAL_COLUMN (1ULL << 44)
+#define ALTER_DROP_STORED_COLUMN (1ULL << 45)
+
+// Rename column (verified; ALTER_RENAME_COLUMN may use original name)
+#define ALTER_COLUMN_NAME (1ULL << 46)
+
+// Change column datatype
+#define ALTER_VIRTUAL_COLUMN_TYPE (1ULL << 47)
+#define ALTER_STORED_COLUMN_TYPE (1ULL << 48)
+
+/**
+ Change column datatype in such way that new type has compatible
+ packed representation with old type, so it is theoretically
+ possible to perform change by only updating data dictionary
+ without changing table rows.
+*/
+#define ALTER_COLUMN_EQUAL_PACK_LENGTH (1ULL << 49)
+
+// Reorder column
+#define ALTER_STORED_COLUMN_ORDER (1ULL << 50)
+
+// Reorder column
+#define ALTER_VIRTUAL_COLUMN_ORDER (1ULL << 51)
+
+// Change column from NOT NULL to NULL
+#define ALTER_COLUMN_NULLABLE (1ULL << 52)
+
+// Change column from NULL to NOT NULL
+#define ALTER_COLUMN_NOT_NULLABLE (1ULL << 53)
+
+// Change column generation expression
+#define ALTER_VIRTUAL_GCOL_EXPR (1ULL << 54)
+#define ALTER_STORED_GCOL_EXPR (1ULL << 55)
+
+// column's engine options changed, something in field->option_struct
+#define ALTER_COLUMN_OPTION (1ULL << 56)
+
+// MySQL alias for the same thing:
+#define ALTER_COLUMN_STORAGE_TYPE ALTER_COLUMN_OPTION
+
+// Change the column format of column
+#define ALTER_COLUMN_COLUMN_FORMAT (1ULL << 57)
+
+/**
+ Changes in generated columns that affect storage,
+ for example, when a vcol type or expression changes
+ and this vcol is indexed or used in a partitioning expression
+*/
+#define ALTER_COLUMN_VCOL (1ULL << 58)
+
+/**
+ ALTER TABLE for a partitioned table. The engine needs to commit
+ online alter of all partitions atomically (using group_commit_ctx)
+*/
+#define ALTER_PARTITIONED (1ULL << 59)
+
+/**
+ Change in index length such that it doesn't require index rebuild.
+*/
+#define ALTER_COLUMN_INDEX_LENGTH (1ULL << 60)
+
+/*
+ Flags set in partition_flags when altering partitions
+*/
+
+// Set for ADD PARTITION
+#define ALTER_PARTITION_ADD (1ULL << 1)
+// Set for DROP PARTITION
+#define ALTER_PARTITION_DROP (1ULL << 2)
+// Set for COALESCE PARTITION
+#define ALTER_PARTITION_COALESCE (1ULL << 3)
+// Set for REORGANIZE PARTITION ... INTO
+#define ALTER_PARTITION_REORGANIZE (1ULL << 4)
+// Set for partition_options
+#define ALTER_PARTITION_INFO (1ULL << 5)
+// Set for LOAD INDEX INTO CACHE ... PARTITION
+// Set for CACHE INDEX ... PARTITION
+#define ALTER_PARTITION_ADMIN (1ULL << 6)
+// Set for REBUILD PARTITION
+#define ALTER_PARTITION_REBUILD (1ULL << 7)
+// Set for partitioning operations specifying ALL keyword
+#define ALTER_PARTITION_ALL (1ULL << 8)
+// Set for REMOVE PARTITIONING
+#define ALTER_PARTITION_REMOVE (1ULL << 9)
+// Set for EXCHANGE PARITION
+#define ALTER_PARTITION_EXCHANGE (1ULL << 10)
+// Set by Sql_cmd_alter_table_truncate_partition::execute()
+#define ALTER_PARTITION_TRUNCATE (1ULL << 11)
+// Set for REORGANIZE PARTITION
+#define ALTER_PARTITION_TABLE_REORG (1ULL << 12)
/*
This is master database for most of system tables. However there
@@ -634,6 +862,15 @@ struct xid_t {
};
typedef struct xid_t XID;
+/*
+ The size of XID string representation in the form
+ 'gtrid', 'bqual', formatID
+ see xid_t::get_sql_string() for details.
+*/
+#define SQL_XIDSIZE (XIDDATASIZE * 2 + 8 + MY_INT64_NUM_DECIMAL_DIGITS)
+/* The 'buf' has to have space for at least SQL_XIDSIZE bytes. */
+uint get_sql_xid(XID *xid, char *buf);
+
/* for recover() handlerton call */
#define MIN_XID_LIST_SIZE 128
#define MAX_XID_LIST_SIZE (1024*128)
@@ -1203,7 +1440,7 @@ struct handlerton
bool (*flush_logs)(handlerton *hton);
bool (*show_status)(handlerton *hton, THD *thd, stat_print_fn *print, enum ha_stat_type stat);
uint (*partition_flags)();
- uint (*alter_table_flags)(uint flags);
+ alter_table_operations (*alter_table_flags)(alter_table_operations flags);
int (*alter_tablespace)(handlerton *hton, THD *thd, st_alter_tablespace *ts_info);
int (*fill_is_table)(handlerton *hton, THD *thd, TABLE_LIST *tables,
class Item *cond,
@@ -1257,7 +1494,7 @@ struct handlerton
For engines that have more than one file name extensions (separate
metadata, index, and/or data files), the order of elements is relevant.
First element of engine file name extensions array should be metadata
- file extension. This is implied by the open_table_error()
+ file extention. This is implied by the open_table_error()
and the default discovery implementation.
Second element - data file extension. This is implied
@@ -1323,7 +1560,7 @@ struct handlerton
Returns 0 on success and 1 on error.
*/
- int (*discover_table_names)(handlerton *hton, LEX_STRING *db, MY_DIR *dir,
+ int (*discover_table_names)(handlerton *hton, LEX_CSTRING *db, MY_DIR *dir,
discovered_list *result);
/*
@@ -1366,10 +1603,20 @@ struct handlerton
*/
int (*discover_table_structure)(handlerton *hton, THD* thd,
TABLE_SHARE *share, HA_CREATE_INFO *info);
+
+ /*
+ System Versioning
+ */
+ /** Determine if system-versioned data was modified by the transaction.
+ @param[in,out] thd current session
+ @param[out] trx_id transaction start ID
+ @return transaction commit ID
+ @retval 0 if no system-versioned data was affected by the transaction */
+ ulonglong (*prepare_commit_versioned)(THD *thd, ulonglong *trx_id);
};
-static inline LEX_STRING *hton_name(const handlerton *hton)
+static inline LEX_CSTRING *hton_name(const handlerton *hton)
{
return &(hton2plugin[hton->slot]->name);
}
@@ -1413,10 +1660,15 @@ handlerton *ha_default_tmp_handlerton(THD *thd);
*/
#define HTON_NO_BINLOG_ROW_OPT (1 << 9)
#define HTON_SUPPORTS_EXTENDED_KEYS (1 <<10) //supports extended keys
+#define HTON_NATIVE_SYS_VERSIONING (1 << 11) //Engine supports System Versioning
// MySQL compatibility. Unused.
#define HTON_SUPPORTS_FOREIGN_KEYS (1 << 0) //Foreign key constraint supported.
+#define HTON_CAN_MERGE (1 <<11) //Merge type table
+// Engine needs to access the main connect string in partitions
+#define HTON_CAN_READ_CONNECT_STRING_IN_PARTITION (1 <<12)
+
class Ha_trx_info;
struct THD_TRANS
@@ -1465,14 +1717,16 @@ struct THD_TRANS
unsigned int m_unsafe_rollback_flags;
/*
- Define the type of statemens which cannot be rolled back safely.
+ Define the type of statements which cannot be rolled back safely.
Each type occupies one bit in m_unsafe_rollback_flags.
*/
- static unsigned int const MODIFIED_NON_TRANS_TABLE= 0x01;
- static unsigned int const CREATED_TEMP_TABLE= 0x02;
- static unsigned int const DROPPED_TEMP_TABLE= 0x04;
- static unsigned int const DID_WAIT= 0x08;
- static unsigned int const DID_DDL= 0x10;
+ enum unsafe_statement_types
+ {
+ CREATED_TEMP_TABLE= 2,
+ DROPPED_TEMP_TABLE= 4,
+ DID_WAIT= 8,
+ DID_DDL= 0x10
+ };
void mark_created_temp_table()
{
@@ -1492,6 +1746,7 @@ struct THD_TRANS
bool trans_did_wait() const {
return (m_unsafe_rollback_flags & DID_WAIT) != 0;
}
+ bool is_trx_read_write() const;
void mark_trans_did_ddl() { m_unsafe_rollback_flags|= DID_DDL; }
bool trans_did_ddl() const {
return (m_unsafe_rollback_flags & DID_DDL) != 0;
@@ -1596,6 +1851,16 @@ private:
};
+inline bool THD_TRANS::is_trx_read_write() const
+{
+ Ha_trx_info *ha_info;
+ for (ha_info= ha_list; ha_info; ha_info= ha_info->next())
+ if (ha_info->is_trx_read_write())
+ return TRUE;
+ return FALSE;
+}
+
+
enum enum_tx_isolation { ISO_READ_UNCOMMITTED, ISO_READ_COMMITTED,
ISO_REPEATABLE_READ, ISO_SERIALIZABLE};
@@ -1604,6 +1869,7 @@ typedef struct {
ulonglong data_file_length;
ulonglong max_data_file_length;
ulonglong index_file_length;
+ ulonglong max_index_file_length;
ulonglong delete_length;
ha_rows records;
ulong mean_rec_length;
@@ -1645,6 +1911,95 @@ struct Schema_specification_st
}
};
+class Create_field;
+
+enum vers_sys_type_t
+{
+ VERS_UNDEFINED= 0,
+ VERS_TIMESTAMP,
+ VERS_TRX_ID
+};
+
+extern const LEX_CSTRING null_clex_str;
+
+struct Vers_parse_info
+{
+ Vers_parse_info() :
+ versioned_fields(false),
+ unversioned_fields(false)
+ {}
+
+ void init() // Deep initialization
+ {
+ system_time= start_end_t(null_clex_str, null_clex_str);
+ as_row= start_end_t(null_clex_str, null_clex_str);
+ versioned_fields= false;
+ unversioned_fields= false;
+ }
+
+ struct start_end_t
+ {
+ start_end_t()
+ {}
+ start_end_t(LEX_CSTRING _start, LEX_CSTRING _end) :
+ start(_start),
+ end(_end) {}
+ Lex_ident start;
+ Lex_ident end;
+ };
+
+ start_end_t system_time;
+ start_end_t as_row;
+
+ void set_system_time(Lex_ident start, Lex_ident end)
+ {
+ system_time.start= start;
+ system_time.end= end;
+ }
+
+protected:
+ friend struct Table_scope_and_contents_source_st;
+ void set_start(const LEX_CSTRING field_name)
+ {
+ as_row.start= field_name;
+ system_time.start= field_name;
+ }
+ void set_end(const LEX_CSTRING field_name)
+ {
+ as_row.end= field_name;
+ system_time.end= field_name;
+ }
+ bool is_start(const char *name) const;
+ bool is_end(const char *name) const;
+ bool is_start(const Create_field &f) const;
+ bool is_end(const Create_field &f) const;
+ bool fix_implicit(THD *thd, Alter_info *alter_info);
+ operator bool() const
+ {
+ return as_row.start || as_row.end || system_time.start || system_time.end;
+ }
+ bool need_check(const Alter_info *alter_info) const;
+ bool check_conditions(const Lex_table_name &table_name,
+ const Lex_table_name &db) const;
+public:
+ static const Lex_ident default_start;
+ static const Lex_ident default_end;
+
+ bool fix_alter_info(THD *thd, Alter_info *alter_info,
+ HA_CREATE_INFO *create_info, TABLE *table);
+ 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;
+
+ /**
+ At least one field was specified 'WITH/WITHOUT SYSTEM VERSIONING'.
+ Useful for error handling.
+ */
+ bool versioned_fields : 1;
+ bool unversioned_fields : 1;
+};
/**
A helper struct for table DDL statements, e.g.:
@@ -1660,15 +2015,15 @@ struct Schema_specification_st
- [AS] SELECT ... // Copy structure from a subquery
*/
-struct Table_scope_and_contents_source_st
+struct Table_scope_and_contents_source_pod_st // For trivial members
{
CHARSET_INFO *table_charset;
LEX_CUSTRING tabledef_version;
- LEX_STRING connect_string;
+ LEX_CSTRING connect_string;
+ LEX_CSTRING comment;
+ LEX_CSTRING alias;
const char *password, *tablespace;
- LEX_STRING comment;
const char *data_file_name, *index_file_name;
- const char *alias;
ulonglong max_rows,min_rows;
ulonglong auto_increment_value;
ulong table_options; ///< HA_OPTION_ values
@@ -1703,6 +2058,7 @@ struct Table_scope_and_contents_source_st
engine_option_value *option_list; ///< list of table create options
enum_stats_auto_recalc stats_auto_recalc;
bool varchar; ///< 1 if table has a VARCHAR
+ bool sequence; // If SEQUENCE=1 was used
List<Virtual_column_info> *check_constraint_list;
@@ -1714,9 +2070,10 @@ struct Table_scope_and_contents_source_st
/* The following is used to remember the old state for CREATE OR REPLACE */
TABLE *table;
TABLE_LIST *pos_in_locked_tables;
+ TABLE_LIST *merge_list;
MDL_ticket *mdl_ticket;
bool table_was_deleted;
- TABLE_LIST *merge_list;
+ sequence_definition *seq_create_info;
void init()
{
@@ -1728,6 +2085,33 @@ struct Table_scope_and_contents_source_st
db_type= tmp_table() ? ha_default_tmp_handlerton(thd)
: ha_default_handlerton(thd);
}
+
+ bool versioned() const
+ {
+ return options & HA_VERSIONED_TABLE;
+ }
+};
+
+
+struct Table_scope_and_contents_source_st:
+ public Table_scope_and_contents_source_pod_st
+{
+ Vers_parse_info vers_info;
+
+ void init()
+ {
+ Table_scope_and_contents_source_pod_st::init();
+ vers_info.init();
+ }
+
+ bool vers_fix_system_fields(THD *thd, Alter_info *alter_info,
+ const TABLE_LIST &create_table);
+
+ bool vers_check_system_fields(THD *thd, Alter_info *alter_info,
+ const Lex_table_name &table_name,
+ const Lex_table_name &db,
+ int select_count= 0);
+
};
@@ -1849,163 +2233,6 @@ public:
class Alter_inplace_info
{
public:
- /**
- Bits to show in detail what operations the storage engine is
- to execute.
-
- All these operations are supported as in-place operations by the
- SQL layer. This means that operations that by their nature must
- be performed by copying the table to a temporary table, will not
- have their own flags here.
-
- We generally try to specify handler flags only if there are real
- changes. But in cases when it is cumbersome to determine if some
- attribute has really changed we might choose to set flag
- pessimistically, for example, relying on parser output only.
- */
- typedef ulonglong HA_ALTER_FLAGS;
-
- // Add non-unique, non-primary index
- static const HA_ALTER_FLAGS ADD_INDEX = 1ULL << 0;
-
- // Drop non-unique, non-primary index
- static const HA_ALTER_FLAGS DROP_INDEX = 1ULL << 1;
-
- // Add unique, non-primary index
- static const HA_ALTER_FLAGS ADD_UNIQUE_INDEX = 1ULL << 2;
-
- // Drop unique, non-primary index
- static const HA_ALTER_FLAGS DROP_UNIQUE_INDEX = 1ULL << 3;
-
- // Add primary index
- static const HA_ALTER_FLAGS ADD_PK_INDEX = 1ULL << 4;
-
- // Drop primary index
- static const HA_ALTER_FLAGS DROP_PK_INDEX = 1ULL << 5;
-
- // Virtual generated column
- static const HA_ALTER_FLAGS ADD_VIRTUAL_COLUMN = 1ULL << 6;
- // Stored base (non-generated) column
- static const HA_ALTER_FLAGS ADD_STORED_BASE_COLUMN = 1ULL << 7;
- // Stored generated column
- static const HA_ALTER_FLAGS ADD_STORED_GENERATED_COLUMN= 1ULL << 8;
- // Add generic column (convience constant).
- static const HA_ALTER_FLAGS ADD_COLUMN= ADD_VIRTUAL_COLUMN |
- ADD_STORED_BASE_COLUMN |
- ADD_STORED_GENERATED_COLUMN;
-
- // Drop column
- static const HA_ALTER_FLAGS DROP_VIRTUAL_COLUMN = 1ULL << 9;
- static const HA_ALTER_FLAGS DROP_STORED_COLUMN = 1ULL << 10;
- static const HA_ALTER_FLAGS DROP_COLUMN= DROP_VIRTUAL_COLUMN |
- DROP_STORED_COLUMN;
-
- // Rename column
- static const HA_ALTER_FLAGS ALTER_COLUMN_NAME = 1ULL << 11;
-
- // Change column datatype
- static const HA_ALTER_FLAGS ALTER_VIRTUAL_COLUMN_TYPE = 1ULL << 12;
- static const HA_ALTER_FLAGS ALTER_STORED_COLUMN_TYPE = 1ULL << 13;
-
- /**
- Change column datatype in such way that new type has compatible
- packed representation with old type, so it is theoretically
- possible to perform change by only updating data dictionary
- without changing table rows.
- */
- static const HA_ALTER_FLAGS ALTER_COLUMN_EQUAL_PACK_LENGTH = 1ULL << 14;
-
- // Reorder column
- static const HA_ALTER_FLAGS ALTER_STORED_COLUMN_ORDER = 1ULL << 15;
-
- // Reorder column
- static const HA_ALTER_FLAGS ALTER_VIRTUAL_COLUMN_ORDER = 1ULL << 16;
-
- // Change column from NOT NULL to NULL
- static const HA_ALTER_FLAGS ALTER_COLUMN_NULLABLE = 1ULL << 17;
-
- // Change column from NULL to NOT NULL
- static const HA_ALTER_FLAGS ALTER_COLUMN_NOT_NULLABLE = 1ULL << 18;
-
- // Set or remove default column value
- static const HA_ALTER_FLAGS ALTER_COLUMN_DEFAULT = 1ULL << 19;
-
- // Change column generation expression
- static const HA_ALTER_FLAGS ALTER_VIRTUAL_GCOL_EXPR = 1ULL << 20;
- static const HA_ALTER_FLAGS ALTER_STORED_GCOL_EXPR = 1ULL << 21;
- //
- // Add foreign key
- static const HA_ALTER_FLAGS ADD_FOREIGN_KEY = 1ULL << 22;
-
- // Drop foreign key
- static const HA_ALTER_FLAGS DROP_FOREIGN_KEY = 1ULL << 23;
-
- // table_options changed, see HA_CREATE_INFO::used_fields for details.
- static const HA_ALTER_FLAGS CHANGE_CREATE_OPTION = 1ULL << 24;
-
- // Table is renamed
- static const HA_ALTER_FLAGS ALTER_RENAME = 1ULL << 25;
-
- // column's engine options changed, something in field->option_struct
- static const HA_ALTER_FLAGS ALTER_COLUMN_OPTION = 1ULL << 26;
-
- // MySQL alias for the same thing:
- static const HA_ALTER_FLAGS ALTER_COLUMN_STORAGE_TYPE = 1ULL << 26;
-
- // Change the column format of column
- static const HA_ALTER_FLAGS ALTER_COLUMN_COLUMN_FORMAT = 1ULL << 27;
-
- // Add partition
- static const HA_ALTER_FLAGS ADD_PARTITION = 1ULL << 28;
-
- // Drop partition
- static const HA_ALTER_FLAGS DROP_PARTITION = 1ULL << 29;
-
- // Changing partition options
- static const HA_ALTER_FLAGS ALTER_PARTITION = 1ULL << 30;
-
- // Coalesce partition
- static const HA_ALTER_FLAGS COALESCE_PARTITION = 1ULL << 31;
-
- // Reorganize partition ... into
- static const HA_ALTER_FLAGS REORGANIZE_PARTITION = 1ULL << 32;
-
- // Reorganize partition
- static const HA_ALTER_FLAGS ALTER_TABLE_REORG = 1ULL << 33;
-
- // Remove partitioning
- static const HA_ALTER_FLAGS ALTER_REMOVE_PARTITIONING = 1ULL << 34;
-
- // Partition operation with ALL keyword
- static const HA_ALTER_FLAGS ALTER_ALL_PARTITION = 1ULL << 35;
-
- /**
- Recreate the table for ALTER TABLE FORCE, ALTER TABLE ENGINE
- and OPTIMIZE TABLE operations.
- */
- static const HA_ALTER_FLAGS RECREATE_TABLE = 1ULL << 36;
-
- /**
- Changes in generated columns that affect storage,
- for example, when a vcol type or expression changes
- and this vcol is indexed or used in a partitioning expression
- */
- static const HA_ALTER_FLAGS ALTER_COLUMN_VCOL = 1ULL << 37;
-
- /**
- ALTER TABLE for a partitioned table. The engine needs to commit
- online alter of all partitions atomically (using group_commit_ctx)
- */
- static const HA_ALTER_FLAGS ALTER_PARTITIONED = 1ULL << 38;
-
- static const HA_ALTER_FLAGS ALTER_ADD_CHECK_CONSTRAINT = 1ULL << 39;
-
- static const HA_ALTER_FLAGS ALTER_DROP_CHECK_CONSTRAINT= 1ULL << 40;
-
- /**
- Change in index length such that it doesn't require index rebuild.
- */
- static const HA_ALTER_FLAGS ALTER_COLUMN_INDEX_LENGTH= 1ULL << 41;
/**
Create options (like MAX_ROWS) for the new version of table.
@@ -2090,9 +2317,13 @@ public:
inplace_alter_handler_ctx **group_commit_ctx;
/**
- Flags describing in detail which operations the storage engine is to execute.
+ Flags describing in detail which operations the storage engine is to
+ execute. Flags are defined in sql_alter.h
*/
- HA_ALTER_FLAGS handler_flags;
+ alter_table_operations handler_flags;
+
+ /* Alter operations involving parititons are strored here */
+ ulong partition_flags;
/**
Partition_info taking into account the partition changes to be performed.
@@ -2111,8 +2342,7 @@ public:
/**
Can be set by handler to describe why a given operation cannot be done
in-place (HA_ALTER_INPLACE_NOT_SUPPORTED) or why it cannot be done
- online (HA_ALTER_INPLACE_NO_LOCK or
- HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE)
+ online (HA_ALTER_INPLACE_NO_LOCK or HA_ALTER_INPLACE_COPY_NO_LOCK)
If set, it will be used with ER_ALTER_OPERATION_NOT_SUPPORTED_REASON if
results from handler::check_if_supported_inplace_alter() doesn't match
requirements set by user. If not set, the more generic
@@ -2123,11 +2353,14 @@ public:
*/
const char *unsupported_reason;
+ /** true when InnoDB should abort the alter when table is not empty */
+ bool error_if_not_empty;
+
Alter_inplace_info(HA_CREATE_INFO *create_info_arg,
Alter_info *alter_info_arg,
KEY *key_info_arg, uint key_count_arg,
partition_info *modified_part_info_arg,
- bool ignore_arg)
+ bool ignore_arg, bool error_non_empty)
: create_info(create_info_arg),
alter_info(alter_info_arg),
key_info_buffer(key_info_arg),
@@ -2142,7 +2375,8 @@ public:
modified_part_info(modified_part_info_arg),
ignore(ignore_arg),
online(false),
- unsupported_reason(NULL)
+ unsupported_reason(NULL),
+ error_if_not_empty(error_non_empty)
{}
~Alter_inplace_info()
@@ -2160,7 +2394,7 @@ public:
replace not_supported with.
*/
void report_unsupported_error(const char *not_supported,
- const char *try_instead);
+ const char *try_instead) const;
};
@@ -2168,8 +2402,9 @@ typedef struct st_key_create_information
{
enum ha_key_alg algorithm;
ulong block_size;
- LEX_STRING parser_name;
- LEX_STRING comment;
+ uint flags; /* HA_USE.. flags */
+ LEX_CSTRING parser_name;
+ LEX_CSTRING comment;
/**
A flag to determine if we will check for duplicate indexes.
This typically means that the key information was specified
@@ -2549,6 +2784,7 @@ public:
time_t check_time;
time_t update_time;
uint block_size; /* index block size */
+ ha_checksum checksum;
/*
number of buffer bytes that native mrr implementation needs,
@@ -2557,9 +2793,10 @@ public:
ha_statistics():
data_file_length(0), max_data_file_length(0),
- index_file_length(0), delete_length(0), auto_increment_value(0),
- records(0), deleted(0), mean_rec_length(0), create_time(0),
- check_time(0), update_time(0), block_size(0), mrr_length_per_rec(0)
+ index_file_length(0), max_index_file_length(0), delete_length(0),
+ auto_increment_value(0), records(0), deleted(0), mean_rec_length(0),
+ create_time(0), check_time(0), update_time(0), block_size(0),
+ mrr_length_per_rec(0)
{}
};
@@ -2675,6 +2912,8 @@ 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.
@@ -2688,7 +2927,8 @@ public:
/** Length of ref (1-8 or the clustered key length) */
uint ref_length;
FT_INFO *ft_handler;
- enum {NONE=0, INDEX, RND} inited;
+ enum init_stat { NONE=0, INDEX, RND };
+ init_stat inited, pre_inited;
const COND *pushed_cond;
/**
@@ -2754,6 +2994,11 @@ public:
virtual void unbind_psi();
virtual void rebind_psi();
+ bool set_top_table_fields;
+ struct TABLE *top_table;
+ Field **top_table_field;
+ uint top_table_fields;
+
private:
/**
The lock type set by when calling::ha_external_lock(). This is
@@ -2768,6 +3013,10 @@ private:
*/
Handler_share **ha_share;
+ /** 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),
@@ -2777,17 +3026,20 @@ public:
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),
key_used_on_scan(MAX_KEY),
active_index(MAX_KEY), keyread(MAX_KEY),
ref_length(sizeof(my_off_t)),
- ft_handler(0), inited(NONE),
+ ft_handler(0), inited(NONE), pre_inited(NONE),
pushed_cond(0), next_insert_id(0), insert_id_for_cur_row(0),
tracker(NULL),
pushed_idx_cond(NULL),
pushed_idx_cond_keyno(MAX_KEY),
auto_inc_intervals_count(0),
- m_psi(NULL), m_lock_type(F_UNLCK), ha_share(NULL)
+ m_psi(NULL), set_top_table_fields(FALSE), top_table(0),
+ top_table_field(0), top_table_fields(0),
+ m_lock_type(F_UNLCK), ha_share(NULL), m_prev_insert_id(0)
{
DBUG_PRINT("info",
("handler created F_UNLCK %d F_RDLCK %d F_WRLCK %d",
@@ -2807,7 +3059,8 @@ public:
}
/* ha_ methods: pubilc wrappers for private virtual API */
- int ha_open(TABLE *table, const char *name, int mode, uint test_if_locked);
+ int ha_open(TABLE *table, const char *name, int mode, uint test_if_locked,
+ MEM_ROOT *mem_root= 0, List<String> *partitions_to_open=NULL);
int ha_index_init(uint idx, bool sorted)
{
DBUG_EXECUTE_IF("ha_index_init_fail", return HA_ERR_TABLE_DEF_CHANGED;);
@@ -2880,7 +3133,7 @@ public:
*/
int ha_external_lock(THD *thd, int lock_type);
int ha_write_row(uchar * buf);
- int ha_update_row(const uchar * old_data, uchar * new_data);
+ int ha_update_row(const uchar * old_data, const uchar * new_data);
int ha_delete_row(const uchar * buf);
void ha_release_auto_increment();
@@ -2912,8 +3165,8 @@ public:
DBUG_VOID_RETURN;
}
int ha_end_bulk_insert();
- int ha_bulk_update_row(const uchar *old_data, uchar *new_data,
- uint *dup_key_found);
+ int ha_bulk_update_row(const uchar *old_data, const uchar *new_data,
+ ha_rows *dup_key_found);
int ha_delete_all_rows();
int ha_truncate();
int ha_reset_auto_increment(ulonglong value);
@@ -3007,8 +3260,24 @@ public:
virtual double keyread_time(uint index, uint ranges, ha_rows rows);
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 manly used to decide how to log changes to the table in
+ the binary log.
+ */
bool has_transactions()
- { return (ha_table_flags() & HA_NO_TRANSACTIONS) == 0; }
+ {
+ return ((ha_table_flags() & (HA_NO_TRANSACTIONS | HA_PERSISTENT_TABLE))
+ == 0);
+ }
+ /*
+ True if the underlaying table doesn't support transactions
+ */
+ bool has_transaction_manager()
+ {
+ return ((ha_table_flags() & HA_NO_TRANSACTIONS) == 0);
+ }
/**
This method is used to analyse the error to see whether the error
@@ -3038,6 +3307,7 @@ public:
Number of rows in table. It will only be called if
(table_flags() & (HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT)) != 0
*/
+ virtual int pre_records() { return 0; }
virtual ha_rows records() { return stats.records; }
/**
Return upper bound of current number of records in the table
@@ -3094,7 +3364,7 @@ public:
@retval 0 Success
@retval >0 Error code
*/
- virtual int exec_bulk_update(uint *dup_key_found)
+ virtual int exec_bulk_update(ha_rows *dup_key_found)
{
DBUG_ASSERT(FALSE);
return HA_ERR_WRONG_COMMAND;
@@ -3103,7 +3373,7 @@ public:
Perform any needed clean-up, no outstanding updates are there at the
moment.
*/
- virtual void end_bulk_update() { return; }
+ virtual int end_bulk_update() { return 0; }
/**
Execute all outstanding deletes and close down the bulk delete.
@@ -3115,6 +3385,83 @@ public:
DBUG_ASSERT(FALSE);
return HA_ERR_WRONG_COMMAND;
}
+ virtual int pre_index_read_map(const uchar *key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag,
+ bool use_parallel)
+ { return 0; }
+ virtual int pre_index_first(bool use_parallel)
+ { return 0; }
+ virtual int pre_index_last(bool use_parallel)
+ { return 0; }
+ virtual int pre_index_read_last_map(const uchar *key,
+ key_part_map keypart_map,
+ bool use_parallel)
+ { return 0; }
+/*
+ virtual int pre_read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
+ KEY_MULTI_RANGE *ranges,
+ uint range_count,
+ bool sorted, HANDLER_BUFFER *buffer,
+ bool use_parallel);
+*/
+ virtual int pre_multi_range_read_next(bool use_parallel)
+ { return 0; }
+ virtual int pre_read_range_first(const key_range *start_key,
+ const key_range *end_key,
+ bool eq_range, bool sorted,
+ bool use_parallel)
+ { return 0; }
+ virtual int pre_ft_read(bool use_parallel)
+ { return 0; }
+ virtual int pre_rnd_next(bool use_parallel)
+ { return 0; }
+ int ha_pre_rnd_init(bool scan)
+ {
+ int result;
+ DBUG_ENTER("ha_pre_rnd_init");
+ DBUG_ASSERT(pre_inited==NONE || (pre_inited==RND && scan));
+ pre_inited= (result= pre_rnd_init(scan)) ? NONE: RND;
+ DBUG_RETURN(result);
+ }
+ int ha_pre_rnd_end()
+ {
+ DBUG_ENTER("ha_pre_rnd_end");
+ DBUG_ASSERT(pre_inited==RND);
+ pre_inited=NONE;
+ DBUG_RETURN(pre_rnd_end());
+ }
+ virtual int pre_rnd_init(bool scan) { return 0; }
+ virtual int pre_rnd_end() { return 0; }
+ virtual int pre_index_init(uint idx, bool sorted) { return 0; }
+ virtual int pre_index_end() { return 0; }
+ int ha_pre_index_init(uint idx, bool sorted)
+ {
+ int result;
+ DBUG_ENTER("ha_pre_index_init");
+ DBUG_ASSERT(pre_inited==NONE);
+ if (!(result= pre_index_init(idx, sorted)))
+ pre_inited=INDEX;
+ DBUG_RETURN(result);
+ }
+ int ha_pre_index_end()
+ {
+ DBUG_ENTER("ha_pre_index_end");
+ DBUG_ASSERT(pre_inited==INDEX);
+ pre_inited=NONE;
+ DBUG_RETURN(pre_index_end());
+ }
+ int ha_pre_index_or_rnd_end()
+ {
+ return (pre_inited == INDEX ?
+ ha_pre_index_end() :
+ pre_inited == RND ? ha_pre_rnd_end() : 0 );
+ }
+ virtual bool vers_can_native(THD *thd)
+ {
+ return ht->flags & HTON_NATIVE_SYS_VERSIONING;
+ }
+
/**
@brief
Positions an index cursor to the index specified in the
@@ -3147,6 +3494,18 @@ protected:
virtual int index_last(uchar * buf)
{ return HA_ERR_WRONG_COMMAND; }
virtual int index_next_same(uchar *buf, const uchar *key, uint keylen);
+ /**
+ @brief
+ The following functions works like index_read, but it find the last
+ row with the current key value or prefix.
+ @returns @see index_read_map().
+ */
+ virtual int index_read_last_map(uchar * buf, const uchar * key,
+ key_part_map keypart_map)
+ {
+ uint key_len= calculate_key_len(table, active_index, key, keypart_map);
+ return index_read_last(buf, key, key_len);
+ }
virtual int close(void)=0;
inline void update_rows_read()
{
@@ -3223,10 +3582,12 @@ public:
int compare_key(key_range *range);
int compare_key2(key_range *range) const;
virtual int ft_init() { return HA_ERR_WRONG_COMMAND; }
- void ft_end() { ft_handler=NULL; }
+ virtual int pre_ft_init() { return HA_ERR_WRONG_COMMAND; }
+ virtual void ft_end() {}
+ virtual int pre_ft_end() { return 0; }
virtual FT_INFO *ft_init_ext(uint flags, uint inx,String *key)
{ return NULL; }
-private:
+public:
virtual int ft_read(uchar *buf) { return HA_ERR_WRONG_COMMAND; }
virtual int rnd_next(uchar *buf)=0;
virtual int rnd_pos(uchar * buf, uchar *pos)=0;
@@ -3254,6 +3615,7 @@ public:
/* Same as above, but with statistics */
inline int ha_ft_read(uchar *buf);
+ inline void ha_ft_end() { ft_end(); ft_handler=NULL; }
int ha_rnd_next(uchar *buf);
int ha_rnd_pos(uchar *buf, uchar *pos);
inline int ha_rnd_pos_by_record(uchar *buf);
@@ -3283,6 +3645,9 @@ public:
virtual int info(uint)=0; // see my_base.h for full description
virtual void get_dynamic_partition_info(PARTITION_STATS *stat_info,
uint part_id);
+ virtual void set_partitions_to_open(List<String> *partition_names) {}
+ virtual int change_partitions_to_open(List<String> *partition_names)
+ { return 0; }
virtual int extra(enum ha_extra_function operation)
{ return 0; }
virtual int extra_opt(enum ha_extra_function operation, ulong arg)
@@ -3311,6 +3676,8 @@ public:
virtual void try_semi_consistent_read(bool) {}
virtual void unlock_row() {}
virtual int start_stmt(THD *thd, thr_lock_type lock_type) {return 0;}
+ virtual bool need_info_for_auto_inc() { return 0; }
+ virtual bool can_use_for_auto_inc_init() { return 1; }
virtual void get_auto_increment(ulonglong offset, ulonglong increment,
ulonglong nb_desired_values,
ulonglong *first_value,
@@ -3320,7 +3687,7 @@ public:
DBUG_PRINT("info",("auto_increment: next value %lu", (ulong)id));
next_insert_id= id;
}
- void restore_auto_increment(ulonglong prev_insert_id)
+ virtual void restore_auto_increment(ulonglong prev_insert_id)
{
/*
Insertion of a row failed, re-use the lastly generated auto_increment
@@ -3336,6 +3703,16 @@ public:
insert_id_for_cur_row;
}
+ /** Store and restore next_insert_id over duplicate key errors. */
+ virtual void store_auto_increment()
+ {
+ m_prev_insert_id= next_insert_id;
+ }
+ virtual void restore_auto_increment()
+ {
+ restore_auto_increment(m_prev_insert_id);
+ }
+
virtual void update_create_info(HA_CREATE_INFO *create_info) {}
int check_old_types();
virtual int assign_to_keycache(THD* thd, HA_CHECK_OPT* check_opt)
@@ -3418,6 +3795,7 @@ public:
return 0;
}
virtual void set_part_info(partition_info *part_info) {return;}
+ virtual void return_record_by_parent() { return; }
virtual ulong index_flags(uint idx, uint part, bool all_parts) const =0;
@@ -3439,7 +3817,7 @@ public:
virtual uint max_supported_key_part_length() const { return 255; }
virtual uint min_record_length(uint options) const { return 1; }
- virtual uint checksum() const { return 0; }
+ virtual int calculate_checksum();
virtual bool is_crashed() const { return 0; }
virtual bool auto_repair(int error) const { return 0; }
@@ -3506,7 +3884,7 @@ public:
cached
*/
- virtual my_bool register_query_cache_table(THD *thd, char *table_key,
+ virtual my_bool register_query_cache_table(THD *thd, const char *table_key,
uint key_length,
qc_engine_callback
*engine_callback,
@@ -3622,6 +4000,41 @@ public:
virtual void cond_pop() { return; };
/**
+ Push metadata for the current operation down to the table handler.
+ */
+ 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
@@ -3654,6 +4067,10 @@ public:
pushed_idx_cond_keyno= MAX_KEY;
in_range_check_pushed_down= false;
}
+
+ /* Needed for partition / spider */
+ virtual TABLE_LIST *get_next_global_for_child() { return NULL; }
+
/**
Part of old, deprecated in-place ALTER API.
*/
@@ -3705,8 +4122,8 @@ public:
*) As the first step, we acquire a lock corresponding to the concurrency
level which was returned by handler::check_if_supported_inplace_alter()
and requested by the user. This lock is held for most of the
- duration of in-place ALTER (if HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE
- or HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE were returned we acquire an
+ duration of in-place ALTER (if HA_ALTER_INPLACE_COPY_LOCK
+ or HA_ALTER_INPLACE_COPY_NO_LOCK were returned we acquire an
exclusive lock for duration of the next step only).
*) After that we call handler::ha_prepare_inplace_alter_table() to give the
storage engine a chance to update its internal structures with a higher
@@ -3750,12 +4167,12 @@ public:
@retval HA_ALTER_ERROR Unexpected error.
@retval HA_ALTER_INPLACE_NOT_SUPPORTED Not supported, must use copy.
@retval HA_ALTER_INPLACE_EXCLUSIVE_LOCK Supported, but requires X lock.
- @retval HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE
+ @retval HA_ALTER_INPLACE_COPY_LOCK
Supported, but requires SNW lock
during main phase. Prepare phase
requires X lock.
@retval HA_ALTER_INPLACE_SHARED_LOCK Supported, but requires SNW lock.
- @retval HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE
+ @retval HA_ALTER_INPLACE_COPY_NO_LOCK
Supported, concurrent reads/writes
allowed. However, prepare phase
requires X lock.
@@ -3815,10 +4232,9 @@ protected:
/**
Allows the storage engine to update internal structures with concurrent
writes blocked. If check_if_supported_inplace_alter() returns
- HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE or
- HA_ALTER_INPLACE_SHARED_AFTER_PREPARE, this function is called with
- exclusive lock otherwise the same level of locking as for
- inplace_alter_table() will be used.
+ HA_ALTER_INPLACE_COPY_NO_LOCK or HA_ALTER_INPLACE_COPY_LOCK,
+ this function is called with exclusive lock otherwise the same level
+ of locking as for inplace_alter_table() will be used.
@note Storage engines are responsible for reporting any errors by
calling my_error()/print_error()
@@ -3928,14 +4344,14 @@ public:
but we don't have a primary key
*/
virtual void use_hidden_primary_key();
- virtual uint alter_table_flags(uint flags)
+ virtual alter_table_operations alter_table_flags(alter_table_operations flags)
{
if (ht->alter_table_flags)
return ht->alter_table_flags(flags);
return 0;
}
- LEX_STRING *engine_name() { return hton_name(ht); }
+ virtual LEX_CSTRING *engine_name();
TABLE* get_table() { return table; }
TABLE_SHARE* get_table_share() { return table_share; }
@@ -3965,7 +4381,8 @@ protected:
virtual int delete_table(const char *name);
public:
- inline bool check_table_binlog_row_based(bool binlog_row);
+ bool check_table_binlog_row_based(bool binlog_row);
+
inline void clear_cached_table_binlog_row_based_flag()
{
check_table_binlog_row_based_done= 0;
@@ -3981,6 +4398,8 @@ private:
mark_trx_read_write_internal();
}
}
+
+private:
void mark_trx_read_write_internal();
bool check_table_binlog_row_based_internal(bool binlog_row);
@@ -4027,15 +4446,64 @@ private:
message will contain garbage.
*/
virtual int update_row(const uchar *old_data __attribute__((unused)),
- uchar *new_data __attribute__((unused)))
+ const uchar *new_data __attribute__((unused)))
{
return HA_ERR_WRONG_COMMAND;
}
+ /*
+ Optimized function for updating the first row. Only used by sequence
+ tables
+ */
+ virtual int update_first_row(uchar *new_data);
+
virtual int delete_row(const uchar *buf __attribute__((unused)))
{
return HA_ERR_WRONG_COMMAND;
}
+
+ /* Perform initialization for a direct update request */
+public:
+ int ha_direct_update_rows(ha_rows *update_rows);
+ virtual int direct_update_rows_init(List<Item> *update_fields)
+ {
+ return HA_ERR_WRONG_COMMAND;
+ }
+private:
+ virtual int pre_direct_update_rows_init(List<Item> *update_fields)
+ {
+ return HA_ERR_WRONG_COMMAND;
+ }
+ virtual int direct_update_rows(ha_rows *update_rows __attribute__((unused)))
+ {
+ return HA_ERR_WRONG_COMMAND;
+ }
+ virtual int pre_direct_update_rows()
+ {
+ return HA_ERR_WRONG_COMMAND;
+ }
+
+ /* Perform initialization for a direct delete request */
+public:
+ int ha_direct_delete_rows(ha_rows *delete_rows);
+ virtual int direct_delete_rows_init()
+ {
+ return HA_ERR_WRONG_COMMAND;
+ }
+private:
+ virtual int pre_direct_delete_rows_init()
+ {
+ return HA_ERR_WRONG_COMMAND;
+ }
+ virtual int direct_delete_rows(ha_rows *delete_rows __attribute__((unused)))
+ {
+ return HA_ERR_WRONG_COMMAND;
+ }
+ virtual int pre_direct_delete_rows()
+ {
+ return HA_ERR_WRONG_COMMAND;
+ }
+
/**
Reset state of file to after 'open'.
This function is called after every statement for all tables used
@@ -4093,7 +4561,13 @@ protected:
virtual int index_read(uchar * buf, const uchar * key, uint key_len,
enum ha_rkey_function find_flag)
{ return HA_ERR_WRONG_COMMAND; }
+ virtual int index_read_last(uchar * buf, const uchar * key, uint key_len)
+ {
+ my_errno= HA_ERR_WRONG_COMMAND;
+ return HA_ERR_WRONG_COMMAND;
+ }
friend class ha_partition;
+ friend class ha_sequence;
public:
/**
This method is similar to update_row, however the handler doesn't need
@@ -4108,8 +4582,8 @@ public:
@retval 0 Bulk delete used by handler
@retval 1 Bulk delete not used, normal operation used
*/
- virtual int bulk_update_row(const uchar *old_data, uchar *new_data,
- uint *dup_key_found)
+ virtual int bulk_update_row(const uchar *old_data, const uchar *new_data,
+ ha_rows *dup_key_found)
{
DBUG_ASSERT(FALSE);
return HA_ERR_WRONG_COMMAND;
@@ -4160,7 +4634,6 @@ public:
virtual int enable_indexes(uint mode) { return HA_ERR_WRONG_COMMAND; }
virtual int discard_or_import_tablespace(my_bool discard)
{ return (my_errno=HA_ERR_WRONG_COMMAND); }
- virtual void prepare_for_alter() { return; }
virtual void drop_table(const char *name);
virtual int create(const char *name, TABLE *form, HA_CREATE_INFO *info)=0;
@@ -4194,11 +4667,32 @@ public:
virtual handlerton *partition_ht() const
{ return ht; }
inline int ha_write_tmp_row(uchar *buf);
+ inline int ha_delete_tmp_row(uchar *buf);
inline int ha_update_tmp_row(const uchar * old_data, uchar * new_data);
virtual void set_lock_type(enum thr_lock_type lock);
friend enum icp_result handler_index_cond_check(void* h_arg);
+
+ /**
+ Find unique record by index or unique constrain
+
+ @param record record to find (also will be fillded with
+ actual record fields)
+ @param unique_ref index or unique constraiun number (depends
+ on what used in the engine
+
+ @retval -1 Error
+ @retval 1 Not found
+ @retval 0 Found
+ */
+ virtual int find_unique_row(uchar *record, uint unique_ref)
+ { return -1; /*unsupported */}
+
+ bool native_versioned() const
+ { DBUG_ASSERT(ht); return partition_ht()->flags & HTON_NATIVE_SYS_VERSIONING; }
+ virtual void update_partition(uint part_id)
+ {}
protected:
Handler_share *get_ha_share_ptr();
void set_ha_share_ptr(Handler_share *arg_ha_share);
@@ -4221,7 +4715,7 @@ extern const char *myisam_stats_method_names[];
extern ulong total_ha, total_ha_2pc;
/* lookups */
-plugin_ref ha_resolve_by_name(THD *thd, const LEX_STRING *name, bool tmp_table);
+plugin_ref ha_resolve_by_name(THD *thd, const LEX_CSTRING *name, bool tmp_table);
plugin_ref ha_lock_engine(THD *thd, const handlerton *hton);
handlerton *ha_resolve_by_legacy_type(THD *thd, enum legacy_db_type db_type);
handler *get_new_handler(TABLE_SHARE *share, MEM_ROOT *alloc,
@@ -4276,7 +4770,7 @@ 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 char *db, const char *alias, bool generate_warning);
+ const LEX_CSTRING *db, const LEX_CSTRING *alias, bool generate_warning);
/* statistics and info */
bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat);
@@ -4289,11 +4783,11 @@ class Discovered_table_list: public handlerton::discovered_list
const char *wild, *wend;
bool with_temps; // whether to include temp tables in the result
public:
- Dynamic_array<LEX_STRING*> *tables;
+ Dynamic_array<LEX_CSTRING*> *tables;
- Discovered_table_list(THD *thd_arg, Dynamic_array<LEX_STRING*> *tables_arg,
- const LEX_STRING *wild_arg);
- Discovered_table_list(THD *thd_arg, Dynamic_array<LEX_STRING*> *tables_arg)
+ Discovered_table_list(THD *thd_arg, Dynamic_array<LEX_CSTRING*> *tables_arg,
+ const LEX_CSTRING *wild_arg);
+ Discovered_table_list(THD *thd_arg, Dynamic_array<LEX_CSTRING*> *tables_arg)
: thd(thd_arg), wild(NULL), with_temps(true), tables(tables_arg) {}
~Discovered_table_list() {}
@@ -4302,13 +4796,20 @@ public:
void sort();
void remove_duplicates(); // assumes that the list is sorted
+#ifndef DBUG_OFF
+ /*
+ Used to find unstable mtr tests querying
+ INFORMATION_SCHEMA.TABLES without ORDER BY.
+ */
+ void sort_desc();
+#endif
};
int ha_discover_table(THD *thd, TABLE_SHARE *share);
-int ha_discover_table_names(THD *thd, LEX_STRING *db, MY_DIR *dirp,
+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 char *db, const char *table_name,
- handlerton **hton= 0);
+bool ha_table_exists(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *table_name,
+ handlerton **hton= 0, bool *is_sequence= 0);
#endif
/* key cache */
@@ -4358,11 +4859,16 @@ const char *get_canonical_filename(handler *file, const char *path,
bool mysql_xa_recover(THD *thd);
void commit_checkpoint_notify_ha(handlerton *hton, void *cookie);
-inline const char *table_case_name(HA_CREATE_INFO *info, const char *name)
+inline const LEX_CSTRING *table_case_name(HA_CREATE_INFO *info, const LEX_CSTRING *name)
{
- return ((lower_case_table_names == 2 && info->alias) ? info->alias : name);
+ return ((lower_case_table_names == 2 && info->alias.str) ? &info->alias : name);
}
+typedef bool Log_func(THD*, TABLE*, bool, const uchar*, const uchar*);
+int binlog_log_row(TABLE* table,
+ const uchar *before_record,
+ const uchar *after_record,
+ Log_func *log_func);
#define TABLE_IO_WAIT(TRACKER, PSI, OP, INDEX, FLAGS, PAYLOAD) \
{ \
@@ -4380,7 +4886,7 @@ 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, LEX_STRING *db, LEX_STRING *table);
+int del_global_table_stat(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *table);
#ifndef DBUG_OFF
/** Converts XID to string.
@@ -4392,5 +4898,4 @@ int del_global_table_stat(THD *thd, LEX_STRING *db, LEX_STRING *table);
@note This does not need to be multi-byte safe or anything */
char *xid_to_str(char *buf, const XID &xid);
#endif // !DBUG_OFF
-
#endif /* HANDLER_INCLUDED */
diff --git a/sql/hash_filo.cc b/sql/hash_filo.cc
index 967ef641bab..b359bd95786 100644
--- a/sql/hash_filo.cc
+++ b/sql/hash_filo.cc
@@ -23,7 +23,7 @@
#pragma implementation // gcc: Class implementation
#endif
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "hash_filo.h"
diff --git a/sql/hostname.cc b/sql/hostname.cc
index a35b8a4f5c7..968914fd56e 100644
--- a/sql/hostname.cc
+++ b/sql/hostname.cc
@@ -24,7 +24,7 @@
Hostnames are checked with reverse name lookup and checked that they
doesn't resemble an IP address.
*/
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h" // SPECIAL_NO_HOST_CACHE
#include "hostname.h"
@@ -180,7 +180,7 @@ void hostname_cache_unlock()
static void prepare_hostname_cache_key(const char *ip_string,
char *ip_key)
{
- int ip_string_length= strlen(ip_string);
+ size_t ip_string_length= strlen(ip_string);
DBUG_ASSERT(ip_string_length < HOST_ENTRY_KEY_SIZE);
memset(ip_key, 0, HOST_ENTRY_KEY_SIZE);
@@ -229,12 +229,12 @@ static void add_hostname_impl(const char *ip_key, const char *hostname,
{
if (hostname != NULL)
{
- uint len= strlen(hostname);
+ size_t len= strlen(hostname);
if (len > sizeof(entry->m_hostname) - 1)
len= sizeof(entry->m_hostname) - 1;
memcpy(entry->m_hostname, hostname, len);
entry->m_hostname[len]= '\0';
- entry->m_hostname_length= len;
+ entry->m_hostname_length= (uint)len;
DBUG_PRINT("info",
("Adding/Updating '%s' -> '%s' (validated) to the hostname cache...'",
@@ -460,7 +460,7 @@ int ip_to_hostname(struct sockaddr_storage *ip_storage,
entry->m_last_seen= now;
*connect_errors= entry->m_errors.m_connect;
- if (entry->m_errors.m_connect >= max_connect_errors)
+ if (unlikely(entry->m_errors.m_connect >= max_connect_errors))
{
entry->m_errors.m_host_blocked++;
entry->set_error_timestamps(now);
@@ -702,12 +702,12 @@ int ip_to_hostname(struct sockaddr_storage *ip_storage,
/* Simulating ipv4 192.0.2.126 */
debug_addr= & debug_sock_addr[0];
debug_addr->sin_family= AF_INET;
- debug_addr->sin_addr.s_addr= inet_addr("192.0.2.126");
+ inet_pton(AF_INET,"192.0.2.126", &debug_addr->sin_addr);
/* Simulating ipv4 192.0.2.127 */
debug_addr= & debug_sock_addr[1];
debug_addr->sin_family= AF_INET;
- debug_addr->sin_addr.s_addr= inet_addr("192.0.2.127");
+ inet_pton(AF_INET,"192.0.2.127", &debug_addr->sin_addr);
debug_addr_info[0].ai_addr= (struct sockaddr*) & debug_sock_addr[0];
debug_addr_info[0].ai_addrlen= sizeof (struct sockaddr_in);
@@ -734,12 +734,12 @@ int ip_to_hostname(struct sockaddr_storage *ip_storage,
/* Simulating ipv4 192.0.2.5 */
debug_addr= & debug_sock_addr[0];
debug_addr->sin_family= AF_INET;
- debug_addr->sin_addr.s_addr= inet_addr("192.0.2.5");
+ inet_pton(AF_INET,"192.0.2.5", &debug_addr->sin_addr);
/* Simulating ipv4 192.0.2.4 */
debug_addr= & debug_sock_addr[1];
debug_addr->sin_family= AF_INET;
- debug_addr->sin_addr.s_addr= inet_addr("192.0.2.4");
+ inet_pton(AF_INET,"192.0.2.4", &debug_addr->sin_addr);
debug_addr_info[0].ai_addr= (struct sockaddr*) & debug_sock_addr[0];
debug_addr_info[0].ai_addrlen= sizeof (struct sockaddr_in);
@@ -774,44 +774,13 @@ int ip_to_hostname(struct sockaddr_storage *ip_storage,
debug_addr= & debug_sock_addr[0];
debug_addr->sin6_family= AF_INET6;
ip6= & debug_addr->sin6_addr;
- /* inet_pton not available on Windows XP. */
- ip6->s6_addr[ 0] = 0x20;
- ip6->s6_addr[ 1] = 0x01;
- ip6->s6_addr[ 2] = 0x0d;
- ip6->s6_addr[ 3] = 0xb8;
- ip6->s6_addr[ 4] = 0x00;
- ip6->s6_addr[ 5] = 0x00;
- ip6->s6_addr[ 6] = 0x00;
- ip6->s6_addr[ 7] = 0x00;
- ip6->s6_addr[ 8] = 0x00;
- ip6->s6_addr[ 9] = 0x00;
- ip6->s6_addr[10] = 0x00;
- ip6->s6_addr[11] = 0x00;
- ip6->s6_addr[12] = 0x00;
- ip6->s6_addr[13] = 0x06;
- ip6->s6_addr[14] = 0x00;
- ip6->s6_addr[15] = 0x7e;
+ inet_pton(AF_INET6,"2001:DB8::6:7E",ip6);
/* Simulating ipv6 2001:DB8::6:7F */
debug_addr= & debug_sock_addr[1];
debug_addr->sin6_family= AF_INET6;
ip6= & debug_addr->sin6_addr;
- ip6->s6_addr[ 0] = 0x20;
- ip6->s6_addr[ 1] = 0x01;
- ip6->s6_addr[ 2] = 0x0d;
- ip6->s6_addr[ 3] = 0xb8;
- ip6->s6_addr[ 4] = 0x00;
- ip6->s6_addr[ 5] = 0x00;
- ip6->s6_addr[ 6] = 0x00;
- ip6->s6_addr[ 7] = 0x00;
- ip6->s6_addr[ 8] = 0x00;
- ip6->s6_addr[ 9] = 0x00;
- ip6->s6_addr[10] = 0x00;
- ip6->s6_addr[11] = 0x00;
- ip6->s6_addr[12] = 0x00;
- ip6->s6_addr[13] = 0x06;
- ip6->s6_addr[14] = 0x00;
- ip6->s6_addr[15] = 0x7f;
+ inet_pton(AF_INET6,"2001:DB8::6:7F",ip6);
debug_addr_info[0].ai_addr= (struct sockaddr*) & debug_sock_addr[0];
debug_addr_info[0].ai_addrlen= sizeof (struct sockaddr_in6);
@@ -946,7 +915,7 @@ int ip_to_hostname(struct sockaddr_storage *ip_storage,
{
err_status=
- vio_get_normalized_ip_string(addr_info->ai_addr, addr_info->ai_addrlen,
+ vio_get_normalized_ip_string(addr_info->ai_addr, (int)addr_info->ai_addrlen,
ip_buffer, sizeof (ip_buffer));
DBUG_ASSERT(!err_status);
}
@@ -990,7 +959,7 @@ int ip_to_hostname(struct sockaddr_storage *ip_storage,
char ip_buffer[HOST_ENTRY_KEY_SIZE];
err_status=
- vio_get_normalized_ip_string(addr_info->ai_addr, addr_info->ai_addrlen,
+ vio_get_normalized_ip_string(addr_info->ai_addr, (int)addr_info->ai_addrlen,
ip_buffer, sizeof (ip_buffer));
DBUG_ASSERT(!err_status);
diff --git a/sql/hostname.h b/sql/hostname.h
index 9e84e329a60..3a371dceb92 100644
--- a/sql/hostname.h
+++ b/sql/hostname.h
@@ -16,7 +16,6 @@
#ifndef HOSTNAME_INCLUDED
#define HOSTNAME_INCLUDED
-#include "my_global.h" /* uint */
#include "my_net.h"
#include "hash_filo.h"
diff --git a/sql/init.cc b/sql/init.cc
index 5f452439f23..84465151fb1 100644
--- a/sql/init.cc
+++ b/sql/init.cc
@@ -21,10 +21,9 @@
Init and dummy functions for interface with unireg
*/
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "init.h"
-#include "my_sys.h"
#include "mysqld.h" // abort_loop, ...
#include "my_time.h" // my_init_time
#include "unireg.h" // SPECIAL_SAME_DB_NAME
diff --git a/sql/init.h b/sql/init.h
index 053e9612c70..c7c158082b9 100644
--- a/sql/init.h
+++ b/sql/init.h
@@ -16,8 +16,6 @@
#ifndef INIT_INCLUDED
#define INIT_INCLUDED
-#include "my_global.h" /* ulong */
-
void unireg_init(ulong options);
ATTRIBUTE_NORETURN void unireg_end(void);
diff --git a/sql/innodb_priv.h b/sql/innodb_priv.h
index e01b9f89d88..aaae75689e3 100644
--- a/sql/innodb_priv.h
+++ b/sql/innodb_priv.h
@@ -22,11 +22,11 @@
class THD;
-int get_quote_char_for_identifier(THD *thd, const char *name, uint length);
+int get_quote_char_for_identifier(THD *thd, const char *name, size_t length);
bool schema_table_store_record(THD *thd, TABLE *table);
void localtime_to_TIME(MYSQL_TIME *to, struct tm *from);
-uint strconvert(CHARSET_INFO *from_cs, const char *from, uint from_length,
- CHARSET_INFO *to_cs, char *to, uint to_length,
+uint strconvert(CHARSET_INFO *from_cs, const char *from, size_t from_length,
+ CHARSET_INFO *to_cs, char *to, size_t to_length,
uint *errors);
void sql_print_error(const char *format, ...);
diff --git a/sql/item.cc b/sql/item.cc
index 140eb5244ec..6c7a6943bdf 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -1,6 +1,6 @@
/*
Copyright (c) 2000, 2018, 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
@@ -19,7 +19,7 @@
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation // gcc: Class implementation
#endif
-#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
#include <mysql.h>
#include <m_ctype.h>
@@ -45,6 +45,15 @@
const String my_null_string("NULL", 4, default_charset_info);
const String my_default_string("DEFAULT", 7, default_charset_info);
+/*
+ item_empty_name is used when calling Item::set_name with NULL
+ pointer, to make it easier to use the name in printf.
+ item_used_name is used when calling Item::set_name with a 0 length
+ string.
+*/
+const char *item_empty_name="";
+const char *item_used_name= "\0";
+
static int save_field_in_field(Field *, bool *, Field *, bool);
@@ -69,6 +78,12 @@ inline void set_max_sum_func_level(THD *thd, SELECT_LEX *select)
select->nest_level - 1);
}
+
+MEM_ROOT *get_thd_memroot(THD *thd)
+{
+ return thd->mem_root;
+}
+
/*****************************************************************************
** Item functions
*****************************************************************************/
@@ -84,6 +99,15 @@ 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());
+}
+
+
void Item::push_note_converted_to_negative_complement(THD *thd)
{
push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_UNKNOWN_ERROR,
@@ -100,64 +124,6 @@ void Item::push_note_converted_to_positive_complement(THD *thd)
}
-/**
- @todo
- Make this functions class dependent
-*/
-
-bool Item::val_bool()
-{
- switch(result_type()) {
- case INT_RESULT:
- return val_int() != 0;
- case DECIMAL_RESULT:
- {
- my_decimal decimal_value;
- my_decimal *val= val_decimal(&decimal_value);
- if (val)
- return !my_decimal_is_zero(val);
- return 0;
- }
- case REAL_RESULT:
- case STRING_RESULT:
- return val_real() != 0.0;
- case ROW_RESULT:
- case TIME_RESULT:
- DBUG_ASSERT(0);
- return 0; // Wrong (but safe)
- }
- return 0; // Wrong (but safe)
-}
-
-
-/**
- Get date/time/datetime.
- Optionally extend TIME result to DATETIME.
-*/
-bool Item::get_date_with_conversion(MYSQL_TIME *ltime, ulonglong fuzzydate)
-{
- THD *thd= current_thd;
-
- /*
- Some TIME type items return error when trying to do get_date()
- without TIME_TIME_ONLY set (e.g. Item_field for Field_time).
- In the SQL standard time->datetime conversion mode we add TIME_TIME_ONLY.
- In the legacy time->datetime conversion mode we do not add TIME_TIME_ONLY
- and leave it to get_date() to check date.
- */
- ulonglong time_flag= (field_type() == MYSQL_TYPE_TIME &&
- !(thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST)) ?
- TIME_TIME_ONLY : 0;
- if (get_date(ltime, fuzzydate | time_flag))
- return true;
- if (ltime->time_type == MYSQL_TIMESTAMP_TIME &&
- !(fuzzydate & TIME_TIME_ONLY) &&
- convert_time_to_datetime(thd, ltime, fuzzydate))
- return true;
- return false;
-}
-
-
longlong Item::val_datetime_packed_result()
{
MYSQL_TIME ltime, tmp;
@@ -245,6 +211,19 @@ String *Item::val_str_ascii(String *str)
}
+String *Item::val_str_ascii_revert_empty_string_is_null(THD *thd, String *str)
+{
+ String *res= val_str_ascii(str);
+ if (!res && (thd->variables.sql_mode & MODE_EMPTY_STRING_IS_NULL))
+ {
+ null_value= false;
+ str->set("", 0, &my_charset_latin1);
+ return str;
+ }
+ return res;
+}
+
+
String *Item::val_str(String *str, String *converter, CHARSET_INFO *cs)
{
String *res= val_str(str);
@@ -313,6 +292,65 @@ bool Item::is_null_from_temporal()
}
+longlong Item::val_int_from_str(int *error)
+{
+ char buff[MAX_FIELD_WIDTH];
+ String tmp(buff,sizeof(buff), &my_charset_bin), *res;
+
+ /*
+ For a string result, we must first get the string and then convert it
+ to a longlong
+ */
+ if (!(res= val_str(&tmp)))
+ {
+ *error= 0;
+ return 0;
+ }
+ Converter_strtoll10_with_warn cnv(NULL, Warn_filter_all(),
+ res->charset(), res->ptr(), res->length());
+ *error= cnv.error();
+ return cnv.result();
+}
+
+
+longlong Item::val_int_signed_typecast_from_str()
+{
+ int error;
+ longlong value= val_int_from_str(&error);
+ if (unlikely(!null_value && value < 0 && error == 0))
+ push_note_converted_to_negative_complement(current_thd);
+ return value;
+}
+
+
+longlong Item::val_int_unsigned_typecast_from_str()
+{
+ int error;
+ longlong value= val_int_from_str(&error);
+ if (unlikely(!null_value && error < 0))
+ push_note_converted_to_positive_complement(current_thd);
+ return value;
+}
+
+
+longlong Item::val_int_unsigned_typecast_from_int()
+{
+ longlong value= val_int();
+ if (!null_value && unsigned_flag == 0 && value < 0)
+ push_note_converted_to_positive_complement(current_thd);
+ return value;
+}
+
+
+longlong Item::val_int_signed_typecast_from_int()
+{
+ longlong value= val_int();
+ if (!null_value && unsigned_flag && value < 0)
+ push_note_converted_to_negative_complement(current_thd);
+ return value;
+}
+
+
String *Item::val_string_from_date(String *str)
{
MYSQL_TIME ltime;
@@ -430,21 +468,33 @@ longlong Item::val_int_from_decimal()
return result;
}
-int Item::save_time_in_field(Field *field)
+
+longlong Item::val_int_unsigned_typecast_from_decimal()
+{
+ longlong result;
+ my_decimal tmp, *dec= val_decimal(&tmp);
+ if (null_value)
+ return 0;
+ my_decimal2int(E_DEC_FATAL_ERROR, dec, 1, &result);
+ return result;
+}
+
+
+int Item::save_time_in_field(Field *field, bool no_conversions)
{
MYSQL_TIME ltime;
if (get_time(&ltime))
- return set_field_to_null_with_conversions(field, 0);
+ return set_field_to_null_with_conversions(field, no_conversions);
field->set_notnull();
return field->store_time_dec(&ltime, decimals);
}
-int Item::save_date_in_field(Field *field)
+int Item::save_date_in_field(Field *field, bool no_conversions)
{
MYSQL_TIME ltime;
if (get_date(&ltime, sql_mode_for_dates(field->table->in_use)))
- return set_field_to_null_with_conversions(field, 0);
+ return set_field_to_null_with_conversions(field, no_conversions);
field->set_notnull();
return field->store_time_dec(&ltime, decimals);
}
@@ -482,17 +532,16 @@ int Item::save_str_value_in_field(Field *field, String *result)
Item::Item(THD *thd):
- is_expensive_cache(-1), rsize(0), name(0), orig_name(0), name_length(0),
+ is_expensive_cache(-1), rsize(0), name(null_clex_str), orig_name(0),
fixed(0), is_autogenerated_name(TRUE)
{
DBUG_ASSERT(thd);
marker= 0;
maybe_null=null_value=with_sum_func=with_window_func=with_field=0;
in_rollup= 0;
- with_subselect= 0;
with_param= 0;
- /* Initially this item is not attached to any JOIN_TAB. */
+ /* Initially this item is not attached to any JOIN_TAB. */
join_tab_idx= MAX_TABLES;
/* Put item in free list so that we can free all items at end */
@@ -530,14 +579,13 @@ const TABLE_SHARE *Item::field_table_or_null()
tables.
*/
Item::Item(THD *thd, Item *item):
- Type_std_attributes(item),
+ Type_all_attributes(item),
join_tab_idx(item->join_tab_idx),
is_expensive_cache(-1),
rsize(0),
str_value(item->str_value),
name(item->name),
orig_name(item->orig_name),
- name_length(item->name_length),
marker(item->marker),
maybe_null(item->maybe_null),
in_rollup(item->in_rollup),
@@ -547,57 +595,13 @@ Item::Item(THD *thd, Item *item):
with_window_func(item->with_window_func),
with_field(item->with_field),
fixed(item->fixed),
- is_autogenerated_name(item->is_autogenerated_name),
- with_subselect(item->has_subquery())
+ is_autogenerated_name(item->is_autogenerated_name)
{
next= thd->free_list; // Put in free list
thd->free_list= this;
}
-uint Item::decimal_precision() const
-{
- Item_result restype= result_type();
-
- if ((restype == DECIMAL_RESULT) || (restype == INT_RESULT))
- {
- uint prec=
- my_decimal_length_to_precision(max_char_length(), decimals,
- unsigned_flag);
- return MY_MIN(prec, DECIMAL_MAX_PRECISION);
- }
- uint res= max_char_length();
- /*
- Return at least one decimal digit, even if Item::max_char_length()
- returned 0. This is important to avoid attempts to create fields of types
- INT(0) or DECIMAL(0,0) when converting NULL or empty strings to INT/DECIMAL:
- CREATE TABLE t1 AS SELECT CONVERT(NULL,SIGNED) AS a;
- */
- return res ? MY_MIN(res, DECIMAL_MAX_PRECISION) : 1;
-}
-
-
-uint Item::temporal_precision(enum_field_types type_arg)
-{
- if (const_item() && result_type() == STRING_RESULT &&
- !is_temporal_type(field_type()))
- {
- MYSQL_TIME ltime;
- String buf, *tmp;
- MYSQL_TIME_STATUS status;
- DBUG_ASSERT(fixed);
- if ((tmp= val_str(&buf)) &&
- !(type_arg == MYSQL_TYPE_TIME ?
- str_to_time(tmp->charset(), tmp->ptr(), tmp->length(),
- &ltime, TIME_TIME_ONLY, &status) :
- str_to_datetime(tmp->charset(), tmp->ptr(), tmp->length(),
- &ltime, TIME_FUZZY_DATES, &status)))
- return MY_MIN(status.precision, TIME_SECOND_PART_DIGITS);
- }
- return MY_MIN(decimals, TIME_SECOND_PART_DIGITS);
-}
-
-
void Item::print_parenthesised(String *str, enum_query_type query_type,
enum precedence parent_prec)
{
@@ -620,11 +624,12 @@ void Item::print_item_w_name(String *str, enum_query_type query_type)
{
print(str, query_type);
- if (name)
+ if (name.str)
{
+ DBUG_ASSERT(name.length == strlen(name.str));
THD *thd= current_thd;
str->append(STRING_WITH_LEN(" AS "));
- append_identifier(thd, str, name, (uint) strlen(name));
+ append_identifier(thd, str, &name);
}
}
@@ -663,7 +668,10 @@ void Item::cleanup()
marker= 0;
join_tab_idx= MAX_TABLES;
if (orig_name)
- name= orig_name;
+ {
+ name.str= orig_name;
+ name.length= strlen(orig_name);
+ }
DBUG_VOID_RETURN;
}
@@ -683,24 +691,6 @@ bool Item::cleanup_processor(void *arg)
/**
- rename item (used for views, cleanup() return original name).
-
- @param new_name new name of item;
-*/
-
-void Item::rename(char *new_name)
-{
- /*
- we can compare pointers to names here, because if name was not changed,
- pointer will be same
- */
- if (!orig_name && new_name != name)
- orig_name= name;
- name= new_name;
-}
-
-
-/**
Traverse item tree possibly transforming it (replacing items).
This function is designed to ease transformation of Item trees.
@@ -756,12 +746,11 @@ Item* Item::set_expr_cache(THD *thd)
{
DBUG_ENTER("Item::set_expr_cache");
Item_cache_wrapper *wrapper;
- if ((wrapper= new (thd->mem_root) Item_cache_wrapper(thd, this)) &&
- !wrapper->fix_fields(thd, (Item**)&wrapper))
+ if (likely((wrapper= new (thd->mem_root) Item_cache_wrapper(thd, this))) &&
+ likely(!wrapper->fix_fields(thd, (Item**)&wrapper)))
{
- if (wrapper->set_cache(thd))
- DBUG_RETURN(NULL);
- DBUG_RETURN(wrapper);
+ if (likely(!wrapper->set_cache(thd)))
+ DBUG_RETURN(wrapper);
}
DBUG_RETURN(NULL);
}
@@ -769,31 +758,31 @@ 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 char *field_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 = (char*) field_name_arg;
- name_length= name ? strlen(name) : 0;
+ name= *field_name_arg;
}
-Item_ident::Item_ident(THD *thd, TABLE_LIST *view_arg, const char *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),
- orig_field_name(field_name_arg), context(&view_arg->view->select_lex.context),
- db_name(NullS), table_name(view_arg->alias),
- field_name(field_name_arg),
+ orig_table_name(view_arg->table_name.str),
+ orig_field_name(*field_name_arg),
+ context(&view_arg->view->select_lex.context),
+ db_name(NullS), table_name(view_arg->alias.str),
+ 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 = (char*) field_name_arg;
- name_length= name ? strlen(name) : 0;
+ name= *field_name_arg;
}
@@ -884,7 +873,8 @@ bool Item_ident::collect_outer_ref_processor(void *param)
bool Item_field::collect_item_field_processor(void *arg)
{
DBUG_ENTER("Item_field::collect_item_field_processor");
- DBUG_PRINT("info", ("%s", field->field_name ? field->field_name : "noname"));
+ DBUG_PRINT("info", ("%s", field->field_name.str ?
+ field->field_name.str : "noname"));
List<Item_field> *item_list= (List<Item_field>*) arg;
List_iterator<Item_field> item_list_it(*item_list);
Item_field *curr_item;
@@ -901,7 +891,8 @@ bool Item_field::collect_item_field_processor(void *arg)
bool Item_field::add_field_to_set_processor(void *arg)
{
DBUG_ENTER("Item_field::add_field_to_set_processor");
- DBUG_PRINT("info", ("%s", field->field_name ? field->field_name : "noname"));
+ DBUG_PRINT("info", ("%s", field->field_name.str ? field->field_name.str :
+ "noname"));
TABLE *table= (TABLE *) arg;
if (field->table == table)
bitmap_set_bit(&table->tmp_set, field->field_index);
@@ -921,12 +912,12 @@ bool Item_field::rename_fields_processor(void *arg)
while ((def=def_it++))
{
- if (def->change &&
+ 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)) &&
- !my_strcasecmp(system_charset_info, field_name, def->change))
+ !my_strcasecmp(system_charset_info, field_name.str, def->change.str))
{
field_name= def->field_name;
break;
@@ -1057,7 +1048,7 @@ bool Item_field::check_field_expression_processor(void *arg)
{
my_error(ER_EXPRESSION_REFERS_TO_UNINIT_FIELD,
MYF(0),
- org_field->field_name, field->field_name);
+ org_field->field_name.str, field->field_name.str);
return 1;
}
}
@@ -1088,13 +1079,151 @@ bool Item::check_cols(uint c)
}
-void Item::set_name(THD *thd, const char *str, uint length, CHARSET_INFO *cs)
+bool Item::check_type_or_binary(const char *opname,
+ const Type_handler *expect) const
+{
+ const Type_handler *handler= type_handler();
+ if (handler == expect ||
+ (handler->is_general_purpose_string_type() &&
+ 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 Item::check_type_general_purpose_string(const char *opname) const
+{
+ const Type_handler *handler= type_handler();
+ if (handler->is_general_purpose_string_type())
+ return false;
+ my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0),
+ handler->name().ptr(), opname);
+ return true;
+}
+
+
+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())
+ return false;
+ my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0),
+ handler->name().ptr(), opname);
+ return true;
+}
+
+
+bool Item::check_type_can_return_int(const char *opname) const
+{
+ const Type_handler *handler= type_handler();
+ if (handler->can_return_int())
+ return false;
+ my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0),
+ handler->name().ptr(), opname);
+ return true;
+}
+
+
+bool Item::check_type_can_return_decimal(const char *opname) const
+{
+ const Type_handler *handler= type_handler();
+ if (handler->can_return_decimal())
+ return false;
+ my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0),
+ handler->name().ptr(), opname);
+ return true;
+}
+
+
+bool Item::check_type_can_return_real(const char *opname) const
+{
+ const Type_handler *handler= type_handler();
+ if (handler->can_return_real())
+ return false;
+ my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0),
+ handler->name().ptr(), opname);
+ return true;
+}
+
+
+bool Item::check_type_can_return_date(const char *opname) const
+{
+ const Type_handler *handler= type_handler();
+ if (handler->can_return_date())
+ return false;
+ my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0),
+ handler->name().ptr(), opname);
+ return true;
+}
+
+
+bool Item::check_type_can_return_time(const char *opname) const
+{
+ const Type_handler *handler= type_handler();
+ if (handler->can_return_time())
+ return false;
+ my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0),
+ handler->name().ptr(), opname);
+ return true;
+}
+
+
+bool Item::check_type_can_return_str(const char *opname) const
+{
+ const Type_handler *handler= type_handler();
+ if (handler->can_return_str())
+ return false;
+ my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0),
+ handler->name().ptr(), opname);
+ return true;
+}
+
+
+bool Item::check_type_can_return_text(const char *opname) const
+{
+ const Type_handler *handler= type_handler();
+ if (handler->can_return_text())
+ return false;
+ my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0),
+ handler->name().ptr(), opname);
+ return true;
+}
+
+
+bool Item::check_type_scalar(const char *opname) const
+{
+ /*
+ fixed==true usually means than the Item has an initialized
+ and reliable data type handler and attributes.
+ Item_outer_ref is an exception. It copies the data type and the attributes
+ from the referenced Item in the constructor, but then sets "fixed" to false,
+ and re-fixes itself again in fix_inner_refs().
+ This hack in Item_outer_ref should probably be refactored eventually.
+ Discuss with Sanja.
+ */
+ DBUG_ASSERT(fixed || type() == REF_ITEM);
+ const Type_handler *handler= type_handler();
+ if (handler->is_scalar_type())
+ return false;
+ my_error(ER_OPERAND_COLUMNS, MYF(0), 1);
+ return true;
+}
+
+
+void Item::set_name(THD *thd, const char *str, size_t length, CHARSET_INFO *cs)
{
if (!length)
{
- /* Empty string, used by AS or internal function like last_insert_id() */
- name= (char*) str;
- name_length= 0;
+ /*
+ Null string are replaced by item_empty_name. This is used by AS or
+ internal function like last_insert_id() to detect if we need to
+ change the name later.
+ Used by sql_yacc.yy in select_alias handling
+ */
+ name.str= str ? item_used_name : item_empty_name;
+ name.length= 0;
return;
}
@@ -1136,13 +1265,13 @@ void Item::set_name(THD *thd, const char *str, uint length, CHARSET_INFO *cs)
if (!my_charset_same(cs, system_charset_info))
{
size_t res_length;
- name= sql_strmake_with_convert(thd, str, length, cs,
- MAX_ALIAS_NAME, system_charset_info,
- &res_length);
- name_length= res_length;
+ name.str= sql_strmake_with_convert(thd, str, length, cs,
+ MAX_ALIAS_NAME, system_charset_info,
+ &res_length);
+ name.length= res_length;
}
else
- name= thd->strmake(str, (name_length= MY_MIN(length,MAX_ALIAS_NAME)));
+ name.str= thd->strmake(str, (name.length= MY_MIN(length,MAX_ALIAS_NAME)));
}
@@ -1152,28 +1281,13 @@ void Item::set_name_no_truncate(THD *thd, const char *str, uint length,
if (!my_charset_same(cs, system_charset_info))
{
size_t res_length;
- name= sql_strmake_with_convert(thd, str, length, cs,
- UINT_MAX, system_charset_info,
- &res_length);
- name_length= res_length;
+ name.str= sql_strmake_with_convert(thd, str, length, cs,
+ UINT_MAX, system_charset_info,
+ &res_length);
+ name.length= res_length;
}
else
- name= thd->strmake(str, (name_length= length));
-}
-
-
-void Item::set_name_for_rollback(THD *thd, const char *str, uint length,
- CHARSET_INFO *cs)
-{
- char *old_name, *new_name;
- old_name= name;
- set_name(thd, str, length, cs);
- new_name= name;
- if (old_name != new_name)
- {
- name= old_name;
- thd->change_item_tree((Item **) &name, (Item *) new_name);
- }
+ name.str= thd->strmake(str, (name.length= length));
}
@@ -1191,8 +1305,8 @@ bool Item::eq(const Item *item, bool binary_cmp) const
for all basic constants we have special checks, and Item_param's
type() can be only among basic constant types.
*/
- return type() == item->type() && name && item->name &&
- !my_strcasecmp(system_charset_info,name,item->name);
+ return type() == item->type() && name.str && item->name.str &&
+ !lex_string_cmp(system_charset_info, &name, &item->name);
}
@@ -1201,7 +1315,7 @@ Item *Item::safe_charset_converter(THD *thd, CHARSET_INFO *tocs)
if (!needs_charset_converter(tocs))
return this;
Item_func_conv_charset *conv= new (thd->mem_root) Item_func_conv_charset(thd, this, tocs, 1);
- return conv->safe ? conv : NULL;
+ return conv && conv->safe ? conv : NULL;
}
@@ -1235,7 +1349,7 @@ Item *Item_cache::safe_charset_converter(THD *thd, CHARSET_INFO *tocs)
return this;
Item_cache *cache;
if (!conv || conv->fix_fields(thd, (Item **) NULL) ||
- !(cache= new (thd->mem_root) Item_cache_str(thd, conv)))
+ unlikely(!(cache= new (thd->mem_root) Item_cache_str(thd, conv))))
return NULL; // Safe conversion is not possible, or OEM
cache->setup(thd, conv);
cache->fixed= false; // Make Item::fix_fields() happy
@@ -1317,7 +1431,7 @@ Item *Item::const_charset_converter(THD *thd, CHARSET_INFO *tocs,
collation.derivation,
collation.repertoire));
- if (!conv || (conv_errors && lossless))
+ if (unlikely(!conv || (conv_errors && lossless)))
{
/*
Safe conversion is not possible (or EOM).
@@ -1357,70 +1471,72 @@ Item *Item_param::safe_charset_converter(THD *thd, CHARSET_INFO *tocs)
As a extra convenience the time structure is reset on error or NULL values!
*/
-bool Item::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
+bool Item::get_date_from_int(MYSQL_TIME *ltime, ulonglong fuzzydate)
{
- if (field_type() == MYSQL_TYPE_TIME)
- fuzzydate|= TIME_TIME_ONLY;
+ longlong value= val_int();
+ bool neg= !unsigned_flag && value < 0;
+ if (null_value || int_to_datetime_with_warn(neg, neg ? -value : value,
+ ltime, fuzzydate,
+ field_table_or_null(),
+ field_name_or_null()))
+ return null_value|= make_zero_date(ltime, fuzzydate);
+ return null_value= false;
+}
- switch (result_type()) {
- case INT_RESULT:
- {
- longlong value= val_int();
- bool neg= !unsigned_flag && value < 0;
- if (field_type() == MYSQL_TYPE_YEAR)
- {
- if (max_length == 2)
- {
- if (value < 70)
- value+= 2000;
- else if (value <= 1900)
- value+= 1900;
- }
- value*= 10000; /* make it YYYYMMHH */
- }
- if (null_value || int_to_datetime_with_warn(neg, neg ? -value : value,
- ltime, fuzzydate,
- field_table_or_null(),
- field_name_or_null()))
- goto err;
- return null_value= false;
- }
- case REAL_RESULT:
- {
- double value= val_real();
- if (null_value || double_to_datetime_with_warn(value, ltime, fuzzydate,
- field_table_or_null(),
- field_name_or_null()))
- goto err;
- return null_value= false;
- }
- case DECIMAL_RESULT:
- {
- my_decimal value, *res;
- if (!(res= val_decimal(&value)) ||
- decimal_to_datetime_with_warn(res, ltime, fuzzydate,
- field_table_or_null(),
- field_name_or_null()))
- goto err;
- return null_value= false;
- }
- case STRING_RESULT:
+
+bool Item::get_date_from_year(MYSQL_TIME *ltime, ulonglong fuzzydate)
+{
+ longlong value= val_int();
+ DBUG_ASSERT(unsigned_flag || value >= 0);
+ if (max_length == 2)
{
- char buff[40];
- String tmp(buff,sizeof(buff), &my_charset_bin),*res;
- if (!(res=val_str(&tmp)) ||
- str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(),
- ltime, fuzzydate))
- goto err;
- return null_value= false;
- }
- default:
- null_value= true;
- DBUG_ASSERT(0);
+ if (value < 70)
+ value+= 2000;
+ else if (value <= 1900)
+ value+= 1900;
}
+ value*= 10000; /* make it YYYYMMHH */
+ if (null_value || int_to_datetime_with_warn(false, value,
+ ltime, fuzzydate,
+ field_table_or_null(),
+ field_name_or_null()))
+ return null_value|= make_zero_date(ltime, fuzzydate);
+ return null_value= false;
+}
-err:
- return null_value|= make_zero_date(ltime, fuzzydate);
+
+bool Item::get_date_from_real(MYSQL_TIME *ltime, ulonglong fuzzydate)
+{
+ double value= val_real();
+ if (null_value || double_to_datetime_with_warn(value, ltime, fuzzydate,
+ field_table_or_null(),
+ field_name_or_null()))
+ return null_value|= make_zero_date(ltime, fuzzydate);
+ return null_value= false;
+}
+
+
+bool Item::get_date_from_decimal(MYSQL_TIME *ltime, ulonglong fuzzydate)
+{
+ my_decimal value, *res;
+ if (!(res= val_decimal(&value)) ||
+ decimal_to_datetime_with_warn(res, ltime, fuzzydate,
+ field_table_or_null(),
+ field_name_or_null()))
+ return null_value|= make_zero_date(ltime, fuzzydate);
+ return null_value= false;
+}
+
+
+bool Item::get_date_from_string(MYSQL_TIME *ltime, ulonglong fuzzydate)
+{
+ char buff[40];
+ String tmp(buff,sizeof(buff), &my_charset_bin),*res;
+ if (!(res=val_str(&tmp)) ||
+ str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(),
+ ltime, fuzzydate))
+ return null_value|= make_zero_date(ltime, fuzzydate);
+ return null_value= false;
}
@@ -1466,6 +1582,25 @@ bool Item::get_seconds(ulonglong *sec, ulong *sec_part)
return my_decimal2seconds(dec, sec, sec_part);
}
+const MY_LOCALE *Item::locale_from_val_str()
+{
+ StringBuffer<MAX_FIELD_WIDTH> tmp;
+ String *locale_name= val_str_ascii(&tmp);
+ const MY_LOCALE *lc;
+ if (!locale_name ||
+ !(lc= my_locale_by_name(locale_name->c_ptr_safe())))
+ {
+ THD *thd= current_thd;
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_UNKNOWN_LOCALE,
+ ER_THD(thd, ER_UNKNOWN_LOCALE),
+ locale_name ? locale_name->c_ptr_safe() : "NULL");
+ lc= &my_locale_en_US;
+ }
+ return lc;
+}
+
+
CHARSET_INFO *Item::default_charset()
{
return current_thd->variables.collation_connection;
@@ -1538,28 +1673,68 @@ bool mark_unsupported_function(const char *w1, const char *w2,
return mark_unsupported_function(ptr, store, result);
}
+
+Query_fragment::Query_fragment(THD *thd, sp_head *sphead,
+ const char *start, const char *end)
+{
+ DBUG_ASSERT(start <= end);
+ if (thd->lex->clone_spec_offset)
+ {
+ Lex_input_stream *lip= (& thd->m_parser_state->m_lip);
+ DBUG_ASSERT(lip->get_buf() <= start);
+ DBUG_ASSERT(end <= lip->get_end_of_query());
+ set(start - lip->get_buf(), end - start);
+ }
+ else if (sphead)
+ {
+ if (sphead->m_tmp_query)
+ {
+ // Normal SP statement
+ DBUG_ASSERT(sphead->m_tmp_query <= start);
+ set(start - sphead->m_tmp_query, end - start);
+ }
+ else
+ {
+ /*
+ We're in the "if" expression of a compound query:
+ if (expr)
+ do_something;
+ end if;
+ sphead->m_tmp_query is not set yet at this point, because
+ the "if" part of such statements is never put into the binary log.
+ Values of Rewritable_query_parameter::pos_in_query and
+ Rewritable_query_parameter:len_in_query will not be important,
+ so setting both to 0 should be fine.
+ */
+ set(0, 0);
+ }
+ }
+ else
+ {
+ // Non-SP statement
+ DBUG_ASSERT(thd->query() <= start);
+ DBUG_ASSERT(end <= thd->query_end());
+ set(start - thd->query(), end - start);
+ }
+}
+
+
/*****************************************************************************
Item_sp_variable methods
*****************************************************************************/
-Item_sp_variable::Item_sp_variable(THD *thd, char *sp_var_name_str,
- uint sp_var_name_length):
- Item(thd), m_thd(0)
+Item_sp_variable::Item_sp_variable(THD *thd, const LEX_CSTRING *sp_var_name)
+ :Item(thd), m_thd(0), m_name(*sp_var_name)
#ifndef DBUG_OFF
, m_sp(0)
#endif
{
- m_name.str= sp_var_name_str;
- m_name.length= sp_var_name_length;
}
-bool Item_sp_variable::fix_fields(THD *thd, Item **)
+bool Item_sp_variable::fix_fields_from_item(THD *thd, Item **, const Item *it)
{
- Item *it;
-
m_thd= thd; /* NOTE: this must be set before any this_xxx() */
- it= this_item();
DBUG_ASSERT(it->fixed);
@@ -1642,62 +1817,106 @@ my_decimal *Item_sp_variable::val_decimal(my_decimal *decimal_value)
}
+bool Item_sp_variable::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+{
+ DBUG_ASSERT(fixed);
+ Item *it= this_item();
+ bool val= it->get_date(ltime, fuzzydate);
+ null_value= it->null_value;
+ return val;
+}
+
+
bool Item_sp_variable::is_null()
{
return this_item()->is_null();
}
+void Item_sp_variable::make_send_field(THD *thd, Send_field *field)
+{
+ Item *it= this_item();
+
+ it->make_send_field(thd, field);
+ if (name.str)
+ field->col_name= name;
+ else
+ field->col_name= m_name;
+}
/*****************************************************************************
Item_splocal methods
*****************************************************************************/
-Item_splocal::Item_splocal(THD *thd, const LEX_STRING &sp_var_name,
+Item_splocal::Item_splocal(THD *thd,
+ const Sp_rcontext_handler *rh,
+ const LEX_CSTRING *sp_var_name,
uint sp_var_idx,
- enum_field_types sp_var_type,
+ const Type_handler *handler,
uint pos_in_q, uint len_in_q):
- Item_sp_variable(thd, sp_var_name.str, sp_var_name.length),
+ Item_sp_variable(thd, sp_var_name),
Rewritable_query_parameter(pos_in_q, len_in_q),
+ Type_handler_hybrid_field_type(handler),
+ m_rcontext_handler(rh),
m_var_idx(sp_var_idx)
{
maybe_null= TRUE;
+ m_type= sp_map_item_type(handler);
+}
- sp_var_type= real_type_to_type(sp_var_type);
- m_type= sp_map_item_type(sp_var_type);
- set_handler_by_field_type(sp_var_type);
+
+sp_rcontext *Item_splocal::get_rcontext(sp_rcontext *local_ctx) const
+{
+ return m_rcontext_handler->get_rcontext(local_ctx);
+}
+
+
+Item_field *Item_splocal::get_variable(sp_rcontext *ctx) const
+{
+ return get_rcontext(ctx)->get_variable(m_var_idx);
+}
+
+
+bool Item_splocal::fix_fields(THD *thd, Item **ref)
+{
+ DBUG_ASSERT(!fixed);
+ Item *item= get_variable(thd->spcont);
+ set_handler(item->type_handler());
+ return fix_fields_from_item(thd, ref, item);
}
Item *
Item_splocal::this_item()
{
- DBUG_ASSERT(m_sp == m_thd->spcont->sp);
-
- return m_thd->spcont->get_item(m_var_idx);
+ DBUG_ASSERT(m_sp == m_thd->spcont->m_sp);
+ DBUG_ASSERT(fixed);
+ return get_variable(m_thd->spcont);
}
const Item *
Item_splocal::this_item() const
{
- DBUG_ASSERT(m_sp == m_thd->spcont->sp);
-
- return m_thd->spcont->get_item(m_var_idx);
+ DBUG_ASSERT(m_sp == m_thd->spcont->m_sp);
+ DBUG_ASSERT(fixed);
+ return get_variable(m_thd->spcont);
}
Item **
Item_splocal::this_item_addr(THD *thd, Item **)
{
- DBUG_ASSERT(m_sp == thd->spcont->sp);
-
- return thd->spcont->get_item_addr(m_var_idx);
+ DBUG_ASSERT(m_sp == thd->spcont->m_sp);
+ DBUG_ASSERT(fixed);
+ return get_rcontext(thd->spcont)->get_variable_addr(m_var_idx);
}
void Item_splocal::print(String *str, enum_query_type)
{
- str->reserve(m_name.length+8);
- str->append(m_name.str, m_name.length);
+ const LEX_CSTRING *prefix= m_rcontext_handler->get_name_prefix();
+ str->reserve(m_name.length + 8 + prefix->length);
+ str->append(prefix);
+ str->append(&m_name);
str->append('@');
str->qs_append(m_var_idx);
}
@@ -1705,7 +1924,181 @@ void Item_splocal::print(String *str, enum_query_type)
bool Item_splocal::set_value(THD *thd, sp_rcontext *ctx, Item **it)
{
- return ctx->set_variable(thd, get_var_idx(), it);
+ return get_rcontext(ctx)->set_variable(thd, get_var_idx(), it);
+}
+
+
+/**
+ These two declarations are different:
+ x INT;
+ ROW(x INT);
+ A ROW with one elements should not be comparable to scalar value.
+
+ TODO: Currently we don't support one argument with the function ROW(), so
+ this query returns a syntax error, meaning that more arguments are expected:
+ SELECT ROW(1);
+
+ Therefore, all around the code we assume that cols()==1 means a scalar value
+ and cols()>1 means a ROW value. With adding ROW SP variables this
+ assumption is not true any more. ROW variables with one element are
+ now possible.
+
+ To implement Item::check_cols() correctly, we now should extend it to
+ know if a ROW or a scalar value is being tested. For example,
+ these new prototypes should work:
+ virtual bool check_cols(Item_result result, uint c);
+ or
+ virtual bool check_cols(const Type_handler *type, uint c);
+
+ The current implementation of Item_splocal::check_cols() is a compromise
+ that should be more or less fine until we extend check_cols().
+ It disallows ROW variables to appear in a scalar context.
+ The "|| n == 1" part of the conditon is responsible for this.
+ For example, it disallows ROW variables to appear in SELECT list:
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ a ROW (a INT);
+BEGIN
+ SELECT a;
+END;
+$$
+DELIMITER ;$$
+--error ER_OPERAND_COLUMNS
+CALL p1();
+
+ But is produces false negatives with ROW variables consisting of one element.
+ For example, this script fails:
+
+SET sql_mode=ORACLE;
+DROP PROCEDURE IF EXISTS p1;
+DELIMITER $$
+CREATE PROCEDURE p1
+AS
+ a ROW(a INT);
+ b ROW(a INT);
+BEGIN
+ SELECT a=b;
+END;
+$$
+DELIMITER ;
+CALL p1();
+
+ and returns "ERROR 1241 (21000): Operand should contain 1 column(s)".
+ This will be fixed that we change check_cols().
+*/
+
+bool Item_splocal::check_cols(uint n)
+{
+ DBUG_ASSERT(m_thd->spcont);
+ if (Type_handler_hybrid_field_type::cmp_type() != ROW_RESULT)
+ return Item::check_cols(n);
+
+ if (n != this_item()->cols() || n == 1)
+ {
+ my_error(ER_OPERAND_COLUMNS, MYF(0), n);
+ return true;
+ }
+ return false;
+}
+
+
+bool Item_splocal_row_field::fix_fields(THD *thd, Item **ref)
+{
+ DBUG_ASSERT(!fixed);
+ Item *item= get_variable(thd->spcont)->element_index(m_field_idx);
+ return fix_fields_from_item(thd, ref, item);
+}
+
+
+Item *
+Item_splocal_row_field::this_item()
+{
+ DBUG_ASSERT(m_sp == m_thd->spcont->m_sp);
+ DBUG_ASSERT(fixed);
+ return get_variable(m_thd->spcont)->element_index(m_field_idx);
+}
+
+
+const Item *
+Item_splocal_row_field::this_item() const
+{
+ DBUG_ASSERT(m_sp == m_thd->spcont->m_sp);
+ DBUG_ASSERT(fixed);
+ return get_variable(m_thd->spcont)->element_index(m_field_idx);
+}
+
+
+Item **
+Item_splocal_row_field::this_item_addr(THD *thd, Item **)
+{
+ DBUG_ASSERT(m_sp == thd->spcont->m_sp);
+ DBUG_ASSERT(fixed);
+ return get_variable(thd->spcont)->addr(m_field_idx);
+}
+
+
+void Item_splocal_row_field::print(String *str, enum_query_type)
+{
+ const LEX_CSTRING *prefix= m_rcontext_handler->get_name_prefix();
+ str->reserve(m_name.length + m_field_name.length + 8 + prefix->length);
+ str->append(prefix);
+ str->append(&m_name);
+ str->append('.');
+ str->append(&m_field_name);
+ str->append('@');
+ str->qs_append(m_var_idx);
+ str->append('[');
+ str->qs_append(m_field_idx);
+ str->append(']');
+}
+
+
+bool Item_splocal_row_field::set_value(THD *thd, sp_rcontext *ctx, Item **it)
+{
+ return get_rcontext(ctx)->set_variable_row_field(thd, m_var_idx, m_field_idx,
+ it);
+}
+
+
+bool Item_splocal_row_field_by_name::fix_fields(THD *thd, Item **it)
+{
+ DBUG_ASSERT(!fixed);
+ m_thd= thd;
+ if (get_rcontext(thd->spcont)->find_row_field_by_name_or_error(&m_field_idx,
+ m_var_idx,
+ m_field_name))
+ return true;
+ Item *item= get_variable(thd->spcont)->element_index(m_field_idx);
+ set_handler(item->type_handler());
+ return fix_fields_from_item(thd, it, item);
+}
+
+
+void Item_splocal_row_field_by_name::print(String *str, enum_query_type)
+{
+ const LEX_CSTRING *prefix= m_rcontext_handler->get_name_prefix();
+ // +16 should be enough for .NNN@[""]
+ if (str->reserve(m_name.length + 2 * m_field_name.length +
+ prefix->length + 16))
+ return;
+ str->qs_append(prefix);
+ str->qs_append(&m_name);
+ str->qs_append('.');
+ str->qs_append(&m_field_name);
+ str->qs_append('@');
+ str->qs_append(m_var_idx);
+ str->qs_append("[\"", 2);
+ str->qs_append(&m_field_name);
+ str->qs_append("\"]", 2);
+}
+
+
+bool Item_splocal_row_field_by_name::set_value(THD *thd, sp_rcontext *ctx, Item **it)
+{
+ DBUG_ASSERT(fixed); // Make sure m_field_idx is already set
+ return Item_splocal_row_field::set_value(thd, ctx, it);
}
@@ -1713,17 +2106,26 @@ bool Item_splocal::set_value(THD *thd, sp_rcontext *ctx, Item **it)
Item_case_expr methods
*****************************************************************************/
+LEX_CSTRING str_case_expr= { STRING_WITH_LEN("case_expr") };
+
Item_case_expr::Item_case_expr(THD *thd, uint case_expr_id):
- Item_sp_variable(thd, C_STRING_WITH_LEN("case_expr")),
+ Item_sp_variable(thd, &str_case_expr),
m_case_expr_id(case_expr_id)
{
}
+bool Item_case_expr::fix_fields(THD *thd, Item **ref)
+{
+ Item *item= thd->spcont->get_case_expr(m_case_expr_id);
+ return fix_fields_from_item(thd, ref, item);
+}
+
+
Item *
Item_case_expr::this_item()
{
- DBUG_ASSERT(m_sp == m_thd->spcont->sp);
+ DBUG_ASSERT(m_sp == m_thd->spcont->m_sp);
return m_thd->spcont->get_case_expr(m_case_expr_id);
}
@@ -1733,7 +2135,7 @@ Item_case_expr::this_item()
const Item *
Item_case_expr::this_item() const
{
- DBUG_ASSERT(m_sp == m_thd->spcont->sp);
+ DBUG_ASSERT(m_sp == m_thd->spcont->m_sp);
return m_thd->spcont->get_case_expr(m_case_expr_id);
}
@@ -1742,7 +2144,7 @@ Item_case_expr::this_item() const
Item **
Item_case_expr::this_item_addr(THD *thd, Item **)
{
- DBUG_ASSERT(m_sp == thd->spcont->sp);
+ DBUG_ASSERT(m_sp == thd->spcont->m_sp);
return thd->spcont->get_case_expr_addr(m_case_expr_id);
}
@@ -1796,6 +2198,13 @@ my_decimal *Item_name_const::val_decimal(my_decimal *decimal_value)
return val;
}
+bool Item_name_const::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+{
+ DBUG_ASSERT(fixed);
+ bool rc= value_item->get_date(ltime, fuzzydate);
+ null_value= value_item->null_value;
+ return rc;
+}
bool Item_name_const::is_null()
{
@@ -1891,6 +2300,7 @@ bool Item_name_const::fix_fields(THD *thd, Item **ref)
collation.set(value_item->collation.collation, DERIVATION_IMPLICIT);
max_length= value_item->max_length;
decimals= value_item->decimals;
+ unsigned_flag= value_item->unsigned_flag;
fixed= 1;
return FALSE;
}
@@ -1916,7 +2326,7 @@ 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 char *field_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)
@@ -2032,14 +2442,14 @@ void Item::split_sum_func2(THD *thd, Ref_ptr_array ref_pointer_array,
already a reference.
*/
Item *real_itm= real_item();
-
ref_pointer_array[el]= real_itm;
if (type() == WINDOW_FUNC_ITEM)
{
if (!(item_ref= (new (thd->mem_root)
Item_direct_ref(thd,
- &thd->lex->current_select->context,
- &ref_pointer_array[el], 0, name))))
+ &thd->lex->current_select->context,
+ &ref_pointer_array[el], 0,
+ &name))))
return; // fatal_error is set
}
else
@@ -2047,7 +2457,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], 0,
+ &name))))
return; // fatal_error is set
}
if (type() == SUM_FUNC_ITEM)
@@ -2252,9 +2663,9 @@ void my_coll_agg_error(Item** args, uint count, const char *fname,
}
-bool Item_func_or_sum::agg_item_collations(DTCollation &c, const char *fname,
- Item **av, uint count,
- uint flags, int item_sep)
+bool Type_std_attributes::agg_item_collations(DTCollation &c, const char *fname,
+ Item **av, uint count,
+ uint flags, int item_sep)
{
uint i;
Item **arg;
@@ -2299,10 +2710,10 @@ bool Item_func_or_sum::agg_item_collations(DTCollation &c, const char *fname,
}
-bool Item_func_or_sum::agg_item_set_converter(const DTCollation &coll,
- const char *fname,
- Item **args, uint nargs,
- uint flags, int item_sep)
+bool Type_std_attributes::agg_item_set_converter(const DTCollation &coll,
+ const char *fname,
+ Item **args, uint nargs,
+ uint flags, int item_sep)
{
THD *thd= current_thd;
if (thd->lex->is_ps_or_view_context_analysis())
@@ -2363,38 +2774,39 @@ bool Item_func_or_sum::agg_item_set_converter(const DTCollation &coll,
/**
@brief
Building clone for Item_func_or_sum
-
+
@param thd thread handle
- @param mem_root part of the memory for the clone
+ @param mem_root part of the memory for the clone
@details
- This method fisrt builds clones of the arguments. If it is successful with
+ This method first builds clones of the arguments. If it is successful with
buiding the clones then it constructs a copy of this Item_func_or_sum object
and attaches to it the built clones of the arguments.
-
- @retval
- clone of the item on success
- 0 on a failure
-*/
-Item* Item_func_or_sum::build_clone(THD *thd, MEM_ROOT *mem_root)
+ @return clone of the item
+ @retval 0 on a failure
+*/
+
+Item* Item_func_or_sum::build_clone(THD *thd)
{
Item *copy_tmp_args[2]= {0,0};
Item **copy_args= copy_tmp_args;
if (arg_count > 2)
{
- if (!(copy_args= (Item**) alloc_root(mem_root, sizeof(Item*) * arg_count)))
+ copy_args= static_cast<Item**>
+ (alloc_root(thd->mem_root, sizeof(Item*) * arg_count));
+ if (unlikely(!copy_args))
return 0;
}
for (uint i= 0; i < arg_count; i++)
{
- Item *arg_clone= args[i]->build_clone(thd, mem_root);
- if (!arg_clone)
+ Item *arg_clone= args[i]->build_clone(thd);
+ if (unlikely(!arg_clone))
return 0;
copy_args[i]= arg_clone;
}
- Item_func_or_sum *copy= (Item_func_or_sum *) get_copy(thd, mem_root);
- if (!copy)
+ Item_func_or_sum *copy= static_cast<Item_func_or_sum *>(get_copy(thd));
+ if (unlikely(!copy))
return 0;
if (arg_count > 2)
copy->args= copy_args;
@@ -2406,6 +2818,263 @@ Item* Item_func_or_sum::build_clone(THD *thd, MEM_ROOT *mem_root)
return copy;
}
+Item_sp::Item_sp(THD *thd, Name_resolution_context *context_arg,
+ sp_name *name_arg) :
+ context(context_arg), m_name(name_arg), m_sp(NULL), func_ctx(NULL),
+ sp_result_field(NULL)
+{
+ dummy_table= (TABLE*) thd->calloc(sizeof(TABLE) + sizeof(TABLE_SHARE) +
+ sizeof(Query_arena));
+ dummy_table->s= (TABLE_SHARE*) (dummy_table + 1);
+ /* TODO(cvicentiu) Move this sp_query_arena in the class as a direct member.
+ Currently it can not be done due to header include dependencies. */
+ sp_query_arena= (Query_arena *) (dummy_table->s + 1);
+ memset(&sp_mem_root, 0, sizeof(sp_mem_root));
+}
+
+Item_sp::Item_sp(THD *thd, Item_sp *item):
+ context(item->context), m_name(item->m_name),
+ m_sp(item->m_sp), func_ctx(NULL), sp_result_field(NULL)
+{
+ dummy_table= (TABLE*) thd->calloc(sizeof(TABLE)+ sizeof(TABLE_SHARE) +
+ sizeof(Query_arena));
+ dummy_table->s= (TABLE_SHARE*) (dummy_table+1);
+ sp_query_arena= (Query_arena *) (dummy_table->s + 1);
+ memset(&sp_mem_root, 0, sizeof(sp_mem_root));
+}
+
+const char *
+Item_sp::func_name(THD *thd) const
+{
+ /* Calculate length to avoid reallocation of string for sure */
+ size_t len= (((m_name->m_explicit_name ? m_name->m_db.length : 0) +
+ m_name->m_name.length)*2 + //characters*quoting
+ 2 + // ` and `
+ (m_name->m_explicit_name ?
+ 3 : 0) + // '`', '`' and '.' for the db
+ 1 + // end of string
+ ALIGN_SIZE(1)); // to avoid String reallocation
+ String qname((char *)alloc_root(thd->mem_root, len), len,
+ system_charset_info);
+
+ qname.length(0);
+ if (m_name->m_explicit_name)
+ {
+ append_identifier(thd, &qname, &m_name->m_db);
+ qname.append('.');
+ }
+ append_identifier(thd, &qname, &m_name->m_name);
+ return qname.c_ptr_safe();
+}
+
+void
+Item_sp::cleanup()
+{
+ delete sp_result_field;
+ sp_result_field= NULL;
+ m_sp= NULL;
+ delete func_ctx;
+ func_ctx= NULL;
+ free_root(&sp_mem_root, MYF(0));
+ dummy_table->alias.free();
+}
+
+/**
+ @brief Checks if requested access to function can be granted to user.
+ If function isn't found yet, it searches function first.
+ If function can't be found or user don't have requested access
+ error is raised.
+
+ @param thd thread handler
+
+ @return Indication if the access was granted or not.
+ @retval FALSE Access is granted.
+ @retval TRUE Requested access can't be granted or function doesn't exists.
+
+*/
+bool
+Item_sp::sp_check_access(THD *thd)
+{
+ DBUG_ENTER("Item_sp::sp_check_access");
+ DBUG_ASSERT(m_sp);
+ DBUG_RETURN(m_sp->check_execute_access(thd));
+}
+
+/**
+ @brief Execute function & store value in field.
+
+ @return Function returns error status.
+ @retval FALSE on success.
+ @retval TRUE if an error occurred.
+*/
+bool Item_sp::execute(THD *thd, bool *null_value, Item **args, uint arg_count)
+{
+ if (unlikely(execute_impl(thd, args, arg_count)))
+ {
+ *null_value= 1;
+ context->process_error(thd);
+ if (thd->killed)
+ thd->send_kill_message();
+ return true;
+ }
+
+ /* Check that the field (the value) is not NULL. */
+
+ *null_value= sp_result_field->is_null();
+ return (*null_value);
+}
+
+/**
+ @brief Execute function and store the return value in the field.
+
+ @note This function was intended to be the concrete implementation of
+ the interface function execute. This was never realized.
+
+ @return The error state.
+ @retval FALSE on success
+ @retval TRUE if an error occurred.
+*/
+bool
+Item_sp::execute_impl(THD *thd, Item **args, uint arg_count)
+{
+ Sub_statement_state statement_state;
+ Security_context *save_security_ctx= thd->security_ctx;
+ enum enum_sp_data_access access=
+ (m_sp->daccess() == SP_DEFAULT_ACCESS) ?
+ SP_DEFAULT_ACCESS_MAPPING : m_sp->daccess();
+
+ DBUG_ENTER("Item_sp::execute_impl");
+
+ if (context->security_ctx)
+ {
+ /* Set view definer security context */
+ thd->security_ctx= context->security_ctx;
+ }
+
+ if (unlikely(sp_check_access(thd)))
+ {
+ thd->security_ctx= save_security_ctx;
+ DBUG_RETURN(TRUE);
+ }
+
+ /*
+ Throw an error if a non-deterministic function is called while
+ statement-based replication (SBR) is active.
+ */
+
+ if (unlikely(!m_sp->detistic() && !trust_function_creators &&
+ (access == SP_CONTAINS_SQL || access == SP_MODIFIES_SQL_DATA) &&
+ (mysql_bin_log.is_open() &&
+ thd->variables.binlog_format == BINLOG_FORMAT_STMT)))
+ {
+ my_error(ER_BINLOG_UNSAFE_ROUTINE, MYF(0));
+ thd->security_ctx= save_security_ctx;
+ DBUG_RETURN(TRUE);
+ }
+
+ /*
+ Disable the binlogging if this is not a SELECT statement. If this is a
+ SELECT, leave binlogging on, so execute_function() code writes the
+ function call into binlog.
+ */
+ thd->reset_sub_statement_state(&statement_state, SUB_STMT_FUNCTION);
+
+ /*
+ If this function is an aggregate function, we want to initialise the
+ mem_root only once per group. For a regular stored function, we will
+ initialise once for each call to execute_function.
+ */
+ m_sp->agg_type();
+ DBUG_ASSERT(m_sp->agg_type() == GROUP_AGGREGATE ||
+ (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));
+ *sp_query_arena= Query_arena(&sp_mem_root,
+ Query_arena::STMT_INITIALIZED_FOR_SP);
+ }
+
+ bool err_status= m_sp->execute_function(thd, args, arg_count,
+ sp_result_field, &func_ctx,
+ sp_query_arena);
+ /*
+ We free the function context when the function finished executing normally
+ (quit_func == TRUE) or the function has exited with an error.
+ */
+ if (err_status || func_ctx->quit_func)
+ {
+ /* Free Items allocated during function execution. */
+ delete func_ctx;
+ func_ctx= NULL;
+ sp_query_arena->free_items();
+ free_root(&sp_mem_root, MYF(0));
+ memset(&sp_mem_root, 0, sizeof(sp_mem_root));
+ }
+ thd->restore_sub_statement_state(&statement_state);
+
+ thd->security_ctx= save_security_ctx;
+ DBUG_RETURN(err_status);
+}
+
+
+/**
+ @brief Initialize the result field by creating a temporary dummy table
+ and assign it to a newly created field object. Meta data used to
+ create the field is fetched from the sp_head belonging to the stored
+ proceedure found in the stored procedure functon cache.
+
+ @note This function should be called from fix_fields to init the result
+ field. It is some what related to Item_field.
+
+ @see Item_field
+
+ @param thd A pointer to the session and thread context.
+
+ @return Function return error status.
+ @retval TRUE is returned on an error
+ @retval FALSE is returned on success.
+*/
+
+bool
+Item_sp::init_result_field(THD *thd, uint max_length, uint maybe_null,
+ bool *null_value, LEX_CSTRING *name)
+{
+ DBUG_ENTER("Item_sp::init_result_field");
+
+ DBUG_ASSERT(m_sp != NULL);
+ DBUG_ASSERT(sp_result_field == NULL);
+
+ /*
+ A Field needs to be attached to a Table.
+ Below we "create" a dummy table by initializing
+ the needed pointers.
+ */
+ dummy_table->alias.set("", 0, table_alias_charset);
+ dummy_table->in_use= thd;
+ dummy_table->copy_blobs= TRUE;
+ dummy_table->s->table_cache_key= empty_clex_str;
+ dummy_table->s->table_name= empty_clex_str;
+ dummy_table->maybe_null= maybe_null;
+
+ if (!(sp_result_field= m_sp->create_result_field(max_length, name,
+ dummy_table)))
+ DBUG_RETURN(TRUE);
+
+ if (sp_result_field->pack_length() > sizeof(result_buf))
+ {
+ void *tmp;
+ if (!(tmp= thd->alloc(sp_result_field->pack_length())))
+ DBUG_RETURN(TRUE);
+ sp_result_field->move_field((uchar*) tmp);
+ }
+ else
+ sp_result_field->move_field(result_buf);
+
+ sp_result_field->null_ptr= (uchar *) null_value;
+ sp_result_field->null_bit= 1;
+
+ DBUG_RETURN(FALSE);
+}
/**
@brief
@@ -2420,32 +3089,26 @@ Item* Item_func_or_sum::build_clone(THD *thd, MEM_ROOT *mem_root)
@retval
clone of the item
- 0 if an error occured
+ 0 if an error occurred
*/
-Item* Item_ref::build_clone(THD *thd, MEM_ROOT *mem_root)
+Item* Item_ref::build_clone(THD *thd)
{
- Item_ref *copy= (Item_ref *) get_copy(thd, mem_root);
- if (!copy)
+ Item_ref *copy= (Item_ref *) get_copy(thd);
+ if (unlikely(!copy) ||
+ unlikely(!(copy->ref= (Item**) alloc_root(thd->mem_root,
+ sizeof(Item*)))) ||
+ unlikely(!(*copy->ref= (* ref)->build_clone(thd))))
return 0;
- copy->ref=
- (Item**) alloc_root(mem_root, sizeof(Item*));
- if (!copy->ref)
- return 0;
- Item *item_clone= (* ref)->build_clone(thd, mem_root);
- if (!item_clone)
- return 0;
- *copy->ref= item_clone;
return copy;
}
-void Item_ident_for_show::make_field(THD *thd, Send_field *tmp_field)
+void Item_ident_for_show::make_send_field(THD *thd, Send_field *tmp_field)
{
tmp_field->table_name= tmp_field->org_table_name= table_name;
tmp_field->db_name= db_name;
tmp_field->col_name= tmp_field->org_col_name= field->field_name;
- tmp_field->charsetnr= field->charset()->number;
tmp_field->length=field->field_length;
tmp_field->type=field->type();
tmp_field->flags= field->table->maybe_null ?
@@ -2456,16 +3119,14 @@ void Item_ident_for_show::make_field(THD *thd, Send_field *tmp_field)
/**********************************************/
Item_field::Item_field(THD *thd, Field *f)
- :Item_ident(thd, 0, NullS, *f->table_name, f->field_name),
+ :Item_ident(thd, 0, NullS, *f->table_name, &f->field_name),
item_equal(0),
have_privileges(0), any_privileges(0)
{
set_field(f);
- /*
- field_name and table_name should not point to garbage
- if this item is to be reused
- */
- orig_table_name= orig_field_name= "";
+
+ orig_table_name= table_name;
+ orig_field_name= field_name;
with_field= 1;
}
@@ -2479,7 +3140,8 @@ 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.str, *f->table_name,
+ &f->field_name),
item_equal(0),
have_privileges(0), any_privileges(0)
{
@@ -2505,14 +3167,15 @@ Item_field::Item_field(THD *thd, Name_resolution_context *context_arg,
orig_db_name= thd->strdup(db_name);
if (table_name)
orig_table_name= thd->strdup(table_name);
- if (field_name)
- orig_field_name= thd->strdup(field_name);
+ if (field_name.str)
+ thd->make_lex_string(&orig_field_name, field_name.str,
+ field_name.length);
/*
We don't restore 'name' in cleanup because it's not changed
during execution. Still we need it to point to persistent
memory if this item is to be reused.
*/
- name= (char*) orig_field_name;
+ name= orig_field_name;
}
set_field(f);
with_field= 1;
@@ -2521,7 +3184,7 @@ 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 char *field_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)
@@ -2549,75 +3212,15 @@ Item_field::Item_field(THD *thd, Item_field *item)
}
-/**
- Calculate the max column length not taking into account the
- limitations over integer types.
-
- When storing data into fields the server currently just ignores the
- limits specified on integer types, e.g. 1234 can safely be stored in
- an int(2) and will not cause an error.
- Thus when creating temporary tables and doing transformations
- we must adjust the maximum field length to reflect this fact.
- We take the un-restricted maximum length and adjust it similarly to
- how the declared length is adjusted wrt unsignedness etc.
- TODO: this all needs to go when we disable storing 1234 in int(2).
-
- @param field_par Original field the use to calculate the lengths
- @param max_length Item's calculated explicit max length
- @return The adjusted max length
-*/
-
-inline static uint32
-adjust_max_effective_column_length(Field *field_par, uint32 max_length)
-{
- uint32 new_max_length= field_par->max_display_length();
- uint32 sign_length= (field_par->flags & UNSIGNED_FLAG) ? 0 : 1;
-
- switch (field_par->type())
- {
- case MYSQL_TYPE_INT24:
- /*
- Compensate for MAX_MEDIUMINT_WIDTH being 1 too long (8)
- compared to the actual number of digits that can fit into
- the column.
- */
- new_max_length+= 1;
- /* fall through */
- case MYSQL_TYPE_LONG:
- case MYSQL_TYPE_TINY:
- case MYSQL_TYPE_SHORT:
-
- /* Take out the sign and add a conditional sign */
- new_max_length= new_max_length - 1 + sign_length;
- break;
-
- /* BINGINT is always 20 no matter the sign */
- case MYSQL_TYPE_LONGLONG:
- /* make gcc happy */
- default:
- break;
- }
-
- /* Adjust only if the actual precision based one is bigger than specified */
- return new_max_length > max_length ? new_max_length : max_length;
-}
-
-
void Item_field::set_field(Field *field_par)
{
field=result_field=field_par; // for easy coding with fields
maybe_null=field->maybe_null();
- decimals= field->decimals();
+ Type_std_attributes::set(field_par->type_std_attributes());
table_name= *field_par->table_name;
field_name= field_par->field_name;
db_name= field_par->table->s->db.str;
alias_name_used= field_par->table->alias_name_used;
- unsigned_flag= MY_TEST(field_par->flags & UNSIGNED_FLAG);
- collation.set(field_par->charset(), field_par->derivation(),
- field_par->repertoire());
- fix_char_length(field_par->char_length());
-
- max_length= adjust_max_effective_column_length(field_par, max_length);
fixed= 1;
if (field->table->s->tmp_table == SYSTEM_TMP_TABLE)
@@ -2635,13 +3238,13 @@ void Item_field::reset_field(Field *f)
{
set_field(f);
/* 'name' is pointing at field->field_name of old field */
- name= (char*) f->field_name;
+ name= f->field_name;
}
void Item_field::load_data_print_for_log_event(THD *thd, String *to) const
{
- append_identifier(thd, to, name, (uint) strlen(name));
+ append_identifier(thd, to, name.str, name.length);
}
@@ -2699,15 +3302,15 @@ bool Item_field::switch_to_nullable_fields_processor(void *arg)
const char *Item_ident::full_name() const
{
char *tmp;
- if (!table_name || !field_name)
- return field_name ? field_name : name ? name : "tmp_field";
+ if (!table_name || !field_name.str)
+ return field_name.str ? field_name.str : name.str ? name.str : "tmp_field";
if (db_name && db_name[0])
{
THD *thd= current_thd;
tmp=(char*) thd->alloc((uint) strlen(db_name)+(uint) strlen(table_name)+
- (uint) strlen(field_name)+3);
- strxmov(tmp,db_name,".",table_name,".",field_name,NullS);
+ (uint) field_name.length+3);
+ strxmov(tmp,db_name,".",table_name,".",field_name.str,NullS);
}
else
{
@@ -2715,11 +3318,11 @@ const char *Item_ident::full_name() const
{
THD *thd= current_thd;
tmp= (char*) thd->alloc((uint) strlen(table_name) +
- (uint) strlen(field_name) + 2);
- strxmov(tmp, table_name, ".", field_name, NullS);
+ field_name.length + 2);
+ strxmov(tmp, table_name, ".", field_name.str, NullS);
}
else
- tmp= (char*) field_name;
+ return field_name.str;
}
return tmp;
}
@@ -2733,7 +3336,7 @@ void Item_ident::print(String *str, enum_query_type query_type)
bool use_db_name= use_table_name && db_name && db_name[0] && !alias_name_used;
if (use_db_name && (query_type & QT_ITEM_IDENT_SKIP_DB_NAMES))
- use_db_name= !thd->db || strcmp(thd->db, db_name);
+ use_db_name= !thd->db.str || strcmp(thd->db.str, db_name);
if (use_db_name)
use_db_name= !(cached_table && cached_table->belong_to_view &&
@@ -2756,7 +3359,7 @@ void Item_ident::print(String *str, enum_query_type query_type)
use_db_name= use_table_name= false;
}
- if (!field_name || !field_name[0])
+ if (!field_name.str || !field_name.str[0])
{
append_identifier(thd, str, STRING_WITH_LEN("tmp_field"));
return;
@@ -2790,7 +3393,7 @@ void Item_ident::print(String *str, enum_query_type query_type)
append_identifier(thd, str, t_name, (uint) strlen(t_name));
str->append('.');
}
- append_identifier(thd, str, field_name, (uint) strlen(field_name));
+ append_identifier(thd, str, &field_name);
}
/* ARGSUSED */
@@ -2922,8 +3525,8 @@ bool Item_field::eq(const Item *item, bool binary_cmp) const
(In cases where we would choose wrong we would have to generate a
ER_NON_UNIQ_ERROR).
*/
- return (!my_strcasecmp(system_charset_info, item_field->name,
- field_name) &&
+ 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) &&
@@ -2945,6 +3548,11 @@ table_map Item_field::all_used_tables() const
return (get_depended_from() ? OUTER_REF_TABLE_BIT : field->table->map);
}
+
+/*
+ @Note thd->fatal_error can be set in case of OOM
+*/
+
void Item_field::fix_after_pullout(st_select_lex *new_parent, Item **ref,
bool merge)
{
@@ -3004,6 +3612,8 @@ void Item_field::fix_after_pullout(st_select_lex *new_parent, Item **ref,
}
Name_resolution_context *ctx= new Name_resolution_context();
+ if (!ctx)
+ return; // Fatal error set
if (context->select_lex == new_parent)
{
/*
@@ -3057,14 +3667,19 @@ longlong Item_field::val_int_endpoint(bool left_endp, bool *incl_endp)
This is always 'signed'. Unsigned values are created with Item_uint()
*/
-Item_int::Item_int(THD *thd, const char *str_arg, uint length):
+Item_int::Item_int(THD *thd, const char *str_arg, size_t length):
Item_num(thd)
{
char *end_ptr= (char*) str_arg + length;
int error;
value= my_strtoll10(str_arg, &end_ptr, &error);
max_length= (uint) (end_ptr - str_arg);
- name= (char*) str_arg;
+ name.str= str_arg;
+ /*
+ We can't trust max_length as in show_routine_code we are using "Pos" as
+ the field name.
+ */
+ name.length= !str_arg[max_length] ? max_length : strlen(str_arg);
fixed= 1;
}
@@ -3094,12 +3709,12 @@ void Item_int::print(String *str, enum_query_type query_type)
Item *Item_bool::neg_transformer(THD *thd)
{
value= !value;
- name= 0;
+ name= null_clex_str;
return this;
}
-Item_uint::Item_uint(THD *thd, const char *str_arg, uint length):
+Item_uint::Item_uint(THD *thd, const char *str_arg, size_t length):
Item_int(thd, str_arg, length)
{
unsigned_flag= 1;
@@ -3130,12 +3745,13 @@ void Item_uint::print(String *str, enum_query_type query_type)
}
-Item_decimal::Item_decimal(THD *thd, const char *str_arg, uint length,
+Item_decimal::Item_decimal(THD *thd, const char *str_arg, size_t length,
CHARSET_INFO *charset):
Item_num(thd)
{
str2my_decimal(E_DEC_FATAL_ERROR, str_arg, length, charset, &decimal_value);
- name= (char*) str_arg;
+ name.str= str_arg;
+ name.length= safe_strlen(str_arg);
decimals= (uint8) decimal_value.frac;
fixed= 1;
max_length= my_decimal_precision_to_length_no_truncation(decimal_value.intg +
@@ -3175,7 +3791,8 @@ Item_decimal::Item_decimal(THD *thd, const char *str, const my_decimal *val_arg,
Item_num(thd)
{
my_decimal2decimal(val_arg, &decimal_value);
- name= (char*) str;
+ name.str= str;
+ name.length= safe_strlen(str);
decimals= (uint8) decimal_par;
max_length= length;
fixed= 1;
@@ -3267,7 +3884,7 @@ void Item_decimal::set_decimal_value(my_decimal *value_par)
Item *Item_decimal::clone_item(THD *thd)
{
- return new (thd->mem_root) Item_decimal(thd, name, &decimal_value, decimals,
+ return new (thd->mem_root) Item_decimal(thd, name.str, &decimal_value, decimals,
max_length);
}
@@ -3292,7 +3909,7 @@ my_decimal *Item_float::val_decimal(my_decimal *decimal_value)
Item *Item_float::clone_item(THD *thd)
{
- return new (thd->mem_root) Item_float(thd, name, value, decimals,
+ return new (thd->mem_root) Item_float(thd, name.str, value, decimals,
max_length);
}
@@ -3426,34 +4043,47 @@ Item *Item_null::safe_charset_converter(THD *thd, CHARSET_INFO *tocs)
Item *Item_null::clone_item(THD *thd)
{
- return new (thd->mem_root) Item_null(thd, name);
+ return new (thd->mem_root) Item_null(thd, name.str);
}
-/*********************** Item_param related ******************************/
-
-/**
- Default function of Item_param::set_param_func, so in case
- of malformed packet the server won't SIGSEGV.
-*/
-static void
-default_set_param_func(Item_param *param,
- uchar **pos __attribute__((unused)),
- ulong len __attribute__((unused)))
+Item_basic_constant *
+Item_null::make_string_literal_concat(THD *thd, const LEX_CSTRING *str)
{
- param->set_null();
+ DBUG_ASSERT(thd->variables.sql_mode & MODE_EMPTY_STRING_IS_NULL);
+ if (str->length)
+ {
+ CHARSET_INFO *cs= thd->variables.collation_connection;
+ uint 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);
+ }
+ return this;
}
-Item_param::Item_param(THD *thd, uint pos_in_query_arg):
+/*********************** Item_param related ******************************/
+
+Item_param::Item_param(THD *thd, const LEX_CSTRING *name_arg,
+ uint pos_in_query_arg, uint len_in_query_arg):
Item_basic_value(thd),
- Rewritable_query_parameter(pos_in_query_arg, 1),
- Type_handler_hybrid_field_type(MYSQL_TYPE_VARCHAR),
+ Rewritable_query_parameter(pos_in_query_arg, len_in_query_arg),
+ /*
+ Set handler to type_handler_null. Its data type test methods such as:
+ - is_scalar_type()
+ - can_return_int()
+ - can_return_real(),
+ - is_general_purpose_string_type()
+ all return "true". This is needed to avoid any "illegal parameter type"
+ errors in Item::check_type_xxx() at PS prepare time.
+ */
+ Type_handler_hybrid_field_type(&type_handler_null),
state(NO_VALUE),
/* Don't pretend to be a literal unless value for this item is set. */
item_type(PARAM_ITEM),
+ m_empty_string_is_null(false),
indicator(STMT_INDICATOR_NONE),
- set_param_func(default_set_param_func),
m_out_param_info(NULL),
/*
Set m_is_settable_routine_parameter to "true" by default.
@@ -3465,8 +4095,8 @@ Item_param::Item_param(THD *thd, uint pos_in_query_arg):
m_is_settable_routine_parameter(true),
m_clones(thd->mem_root)
{
- name= (char*) "?";
- /*
+ name= *name_arg;
+ /*
Since we can't say whenever this item can be NULL or cannot be NULL
before mysql_stmt_execute(), so we assuming that it can be NULL until
value is set.
@@ -3480,7 +4110,7 @@ Item_param::Item_param(THD *thd, uint pos_in_query_arg):
bool Item_param::add_as_clone(THD *thd)
{
LEX *lex= thd->lex;
- uint master_pos= pos_in_query + lex->clone_spec_offset;
+ my_ptrdiff_t master_pos= pos_in_query + lex->clone_spec_offset;
List_iterator_fast<Item_param> it(lex->param_list);
Item_param *master_param;
while ((master_param = it++))
@@ -3505,22 +4135,26 @@ void Item_param::sync_clones()
/* Scalar-type members: */
c->maybe_null= maybe_null;
c->null_value= null_value;
- c->max_length= max_length;
- c->decimals= decimals;
+ c->Type_std_attributes::operator=(*this);
+ c->Type_handler_hybrid_field_type::operator=(*this);
+ c->Type_geometry_attributes::operator=(*this);
+
c->state= state;
c->item_type= item_type;
- c->set_param_func= set_param_func;
- c->value= value;
- c->unsigned_flag= unsigned_flag;
+ c->m_empty_string_is_null= m_empty_string_is_null;
+
+ c->value.PValue_simple::operator=(value);
+ c->value.Type_handler_hybrid_field_type::operator=(value);
+ type_handler()->Item_param_setup_conversion(current_thd, c);
+
/* Class-type members: */
- c->decimal_value= decimal_value;
+ c->value.m_decimal= value.m_decimal;
/*
Note that String's assignment op properly sets m_is_alloced to 'false',
which is correct here: c->str_value doesn't own anything.
*/
- c->str_value= str_value;
- c->str_value_ptr= str_value_ptr;
- c->collation= collation;
+ c->value.m_string= value.m_string;
+ c->value.m_string_ptr= value.m_string_ptr;
}
}
@@ -3548,8 +4182,9 @@ void Item_param::set_null()
void Item_param::set_int(longlong i, uint32 max_length_arg)
{
DBUG_ENTER("Item_param::set_int");
+ DBUG_ASSERT(value.type_handler()->cmp_type() == INT_RESULT);
value.integer= (longlong) i;
- state= INT_VALUE;
+ state= SHORT_DATA_VALUE;
collation.set_numeric();
max_length= max_length_arg;
decimals= 0;
@@ -3562,8 +4197,9 @@ void Item_param::set_int(longlong i, uint32 max_length_arg)
void Item_param::set_double(double d)
{
DBUG_ENTER("Item_param::set_double");
+ DBUG_ASSERT(value.type_handler()->cmp_type() == REAL_RESULT);
value.real= d;
- state= REAL_VALUE;
+ state= SHORT_DATA_VALUE;
collation.set_numeric();
max_length= DBL_DIG + 8;
decimals= NOT_FIXED_DEC;
@@ -3590,14 +4226,15 @@ void Item_param::set_decimal(const char *str, ulong length)
{
char *end;
DBUG_ENTER("Item_param::set_decimal");
+ DBUG_ASSERT(value.type_handler()->cmp_type() == DECIMAL_RESULT);
end= (char*) str+length;
- str2my_decimal(E_DEC_FATAL_ERROR, str, &decimal_value, &end);
- state= DECIMAL_VALUE;
- decimals= decimal_value.frac;
+ str2my_decimal(E_DEC_FATAL_ERROR, str, &value.m_decimal, &end);
+ state= SHORT_DATA_VALUE;
+ decimals= value.m_decimal.frac;
collation.set_numeric();
max_length=
- my_decimal_precision_to_length_no_truncation(decimal_value.precision(),
+ my_decimal_precision_to_length_no_truncation(value.m_decimal.precision(),
decimals, unsigned_flag);
maybe_null= 0;
null_value= 0;
@@ -3607,14 +4244,15 @@ void Item_param::set_decimal(const char *str, ulong length)
void Item_param::set_decimal(const my_decimal *dv, bool unsigned_arg)
{
- state= DECIMAL_VALUE;
+ DBUG_ASSERT(value.type_handler()->cmp_type() == DECIMAL_RESULT);
+ state= SHORT_DATA_VALUE;
- my_decimal2decimal(dv, &decimal_value);
+ my_decimal2decimal(dv, &value.m_decimal);
- decimals= (uint8) decimal_value.frac;
+ decimals= (uint8) value.m_decimal.frac;
collation.set_numeric();
unsigned_flag= unsigned_arg;
- max_length= my_decimal_precision_to_length(decimal_value.intg + decimals,
+ max_length= my_decimal_precision_to_length(value.m_decimal.intg + decimals,
decimals, unsigned_flag);
maybe_null= 0;
null_value= 0;
@@ -3624,7 +4262,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= TIME_VALUE;
+ state= SHORT_DATA_VALUE;
collation.set_numeric();
max_length= max_length_arg;
decimals= decimals_arg;
@@ -3637,6 +4275,7 @@ void Item_param::fix_temporal(uint32 max_length_arg, uint decimals_arg)
void Item_param::set_time(const MYSQL_TIME *tm,
uint32 max_length_arg, uint decimals_arg)
{
+ DBUG_ASSERT(value.type_handler()->cmp_type() == TIME_RESULT);
value.time= *tm;
maybe_null= 0;
null_value= 0;
@@ -3661,6 +4300,7 @@ void Item_param::set_time(MYSQL_TIME *tm, timestamp_type time_type,
uint32 max_length_arg)
{
DBUG_ENTER("Item_param::set_time");
+ DBUG_ASSERT(value.type_handler()->cmp_type() == TIME_RESULT);
value.time= *tm;
value.time.time_type= time_type;
@@ -3680,18 +4320,34 @@ void Item_param::set_time(MYSQL_TIME *tm, timestamp_type time_type,
}
-bool Item_param::set_str(const char *str, ulong length)
+bool Item_param::set_str(const char *str, ulong length,
+ CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
{
DBUG_ENTER("Item_param::set_str");
+ DBUG_ASSERT(value.type_handler()->cmp_type() == STRING_RESULT);
/*
Assign string with no conversion: data is converted only after it's
been written to the binary log.
*/
uint dummy_errors;
- if (str_value.copy(str, length, &my_charset_bin, &my_charset_bin,
- &dummy_errors))
+ if (unlikely(value.m_string.copy(str, length, fromcs, tocs, &dummy_errors)))
DBUG_RETURN(TRUE);
- state= STRING_VALUE;
+ /*
+ Set str_value_ptr to make sure it's in sync with str_value.
+ This is needed in case if we're called from Item_param::set_value(),
+ from the code responsible for setting OUT parameters in
+ sp_head::execute_procedure(). This makes sure that
+ Protocol_binary::send_out_parameters() later gets a valid value
+ from Item_param::val_str().
+ Note, for IN parameters, Item_param::convert_str_value() will be called
+ later, which will convert the value from the client character set to the
+ connection character set, and will reset both str_value and str_value_ptr.
+ */
+ value.m_string_ptr.set(value.m_string.ptr(),
+ value.m_string.length(),
+ value.m_string.charset());
+ state= SHORT_DATA_VALUE;
+ collation.set(tocs, DERIVATION_COERCIBLE);
max_length= length;
maybe_null= 0;
null_value= 0;
@@ -3705,6 +4361,7 @@ bool Item_param::set_str(const char *str, ulong length)
bool Item_param::set_longdata(const char *str, ulong length)
{
DBUG_ENTER("Item_param::set_longdata");
+ DBUG_ASSERT(value.type_handler()->cmp_type() == STRING_RESULT);
/*
If client character set is multibyte, end of long data packet
@@ -3715,7 +4372,7 @@ 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 (str_value.length() + length > max_long_data_size)
+ if (value.m_string.length() + length > max_long_data_size)
{
my_message(ER_UNKNOWN_ERROR,
"Parameter of prepared statement which is set through "
@@ -3725,7 +4382,7 @@ bool Item_param::set_longdata(const char *str, ulong length)
DBUG_RETURN(true);
}
- if (str_value.append(str, length, &my_charset_bin))
+ if (value.m_string.append(str, length, &my_charset_bin))
DBUG_RETURN(TRUE);
state= LONG_DATA_VALUE;
maybe_null= 0;
@@ -3789,53 +4446,16 @@ bool Item_param::set_from_item(THD *thd, Item *item)
else
{
unsigned_flag= item->unsigned_flag;
- set_int(val, MY_INT64_NUM_DECIMAL_DIGITS);
- set_handler_by_result_type(item->result_type());
- DBUG_RETURN(!unsigned_flag && value.integer < 0 ? 1 : 0);
+ set_handler(item->type_handler());
+ DBUG_RETURN(set_limit_clause_param(val));
}
}
struct st_value tmp;
- if (!item->store(&tmp, 0))
+ if (!item->save_in_value(&tmp))
{
- unsigned_flag= item->unsigned_flag;
- switch (item->cmp_type()) {
- case REAL_RESULT:
- set_double(tmp.value.m_double);
- set_handler_by_field_type(MYSQL_TYPE_DOUBLE);
- break;
- case INT_RESULT:
- set_int(tmp.value.m_longlong, MY_INT64_NUM_DECIMAL_DIGITS);
- set_handler_by_field_type(MYSQL_TYPE_LONGLONG);
- break;
- case STRING_RESULT:
- {
- value.cs_info.set(thd, item->collation.collation);
- /*
- Exact value of max_length is not known unless data is converted to
- charset of connection, so we have to set it later.
- */
- set_handler_by_field_type(MYSQL_TYPE_VARCHAR);
-
- if (set_str(tmp.m_string.ptr(), tmp.m_string.length()))
- DBUG_RETURN(1);
- break;
- }
- case DECIMAL_RESULT:
- {
- set_decimal(&tmp.m_decimal, unsigned_flag);
- set_handler_by_field_type(MYSQL_TYPE_NEWDECIMAL);
- break;
- }
- case TIME_RESULT:
- {
- set_time(&tmp.value.m_time, item->max_length, item->decimals);
- set_handler(item->type_handler());
- break;
- }
- case ROW_RESULT:
- DBUG_ASSERT(0);
- set_null();
- }
+ const Type_handler *h= item->type_handler();
+ set_handler(h);
+ DBUG_RETURN(set_value(thd, item, &tmp, h));
}
else
set_null();
@@ -3855,16 +4475,16 @@ void Item_param::reset()
{
DBUG_ENTER("Item_param::reset");
/* Shrink string buffer if it's bigger than max possible CHAR column */
- if (str_value.alloced_length() > MAX_CHAR_WIDTH)
- str_value.free();
+ if (value.m_string.alloced_length() > MAX_CHAR_WIDTH)
+ value.m_string.free();
else
- str_value.length(0);
- str_value_ptr.length(0);
+ value.m_string.length(0);
+ value.m_string_ptr.length(0);
/*
We must prevent all charset conversions until data has been written
to the binary log.
*/
- str_value.set_charset(&my_charset_bin);
+ value.m_string.set_charset(&my_charset_bin);
collation.set(&my_charset_bin, DERIVATION_COERCIBLE);
state= NO_VALUE;
maybe_null= 1;
@@ -3893,19 +4513,9 @@ int Item_param::save_in_field(Field *field, bool no_conversions)
Garbage (e.g. in case of a memory overrun) is handled after the switch.
*/
switch (state) {
- case INT_VALUE:
- return field->store(value.integer, unsigned_flag);
- case REAL_VALUE:
- return field->store(value.real);
- case DECIMAL_VALUE:
- return field->store_decimal(&decimal_value);
- case TIME_VALUE:
- field->store_time_dec(&value.time, decimals);
- return 0;
- case STRING_VALUE:
+ case SHORT_DATA_VALUE:
case LONG_DATA_VALUE:
- return field->store(str_value.ptr(), str_value.length(),
- str_value.charset());
+ return value.type_handler()->Item_save_in_field(this, field, no_conversions);
case NULL_VALUE:
return set_field_to_null_with_conversions(field, no_conversions);
case DEFAULT_VALUE:
@@ -3925,6 +4535,45 @@ int Item_param::save_in_field(Field *field, bool no_conversions)
}
+bool Item_param::is_evaluable_expression() const
+{
+ switch (state) {
+ case SHORT_DATA_VALUE:
+ case LONG_DATA_VALUE:
+ case NULL_VALUE:
+ return true;
+ case NO_VALUE:
+ return true; // Not assigned yet, so we don't know
+ case IGNORE_VALUE:
+ case DEFAULT_VALUE:
+ break;
+ }
+ return false;
+}
+
+
+bool Item_param::can_return_value() const
+{
+ // There's no "default". See comments in Item_param::save_in_field().
+ switch (state) {
+ case SHORT_DATA_VALUE:
+ case LONG_DATA_VALUE:
+ return true;
+ case IGNORE_VALUE:
+ case DEFAULT_VALUE:
+ invalid_default_param();
+ // fall through
+ case NULL_VALUE:
+ return false;
+ case NO_VALUE:
+ DBUG_ASSERT(0); // Should not be possible
+ return false;
+ }
+ DBUG_ASSERT(0); // Garbage
+ return false;
+}
+
+
void Item_param::invalid_default_param() const
{
my_message(ER_INVALID_DEFAULT_PARAM,
@@ -3934,166 +4583,133 @@ void Item_param::invalid_default_param() const
bool Item_param::get_date(MYSQL_TIME *res, ulonglong fuzzydate)
{
- if (state == TIME_VALUE)
+ /*
+ LIMIT clause parameter should not call get_date()
+ For non-LIMIT parameters, handlers must be the same.
+ */
+ DBUG_ASSERT(type_handler()->result_type() ==
+ value.type_handler()->result_type());
+ if (state == SHORT_DATA_VALUE &&
+ value.type_handler()->cmp_type() == TIME_RESULT)
{
*res= value.time;
return 0;
}
- return Item::get_date(res, fuzzydate);
+ return type_handler()->Item_get_date(this, res, fuzzydate);
}
-double Item_param::val_real()
+double Item_param::PValue::val_real() const
{
- // There's no "default". See comments in Item_param::save_in_field().
- switch (state) {
- case REAL_VALUE:
- return value.real;
- case INT_VALUE:
- return (double) value.integer;
- case DECIMAL_VALUE:
+ switch (type_handler()->cmp_type()) {
+ case REAL_RESULT:
+ return real;
+ case INT_RESULT:
+ return (double) integer;
+ case DECIMAL_RESULT:
{
double result;
- my_decimal2double(E_DEC_FATAL_ERROR, &decimal_value, &result);
+ my_decimal2double(E_DEC_FATAL_ERROR, &m_decimal, &result);
return result;
}
- case STRING_VALUE:
- case LONG_DATA_VALUE:
- {
- return double_from_string_with_check(&str_value);
- }
- case TIME_VALUE:
+ case STRING_RESULT:
+ return double_from_string_with_check(&m_string);
+ case TIME_RESULT:
/*
This works for example when user says SELECT ?+0.0 and supplies
time value for the placeholder.
*/
- return TIME_to_double(&value.time);
- case IGNORE_VALUE:
- case DEFAULT_VALUE:
- invalid_default_param();
- // fall through
- case NULL_VALUE:
- return 0.0;
- case NO_VALUE:
- DBUG_ASSERT(0); // Should not be possible
- return 0.0;
+ return TIME_to_double(&time);
+ case ROW_RESULT:
+ DBUG_ASSERT(0);
+ break;
}
- DBUG_ASSERT(0); // Garbage
return 0.0;
-}
+}
-longlong Item_param::val_int()
-{
- // There's no "default". See comments in Item_param::save_in_field().
- switch (state) {
- case REAL_VALUE:
- return Converter_double_to_longlong(value.real, unsigned_flag).result();
- case INT_VALUE:
- return value.integer;
- case DECIMAL_VALUE:
+longlong Item_param::PValue::val_int(const Type_std_attributes *attr) const
+{
+ switch (type_handler()->cmp_type()) {
+ case REAL_RESULT:
+ return Converter_double_to_longlong(real, attr->unsigned_flag).result();
+ case INT_RESULT:
+ return integer;
+ case DECIMAL_RESULT:
{
longlong i;
- my_decimal2int(E_DEC_FATAL_ERROR, &decimal_value, unsigned_flag, &i);
+ my_decimal2int(E_DEC_FATAL_ERROR, &m_decimal, attr->unsigned_flag, &i);
return i;
}
- case STRING_VALUE:
- case LONG_DATA_VALUE:
- {
- return longlong_from_string_with_check(&str_value);
- }
- case TIME_VALUE:
- return (longlong) TIME_to_ulonglong(&value.time);
- case IGNORE_VALUE:
- case DEFAULT_VALUE:
- invalid_default_param();
- // fall through
- case NULL_VALUE:
- return 0;
- case NO_VALUE:
- DBUG_ASSERT(0); // Should not be possible
- return 0;
+ case STRING_RESULT:
+ return longlong_from_string_with_check(&m_string);
+ case TIME_RESULT:
+ return (longlong) TIME_to_ulonglong(&time);
+ case ROW_RESULT:
+ DBUG_ASSERT(0);
+ break;
}
- DBUG_ASSERT(0); // Garbage
return 0;
}
-my_decimal *Item_param::val_decimal(my_decimal *dec)
+my_decimal *Item_param::PValue::val_decimal(my_decimal *dec,
+ const Type_std_attributes *attr)
{
- // There's no "default". See comments in Item_param::save_in_field().
- switch (state) {
- case DECIMAL_VALUE:
- return &decimal_value;
- case REAL_VALUE:
- double2my_decimal(E_DEC_FATAL_ERROR, value.real, dec);
+ switch (type_handler()->cmp_type()) {
+ case DECIMAL_RESULT:
+ return &m_decimal;
+ case REAL_RESULT:
+ double2my_decimal(E_DEC_FATAL_ERROR, real, dec);
return dec;
- case INT_VALUE:
- int2my_decimal(E_DEC_FATAL_ERROR, value.integer, unsigned_flag, dec);
+ case INT_RESULT:
+ int2my_decimal(E_DEC_FATAL_ERROR, integer, attr->unsigned_flag, dec);
return dec;
- case STRING_VALUE:
- case LONG_DATA_VALUE:
- return decimal_from_string_with_check(dec, &str_value);
- case TIME_VALUE:
- {
- return TIME_to_my_decimal(&value.time, dec);
- }
- case IGNORE_VALUE:
- case DEFAULT_VALUE:
- invalid_default_param();
- // fall through
- case NULL_VALUE:
- return 0;
- case NO_VALUE:
- DBUG_ASSERT(0); // Should not be possible
- return 0;
+ case STRING_RESULT:
+ return decimal_from_string_with_check(dec, &m_string);
+ case TIME_RESULT:
+ return TIME_to_my_decimal(&time, dec);
+ case ROW_RESULT:
+ DBUG_ASSERT(0);
+ break;
}
- DBUG_ASSERT(0); // Gabrage
return 0;
}
-String *Item_param::val_str(String* str)
-{
- // There's no "default". See comments in Item_param::save_in_field().
- switch (state) {
- case STRING_VALUE:
- case LONG_DATA_VALUE:
- return &str_value_ptr;
- case REAL_VALUE:
- str->set_real(value.real, NOT_FIXED_DEC, &my_charset_bin);
+String *Item_param::PValue::val_str(String *str,
+ const Type_std_attributes *attr)
+{
+ switch (type_handler()->cmp_type()) {
+ case STRING_RESULT:
+ return &m_string_ptr;
+ case REAL_RESULT:
+ str->set_real(real, NOT_FIXED_DEC, &my_charset_bin);
return str;
- case INT_VALUE:
- str->set(value.integer, &my_charset_bin);
+ case INT_RESULT:
+ str->set(integer, &my_charset_bin);
return str;
- case DECIMAL_VALUE:
- if (my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value,
- 0, 0, 0, str) <= 1)
+ case DECIMAL_RESULT:
+ if (my_decimal2string(E_DEC_FATAL_ERROR, &m_decimal, 0, 0, 0, str) <= 1)
return str;
return NULL;
- case TIME_VALUE:
+ case TIME_RESULT:
{
if (str->reserve(MAX_DATE_STRING_REP_LENGTH))
- break;
- str->length((uint) my_TIME_to_str(&value.time, (char*) str->ptr(),
- decimals));
+ return NULL;
+ str->length((uint) my_TIME_to_str(&time, (char*) str->ptr(),
+ attr->decimals));
str->set_charset(&my_charset_bin);
return str;
}
- case IGNORE_VALUE:
- case DEFAULT_VALUE:
- invalid_default_param();
- // fall through
- case NULL_VALUE:
- return NULL;
- case NO_VALUE:
- DBUG_ASSERT(0); // Should not be possible
- return NULL;
+ case ROW_RESULT:
+ DBUG_ASSERT(0);
+ break;
}
- DBUG_ASSERT(0); // Garbage
return NULL;
}
+
/**
Return Param item values in string format, for generating the dynamic
query used in update/binary logs.
@@ -4105,43 +4721,42 @@ String *Item_param::val_str(String* str)
that binary log contains wrong statement
*/
-const String *Item_param::query_val_str(THD *thd, String* str) const
+const String *Item_param::value_query_val_str(THD *thd, String *str) const
{
- // There's no "default". See comments in Item_param::save_in_field().
- switch (state) {
- case INT_VALUE:
+ switch (value.type_handler()->cmp_type()) {
+ case INT_RESULT:
str->set_int(value.integer, unsigned_flag, &my_charset_bin);
return str;
- case REAL_VALUE:
+ case REAL_RESULT:
str->set_real(value.real, NOT_FIXED_DEC, &my_charset_bin);
return str;
- case DECIMAL_VALUE:
- if (my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value,
+ case DECIMAL_RESULT:
+ if (my_decimal2string(E_DEC_FATAL_ERROR, &value.m_decimal,
0, 0, 0, str) > 1)
return &my_null_string;
return str;
- case TIME_VALUE:
+ case TIME_RESULT:
{
static const uint32 typelen= 9; // "TIMESTAMP" is the longest type name
char *buf, *ptr;
str->length(0);
/*
TODO: in case of error we need to notify replication
- that binary log contains wrong statement
+ that binary log contains wrong statement
*/
if (str->reserve(MAX_DATE_STRING_REP_LENGTH + 3 + typelen))
- break;
+ return NULL;
/* Create date string inplace */
switch (value.time.time_type) {
case MYSQL_TIMESTAMP_DATE:
- str->append(C_STRING_WITH_LEN("DATE"));
+ str->append(STRING_WITH_LEN("DATE"));
break;
case MYSQL_TIMESTAMP_TIME:
- str->append(C_STRING_WITH_LEN("TIME"));
+ str->append(STRING_WITH_LEN("TIME"));
break;
case MYSQL_TIMESTAMP_DATETIME:
- str->append(C_STRING_WITH_LEN("TIMESTAMP"));
+ str->append(STRING_WITH_LEN("TIMESTAMP"));
break;
case MYSQL_TIMESTAMP_ERROR:
case MYSQL_TIMESTAMP_NONE:
@@ -4156,15 +4771,29 @@ const String *Item_param::query_val_str(THD *thd, String* str) const
str->length((uint32) (ptr - buf));
return str;
}
- case STRING_VALUE:
- case LONG_DATA_VALUE:
+ case STRING_RESULT:
{
str->length(0);
append_query_string(value.cs_info.character_set_client, str,
- str_value.ptr(), str_value.length(),
+ value.m_string.ptr(), value.m_string.length(),
thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES);
return str;
}
+ case ROW_RESULT:
+ DBUG_ASSERT(0);
+ break;
+ }
+ return NULL;
+}
+
+
+const String *Item_param::query_val_str(THD *thd, String* str) const
+{
+ // There's no "default". See comments in Item_param::save_in_field().
+ switch (state) {
+ case SHORT_DATA_VALUE:
+ case LONG_DATA_VALUE:
+ return value_query_val_str(thd, str);
case IGNORE_VALUE:
case DEFAULT_VALUE:
return &my_default_string;
@@ -4187,19 +4816,20 @@ const String *Item_param::query_val_str(THD *thd, String* str) const
bool Item_param::convert_str_value(THD *thd)
{
bool rc= FALSE;
- if (state == STRING_VALUE || state == LONG_DATA_VALUE)
+ if ((state == SHORT_DATA_VALUE || state == LONG_DATA_VALUE) &&
+ value.type_handler()->cmp_type() == STRING_RESULT)
{
- rc= value.cs_info.convert_if_needed(thd, &str_value);
+ rc= value.cs_info.convert_if_needed(thd, &value.m_string);
/* Here str_value is guaranteed to be in final_character_set_of_str_value */
/*
str_value_ptr is returned from val_str(). It must be not alloced
to prevent it's modification by val_str() invoker.
*/
- str_value_ptr.set(str_value.ptr(), str_value.length(),
- str_value.charset());
+ value.m_string_ptr.set(value.m_string.ptr(), value.m_string.length(),
+ value.m_string.charset());
/* Synchronize item charset and length with value charset */
- fix_charset_and_length_from_str_value(DERIVATION_COERCIBLE);
+ fix_charset_and_length_from_str_value(value.m_string, DERIVATION_COERCIBLE);
}
return rc;
}
@@ -4208,18 +4838,48 @@ bool Item_param::convert_str_value(THD *thd)
bool Item_param::basic_const_item() const
{
DBUG_ASSERT(fixed || state == NO_VALUE);
- if (state == NO_VALUE || state == TIME_VALUE)
+ if (state == NO_VALUE ||
+ (state == SHORT_DATA_VALUE && type_handler()->cmp_type() == TIME_RESULT))
return FALSE;
return TRUE;
}
+Item *Item_param::value_clone_item(THD *thd)
+{
+ MEM_ROOT *mem_root= thd->mem_root;
+ switch (value.type_handler()->cmp_type()) {
+ case INT_RESULT:
+ return (unsigned_flag ?
+ new (mem_root) Item_uint(thd, name.str, value.integer, max_length) :
+ new (mem_root) Item_int(thd, name.str, value.integer, max_length));
+ case REAL_RESULT:
+ return new (mem_root) Item_float(thd, name.str, value.real, decimals,
+ max_length);
+ 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(),
+ value.m_string.charset(),
+ collation.derivation,
+ collation.repertoire);
+ case TIME_RESULT:
+ break;
+ case ROW_RESULT:
+ DBUG_ASSERT(0);
+ break;
+ }
+ return 0;
+}
+
+
/* see comments in the header file */
Item *
Item_param::clone_item(THD *thd)
{
- MEM_ROOT *mem_root= thd->mem_root;
// There's no "default". See comments in Item_param::save_in_field().
switch (state) {
case IGNORE_VALUE:
@@ -4227,24 +4887,13 @@ Item_param::clone_item(THD *thd)
invalid_default_param();
// fall through
case NULL_VALUE:
- return new (mem_root) Item_null(thd, name);
- case INT_VALUE:
- return (unsigned_flag ?
- new (mem_root) Item_uint(thd, name, value.integer, max_length) :
- new (mem_root) Item_int(thd, name, value.integer, max_length));
- case REAL_VALUE:
- return new (mem_root) Item_float(thd, name, value.real, decimals,
- max_length);
- case DECIMAL_VALUE:
- return 0; // Should create Item_decimal. See MDEV-11361.
- case STRING_VALUE:
+ return new (thd->mem_root) Item_null(thd, name.str);
+ case SHORT_DATA_VALUE:
case LONG_DATA_VALUE:
- return new (mem_root) Item_string(thd, name, str_value.c_ptr_quick(),
- str_value.length(), str_value.charset(),
- collation.derivation,
- collation.repertoire);
- case TIME_VALUE:
- return 0;
+ {
+ DBUG_ASSERT(type_handler()->cmp_type() == value.type_handler()->cmp_type());
+ return value_clone_item(thd);
+ }
case NO_VALUE:
return 0;
}
@@ -4253,6 +4902,24 @@ Item_param::clone_item(THD *thd)
}
+bool Item_param::value_eq(const Item *item, bool binary_cmp) const
+{
+ switch (value.type_handler()->cmp_type()) {
+ case INT_RESULT:
+ return int_eq(value.integer, item);
+ case REAL_RESULT:
+ return real_eq(value.real, item);
+ case STRING_RESULT:
+ return str_eq(&value.m_string, item, binary_cmp);
+ case DECIMAL_RESULT:
+ case TIME_RESULT:
+ case ROW_RESULT:
+ break;
+ }
+ return false;
+}
+
+
bool
Item_param::eq(const Item *item, bool binary_cmp) const
{
@@ -4267,15 +4934,9 @@ Item_param::eq(const Item *item, bool binary_cmp) const
return false;
case NULL_VALUE:
return null_eq(item);
- case INT_VALUE:
- return int_eq(value.integer, item);
- case REAL_VALUE:
- return real_eq(value.real, item);
- case STRING_VALUE:
+ case SHORT_DATA_VALUE:
case LONG_DATA_VALUE:
- return str_eq(&str_value, item, binary_cmp);
- case DECIMAL_VALUE:
- case TIME_VALUE:
+ return value_eq(item, binary_cmp);
case NO_VALUE:
return false;
}
@@ -4336,18 +4997,14 @@ Item_param::set_param_type_and_swap_value(Item_param *src)
{
Type_std_attributes::set(src);
set_handler(src->type_handler());
- set_param_func= src->set_param_func;
item_type= src->item_type;
maybe_null= src->maybe_null;
null_value= src->null_value;
state= src->state;
fixed= src->fixed;
- value= src->value;
- decimal_value.swap(src->decimal_value);
- str_value.swap(src->str_value);
- str_value_ptr.swap(src->str_value_ptr);
+ value.swap(src->value);
}
@@ -4392,66 +5049,23 @@ bool
Item_param::set_value(THD *thd, sp_rcontext *ctx, Item **it)
{
Item *arg= *it;
-
- if (arg->is_null())
+ struct st_value tmp;
+ /*
+ The OUT parameter is bound to some data type.
+ It's important not to touch m_type_handler,
+ to make sure the next mysql_stmt_execute()
+ correctly fetches the value from the client-server protocol,
+ using set_param_func().
+ */
+ if (arg->save_in_value(&tmp) ||
+ set_value(thd, arg, &tmp, arg->type_handler()))
{
set_null();
- return FALSE;
- }
-
- null_value= FALSE;
- unsigned_flag= arg->unsigned_flag;
-
- switch (arg->result_type()) {
- case STRING_RESULT:
- {
- char str_buffer[STRING_BUFFER_USUAL_SIZE];
- String sv_buffer(str_buffer, sizeof(str_buffer), &my_charset_bin);
- String *sv= arg->val_str(&sv_buffer);
-
- if (!sv)
- return TRUE;
-
- set_str(sv->c_ptr_safe(), sv->length());
- str_value_ptr.set(str_value.ptr(),
- str_value.length(),
- str_value.charset());
- collation.set(str_value.charset(), DERIVATION_COERCIBLE);
- decimals= 0;
- break;
- }
-
- case REAL_RESULT:
- set_double(arg->val_real());
- break;
-
- case INT_RESULT:
- set_int(arg->val_int(), arg->max_length);
- break;
-
- case DECIMAL_RESULT:
- {
- my_decimal dv_buf;
- my_decimal *dv= arg->val_decimal(&dv_buf);
-
- if (!dv)
- return TRUE;
-
- set_decimal(dv, !dv->sign());
- break;
- }
-
- default:
- /* That can not happen. */
-
- DBUG_ASSERT(TRUE); // Abort in debug mode.
-
- set_null(); // Set to NULL in release mode.
- return FALSE;
+ return false;
}
-
- set_handler_by_result_type(arg->result_type());
- return FALSE;
+ /* It is wrapper => other set_* shoud set null_value */
+ DBUG_ASSERT(null_value == false);
+ return false;
}
@@ -4497,9 +5111,9 @@ Item_param::get_out_param_info() const
@param field container for meta-data to be filled
*/
-void Item_param::make_field(THD *thd, Send_field *field)
+void Item_param::make_send_field(THD *thd, Send_field *field)
{
- Item::make_field(thd, field);
+ Item::make_send_field(thd, field);
if (!m_out_param_info)
return;
@@ -4516,7 +5130,6 @@ void Item_param::make_field(THD *thd, Send_field *field)
field->org_col_name= m_out_param_info->org_col_name;
field->length= m_out_param_info->length;
- field->charsetnr= m_out_param_info->charsetnr;
field->flags= m_out_param_info->flags;
field->decimals= m_out_param_info->decimals;
field->type= m_out_param_info->type;
@@ -4698,7 +5311,7 @@ static bool mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current,
ER_THD(thd,ER_WARN_FIELD_RESOLVED),
db_name, (db_name[0] ? "." : ""),
table_name, (table_name [0] ? "." : ""),
- resolved_item->field_name,
+ resolved_item->field_name.str,
current->select_number, last->select_number);
}
DBUG_RETURN(FALSE);
@@ -4788,7 +5401,7 @@ static Item** find_field_in_group_list(Item *find_item, ORDER *group_list)
{
const char *db_name;
const char *table_name;
- const char *field_name;
+ LEX_CSTRING *field_name;
ORDER *found_group= NULL;
int found_match_degree= 0;
char name_buff[SAFE_NAME_LEN+1];
@@ -4798,7 +5411,7 @@ 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;
@@ -4811,17 +5424,17 @@ static Item** find_field_in_group_list(Item *find_item, ORDER *group_list)
db_name= name_buff;
}
- DBUG_ASSERT(field_name != 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 && !table_name &&
+ if ((*(cur_group->item))->name.str && !table_name &&
!(*(cur_group->item))->is_autogenerated_name &&
- !my_strcasecmp(system_charset_info,
- (*(cur_group->item))->name, field_name))
+ !lex_string_cmp(system_charset_info,
+ &(*(cur_group->item))->name, field_name))
{
++cur_match_degree;
}
@@ -4832,12 +5445,12 @@ static Item** find_field_in_group_list(Item *find_item, ORDER *group_list)
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_field_name= cur_field->field_name;
+ LEX_CSTRING *l_field_name= &cur_field->field_name;
- DBUG_ASSERT(l_field_name != 0);
+ DBUG_ASSERT(l_field_name->str != 0);
- if (!my_strcasecmp(system_charset_info,
- l_field_name, field_name))
+ if (!lex_string_cmp(system_charset_info,
+ l_field_name, field_name))
++cur_match_degree;
else
continue;
@@ -4977,7 +5590,7 @@ resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select)
the strict mode is enabled.
*/
my_error(ER_NON_GROUPING_FIELD_USED, MYF(0),
- ref->name, "HAVING");
+ ref->name.str, "HAVING");
return NULL;
}
if (select_ref != not_found_item || group_by_ref)
@@ -4988,7 +5601,7 @@ resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select)
if (!select->ref_pointer_array[counter])
{
my_error(ER_ILLEGAL_REFERENCE, MYF(0),
- ref->name, "forward reference in item list");
+ ref->name.str, "forward reference in item list");
return NULL;
}
DBUG_ASSERT((*select_ref)->fixed);
@@ -5321,15 +5934,15 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
*ref= NULL; // Don't call set_properties()
rf= (place == IN_HAVING ?
new (thd->mem_root)
- Item_ref(thd, context, ref, (char*) table_name,
- (char*) field_name, alias_name_used) :
+ Item_ref(thd, context, ref, table_name,
+ &field_name, alias_name_used) :
(!select->group_list.elements ?
new (thd->mem_root)
- Item_direct_ref(thd, context, ref, (char*) table_name,
- (char*) field_name, alias_name_used) :
+ Item_direct_ref(thd, context, ref, table_name,
+ &field_name, alias_name_used) :
new (thd->mem_root)
- Item_outer_ref(thd, context, ref, (char*) table_name,
- (char*) field_name, alias_name_used)));
+ Item_outer_ref(thd, context, ref, table_name,
+ &field_name, alias_name_used)));
*ref= save;
if (!rf)
return -1;
@@ -5373,9 +5986,10 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
if (last_checked_context->select_lex->having_fix_field)
{
Item_ref *rf;
- rf= new (thd->mem_root) Item_ref(thd, context, (*from_field)->table->s->db.str,
- (*from_field)->table->alias.c_ptr(),
- (char*) field_name);
+ rf= new (thd->mem_root) Item_ref(thd, context,
+ (*from_field)->table->s->db.str,
+ (*from_field)->table->alias.c_ptr(),
+ &field_name);
if (!rf)
return -1;
thd->change_item_tree(reference, rf);
@@ -5444,6 +6058,12 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
Field *from_field= (Field *)not_found_field;
bool outer_fixed= false;
SELECT_LEX *select= thd->lex->current_select;
+
+ if (select && select->in_tvc)
+ {
+ my_error(ER_FIELD_REFERENCE_IN_TVC, MYF(0), full_name());
+ return(1);
+ }
if (!field) // If field is not checked
{
@@ -5453,6 +6073,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
expression to 'reference', i.e. it substitute that expression instead
of this Item_field
*/
+ DBUG_ASSERT(context);
if ((from_field= find_field_in_tables(thd, this,
context->first_name_resolution_table,
context->last_name_resolution_table,
@@ -5492,10 +6113,10 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
Field *new_field= (*((Item_field**)res))->field;
- if (new_field == NULL)
+ if (unlikely(new_field == NULL))
{
/* The column to which we link isn't valid. */
- my_error(ER_BAD_FIELD_ERROR, MYF(0), (*res)->name,
+ my_error(ER_BAD_FIELD_ERROR, MYF(0), (*res)->name.str,
thd->where);
return(1);
}
@@ -5516,8 +6137,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);
@@ -5538,7 +6158,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
}
}
- if (!select)
+ if (unlikely(!select))
{
my_error(ER_BAD_FIELD_ERROR, MYF(0), full_name(), thd->where);
goto error;
@@ -5591,11 +6211,11 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
set_field(from_field);
}
- else if (thd->mark_used_columns != MARK_COLUMNS_NONE)
+ else if (should_mark_column(thd->column_usage))
{
TABLE *table= field->table;
MY_BITMAP *current_bitmap, *other_bitmap;
- if (thd->mark_used_columns == MARK_COLUMNS_READ)
+ if (thd->column_usage == MARK_COLUMNS_READ)
{
current_bitmap= table->read_set;
other_bitmap= table->write_set;
@@ -5620,16 +6240,16 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (any_privileges)
{
- char *db, *tab;
+ const char *db, *tab;
db= field->table->s->db.str;
tab= field->table->s->table_name.str;
if (!(have_privileges= (get_column_grant(thd, &field->table->grant,
- db, tab, field_name) &
+ db, tab, field_name.str) &
VIEW_ANY_ACL)))
{
my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
"ANY", thd->security_ctx->priv_user,
- thd->security_ctx->host_or_ip, field_name, tab);
+ thd->security_ctx->host_or_ip, field_name.str, tab);
goto error;
}
}
@@ -5879,7 +6499,8 @@ Item *Item_field::replace_equal_field(THD *thd, uchar *arg)
comparison context, and it's safe to replace it to the constant from
item_equal.
*/
- DBUG_ASSERT(cmp_type() == item_equal->compare_type());
+ DBUG_ASSERT(type_handler()->type_handler_for_comparison()->cmp_type() ==
+ item_equal->compare_type_handler()->cmp_type());
return const_item2;
}
Item_field *subst=
@@ -5893,16 +6514,14 @@ Item *Item_field::replace_equal_field(THD *thd, uchar *arg)
}
-void Item::init_make_field(Send_field *tmp_field,
- enum enum_field_types field_type_arg)
+void Item::init_make_send_field(Send_field *tmp_field,
+ enum enum_field_types field_type_arg)
{
- char *empty_name= (char*) "";
- tmp_field->db_name= empty_name;
- tmp_field->org_table_name= empty_name;
- tmp_field->org_col_name= empty_name;
- tmp_field->table_name= empty_name;
- tmp_field->col_name= name;
- tmp_field->charsetnr= collation.collation->number;
+ tmp_field->db_name= "";
+ tmp_field->org_table_name= "";
+ tmp_field->org_col_name= empty_clex_str;
+ tmp_field->table_name= "";
+ tmp_field->col_name= name;
tmp_field->flags= (maybe_null ? 0 : NOT_NULL_FLAG) |
(my_binary_compare(charset_for_protocol()) ?
BINARY_FLAG : 0);
@@ -5913,15 +6532,15 @@ void Item::init_make_field(Send_field *tmp_field,
tmp_field->flags |= UNSIGNED_FLAG;
}
-void Item::make_field(THD *thd, Send_field *tmp_field)
+void Item::make_send_field(THD *thd, Send_field *tmp_field)
{
- init_make_field(tmp_field, field_type());
+ init_make_send_field(tmp_field, field_type());
}
-void Item_empty_string::make_field(THD *thd, Send_field *tmp_field)
+void Item_empty_string::make_send_field(THD *thd, Send_field *tmp_field)
{
- init_make_field(tmp_field, string_field_type());
+ init_make_send_field(tmp_field, string_type_handler()->field_type());
}
@@ -5943,7 +6562,7 @@ String *Item::check_well_formed_result(String *str, bool send_error)
CHARSET_INFO *cs= str->charset();
uint wlen= str->well_formed_length();
null_value= false;
- if (wlen < str->length())
+ if (unlikely(wlen < str->length()))
{
THD *thd= current_thd;
char hexbuf[7];
@@ -5982,9 +6601,10 @@ String_copier_for_item::copy_with_warn(CHARSET_INFO *dstcs, String *dst,
CHARSET_INFO *srccs, const char *src,
uint32 src_length, uint32 nchars)
{
- if ((dst->copy(dstcs, srccs, src, src_length, nchars, this)))
+ if (unlikely((dst->copy(dstcs, srccs, src, src_length, nchars, this))))
return true; // EOM
- if (const char *pos= well_formed_error_pos())
+ const char *pos;
+ if (unlikely(pos= well_formed_error_pos()))
{
ErrConvString err(pos, src_length - (pos - src), &my_charset_bin);
push_warning_printf(m_thd, Sql_condition::WARN_LEVEL_WARN,
@@ -5995,7 +6615,7 @@ String_copier_for_item::copy_with_warn(CHARSET_INFO *dstcs, String *dst,
err.ptr());
return false;
}
- if (const char *pos= cannot_convert_error_pos())
+ if (unlikely(pos= cannot_convert_error_pos()))
{
char buf[16];
int mblen= my_charlen(srccs, pos, src + src_length);
@@ -6055,180 +6675,16 @@ bool Item::eq_by_collation(Item *item, bool binary_cmp, CHARSET_INFO *cs)
}
-/**
- Create a field to hold a string value from an item.
-
- If too_big_for_varchar() create a blob @n
- If max_length > 0 create a varchar @n
- If max_length == 0 create a CHAR(0)
-
- @param table Table for which the field is created
-*/
-
-Field *Item::make_string_field(TABLE *table)
-{
- Field *field;
- MEM_ROOT *mem_root= table->in_use->mem_root;
-
- DBUG_ASSERT(collation.collation);
- /*
- Note: the following check is repeated in
- subquery_types_allow_materialization():
- */
- if (too_big_for_varchar())
- field= new (mem_root)
- Field_blob(max_length, maybe_null, name,
- collation.collation, TRUE);
- /* Item_type_holder holds the exact type, do not change it */
- else if (max_length > 0 &&
- (type() != Item::TYPE_HOLDER || field_type() != MYSQL_TYPE_STRING))
- field= new (mem_root)
- Field_varstring(max_length, maybe_null, name, table->s,
- collation.collation);
- else
- field= new (mem_root)
- Field_string(max_length, maybe_null, name, collation.collation);
- if (field)
- field->init(table);
- return field;
-}
-
-
-/**
- Create a field based on field_type of argument.
-
- For now, this is only used to create a field for
- IFNULL(x,something) and time functions
-
- @retval
- NULL error
- @retval
- \# Created field
-*/
-
-Field *Item::tmp_table_field_from_field_type(TABLE *table,
- bool fixed_length,
- bool set_blob_packlength)
-{
- /*
- The field functions defines a field to be not null if null_ptr is not 0
- */
- uchar *null_ptr= maybe_null ? (uchar*) "" : 0;
- Field *field;
- MEM_ROOT *mem_root= table->in_use->mem_root;
-
- switch (field_type()) {
- case MYSQL_TYPE_DECIMAL:
- case MYSQL_TYPE_NEWDECIMAL:
- field= Field_new_decimal::create_from_item(mem_root, this);
- break;
- case MYSQL_TYPE_TINY:
- field= new (mem_root)
- Field_tiny((uchar*) 0, max_length, null_ptr, 0, Field::NONE,
- name, 0, unsigned_flag);
- break;
- case MYSQL_TYPE_SHORT:
- field= new (mem_root)
- Field_short((uchar*) 0, max_length, null_ptr, 0, Field::NONE,
- name, 0, unsigned_flag);
- break;
- case MYSQL_TYPE_LONG:
- field= new (mem_root)
- Field_long((uchar*) 0, max_length, null_ptr, 0, Field::NONE,
- name, 0, unsigned_flag);
- break;
-#ifdef HAVE_LONG_LONG
- case MYSQL_TYPE_LONGLONG:
- field= new (mem_root)
- Field_longlong((uchar*) 0, max_length, null_ptr, 0, Field::NONE,
- name, 0, unsigned_flag);
- break;
-#endif
- case MYSQL_TYPE_FLOAT:
- field= new (mem_root)
- Field_float((uchar*) 0, max_length, null_ptr, 0, Field::NONE,
- name, decimals, 0, unsigned_flag);
- break;
- case MYSQL_TYPE_DOUBLE:
- field= new (mem_root)
- Field_double((uchar*) 0, max_length, null_ptr, 0, Field::NONE,
- name, decimals, 0, unsigned_flag);
- break;
- case MYSQL_TYPE_INT24:
- field= new (mem_root)
- Field_medium((uchar*) 0, max_length, null_ptr, 0, Field::NONE,
- name, 0, unsigned_flag);
- break;
- case MYSQL_TYPE_NEWDATE:
- case MYSQL_TYPE_DATE:
- field= new (mem_root)
- Field_newdate(0, null_ptr, 0, Field::NONE, name);
- break;
- case MYSQL_TYPE_TIME:
- field= new_Field_time(mem_root, 0, null_ptr, 0, Field::NONE, name,
- decimals);
- break;
- case MYSQL_TYPE_TIMESTAMP:
- field= new_Field_timestamp(mem_root, 0, null_ptr, 0,
- Field::NONE, name, 0, decimals);
- break;
- case MYSQL_TYPE_DATETIME:
- field= new_Field_datetime(mem_root, 0, null_ptr, 0, Field::NONE, name,
- decimals);
- break;
- case MYSQL_TYPE_YEAR:
- field= new (mem_root)
- Field_year((uchar*) 0, max_length, null_ptr, 0, Field::NONE, name);
- break;
- case MYSQL_TYPE_BIT:
- field= new (mem_root)
- Field_bit_as_char(NULL, max_length, null_ptr, 0, Field::NONE, name);
- break;
- default:
- /* This case should never be chosen */
- DBUG_ASSERT(0);
- /* If something goes awfully wrong, it's better to get a string than die */
- case MYSQL_TYPE_NULL:
- case MYSQL_TYPE_STRING:
- if (fixed_length && !too_big_for_varchar())
- {
- field= new (mem_root)
- Field_string(max_length, maybe_null, name, collation.collation);
- break;
- }
- /* fall through */
- case MYSQL_TYPE_ENUM:
- case MYSQL_TYPE_SET:
- case MYSQL_TYPE_VAR_STRING:
- case MYSQL_TYPE_VARCHAR:
- return make_string_field(table);
- case MYSQL_TYPE_TINY_BLOB:
- case MYSQL_TYPE_MEDIUM_BLOB:
- case MYSQL_TYPE_LONG_BLOB:
- case MYSQL_TYPE_BLOB:
- field= new (mem_root)
- Field_blob(max_length, maybe_null, name,
- collation.collation, set_blob_packlength);
- break; // Blob handled outside of case
-#ifdef HAVE_SPATIAL
- case MYSQL_TYPE_GEOMETRY:
- field= new (mem_root)
- Field_geom(max_length, maybe_null, name, table->s, get_geometry_type());
-#endif /* HAVE_SPATIAL */
- }
- if (field)
- field->init(table);
- return field;
-}
-
-
/* ARGSUSED */
-void Item_field::make_field(THD *thd, Send_field *tmp_field)
+void Item_field::make_send_field(THD *thd, Send_field *tmp_field)
{
- field->make_field(tmp_field);
+ field->make_send_field(tmp_field);
DBUG_ASSERT(tmp_field->table_name != 0);
- if (name)
- tmp_field->col_name=name; // Use user supplied name
+ if (name.str)
+ {
+ DBUG_ASSERT(name.length == strlen(name.str));
+ tmp_field->col_name= name; // Use user supplied name
+ }
if (table_name)
tmp_field->table_name= table_name;
if (db_name)
@@ -6292,6 +6748,11 @@ fast_field_copier Item_field::setup_fast_field_copier(Field *to)
return to->get_fast_field_copier(field);
}
+void Item_field::save_in_result_field(bool no_conversions)
+{
+ bool unused;
+ save_field_in_field(field, &unused, result_field, no_conversions);
+}
/**
Set a field's value from a item.
@@ -6376,54 +6837,62 @@ int Item_null::save_safe_in_field(Field *field)
Note: all Item_XXX::val_str(str) methods must NOT assume that
str != str_value. For example, see fix for bug #44743.
*/
+int Item::save_str_in_field(Field *field, bool no_conversions)
+{
+ String *result;
+ CHARSET_INFO *cs= collation.collation;
+ char buff[MAX_FIELD_WIDTH]; // Alloc buffer for small columns
+ str_value.set_quick(buff, sizeof(buff), cs);
+ result=val_str(&str_value);
+ if (null_value)
+ {
+ str_value.set_quick(0, 0, cs);
+ return set_field_to_null_with_conversions(field, no_conversions);
+ }
-int Item::save_in_field(Field *field, bool no_conversions)
+ /* NOTE: If null_value == FALSE, "result" must be not NULL. */
+
+ field->set_notnull();
+ int error= field->store(result->ptr(),result->length(),cs);
+ str_value.set_quick(0, 0, cs);
+ return error;
+}
+
+
+int Item::save_real_in_field(Field *field, bool no_conversions)
{
- int error;
- if (result_type() == STRING_RESULT)
- {
- String *result;
- CHARSET_INFO *cs= collation.collation;
- char buff[MAX_FIELD_WIDTH]; // Alloc buffer for small columns
- str_value.set_quick(buff, sizeof(buff), cs);
- result=val_str(&str_value);
- if (null_value)
- {
- str_value.set_quick(0, 0, cs);
- return set_field_to_null_with_conversions(field, no_conversions);
- }
+ double nr= val_real();
+ if (null_value)
+ return set_field_to_null_with_conversions(field, no_conversions);
+ field->set_notnull();
+ return field->store(nr);
+}
- /* NOTE: If null_value == FALSE, "result" must be not NULL. */
- field->set_notnull();
- error=field->store(result->ptr(),result->length(),cs);
- str_value.set_quick(0, 0, cs);
- }
- else if (result_type() == REAL_RESULT)
- {
- double nr= val_real();
- if (null_value)
- return set_field_to_null_with_conversions(field, no_conversions);
- field->set_notnull();
- error=field->store(nr);
- }
- else if (result_type() == DECIMAL_RESULT)
- {
- my_decimal decimal_value;
- my_decimal *value= val_decimal(&decimal_value);
- if (null_value)
- return set_field_to_null_with_conversions(field, no_conversions);
- field->set_notnull();
- error=field->store_decimal(value);
- }
- else
- {
- longlong nr=val_int();
- if (null_value)
- return set_field_to_null_with_conversions(field, no_conversions);
- field->set_notnull();
- error=field->store(nr, unsigned_flag);
- }
+int Item::save_decimal_in_field(Field *field, bool no_conversions)
+{
+ my_decimal decimal_value;
+ my_decimal *value= val_decimal(&decimal_value);
+ if (null_value)
+ return set_field_to_null_with_conversions(field, no_conversions);
+ field->set_notnull();
+ return field->store_decimal(value);
+}
+
+
+int Item::save_int_in_field(Field *field, bool no_conversions)
+{
+ longlong nr= val_int();
+ if (null_value)
+ return set_field_to_null_with_conversions(field, no_conversions);
+ field->set_notnull();
+ return field->store(nr, unsigned_flag);
+}
+
+
+int Item::save_in_field(Field *field, bool no_conversions)
+{
+ int error= type_handler()->Item_save_in_field(this, field, no_conversions);
return error ? error : (field->table->in_use->is_error() ? 1 : 0);
}
@@ -6445,11 +6914,46 @@ 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_value.ptr(),
+ Item_string(thd, name.str, str_value.ptr(),
str_value.length(), collation.collation);
}
+Item_basic_constant *
+Item_string::make_string_literal_concat(THD *thd, const LEX_CSTRING *str)
+{
+ append(str->str, (uint32) str->length);
+ if (!(collation.repertoire & MY_REPERTOIRE_EXTENDED))
+ {
+ // If the string has been pure ASCII so far, check the new part.
+ CHARSET_INFO *cs= thd->variables.collation_connection;
+ collation.repertoire|= my_string_repertoire(cs, str->str, str->length);
+ }
+ return this;
+}
+
+
+/*
+ If "this" is a reasonably short pure ASCII string literal,
+ try to parse known ODBC-style date, time or timestamp literals,
+ e.g:
+ SELECT {d'2001-01-01'};
+ SELECT {t'10:20:30'};
+ SELECT {ts'2001-01-01 10:20:30'};
+*/
+Item *Item_string::make_odbc_literal(THD *thd, const LEX_CSTRING *typestr)
+{
+ enum_field_types type= odbc_temporal_literal_type(typestr);
+ Item *res= type == MYSQL_TYPE_STRING ? this :
+ create_temporal_literal(thd, val_str(NULL), type, false);
+ /*
+ create_temporal_literal() returns NULL if failed to parse the string,
+ or the string format did not match the type, e.g.: {d'2001-01-01 10:10:10'}
+ */
+ return res ? res : this;
+}
+
+
static int save_int_value_in_field (Field *field, longlong nr,
bool null_value, bool unsigned_flag)
{
@@ -6468,13 +6972,13 @@ int Item_int::save_in_field(Field *field, bool no_conversions)
Item *Item_int::clone_item(THD *thd)
{
- return new (thd->mem_root) Item_int(thd, name, value, max_length);
+ return new (thd->mem_root) Item_int(thd, name.str, value, max_length, unsigned_flag);
}
-void Item_datetime::set(longlong packed)
+void Item_datetime::set(longlong packed, enum_mysql_timestamp_type ts_type)
{
- unpack_time(packed, &ltime);
+ unpack_time(packed, &ltime, ts_type);
}
int Item_datetime::save_in_field(Field *field, bool no_conversions)
@@ -6504,9 +7008,9 @@ Item *Item_int_with_ref::clone_item(THD *thd)
*/
return (ref->unsigned_flag ?
new (thd->mem_root)
- Item_uint(thd, ref->name, ref->val_int(), ref->max_length) :
+ Item_uint(thd, ref->name.str, ref->val_int(), ref->max_length) :
new (thd->mem_root)
- Item_int(thd, ref->name, ref->val_int(), ref->max_length));
+ Item_int(thd, ref->name.str, ref->val_int(), ref->max_length));
}
@@ -6534,7 +7038,7 @@ Item *Item_int::neg(THD *thd)
else if (value < 0 && max_length)
max_length--;
value= -value;
- name= 0;
+ name= null_clex_str;
return this;
}
@@ -6542,7 +7046,7 @@ Item *Item_decimal::neg(THD *thd)
{
my_decimal_neg(&decimal_value);
unsigned_flag= 0;
- name= 0;
+ name= null_clex_str;
max_length= my_decimal_precision_to_length_no_truncation(
decimal_value.intg + decimals, decimals, unsigned_flag);
return this;
@@ -6555,7 +7059,8 @@ Item *Item_float::neg(THD *thd)
else if (value < 0 && max_length)
max_length--;
value= -value;
- name= presentation= 0 ;
+ presentation= 0;
+ name= null_clex_str;
return this;
}
@@ -6574,7 +7079,7 @@ Item *Item_uint::neg(THD *thd)
Item *Item_uint::clone_item(THD *thd)
{
- return new (thd->mem_root) Item_uint(thd, name, value, max_length);
+ return new (thd->mem_root) Item_uint(thd, name.str, value, max_length);
}
static uint nr_of_decimals(const char *str, const char *end)
@@ -6631,22 +7136,23 @@ static uint nr_of_decimals(const char *str, const char *end)
Item->name should be fixed to use LEX_STRING eventually.
*/
-Item_float::Item_float(THD *thd, const char *str_arg, uint length):
+Item_float::Item_float(THD *thd, const char *str_arg, size_t length):
Item_num(thd)
{
int error;
char *end_not_used;
value= my_strntod(&my_charset_bin, (char*) str_arg, length, &end_not_used,
&error);
- if (error)
+ if (unlikely(error))
{
char tmp[NAME_LEN + 2];
- my_snprintf(tmp, sizeof(tmp), "%.*s", length, str_arg);
+ my_snprintf(tmp, sizeof(tmp), "%.*s", static_cast<int>(length), str_arg);
my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "double", tmp);
}
- presentation= name=(char*) str_arg;
+ presentation= name.str= str_arg;
+ name.length= strlen(str_arg);
decimals=(uint8) nr_of_decimals(str_arg, str_arg+length);
- max_length=length;
+ max_length=(uint32)length;
fixed= 1;
}
@@ -6683,10 +7189,9 @@ inline uint char_val(char X)
}
-void Item_hex_constant::hex_string_init(THD *thd, const char *str,
- uint str_length)
+void Item_hex_constant::hex_string_init(THD *thd, const char *str, size_t str_length)
{
- max_length=(str_length+1)/2;
+ max_length=(uint)((str_length+1)/2);
char *ptr=(char*) thd->alloc(max_length+1);
if (!ptr)
{
@@ -6718,6 +7223,22 @@ void Item_hex_hybrid::print(String *str, enum_query_type query_type)
}
+uint Item_hex_hybrid::decimal_precision() const
+{
+ switch (max_length) {// HEX DEC
+ case 0: // ---- ---
+ case 1: return 3; // 0xFF 255
+ case 2: return 5; // 0xFFFF 65535
+ case 3: return 8; // 0xFFFFFF 16777215
+ case 4: return 10; // 0xFFFFFFFF 4294967295
+ case 5: return 13; // 0xFFFFFFFFFF 1099511627775
+ case 6: return 15; // 0xFFFFFFFFFFFF 281474976710655
+ case 7: return 17; // 0xFFFFFFFFFFFFFF 72057594037927935
+ }
+ return 20; // 0xFFFFFFFFFFFFFFFF 18446744073709551615
+}
+
+
void Item_hex_string::print(String *str, enum_query_type query_type)
{
str->append("X'");
@@ -6732,7 +7253,7 @@ void Item_hex_string::print(String *str, enum_query_type query_type)
In number context this is a longlong value.
*/
-Item_bin_string::Item_bin_string(THD *thd, const char *str, uint str_length):
+Item_bin_string::Item_bin_string(THD *thd, const char *str, size_t str_length):
Item_hex_hybrid(thd)
{
const char *end= str + str_length - 1;
@@ -6740,7 +7261,7 @@ Item_bin_string::Item_bin_string(THD *thd, const char *str, uint str_length):
uchar bits= 0;
uint power= 1;
- max_length= (str_length + 7) >> 3;
+ max_length= (uint)((str_length + 7) >> 3);
if (!(ptr= (char*) thd->alloc(max_length + 1)))
return;
str_value.set(ptr, max_length, &my_charset_bin);
@@ -6780,7 +7301,6 @@ bool Item_temporal_literal::eq(const Item *item, bool binary_cmp) const
&((Item_temporal_literal *) item)->cached_time);
}
-
void Item_date_literal::print(String *str, enum_query_type query_type)
{
str->append("DATE'");
@@ -6865,127 +7385,11 @@ bool Item_time_literal::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
Pack data in buffer for sending.
*/
-bool Item_null::send(Protocol *protocol, String *packet)
+bool Item_null::send(Protocol *protocol, st_value *buffer)
{
return protocol->store_null();
}
-/**
- This is only called from items that is not of type item_field.
-*/
-
-bool Item::send(Protocol *protocol, String *buffer)
-{
- bool UNINIT_VAR(result); // Will be set if null_value == 0
- enum_field_types f_type;
-
- switch ((f_type=field_type())) {
- default:
- case MYSQL_TYPE_NULL:
- case MYSQL_TYPE_DECIMAL:
- 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_GEOMETRY:
- case MYSQL_TYPE_STRING:
- case MYSQL_TYPE_VAR_STRING:
- case MYSQL_TYPE_VARCHAR:
- case MYSQL_TYPE_BIT:
- case MYSQL_TYPE_NEWDECIMAL:
- {
- String *res;
- if ((res=val_str(buffer)))
- {
- DBUG_ASSERT(!null_value);
- result= protocol->store(res->ptr(),res->length(),res->charset());
- }
- else
- {
- DBUG_ASSERT(null_value);
- }
- break;
- }
- case MYSQL_TYPE_TINY:
- {
- longlong nr;
- nr= val_int();
- if (!null_value)
- result= protocol->store_tiny(nr);
- break;
- }
- case MYSQL_TYPE_SHORT:
- case MYSQL_TYPE_YEAR:
- {
- longlong nr;
- nr= val_int();
- if (!null_value)
- result= protocol->store_short(nr);
- break;
- }
- case MYSQL_TYPE_INT24:
- case MYSQL_TYPE_LONG:
- {
- longlong nr;
- nr= val_int();
- if (!null_value)
- result= protocol->store_long(nr);
- break;
- }
- case MYSQL_TYPE_LONGLONG:
- {
- longlong nr;
- nr= val_int();
- if (!null_value)
- result= protocol->store_longlong(nr, unsigned_flag);
- break;
- }
- case MYSQL_TYPE_FLOAT:
- {
- float nr;
- nr= (float) val_real();
- if (!null_value)
- result= protocol->store(nr, decimals, buffer);
- break;
- }
- case MYSQL_TYPE_DOUBLE:
- {
- double nr= val_real();
- if (!null_value)
- result= protocol->store(nr, decimals, buffer);
- break;
- }
- case MYSQL_TYPE_DATETIME:
- case MYSQL_TYPE_DATE:
- case MYSQL_TYPE_TIMESTAMP:
- {
- MYSQL_TIME tm;
- get_date(&tm, sql_mode_for_dates(current_thd));
- if (!null_value)
- {
- if (f_type == MYSQL_TYPE_DATE)
- return protocol->store_date(&tm);
- else
- result= protocol->store(&tm, decimals);
- }
- break;
- }
- case MYSQL_TYPE_TIME:
- {
- MYSQL_TIME tm;
- get_time(&tm);
- if (!null_value)
- result= protocol->store_time(&tm, decimals);
- break;
- }
- }
- if (null_value)
- result= protocol->store_null();
- return result;
-}
-
/**
Check if an item is a constant one and can be cached.
@@ -7040,7 +7444,7 @@ Item* Item::cache_const_expr_transformer(THD *thd, uchar *arg)
if (*(bool*)arg)
{
*((bool*)arg)= FALSE;
- Item_cache *cache= Item_cache::get_cache(thd, this);
+ Item_cache *cache= get_cache(thd);
if (!cache)
return NULL;
cache->setup(thd, this);
@@ -7058,7 +7462,7 @@ bool Item::find_item_processor(void *arg)
return (this == ((Item *) arg));
}
-bool Item_field::send(Protocol *protocol, String *buffer)
+bool Item_field::send(Protocol *protocol, st_value *buffer)
{
return protocol->store(result_field);
}
@@ -7131,7 +7535,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;
@@ -7158,7 +7562,7 @@ Item *get_field_item_for_having(THD *thd, Item *item, st_select_lex *sel)
if (equal_item->used_tables() == map)
{
field_item= (Item_field *)(equal_item->real_item());
- break;
+ break;
}
}
}
@@ -7166,7 +7570,7 @@ Item *get_field_item_for_having(THD *thd, Item *item, st_select_lex *sel)
{
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);
@@ -7248,7 +7652,7 @@ Item *Item_field::derived_field_transformer_for_where(THD *thd, uchar *arg)
Item *producing_item= find_producing_item(this, sel);
if (producing_item)
{
- Item *producing_clone= producing_item->build_clone(thd, thd->mem_root);
+ Item *producing_clone= producing_item->build_clone(thd);
if (producing_clone)
producing_clone->marker|= SUBSTITUTION_FL;
return producing_clone;
@@ -7266,7 +7670,7 @@ Item *Item_direct_view_ref::derived_field_transformer_for_where(THD *thd,
st_select_lex *sel= (st_select_lex *)arg;
Item *producing_item= find_producing_item(this, sel);
DBUG_ASSERT (producing_item != NULL);
- return producing_item->build_clone(thd, thd->mem_root);
+ return producing_item->build_clone(thd);
}
return (*ref);
}
@@ -7314,7 +7718,7 @@ Item *Item_field::derived_grouping_field_transformer_for_where(THD *thd,
if (gr_field)
{
Item *producing_clone=
- gr_field->producing_item->build_clone(thd, thd->mem_root);
+ gr_field->producing_item->build_clone(thd);
if (producing_clone)
producing_clone->marker|= SUBSTITUTION_FL;
return producing_clone;
@@ -7336,7 +7740,7 @@ Item_direct_view_ref::derived_grouping_field_transformer_for_where(THD *thd,
return this;
st_select_lex *sel= (st_select_lex *)arg;
Grouping_tmp_field *gr_field= find_matching_grouping_field(this, sel);
- return gr_field->producing_item->build_clone(thd, thd->mem_root);
+ return gr_field->producing_item->build_clone(thd);
}
void Item_field::print(String *str, enum_query_type query_type)
@@ -7363,7 +7767,7 @@ 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 char *field_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),
ref(item), reference_trough_name(0)
@@ -7412,7 +7816,8 @@ public:
};
Item_ref::Item_ref(THD *thd, TABLE_LIST *view_arg, Item **item,
- const char *field_name_arg, bool alias_name_used_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)
{
@@ -7513,7 +7918,7 @@ bool Item_ref::fix_fields(THD *thd, Item **reference)
Field *from_field;
ref= 0;
- if (!outer_context)
+ if (unlikely(!outer_context))
{
/* The current reference cannot be resolved in this query. */
my_error(ER_BAD_FIELD_ERROR,MYF(0),
@@ -7661,7 +8066,7 @@ bool Item_ref::fix_fields(THD *thd, Item **reference)
last_checked_context->select_lex->nest_level);
return FALSE;
}
- if (ref == 0)
+ if (unlikely(ref == 0))
{
/* The item was not a table field and not a reference */
my_error(ER_BAD_FIELD_ERROR, MYF(0),
@@ -7694,13 +8099,13 @@ bool Item_ref::fix_fields(THD *thd, Item **reference)
*/
if (!((*ref)->type() == REF_ITEM &&
((Item_ref *)(*ref))->ref_type() == OUTER_REF) &&
- (((*ref)->with_sum_func && name &&
+ (((*ref)->with_sum_func && name.str &&
!(current_sel->linkage != GLOBAL_OPTIONS_TYPE &&
current_sel->having_fix_field)) ||
!(*ref)->fixed))
{
my_error(ER_ILLEGAL_REFERENCE, MYF(0),
- name, ((*ref)->with_sum_func?
+ name.str, ((*ref)->with_sum_func?
"reference to group function":
"forward reference in item list"));
goto error;
@@ -7845,11 +8250,10 @@ 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 && alias_name_used)
+ !table_name && name.str && alias_name_used)
{
THD *thd= current_thd;
- append_identifier(thd, str, (*ref)->real_item()->name,
- strlen((*ref)->real_item()->name));
+ append_identifier(thd, str, &(*ref)->real_item()->name);
}
else
(*ref)->print(str, query_type);
@@ -7859,11 +8263,11 @@ void Item_ref::print(String *str, enum_query_type query_type)
}
-bool Item_ref::send(Protocol *prot, String *tmp)
+bool Item_ref::send(Protocol *prot, st_value *buffer)
{
if (result_field)
return prot->store(result_field);
- return (*ref)->send(prot, tmp);
+ return (*ref)->send(prot, buffer);
}
@@ -8060,17 +8464,17 @@ void Item_ref::save_org_in_field(Field *field, fast_field_copier optimizer_data)
}
-void Item_ref::make_field(THD *thd, Send_field *field)
+void Item_ref::make_send_field(THD *thd, Send_field *field)
{
- (*ref)->make_field(thd, field);
+ (*ref)->make_send_field(thd, field);
/* Non-zero in case of a view */
- if (name)
+ if (name.str)
field->col_name= name;
if (table_name)
field->table_name= table_name;
if (db_name)
field->db_name= db_name;
- if (orig_field_name)
+ if (orig_field_name.str)
field->org_col_name= orig_field_name;
if (orig_table_name)
field->org_table_name= orig_table_name;
@@ -8177,10 +8581,9 @@ Item_cache_wrapper::Item_cache_wrapper(THD *thd, Item *item_arg):
with_param= orig_item->with_param;
with_field= orig_item->with_field;
name= item_arg->name;
- name_length= item_arg->name_length;
- with_subselect= orig_item->with_subselect;
+ m_with_subquery= orig_item->with_subquery();
- if ((expr_value= Item_cache::get_cache(thd, orig_item)))
+ if ((expr_value= orig_item->get_cache(thd)))
expr_value->setup(thd, orig_item);
fixed= 1;
@@ -8238,7 +8641,7 @@ bool Item_cache_wrapper::fix_fields(THD *thd __attribute__((unused)),
return FALSE;
}
-bool Item_cache_wrapper::send(Protocol *protocol, String *buffer)
+bool Item_cache_wrapper::send(Protocol *protocol, st_value *buffer)
{
if (result_field)
return protocol->store(result_field);
@@ -8569,7 +8972,7 @@ Item* Item_cache_wrapper::get_tmp_table_item(THD *thd)
}
-bool Item_direct_view_ref::send(Protocol *protocol, String *buffer)
+bool Item_direct_view_ref::send(Protocol *protocol, st_value *buffer)
{
if (check_null_ref())
return protocol->store_null();
@@ -8605,12 +9008,11 @@ bool Item_direct_view_ref::fix_fields(THD *thd, Item **reference)
*/
Field *fld= ((Item_field*) ref_item)->field;
DBUG_ASSERT(fld && fld->table);
- if (thd->mark_used_columns == MARK_COLUMNS_READ)
+ if (thd->column_usage == MARK_COLUMNS_READ)
bitmap_set_bit(fld->table->read_set, fld->field_index);
}
}
- else if (!(*ref)->fixed &&
- ((*ref)->fix_fields(thd, ref)))
+ else if ((*ref)->fix_fields_if_needed(thd, ref))
return TRUE;
if (Item_direct_ref::fix_fields(thd, reference))
@@ -8638,7 +9040,7 @@ bool Item_outer_ref::fix_fields(THD *thd, Item **reference)
{
bool err;
/* outer_ref->check_cols() will be made in Item_direct_ref::fix_fields */
- if ((*ref) && !(*ref)->fixed && ((*ref)->fix_fields(thd, reference)))
+ if ((*ref) && (*ref)->fix_fields_if_needed(thd, reference))
return TRUE;
err= Item_direct_ref::fix_fields(thd, reference);
if (!outer_ref)
@@ -8911,38 +9313,38 @@ bool Item_default_value::fix_fields(THD *thd, Item **items)
Item_field *field_arg;
Field *def_field;
DBUG_ASSERT(fixed == 0);
-
- if (!arg)
- {
- fixed= 1;
- return FALSE;
- }
+ DBUG_ASSERT(arg);
/*
DEFAULT() do not need table field so should not ask handler to bring
field value (mark column for read)
*/
- enum_mark_columns save_mark_used_columns= thd->mark_used_columns;
- thd->mark_used_columns= MARK_COLUMNS_NONE;
- if (!arg->fixed && arg->fix_fields(thd, &arg))
+ enum_column_usage save_column_usage= thd->column_usage;
+ /*
+ Fields which has defult value could be read, so it is better hide system
+ invisible columns.
+ */
+ thd->column_usage= COLUMNS_WRITE;
+ if (arg->fix_fields_if_needed(thd, &arg))
{
- thd->mark_used_columns= save_mark_used_columns;
+ thd->column_usage= save_column_usage;
goto error;
}
- thd->mark_used_columns= save_mark_used_columns;
+ thd->column_usage= save_column_usage;
real_arg= arg->real_item();
if (real_arg->type() != FIELD_ITEM)
{
- my_error(ER_NO_DEFAULT_FOR_FIELD, MYF(0), arg->name);
+ my_error(ER_NO_DEFAULT_FOR_FIELD, MYF(0), arg->name.str);
goto error;
}
field_arg= (Item_field *)real_arg;
if ((field_arg->field->flags & NO_DEFAULT_VALUE_FLAG))
{
- my_error(ER_NO_DEFAULT_FOR_FIELD, MYF(0), field_arg->field->field_name);
+ my_error(ER_NO_DEFAULT_FOR_FIELD, MYF(0),
+ field_arg->field->field_name.str);
goto error;
}
if (!(def_field= (Field*) thd->alloc(field_arg->field->size_of())))
@@ -8962,7 +9364,7 @@ bool Item_default_value::fix_fields(THD *thd, Item **items)
expression can do it.
*/
fix_session_vcol_expr_for_read(thd, def_field, def_field->default_value);
- if (thd->mark_used_columns != MARK_COLUMNS_NONE)
+ if (should_mark_column(thd->column_usage))
def_field->default_value->expr->update_used_tables();
def_field->move_field(newptr+1, def_field->maybe_null() ? newptr : 0, 1);
}
@@ -8987,11 +9389,7 @@ void Item_default_value::cleanup()
void Item_default_value::print(String *str, enum_query_type query_type)
{
- if (!arg)
- {
- str->append(STRING_WITH_LEN("default"));
- return;
- }
+ DBUG_ASSERT(arg);
str->append(STRING_WITH_LEN("default("));
/*
We take DEFAULT from a field so do not need it value in case of const
@@ -9005,6 +9403,7 @@ void Item_default_value::print(String *str, enum_query_type query_type)
void Item_default_value::calculate()
{
+ DBUG_ASSERT(arg);
if (field->default_value)
field->set_default();
DEBUG_SYNC(field->table->in_use, "after_Item_default_value_calculate");
@@ -9040,7 +9439,7 @@ bool Item_default_value::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
return Item_field::get_date(ltime, fuzzydate);
}
-bool Item_default_value::send(Protocol *protocol, String *buffer)
+bool Item_default_value::send(Protocol *protocol, st_value *buffer)
{
calculate();
return Item_field::send(protocol, buffer);
@@ -9048,14 +9447,8 @@ bool Item_default_value::send(Protocol *protocol, String *buffer)
int Item_default_value::save_in_field(Field *field_arg, bool no_conversions)
{
- if (arg)
- {
- calculate();
- return Item_field::save_in_field(field_arg, no_conversions);
- }
-
- return field_arg->save_in_field_default_value(context->error_processor ==
- &view_error_processor);
+ calculate();
+ return Item_field::save_in_field(field_arg, no_conversions);
}
table_map Item_default_value::used_tables() const
@@ -9076,13 +9469,7 @@ Item *Item_default_value::transform(THD *thd, Item_transformer transformer,
uchar *args)
{
DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare());
-
- /*
- If the value of arg is NULL, then this object represents a constant,
- so further transformation is unnecessary (and impossible).
- */
- if (!arg)
- return 0;
+ DBUG_ASSERT(arg);
Item *new_item= arg->transform(thd, transformer, args);
if (!new_item)
@@ -9099,57 +9486,6 @@ Item *Item_default_value::transform(THD *thd, Item_transformer transformer,
return (this->*transformer)(thd, args);
}
-void Item_ignore_value::print(String *str, enum_query_type query_type)
-{
- str->append(STRING_WITH_LEN("ignore"));
-}
-
-int Item_ignore_value::save_in_field(Field *field_arg, bool no_conversions)
-{
- return field_arg->save_in_field_ignore_value(context->error_processor ==
- &view_error_processor);
-}
-
-String *Item_ignore_value::val_str(String *str)
-{
- DBUG_ASSERT(0); // never should be called
- null_value= 1;
- return 0;
-}
-
-double Item_ignore_value::val_real()
-{
- DBUG_ASSERT(0); // never should be called
- null_value= 1;
- return 0.0;
-}
-
-longlong Item_ignore_value::val_int()
-{
- DBUG_ASSERT(0); // never should be called
- null_value= 1;
- return 0;
-}
-
-my_decimal *Item_ignore_value::val_decimal(my_decimal *decimal_value)
-{
- DBUG_ASSERT(0); // never should be called
- null_value= 1;
- return 0;
-}
-
-bool Item_ignore_value::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
-{
- DBUG_ASSERT(0); // never should be called
- null_value= 1;
- return TRUE;
-}
-
-bool Item_ignore_value::send(Protocol *protocol, String *buffer)
-{
- DBUG_ASSERT(0); // never should be called
- return TRUE;
-}
bool Item_insert_value::eq(const Item *item, bool binary_cmp) const
{
@@ -9175,7 +9511,7 @@ bool Item_insert_value::fix_fields(THD *thd, Item **items)
if (arg->type() == REF_ITEM)
arg= static_cast<Item_ref *>(arg)->ref[0];
- if (arg->type() != FIELD_ITEM)
+ if (unlikely(arg->type() != FIELD_ITEM))
{
my_error(ER_BAD_FIELD_ERROR, MYF(0), "", "VALUES() function");
return TRUE;
@@ -9200,7 +9536,7 @@ bool Item_insert_value::fix_fields(THD *thd, Item **items)
static uchar null_bit=1;
/* charset doesn't matter here */
Field *tmp_field= new Field_string(0, 0, &null_bit, 1, Field::NONE,
- field_arg->field->field_name, &my_charset_bin);
+ &field_arg->field->field_name, &my_charset_bin);
if (tmp_field)
{
tmp_field->init(field_arg->field->table);
@@ -9214,7 +9550,7 @@ bool Item_insert_value::fix_fields(THD *thd, Item **items)
void Item_insert_value::print(String *str, enum_query_type query_type)
{
- str->append(STRING_WITH_LEN("values("));
+ str->append(STRING_WITH_LEN("value("));
arg->print(str, query_type);
str->append(')');
}
@@ -9250,15 +9586,16 @@ void Item_trigger_field::setup_field(THD *thd, TABLE *table,
So instead we do it in Table_triggers_list::mark_fields_used()
method which is called during execution of these statements.
*/
- enum_mark_columns save_mark_used_columns= thd->mark_used_columns;
- thd->mark_used_columns= MARK_COLUMNS_NONE;
+ enum_column_usage saved_column_usage= thd->column_usage;
+ thd->column_usage= want_privilege == SELECT_ACL ? COLUMNS_READ
+ : COLUMNS_WRITE;
/*
Try to find field by its name and if it will be found
set field_idx properly.
*/
- (void)find_field_in_table(thd, table, field_name, (uint) strlen(field_name),
+ (void)find_field_in_table(thd, table, field_name.str, field_name.length,
0, &field_idx);
- thd->mark_used_columns= save_mark_used_columns;
+ thd->column_usage= saved_column_usage;
triggers= table->triggers;
table_grants= table_grant_info;
}
@@ -9268,8 +9605,8 @@ bool Item_trigger_field::eq(const Item *item, bool binary_cmp) const
{
return item->type() == TRIGGER_FIELD_ITEM &&
row_version == ((Item_trigger_field *)item)->row_version &&
- !my_strcasecmp(system_charset_info, field_name,
- ((Item_trigger_field *)item)->field_name);
+ !lex_string_cmp(system_charset_info, &field_name,
+ &((Item_trigger_field *)item)->field_name);
}
@@ -9285,17 +9622,11 @@ void Item_trigger_field::set_required_privilege(bool rw)
bool Item_trigger_field::set_value(THD *thd, sp_rcontext * /*ctx*/, Item **it)
{
- Item *item= sp_prepare_func_item(thd, it);
+ Item *item= thd->sp_prepare_func_item(it);
- if (!item)
+ if (!item || fix_fields_if_needed(thd, NULL))
return true;
- if (!fixed)
- {
- if (fix_fields(thd, NULL))
- return true;
- }
-
// NOTE: field->table->copy_blobs should be false here, but let's
// remember the value at runtime to avoid subtle bugs.
bool copy_blobs_saved= field->table->copy_blobs;
@@ -9323,7 +9654,7 @@ bool Item_trigger_field::fix_fields(THD *thd, Item **items)
/* Set field. */
- if (field_idx != (uint)-1)
+ if (likely(field_idx != (uint)-1))
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/*
@@ -9335,9 +9666,11 @@ bool Item_trigger_field::fix_fields(THD *thd, Item **items)
{
table_grants->want_privilege= want_privilege;
- if (check_grant_column(thd, table_grants, triggers->trigger_table->s->db.str,
- triggers->trigger_table->s->table_name.str, field_name,
- strlen(field_name), thd->security_ctx))
+ if (check_grant_column(thd, table_grants,
+ triggers->trigger_table->s->db.str,
+ triggers->trigger_table->s->table_name.str,
+ field_name.str, field_name.length,
+ thd->security_ctx))
return TRUE;
}
#endif // NO_EMBEDDED_ACCESS_CHECKS
@@ -9349,7 +9682,7 @@ bool Item_trigger_field::fix_fields(THD *thd, Item **items)
return FALSE;
}
- my_error(ER_BAD_FIELD_ERROR, MYF(0), field_name,
+ my_error(ER_BAD_FIELD_ERROR, MYF(0), field_name.str,
(row_version == NEW_ROW) ? "NEW" : "OLD");
return TRUE;
}
@@ -9359,14 +9692,14 @@ void Item_trigger_field::print(String *str, enum_query_type query_type)
{
str->append((row_version == NEW_ROW) ? "NEW" : "OLD", 3);
str->append('.');
- str->append(field_name);
+ str->append(&field_name);
}
bool Item_trigger_field::check_vcol_func_processor(void *arg)
{
const char *ver= row_version == NEW_ROW ? "NEW." : "OLD.";
- return mark_unsupported_function(ver, field_name, arg, VCOL_IMPOSSIBLE);
+ return mark_unsupported_function(ver, field_name.str, arg, VCOL_IMPOSSIBLE);
}
@@ -9401,104 +9734,14 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item)
Item *item= *ref;
if (item->basic_const_item())
return; // Can't be better
-
- Item *new_item= NULL;
- Item_result res_type= item_cmp_type(comp_item, item);
- char *name= item->name; // Alloced on THD::mem_root
- MEM_ROOT *mem_root= thd->mem_root;
-
- switch (res_type) {
- case TIME_RESULT:
- {
- enum_field_types type= item->field_type_for_temporal_comparison(comp_item);
- longlong value= item->val_temporal_packed(type);
- if (item->null_value)
- new_item= new (mem_root) Item_null(thd, name);
- else
- {
- Item_cache_temporal *cache= new (mem_root) Item_cache_temporal(thd, type);
- cache->store_packed(value, item);
- new_item= cache;
- }
- break;
- }
- case STRING_RESULT:
+ Type_handler_hybrid_field_type cmp(comp_item->type_handler_for_comparison());
+ if (!cmp.aggregate_for_comparison(item->type_handler_for_comparison()))
{
- char buff[MAX_FIELD_WIDTH];
- String tmp(buff,sizeof(buff),&my_charset_bin),*result;
- result=item->val_str(&tmp);
- if (item->null_value)
- new_item= new (mem_root) Item_null(thd, name);
- else
- {
- uint length= result->length();
- char *tmp_str= thd->strmake(result->ptr(), length);
- new_item= new (mem_root) Item_string(thd, name, tmp_str, length, result->charset());
- }
- break;
- }
- case INT_RESULT:
- {
- longlong result=item->val_int();
- uint length=item->max_length;
- bool null_value=item->null_value;
- new_item= (null_value ? (Item*) new (mem_root) Item_null(thd, name) :
- (Item*) new (mem_root) Item_int(thd, name, result, length));
- break;
- }
- case ROW_RESULT:
- if (item->type() == Item::ROW_ITEM && comp_item->type() == Item::ROW_ITEM)
- {
- /*
- Substitute constants only in Item_row's. Don't affect other Items
- with ROW_RESULT (eg Item_singlerow_subselect).
-
- For such Items more optimal is to detect if it is constant and replace
- it with Item_row. This would optimize queries like this:
- SELECT * FROM t1 WHERE (a,b) = (SELECT a,b FROM t2 LIMIT 1);
- */
- Item_row *item_row= (Item_row*) item;
- Item_row *comp_item_row= (Item_row*) comp_item;
- uint col;
- new_item= 0;
- /*
- If item and comp_item are both Item_row's and have same number of cols
- then process items in Item_row one by one.
- We can't ignore NULL values here as this item may be used with <=>, in
- which case NULL's are significant.
- */
- DBUG_ASSERT(item->result_type() == comp_item->result_type());
- DBUG_ASSERT(item_row->cols() == comp_item_row->cols());
- col= item_row->cols();
- while (col-- > 0)
- resolve_const_item(thd, item_row->addr(col),
- comp_item_row->element_index(col));
- break;
- }
- /* Fallthrough */
- case REAL_RESULT:
- { // It must REAL_RESULT
- double result= item->val_real();
- uint length=item->max_length,decimals=item->decimals;
- bool null_value=item->null_value;
- new_item= (null_value ? (Item*) new (mem_root) Item_null(thd, name) : (Item*)
- new (mem_root) Item_float(thd, name, result, decimals, length));
- break;
- }
- case DECIMAL_RESULT:
- {
- my_decimal decimal_value;
- my_decimal *result= item->val_decimal(&decimal_value);
- uint length= item->max_length, decimals= item->decimals;
- bool null_value= item->null_value;
- new_item= (null_value ?
- (Item*) new (mem_root) Item_null(thd, name) :
- (Item*) new (mem_root) Item_decimal(thd, name, result, length, decimals));
- break;
- }
+ Item *new_item= cmp.type_handler()->
+ make_const_item_for_comparison(thd, item, comp_item);
+ if (new_item)
+ thd->change_item_tree(ref, new_item);
}
- if (new_item)
- thd->change_item_tree(ref, new_item);
}
/**
@@ -9595,35 +9838,6 @@ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item)
return 0;
}
-/**
- Get a cache item of given type.
-
- @param item value to be cached
- @param type required type of cache
-
- @return cache item
-*/
-
-Item_cache* Item_cache::get_cache(THD *thd, const Item *item,
- const Item_result type, const enum_field_types f_type)
-{
- MEM_ROOT *mem_root= thd->mem_root;
- switch (type) {
- case INT_RESULT:
- return new (mem_root) Item_cache_int(thd, f_type);
- case REAL_RESULT:
- return new (mem_root) Item_cache_real(thd);
- case DECIMAL_RESULT:
- return new (mem_root) Item_cache_decimal(thd);
- case STRING_RESULT:
- return new (mem_root) Item_cache_str(thd, item);
- case ROW_RESULT:
- return new (mem_root) Item_cache_row(thd);
- case TIME_RESULT:
- return new (mem_root) Item_cache_temporal(thd, f_type);
- }
- return 0; // Impossible
-}
void Item_cache::store(Item *item)
{
@@ -9743,19 +9957,19 @@ Item *Item_cache_int::convert_to_basic_const_item(THD *thd)
}
-Item_cache_temporal::Item_cache_temporal(THD *thd,
- enum_field_types field_type_arg):
- Item_cache_int(thd, field_type_arg)
+Item_cache_temporal::Item_cache_temporal(THD *thd, const Type_handler *handler)
+ :Item_cache_int(thd, handler)
{
- if (mysql_type_to_time_type(Item_cache_temporal::field_type()) ==
- MYSQL_TIMESTAMP_ERROR)
- set_handler_by_field_type(MYSQL_TYPE_DATETIME);
+ if (mysql_timestamp_type() == MYSQL_TIMESTAMP_ERROR)
+ set_handler(&type_handler_datetime2);
}
longlong Item_cache_temporal::val_datetime_packed()
{
DBUG_ASSERT(fixed == 1);
+ if (Item_cache_temporal::field_type() == MYSQL_TYPE_TIME)
+ return Item::val_datetime_packed(); // TIME-to-DATETIME conversion needed
if ((!value_cached && !cache_value()) || null_value)
{
null_value= TRUE;
@@ -9768,7 +9982,8 @@ longlong Item_cache_temporal::val_datetime_packed()
longlong Item_cache_temporal::val_time_packed()
{
DBUG_ASSERT(fixed == 1);
- DBUG_ASSERT(Item_cache_temporal::field_type() == MYSQL_TYPE_TIME);
+ if (Item_cache_temporal::field_type() != MYSQL_TYPE_TIME)
+ return Item::val_time_packed(); // DATETIME-to-TIME conversion needed
if ((!value_cached && !cache_value()) || null_value)
{
null_value= TRUE;
@@ -9831,21 +10046,18 @@ bool Item_cache_temporal::cache_value()
if (!example)
return false;
value_cached= true;
+ value= example->val_datetime_packed_result();
+ null_value= example->null_value;
+ return true;
+}
- MYSQL_TIME ltime;
- uint fuzzydate= TIME_FUZZY_DATES | TIME_INVALID_DATES;
- if (Item_cache_temporal::field_type() == MYSQL_TYPE_TIME)
- fuzzydate|= TIME_TIME_ONLY;
- value= 0;
- if (!example->get_date_result(&ltime, fuzzydate))
- {
- if (ltime.time_type == MYSQL_TIMESTAMP_TIME &&
- !(fuzzydate & TIME_TIME_ONLY) &&
- convert_time_to_datetime(current_thd, &ltime, fuzzydate))
- return true;
- value= pack_time(&ltime);
- }
+bool Item_cache_time::cache_value()
+{
+ if (!example)
+ return false;
+ value_cached= true;
+ value= example->val_time_packed_result();
null_value= example->null_value;
return true;
}
@@ -9858,21 +10070,10 @@ bool Item_cache_temporal::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
if (!has_value())
{
bzero((char*) ltime,sizeof(*ltime));
- return null_value= true;
+ return (null_value= true);
}
- unpack_time(value, ltime);
- ltime->time_type= mysql_type_to_time_type(field_type());
- if (ltime->time_type == MYSQL_TIMESTAMP_TIME)
- {
- if (fuzzydate & TIME_TIME_ONLY)
- {
- ltime->hour+= (ltime->month*32+ltime->day)*24;
- ltime->month= ltime->day= 0;
- }
- else if (convert_time_to_datetime(current_thd, ltime, fuzzydate))
- return true;
- }
+ unpack_time(value, ltime, mysql_timestamp_type());
return 0;
}
@@ -9900,8 +10101,8 @@ void Item_cache_temporal::store_packed(longlong val_arg, Item *example_arg)
Item *Item_cache_temporal::clone_item(THD *thd)
{
- Item_cache_temporal *item= new (thd->mem_root)
- Item_cache_temporal(thd, Item_cache_temporal::field_type());
+ Item_cache *tmp= type_handler()->Item_get_cache(thd, this);
+ Item_cache_temporal *item= static_cast<Item_cache_temporal*>(tmp);
item->store_packed(value, example);
return item;
}
@@ -9914,26 +10115,32 @@ Item *Item_cache_temporal::convert_to_basic_const_item(THD *thd)
if (!value_cached)
cache_value();
if (null_value)
- new_item= (Item*) new (thd->mem_root) Item_null(thd);
+ return new (thd->mem_root) Item_null(thd);
else
- {
- MYSQL_TIME ltime;
- if (Item_cache_temporal::field_type() == MYSQL_TYPE_TIME)
- {
- unpack_time(val_time_packed(), &ltime);
- new_item= (Item*) new (thd->mem_root) Item_time_literal(thd, &ltime,
- decimals);
- }
- else
- {
- unpack_time(val_datetime_packed(), &ltime);
- new_item= (Item*) new (thd->mem_root) Item_datetime_literal(thd, &ltime,
- decimals);
- }
- }
+ return make_literal(thd);
return new_item;
}
+Item *Item_cache_datetime::make_literal(THD *thd)
+{
+ MYSQL_TIME ltime;
+ unpack_time(val_datetime_packed(), &ltime, MYSQL_TIMESTAMP_DATETIME);
+ return new (thd->mem_root) Item_datetime_literal(thd, &ltime, decimals);
+}
+
+Item *Item_cache_date::make_literal(THD *thd)
+{
+ MYSQL_TIME ltime;
+ unpack_time(val_datetime_packed(), &ltime, MYSQL_TIMESTAMP_DATE);
+ return new (thd->mem_root) Item_date_literal(thd, &ltime);
+}
+
+Item *Item_cache_time::make_literal(THD *thd)
+{
+ MYSQL_TIME ltime;
+ unpack_time(val_time_packed(), &ltime, MYSQL_TIMESTAMP_TIME);
+ return new (thd->mem_root) Item_time_literal(thd, &ltime, decimals);
+}
bool Item_cache_real::cache_value()
{
@@ -9963,7 +10170,7 @@ longlong Item_cache_real::val_int()
}
-String* Item_cache_real::val_str(String *str)
+String* Item_cache_double::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
if (!has_value())
@@ -9973,6 +10180,16 @@ String* Item_cache_real::val_str(String *str)
}
+String* Item_cache_float::val_str(String *str)
+{
+ DBUG_ASSERT(fixed == 1);
+ if (!has_value())
+ return NULL;
+ Float(value).to_string(str, decimals);
+ return str;
+}
+
+
my_decimal *Item_cache_real::val_decimal(my_decimal *decimal_val)
{
DBUG_ASSERT(fixed == 1);
@@ -10178,7 +10395,7 @@ bool Item_cache_row::setup(THD *thd, Item *item)
{
Item *el= item->element_index(i);
Item_cache *tmp;
- if (!(tmp= values[i]= Item_cache::get_cache(thd, el)))
+ if (!(tmp= values[i]= el->get_cache(thd)))
return 1;
tmp->setup(thd, el);
}
@@ -10281,392 +10498,6 @@ void Item_cache_row::set_null()
};
-Item_type_holder::Item_type_holder(THD *thd, Item *item)
- :Item(thd, item),
- Type_handler_hybrid_real_field_type(get_real_type(item)),
- enum_set_typelib(0),
- geometry_type(Field::GEOM_GEOMETRY)
-{
- DBUG_ASSERT(item->fixed);
- maybe_null= item->maybe_null;
- collation.set(item->collation);
- get_full_info(item);
- /**
- Field::result_merge_type(real_field_type()) should be equal to
- result_type(), with one exception when "this" is a Item_field for
- a BIT field:
- - Field_bit::result_type() returns INT_RESULT, so does its Item_field.
- - Field::result_merge_type(MYSQL_TYPE_BIT) returns STRING_RESULT.
- Perhaps we need a new method in Type_handler to cover these type
- merging rules for UNION.
- */
- DBUG_ASSERT(real_field_type() == MYSQL_TYPE_BIT ||
- Item_type_holder::result_type() ==
- Field::result_merge_type(Item_type_holder::real_field_type()));
- /* fix variable decimals which always is NOT_FIXED_DEC */
- if (Field::result_merge_type(real_field_type()) == INT_RESULT)
- decimals= 0;
- prev_decimal_int_part= item->decimal_int_part();
-#ifdef HAVE_SPATIAL
- if (item->field_type() == MYSQL_TYPE_GEOMETRY)
- geometry_type= item->get_geometry_type();
-#endif /* HAVE_SPATIAL */
-}
-
-
-/**
- Find real field type of item.
-
- @return
- type of field which should be created to store item value
-*/
-
-enum_field_types Item_type_holder::get_real_type(Item *item)
-{
- if (item->type() == REF_ITEM)
- item= item->real_item();
- switch(item->type())
- {
- case FIELD_ITEM:
- {
- /*
- Item_field::field_type ask Field_type() but sometimes field return
- a different type, like for enum/set, so we need to ask real type.
- */
- Field *field= ((Item_field *) item)->field;
- enum_field_types type= field->real_type();
- if (field->is_created_from_null_item)
- return MYSQL_TYPE_NULL;
- /* work around about varchar type field detection */
- if (type == MYSQL_TYPE_STRING && field->type() == MYSQL_TYPE_VAR_STRING)
- return MYSQL_TYPE_VAR_STRING;
- return type;
- }
- case SUM_FUNC_ITEM:
- {
- /*
- Argument of aggregate function sometimes should be asked about field
- type
- */
- Item_sum *item_sum= (Item_sum *) item;
- if (item_sum->keep_field_type())
- return get_real_type(item_sum->get_arg(0));
- break;
- }
- case FUNC_ITEM:
- if (((Item_func *) item)->functype() == Item_func::GUSERVAR_FUNC)
- {
- /*
- There are work around of problem with changing variable type on the
- fly and variable always report "string" as field type to get
- acceptable information for client in send_field, so we make field
- type from expression type.
- */
- switch (item->result_type()) {
- case STRING_RESULT:
- return MYSQL_TYPE_VARCHAR;
- case INT_RESULT:
- return MYSQL_TYPE_LONGLONG;
- case REAL_RESULT:
- return MYSQL_TYPE_DOUBLE;
- case DECIMAL_RESULT:
- return MYSQL_TYPE_NEWDECIMAL;
- case ROW_RESULT:
- case TIME_RESULT:
- DBUG_ASSERT(0);
- return MYSQL_TYPE_VARCHAR;
- }
- }
- break;
- case TYPE_HOLDER:
- /*
- Item_type_holder and Item_blob should not appear in this context.
- In case they for some reasons do, returning field_type() is wrong anyway.
- They must return Item_type_holder::real_field_type() instead, to make
- the code in sql_type.cc and sql_type.h happy, as it expectes
- Field::real_type()-compatible rather than Field::field_type()-compatible
- valies in some places, and may in the future add some asserts preventing
- use of field_type() instead of real_type() and the other way around.
- */
- DBUG_ASSERT(0);
- default:
- break;
- }
- return item->field_type();
-}
-
-/**
- Find field type which can carry current Item_type_holder type and
- type of given Item.
-
- @param thd thread handler
- @param item given item to join its parameters with this item ones
-
- @retval
- TRUE error - types are incompatible
- @retval
- FALSE OK
-*/
-
-bool Item_type_holder::join_types(THD *thd, Item *item)
-{
- uint max_length_orig= max_length;
- uint decimals_orig= decimals;
- DBUG_ENTER("Item_type_holder::join_types");
- DBUG_PRINT("info:", ("was type %d len %d, dec %d name %s",
- real_field_type(), max_length, decimals,
- (name ? name : "<NULL>")));
- DBUG_PRINT("info:", ("in type %d len %d, dec %d",
- get_real_type(item),
- item->max_length, item->decimals));
- set_handler_by_real_type(Field::field_type_merge(real_field_type(),
- get_real_type(item)));
- {
- uint item_decimals= item->decimals;
- /* fix variable decimals which always is NOT_FIXED_DEC */
- if (Field::result_merge_type(real_field_type()) == INT_RESULT)
- item_decimals= 0;
- decimals= MY_MAX(decimals, item_decimals);
- }
-
- if (Item_type_holder::field_type() == FIELD_TYPE_GEOMETRY)
- geometry_type=
- Field_geom::geometry_type_merge(geometry_type, item->get_geometry_type());
-
- if (Field::result_merge_type(real_field_type()) == DECIMAL_RESULT)
- {
- collation.set_numeric();
- decimals= MY_MIN(MY_MAX(decimals, item->decimals), DECIMAL_MAX_SCALE);
- int item_int_part= item->decimal_int_part();
- int item_prec = MY_MAX(prev_decimal_int_part, item_int_part) + decimals;
- int precision= MY_MIN(item_prec, DECIMAL_MAX_PRECISION);
- unsigned_flag&= item->unsigned_flag;
- max_length= my_decimal_precision_to_length_no_truncation(precision,
- decimals,
- unsigned_flag);
- }
-
- switch (Field::result_merge_type(real_field_type()))
- {
- case STRING_RESULT:
- {
- const char *old_cs, *old_derivation;
- uint32 old_max_chars= max_length / collation.collation->mbmaxlen;
- old_cs= collation.collation->name;
- old_derivation= collation.derivation_name();
- if (collation.aggregate(item->collation, MY_COLL_ALLOW_CONV))
- {
- my_error(ER_CANT_AGGREGATE_2COLLATIONS, MYF(0),
- old_cs, old_derivation,
- item->collation.collation->name,
- item->collation.derivation_name(),
- "UNION");
- DBUG_RETURN(TRUE);
- }
- /*
- To figure out max_length, we have to take into account possible
- expansion of the size of the values because of character set
- conversions.
- */
- if (collation.collation != &my_charset_bin)
- {
- max_length= MY_MAX(old_max_chars * collation.collation->mbmaxlen,
- display_length(item) /
- item->collation.collation->mbmaxlen *
- collation.collation->mbmaxlen);
- }
- else
- set_if_bigger(max_length, display_length(item));
- break;
- }
- case REAL_RESULT:
- {
- if (decimals != NOT_FIXED_DEC)
- {
- /*
- For FLOAT(M,D)/DOUBLE(M,D) do not change precision
- if both fields have the same M and D
- */
- if (item->max_length != max_length_orig ||
- item->decimals != decimals_orig)
- {
- int delta1= max_length_orig - decimals_orig;
- int delta2= item->max_length - item->decimals;
- max_length= MY_MAX(delta1, delta2) + decimals;
- if (Item_type_holder::real_field_type() == MYSQL_TYPE_FLOAT &&
- max_length > FLT_DIG + 2)
- {
- max_length= MAX_FLOAT_STR_LENGTH;
- decimals= NOT_FIXED_DEC;
- }
- else if (Item_type_holder::real_field_type() == MYSQL_TYPE_DOUBLE &&
- max_length > DBL_DIG + 2)
- {
- max_length= MAX_DOUBLE_STR_LENGTH;
- decimals= NOT_FIXED_DEC;
- }
- }
- }
- else
- max_length= (Item_type_holder::field_type() == MYSQL_TYPE_FLOAT) ?
- FLT_DIG+6 : DBL_DIG+7;
- break;
- }
- default:
- if (real_field_type() == MYSQL_TYPE_YEAR)
- max_length= MY_MAX(max_length, item->max_length);
- else
- max_length= MY_MAX(max_length, display_length(item));
- };
- maybe_null|= item->maybe_null;
- get_full_info(item);
-
- /* Remember decimal integer part to be used in DECIMAL_RESULT handleng */
- prev_decimal_int_part= decimal_int_part();
- DBUG_PRINT("info", ("become type: %d len: %u dec: %u",
- (int) real_field_type(), max_length, (uint) decimals));
- DBUG_RETURN(FALSE);
-}
-
-/**
- Calculate length for merging result for given Item type.
-
- @param item Item for length detection
-
- @return
- length
-*/
-
-uint32 Item_type_holder::display_length(Item *item)
-{
- if (item->type() == Item::FIELD_ITEM)
- return ((Item_field *)item)->max_disp_length();
-
- switch (item->field_type())
- {
- case MYSQL_TYPE_DECIMAL:
- case MYSQL_TYPE_TIMESTAMP:
- case MYSQL_TYPE_DATE:
- case MYSQL_TYPE_TIME:
- case MYSQL_TYPE_DATETIME:
- case MYSQL_TYPE_YEAR:
- case MYSQL_TYPE_NEWDATE:
- case MYSQL_TYPE_VARCHAR:
- case MYSQL_TYPE_BIT:
- case MYSQL_TYPE_NEWDECIMAL:
- 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:
- return item->max_length;
- case MYSQL_TYPE_TINY:
- return 4;
- case MYSQL_TYPE_SHORT:
- return 6;
- case MYSQL_TYPE_LONG:
- return MY_INT32_NUM_DECIMAL_DIGITS;
- case MYSQL_TYPE_FLOAT:
- return 25;
- case MYSQL_TYPE_DOUBLE:
- return 53;
- case MYSQL_TYPE_NULL:
- return 0;
- case MYSQL_TYPE_LONGLONG:
- return 20;
- case MYSQL_TYPE_INT24:
- return 8;
- default:
- DBUG_ASSERT(0); // we should never go there
- return 0;
- }
-}
-
-
-/**
- Make temporary table field according collected information about type
- of UNION result.
-
- @param table temporary table for which we create fields
-
- @return
- created field
-*/
-
-Field *Item_type_holder::make_field_by_type(TABLE *table)
-{
- /*
- The field functions defines a field to be not null if null_ptr is not 0
- */
- uchar *null_ptr= maybe_null ? (uchar*) "" : 0;
- Field *field;
-
- switch (Item_type_holder::real_field_type()) {
- case MYSQL_TYPE_ENUM:
- DBUG_ASSERT(enum_set_typelib);
- field= new Field_enum((uchar *) 0, max_length, null_ptr, 0,
- Field::NONE, name,
- get_enum_pack_length(enum_set_typelib->count),
- enum_set_typelib, collation.collation);
- if (field)
- field->init(table);
- return field;
- case MYSQL_TYPE_SET:
- DBUG_ASSERT(enum_set_typelib);
- field= new Field_set((uchar *) 0, max_length, null_ptr, 0,
- Field::NONE, name,
- get_set_pack_length(enum_set_typelib->count),
- enum_set_typelib, collation.collation);
- if (field)
- field->init(table);
- return field;
- case MYSQL_TYPE_NULL:
- return make_string_field(table);
- default:
- break;
- }
- return tmp_table_field_from_field_type(table, false, true);
-}
-
-
-/**
- Get full information from Item about enum/set fields to be able to create
- them later.
-
- @param item Item for information collection
-*/
-void Item_type_holder::get_full_info(Item *item)
-{
- if (Item_type_holder::real_field_type() == MYSQL_TYPE_ENUM ||
- Item_type_holder::real_field_type() == MYSQL_TYPE_SET)
- {
- if (item->type() == Item::SUM_FUNC_ITEM &&
- (((Item_sum*)item)->sum_func() == Item_sum::MAX_FUNC ||
- ((Item_sum*)item)->sum_func() == Item_sum::MIN_FUNC))
- item = ((Item_sum*)item)->get_arg(0);
- /*
- We can have enum/set type after merging only if we have one enum|set
- field (or MIN|MAX(enum|set field)) and number of NULL fields
- */
- DBUG_ASSERT((enum_set_typelib &&
- get_real_type(item) == MYSQL_TYPE_NULL) ||
- (!enum_set_typelib &&
- item->real_item()->type() == Item::FIELD_ITEM &&
- (get_real_type(item->real_item()) == MYSQL_TYPE_ENUM ||
- get_real_type(item->real_item()) == MYSQL_TYPE_SET) &&
- ((Field_enum*)((Item_field *) item->real_item())->field)->typelib));
- if (!enum_set_typelib)
- {
- enum_set_typelib= ((Field_enum*)((Item_field *) item->real_item())->field)->typelib;
- }
- }
-}
-
-
double Item_type_holder::val_real()
{
DBUG_ASSERT(0); // should never be called
@@ -10692,6 +10523,12 @@ String *Item_type_holder::val_str(String*)
return 0;
}
+bool Item_type_holder::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+{
+ DBUG_ASSERT(0); // should never be called
+ return true;
+}
+
void Item_result_field::cleanup()
{
DBUG_ENTER("Item_result_field::cleanup()");
@@ -10883,6 +10720,7 @@ Item_field::excl_dep_on_grouping_fields(st_select_lex *sel)
return find_matching_grouping_field(this, sel) != NULL;
}
+
void Item::register_in(THD *thd)
{
next= thd->free_list;
diff --git a/sql/item.h b/sql/item.h
index 4a761bfd70a..1b77a2f5466 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -2,7 +2,7 @@
#define SQL_ITEM_INCLUDED
/* Copyright (c) 2000, 2017, 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
@@ -57,12 +57,46 @@ struct st_value
C_MODE_END
+
+class Value: public st_value
+{
+public:
+ bool is_null() const { return m_type == DYN_COL_NULL; }
+ bool is_longlong() const
+ {
+ return m_type == DYN_COL_UINT || m_type == DYN_COL_INT;
+ }
+ bool is_double() const { return m_type == DYN_COL_DOUBLE; }
+ bool is_temporal() const { return m_type == DYN_COL_DATETIME; }
+ bool is_string() const { return m_type == DYN_COL_STRING; }
+ bool is_decimal() const { return m_type == DYN_COL_DECIMAL; }
+};
+
+
+template<size_t buffer_size>
+class ValueBuffer: public Value
+{
+ char buffer[buffer_size];
+ void reset_buffer()
+ {
+ m_string.set(buffer, buffer_size, &my_charset_bin);
+ }
+public:
+ ValueBuffer()
+ {
+ reset_buffer();
+ }
+};
+
+
#ifdef DBUG_OFF
static inline const char *dbug_print_item(Item *item) { return NULL; }
#else
const char *dbug_print_item(Item *item);
#endif
+class Virtual_tmp_table;
+class sp_head;
class Protocol;
struct TABLE_LIST;
void item_init(void); /* Init item functions */
@@ -100,15 +134,6 @@ enum precedence {
HIGHEST_PRECEDENCE
};
-typedef Bounds_checked_array<Item*> Ref_ptr_array;
-
-static inline uint32
-char_to_byte_length_safe(size_t char_length_arg, uint32 mbmaxlen_arg)
-{
- ulonglong tmp= ((ulonglong) char_length_arg) * mbmaxlen_arg;
- return tmp > UINT_MAX32 ? UINT_MAX32 : static_cast<uint32>(tmp);
-}
-
bool mark_unsupported_function(const char *where, void *store, uint result);
/* convenience helper for mark_unsupported_function() above */
@@ -119,125 +144,13 @@ bool mark_unsupported_function(const char *w1, const char *w2,
#define SPLIT_SUM_SKIP_REGISTERED 1 /* Skip registered funcs */
#define SPLIT_SUM_SELECT 2 /* SELECT item; Split all parts */
-/*
- "Declared Type Collation"
- A combination of collation and its derivation.
-
- Flags for collation aggregation modes:
- MY_COLL_ALLOW_SUPERSET_CONV - allow conversion to a superset
- MY_COLL_ALLOW_COERCIBLE_CONV - allow conversion of a coercible value
- (i.e. constant).
- MY_COLL_ALLOW_CONV - allow any kind of conversion
- (combination of the above two)
- MY_COLL_ALLOW_NUMERIC_CONV - if all items were numbers, convert to
- @@character_set_connection
- MY_COLL_DISALLOW_NONE - don't allow return DERIVATION_NONE
- (e.g. when aggregating for comparison)
- MY_COLL_CMP_CONV - combination of MY_COLL_ALLOW_CONV
- and MY_COLL_DISALLOW_NONE
-*/
-
-#define MY_COLL_ALLOW_SUPERSET_CONV 1
-#define MY_COLL_ALLOW_COERCIBLE_CONV 2
-#define MY_COLL_DISALLOW_NONE 4
-#define MY_COLL_ALLOW_NUMERIC_CONV 8
-
-#define MY_COLL_ALLOW_CONV (MY_COLL_ALLOW_SUPERSET_CONV | MY_COLL_ALLOW_COERCIBLE_CONV)
-#define MY_COLL_CMP_CONV (MY_COLL_ALLOW_CONV | MY_COLL_DISALLOW_NONE)
#define NO_EXTRACTION_FL (1 << 6)
#define FULL_EXTRACTION_FL (1 << 7)
#define SUBSTITUTION_FL (1 << 8)
#define EXTRACTION_MASK (NO_EXTRACTION_FL | FULL_EXTRACTION_FL)
-class DTCollation {
-public:
- CHARSET_INFO *collation;
- enum Derivation derivation;
- uint repertoire;
-
- void set_repertoire_from_charset(CHARSET_INFO *cs)
- {
- repertoire= cs->state & MY_CS_PUREASCII ?
- MY_REPERTOIRE_ASCII : MY_REPERTOIRE_UNICODE30;
- }
- DTCollation()
- {
- collation= &my_charset_bin;
- derivation= DERIVATION_NONE;
- repertoire= MY_REPERTOIRE_UNICODE30;
- }
- DTCollation(CHARSET_INFO *collation_arg, Derivation derivation_arg)
- {
- collation= collation_arg;
- derivation= derivation_arg;
- set_repertoire_from_charset(collation_arg);
- }
- DTCollation(CHARSET_INFO *collation_arg,
- Derivation derivation_arg,
- uint 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;
- }
- void set(CHARSET_INFO *collation_arg, Derivation derivation_arg)
- {
- collation= collation_arg;
- derivation= derivation_arg;
- set_repertoire_from_charset(collation_arg);
- }
- void set(CHARSET_INFO *collation_arg,
- Derivation derivation_arg,
- uint 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;
- set_repertoire_from_charset(collation_arg);
- }
- void set(Derivation derivation_arg)
- { derivation= derivation_arg; }
- bool aggregate(const DTCollation &dt, uint flags= 0);
- bool set(DTCollation &dt1, DTCollation &dt2, uint flags= 0)
- { set(dt1); return aggregate(dt2, flags); }
- const char *derivation_name() const
- {
- switch(derivation)
- {
- case DERIVATION_NUMERIC: return "NUMERIC";
- case DERIVATION_IGNORABLE: return "IGNORABLE";
- case DERIVATION_COERCIBLE: return "COERCIBLE";
- case DERIVATION_IMPLICIT: return "IMPLICIT";
- case DERIVATION_SYSCONST: return "SYSCONST";
- case DERIVATION_EXPLICIT: return "EXPLICIT";
- case DERIVATION_NONE: return "NONE";
- default: return "UNKNOWN";
- }
- }
- int sortcmp(const String *s, const String *t) const
- {
- return collation->coll->strnncollsp(collation,
- (uchar *) s->ptr(), s->length(),
- (uchar *) t->ptr(), t->length());
- }
-};
-
+extern const char *item_empty_name;
void dummy_error_processor(THD *thd, void *data);
@@ -395,6 +308,28 @@ public:
}
};
+class Name_resolution_context_backup
+{
+ Name_resolution_context &ctx;
+ TABLE_LIST &table_list;
+ table_map save_map;
+ Name_resolution_context_state ctx_state;
+
+public:
+ Name_resolution_context_backup(Name_resolution_context &_ctx, TABLE_LIST &_table_list)
+ : ctx(_ctx), table_list(_table_list), save_map(_table_list.map)
+ {
+ ctx_state.save_state(&ctx, &table_list);
+ ctx.table_list= &table_list;
+ ctx.first_name_resolution_table= &table_list;
+ }
+ ~Name_resolution_context_backup()
+ {
+ ctx_state.restore_state(&ctx, &table_list);
+ table_list.map= save_map;
+ }
+};
+
/*
This enum is used to report information about monotonicity of function
@@ -435,6 +370,65 @@ typedef enum monotonicity_info
class sp_rcontext;
+/**
+ A helper class to collect different behavior of various kinds of SP variables:
+ - local SP variables and SP parameters
+ - PACKAGE BODY routine variables
+ - (there will be more kinds in the future)
+*/
+
+class Sp_rcontext_handler
+{
+public:
+ virtual ~Sp_rcontext_handler() {}
+ /**
+ A prefix used for SP variable names in queries:
+ - EXPLAIN EXTENDED
+ - SHOW PROCEDURE CODE
+ Local variables and SP parameters have empty prefixes.
+ Package body variables are marked with a special prefix.
+ This improves readability of the output of these queries,
+ especially when a local variable or a parameter has the same
+ name with a package body variable.
+ */
+ virtual const LEX_CSTRING *get_name_prefix() const= 0;
+ /**
+ At execution time THD->spcont points to the run-time context (sp_rcontext)
+ of the currently executed routine.
+ Local variables store their data in the sp_rcontext pointed by thd->spcont.
+ Package body variables store data in separate sp_rcontext that belongs
+ to the package.
+ This method provides access to the proper sp_rcontext structure,
+ depending on the SP variable kind.
+ */
+ virtual sp_rcontext *get_rcontext(sp_rcontext *ctx) const= 0;
+};
+
+
+class Sp_rcontext_handler_local: public Sp_rcontext_handler
+{
+public:
+ const LEX_CSTRING *get_name_prefix() const;
+ sp_rcontext *get_rcontext(sp_rcontext *ctx) const;
+};
+
+
+class Sp_rcontext_handler_package_body: public Sp_rcontext_handler
+{
+public:
+ const LEX_CSTRING *get_name_prefix() const;
+ sp_rcontext *get_rcontext(sp_rcontext *ctx) const;
+};
+
+
+extern MYSQL_PLUGIN_IMPORT
+ Sp_rcontext_handler_local sp_rcontext_handler_local;
+
+
+extern MYSQL_PLUGIN_IMPORT
+ Sp_rcontext_handler_package_body sp_rcontext_handler_package_body;
+
+
class Item_equal;
@@ -483,8 +477,35 @@ public:
virtual const Send_field *get_out_param_info() const
{ return NULL; }
+
+ virtual Item_param *get_item_param() { return 0; }
};
+
+/*
+ A helper class to calculate offset and length of a query fragment
+ - outside of SP
+ - inside an SP
+ - inside a compound block
+*/
+class Query_fragment
+{
+ uint m_pos;
+ uint m_length;
+ void set(size_t pos, size_t length)
+ {
+ DBUG_ASSERT(pos < UINT_MAX32);
+ DBUG_ASSERT(length < UINT_MAX32);
+ m_pos= (uint) pos;
+ m_length= (uint) length;
+ }
+public:
+ Query_fragment(THD *thd, sp_head *sphead, const char *start, const char *end);
+ uint pos() const { return m_pos; }
+ uint length() const { return m_length; }
+};
+
+
/**
This is used for items in the query that needs to be rewritten
before binlogging
@@ -499,11 +520,11 @@ class Rewritable_query_parameter
Value of 0 means that this object doesn't have to be replaced
(for example SP variables in control statements)
*/
- uint pos_in_query;
+ my_ptrdiff_t pos_in_query;
/*
Byte length of parameter name in the statement. This is not
- Item::name_length because name_length contains byte length of UTF8-encoded
+ Item::name.length because name.length contains byte length of UTF8-encoded
name, but the query string is in the client charset.
*/
uint len_in_query;
@@ -585,6 +606,8 @@ struct find_selective_predicates_list_processor_data
List<st_cond_statistic> list;
};
+class MY_LOCALE;
+
class Item_equal;
class COND_EQUAL;
@@ -606,50 +629,8 @@ public:
String_copier_for_item(THD *thd): m_thd(thd) { }
};
-
-/**
- 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:
- DTCollation collation;
- uint decimals;
- /*
- The maximum value length in characters multiplied by collation->mbmaxlen.
- Almost always it's the maximum value length in bytes.
- */
- uint32 max_length;
- bool unsigned_flag;
- Type_std_attributes()
- :collation(&my_charset_bin, DERIVATION_COERCIBLE),
- decimals(0), max_length(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)
- { }
- void set(const Type_std_attributes *other)
- {
- *this= *other;
- }
- void set(const Field *field)
- {
- decimals= field->decimals();
- max_length= field->field_length;
- collation.set(field->charset());
- unsigned_flag= MY_TEST(field->flags & UNSIGNED_FLAG);
- }
-};
-
-
class Item: public Value_source,
- public Type_std_attributes,
- public Type_handler
+ public Type_all_attributes
{
/**
The index in the JOIN::join_tab array of the JOIN_TAB this Item is attached
@@ -674,6 +655,7 @@ public:
WINDOW_FUNC_ITEM, STRING_ITEM,
INT_ITEM, REAL_ITEM, NULL_ITEM, VARBIN_ITEM,
COPY_STR_ITEM, FIELD_AVG_ITEM, DEFAULT_VALUE_ITEM,
+ CONTEXTUALLY_TYPED_VALUE_ITEM,
PROC_ITEM,COND_ITEM, REF_ITEM, FIELD_STD_ITEM,
FIELD_VARIANCE_ITEM, INSERT_VALUE_ITEM,
SUBSELECT_ITEM, ROW_ITEM, CACHE_ITEM, TYPE_HOLDER,
@@ -701,11 +683,37 @@ protected:
SEL_TREE *get_mm_tree_for_const(RANGE_OPT_PARAM *param);
- virtual Field *make_string_field(TABLE *table);
- Field *tmp_table_field_from_field_type(TABLE *table,
- bool fixed_length,
- bool set_blob_packlength);
- Field *create_tmp_field(bool group, TABLE *table, uint convert_int_length);
+ /**
+ Create a field based on the exact data type handler.
+ */
+ Field *create_table_field_from_handler(TABLE *table)
+ {
+ const Type_handler *h= type_handler();
+ return h->make_and_init_table_field(&name, Record_addr(maybe_null),
+ *this, table);
+ }
+ /**
+ Create a field based on field_type of argument.
+ This is used to create a field for
+ - IFNULL(x,something)
+ - time functions
+ - prepared statement placeholders
+ - SP variables with data type references: DECLARE a TYPE OF t1.a;
+ @retval NULL error
+ @retval !NULL on success
+ */
+ Field *tmp_table_field_from_field_type(TABLE *table)
+ {
+ const Type_handler *h= type_handler()->type_handler_for_tmp_table(this);
+ return h->make_and_init_table_field(&name, Record_addr(maybe_null),
+ *this, table);
+ }
+ Field *create_tmp_field_int(TABLE *table, uint convert_int_length);
+
+ void raise_error_not_evaluable();
+ void push_note_converted_to_negative_complement(THD *thd);
+ void push_note_converted_to_positive_complement(THD *thd);
+
/* Helper methods, to get an Item value from another Item */
double val_real_from_item(Item *item)
{
@@ -752,8 +760,6 @@ protected:
*/
bool make_zero_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
- void push_note_converted_to_negative_complement(THD *thd);
- void push_note_converted_to_positive_complement(THD *thd);
public:
/*
Cache val_str() into the own buffer, e.g. to evaluate constant
@@ -761,9 +767,11 @@ public:
*/
String *val_str() { return val_str(&str_value); }
- char * name; /* Name from select */
+ const MY_LOCALE *locale_from_val_str();
+
+ LEX_CSTRING name; /* Name of item */
/* Original item name (if it was renamed)*/
- char * orig_name;
+ const char *orig_name;
/**
Intrusive list pointer for free list. If not null, points to the next
Item on some Query_arena's free list. For instance, stored procedures
@@ -772,12 +780,6 @@ public:
@see Query_arena::free_list
*/
Item *next;
- /*
- TODO: convert name and name_length fields into LEX_STRING to keep them in
- sync (see bug #11829681/60295 etc). Then also remove some strlen(name)
- calls.
- */
- uint name_length; /* Length of name */
int marker;
bool maybe_null; /* If item may be null */
bool in_rollup; /* If used in GROUP BY list
@@ -793,9 +795,6 @@ public:
bool fixed; /* If item fixed with fix_fields */
bool is_autogenerated_name; /* indicate was name of this Item
autogenerated or set by user */
- bool with_subselect; /* If this item is a subselect or some
- of its arguments is or contains a
- subselect */
// alloc & destruct is done as start of select on THD::mem_root
Item(THD *thd);
/*
@@ -810,18 +809,33 @@ public:
virtual ~Item()
{
#ifdef EXTRA_DEBUG
- name=0;
+ name.str= 0;
+ name.length= 0;
#endif
} /*lint -e1509 */
- void set_name(THD *thd, const char *str, uint length, CHARSET_INFO *cs);
+ void set_name(THD *thd, const char *str, size_t length, CHARSET_INFO *cs);
void set_name_no_truncate(THD *thd, const char *str, uint length,
CHARSET_INFO *cs);
- void set_name_for_rollback(THD *thd, const char *str, uint length,
- CHARSET_INFO *cs);
- void rename(char *new_name);
- void init_make_field(Send_field *tmp_field,enum enum_field_types type);
+ void init_make_send_field(Send_field *tmp_field,enum enum_field_types type);
virtual void cleanup();
- virtual void make_field(THD *thd, Send_field *field);
+ virtual void make_send_field(THD *thd, Send_field *field);
+
+ bool fix_fields_if_needed(THD *thd, Item **ref)
+ {
+ return fixed ? false : fix_fields(thd, ref);
+ }
+ bool fix_fields_if_needed_for_scalar(THD *thd, Item **ref)
+ {
+ return fix_fields_if_needed(thd, ref) || check_cols(1);
+ }
+ bool fix_fields_if_needed_for_bool(THD *thd, Item **ref)
+ {
+ return fix_fields_if_needed_for_scalar(thd, ref);
+ }
+ bool fix_fields_if_needed_for_order_by(THD *thd, Item **ref)
+ {
+ return fix_fields_if_needed_for_scalar(thd, ref);
+ }
virtual bool fix_fields(THD *, Item **);
/*
Fix after some tables has been pulled out. Basically re-calculate all
@@ -844,54 +858,9 @@ public:
*/
virtual inline void quick_fix_field() { fixed= 1; }
- bool store(struct st_value *value, ulonglong fuzzydate)
+ bool save_in_value(struct st_value *value)
{
- switch (cmp_type()) {
- case INT_RESULT:
- {
- value->m_type= unsigned_flag ? DYN_COL_UINT : DYN_COL_INT;
- value->value.m_longlong= val_int();
- break;
- }
- case REAL_RESULT:
- {
- value->m_type= DYN_COL_DOUBLE;
- value->value.m_double= val_real();
- break;
- }
- case DECIMAL_RESULT:
- {
- value->m_type= DYN_COL_DECIMAL;
- my_decimal *dec= val_decimal(&value->m_decimal);
- if (dec != &value->m_decimal && !null_value)
- my_decimal2decimal(dec, &value->m_decimal);
- break;
- }
- case STRING_RESULT:
- {
- value->m_type= DYN_COL_STRING;
- String *str= val_str(&value->m_string);
- if (str != &value->m_string && !null_value)
- value->m_string.set(str->ptr(), str->length(), str->charset());
- break;
- }
- case TIME_RESULT:
- {
- value->m_type= DYN_COL_DATETIME;
- get_date(&value->value.m_time, fuzzydate);
- break;
- }
- case ROW_RESULT:
- DBUG_ASSERT(false);
- null_value= true;
- break;
- }
- if (null_value)
- {
- value->m_type= DYN_COL_NULL;
- return true;
- }
- return false;
+ return type_handler()->Item_save_in_value(this, value);
}
/* Function returns 1 on overflow and -1 on fatal errors */
@@ -906,42 +875,84 @@ public:
{ return NULL; }
virtual int save_safe_in_field(Field *field)
{ return save_in_field(field, 1); }
- virtual bool send(Protocol *protocol, String *str);
+ virtual bool send(Protocol *protocol, st_value *buffer)
+ {
+ return type_handler()->Item_send(this, protocol, buffer);
+ }
virtual bool eq(const Item *, bool binary_cmp) const;
- const Type_handler *type_handler() const
+ enum_field_types field_type() const
{
- return get_handler_by_field_type(field_type());
+ return type_handler()->field_type();
}
- Field *make_num_distinct_aggregator_field(MEM_ROOT *mem_root,
- const Item *item) const
+ virtual const Type_handler *type_handler() const= 0;
+ const Type_handler *type_handler_for_comparison() const
{
- return type_handler()->make_num_distinct_aggregator_field(mem_root, this);
+ return type_handler()->type_handler_for_comparison();
}
- Field *make_conversion_table_field(TABLE *table,
- uint metadata, const Field *target) const
+ virtual const Type_handler *real_type_handler() const
{
- DBUG_ASSERT(0); // Should not be called in Item context
- return NULL;
+ return type_handler();
+ }
+ virtual const Type_handler *cast_to_int_type_handler() const
+ {
+ return type_handler();
+ }
+ virtual const Type_handler *type_handler_for_system_time() const
+ {
+ return real_type_handler();
}
/* result_type() of an item specifies how the value should be returned */
- Item_result result_type() const { return type_handler()->result_type(); }
+ Item_result result_type() const
+ {
+ return type_handler()->result_type();
+ }
/* ... while cmp_type() specifies how it should be compared */
- Item_result cmp_type() const { return type_handler()->cmp_type(); }
- void make_sort_key(uchar *to, Item *item, const SORT_FIELD_ATTR *sort_field,
- Sort_param *param) const
+ Item_result cmp_type() const
+ {
+ return type_handler()->cmp_type();
+ }
+ const Type_handler *string_type_handler() const
+ {
+ return Type_handler::string_type_handler(max_length);
+ }
+ /*
+ Calculate the maximum length of an expression.
+ This method is used in data type aggregation for UNION, e.g.:
+ SELECT 'b' UNION SELECT COALESCE(double_10_3_field) FROM t1;
+
+ The result is usually equal to max_length, except for some numeric types.
+ In case of the INT, FLOAT, DOUBLE data types Item::max_length and
+ Item::decimals are ignored, so the returned value depends only on the
+ data type itself. E.g. for an expression of the DOUBLE(10,3) data type,
+ the result is always 53 (length 10 and precision 3 do not matter).
+
+ max_length is ignored for these numeric data types because the length limit
+ means only "expected maximum length", it is not a hard limit, so it does
+ not impose any data truncation. E.g. a column of the type INT(4) can
+ normally store big values up to 2147483647 without truncation. When we're
+ aggregating such column for UNION it's important to create a long enough
+ result column, not to lose any data.
+
+ For detailed behaviour of various data types see implementations of
+ the corresponding Type_handler_xxx::max_display_length().
+
+ Note, Item_field::max_display_length() overrides this to get
+ max_display_length() from the underlying field.
+ */
+ virtual uint32 max_display_length() const
{
- type_handler()->make_sort_key(to, item, sort_field, param);
+ return type_handler()->max_display_length(this);
}
- void sortlength(THD *thd,
- const Type_std_attributes *item,
- SORT_FIELD_ATTR *attr) 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)
{
- type_handler()->sortlength(thd, item, attr);
+ // Non-field Items (e.g. hybrid functions) never have ENUM/SET types yet.
+ DBUG_ASSERT(0);
}
- virtual Item_result cast_to_int_type() const { return cmp_type(); }
- enum_field_types string_field_type() const
+ Item_cache* get_cache(THD *thd) const
{
- return Type_handler::string_type_handler(max_length)->field_type();
+ return type_handler()->Item_get_cache(thd, this);
}
virtual enum Type type() const =0;
/*
@@ -1019,29 +1030,47 @@ public:
If value is not null null_value flag will be reset to FALSE.
*/
virtual longlong val_int()=0;
+ Longlong_hybrid to_longlong_hybrid()
+ {
+ return Longlong_hybrid(val_int(), unsigned_flag);
+ }
/**
Get a value for CAST(x AS SIGNED).
Too large positive unsigned integer values are converted
to negative complements.
Values of non-integer data types are adjusted to the SIGNED range.
*/
- virtual longlong val_int_signed_typecast();
+ virtual longlong val_int_signed_typecast()
+ {
+ return cast_to_int_type_handler()->Item_val_int_signed_typecast(this);
+ }
+ longlong val_int_signed_typecast_from_str();
/**
Get a value for CAST(x AS UNSIGNED).
Negative signed integer values are converted
to positive complements.
Values of non-integer data types are adjusted to the UNSIGNED range.
*/
- virtual longlong val_int_unsigned_typecast();
- Longlong_hybrid to_longlong_hybrid()
+ virtual longlong val_int_unsigned_typecast()
{
- return Longlong_hybrid(val_int(), unsigned_flag);
+ return cast_to_int_type_handler()->Item_val_int_unsigned_typecast(this);
}
+ longlong val_int_unsigned_typecast_from_decimal();
+ longlong val_int_unsigned_typecast_from_int();
+ longlong val_int_unsigned_typecast_from_str();
+
+ /**
+ Get a value for CAST(x AS UNSIGNED).
+ Huge positive unsigned values are converted to negative complements.
+ */
+ longlong val_int_signed_typecast_from_int();
+
/*
This is just a shortcut to avoid the cast. You should still use
unsigned_flag to check the sign of the item.
*/
inline ulonglong val_uint() { return (ulonglong) val_int(); }
+
/*
Return string representation of this item object.
@@ -1142,7 +1171,13 @@ public:
Similar to val_str()
*/
virtual String *val_str_ascii(String *str);
-
+
+ /*
+ Returns the result of val_str_ascii(), translating NULLs back
+ to empty strings (if MODE_EMPTY_STRING_IS_NULL is set).
+ */
+ String *val_str_ascii_revert_empty_string_is_null(THD *thd, String *str);
+
/*
Returns the val_str() value converted to the given character set.
*/
@@ -1175,7 +1210,10 @@ public:
FALSE value is false or NULL
TRUE value is true (not equal to 0)
*/
- virtual bool val_bool();
+ virtual bool val_bool()
+ {
+ return type_handler()->Item_val_bool(this);
+ }
virtual String *val_nodeset(String*) { return 0; }
bool eval_const_cond()
@@ -1247,16 +1285,21 @@ public:
// Check NULL value for a TIME, DATE or DATETIME expression
bool is_null_from_temporal();
- int save_time_in_field(Field *field);
- int save_date_in_field(Field *field);
+ int save_time_in_field(Field *field, bool no_conversions);
+ int save_date_in_field(Field *field, bool no_conversions);
+ int save_str_in_field(Field *field, bool no_conversions);
+ int save_real_in_field(Field *field, bool no_conversions);
+ int save_int_in_field(Field *field, bool no_conversions);
+ int save_decimal_in_field(Field *field, bool no_conversions);
+
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 const char *full_name() const { return name ? name : "???"; }
+ 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 : NULL; }
+ { return real_item()->type() == Item::FIELD_ITEM ? name.str : NULL; }
const TABLE_SHARE *field_table_or_null();
/*
@@ -1308,14 +1351,35 @@ public:
INSERT INTO t1 (vcol) VALUES (NULL) -> ok
*/
virtual bool vcol_assignment_allowed_value() const { return false; }
+ /*
+ Determines if the Item is an evaluable expression, that is
+ it can return a value, so we can call methods val_xxx(), get_date(), etc.
+ Most items are evaluable expressions.
+ Examples of non-evaluable expressions:
+ - Item_contextually_typed_value_specification (handling DEFAULT and IGNORE)
+ - Item_type_param bound to DEFAULT and IGNORE
+ We cannot call the mentioned methods for these Items,
+ their method implementations typically have DBUG_ASSERT(0).
+ */
+ virtual bool is_evaluable_expression() const { return true; }
+ bool check_is_evaluable_expression_or_error()
+ {
+ if (is_evaluable_expression())
+ return false; // Ok
+ raise_error_not_evaluable();
+ return true; // Error
+ }
/* cloning of constant items (0 if it is not const) */
virtual Item *clone_item(THD *thd) { return 0; }
- virtual Item* build_clone(THD *thd, MEM_ROOT *mem_root) { return get_copy(thd, mem_root); }
+ virtual Item* build_clone(THD *thd) { return get_copy(thd); }
virtual cond_result eq_cmp_result() const { return COND_OK; }
inline uint float_length(uint decimals_par) const
{ return decimals < FLOATING_POINT_DECIMALS ? (DBL_DIG+2+decimals_par) : DBL_DIG+8;}
/* Returns total number of decimal digits */
- virtual uint decimal_precision() const;
+ virtual uint decimal_precision() const
+ {
+ return type_handler()->Item_decimal_precision(this);
+ }
/* Returns the number of integer part digits only */
inline int decimal_int_part() const
{ return my_decimal_int_part(decimal_precision(), decimals); }
@@ -1326,10 +1390,7 @@ public:
*/
uint decimal_scale() const
{
- return decimals < NOT_FIXED_DEC ? decimals :
- is_temporal_type_with_time(field_type()) ?
- TIME_SECOND_PART_DIGITS :
- MY_MIN(max_length, DECIMAL_MAX_SCALE);
+ return type_handler()->Item_decimal_scale(this);
}
/*
Returns how many digits a divisor adds into a division result.
@@ -1350,15 +1411,25 @@ public:
*/
uint divisor_precision_increment() const
{
- return decimals < NOT_FIXED_DEC ? decimals :
- is_temporal_type_with_time(field_type()) ?
- TIME_SECOND_PART_DIGITS :
- decimals;
+ return type_handler()->Item_divisor_precision_increment(this);
}
/**
TIME or DATETIME precision of the item: 0..6
*/
- uint temporal_precision(enum_field_types type);
+ uint time_precision()
+ {
+ return const_item() ? type_handler()->Item_time_precision(this) :
+ MY_MIN(decimals, TIME_SECOND_PART_DIGITS);
+ }
+ uint datetime_precision()
+ {
+ return const_item() ? type_handler()->Item_datetime_precision(this) :
+ MY_MIN(decimals, TIME_SECOND_PART_DIGITS);
+ }
+ virtual longlong val_int_min() const
+ {
+ return LONGLONG_MIN;
+ }
/*
Returns true if this is constant (during query execution, i.e. its value
will not change until next fix_fields) and its value is known.
@@ -1404,6 +1475,16 @@ public:
LOWEST_PRECEDENCE);
}
virtual void print(String *str, enum_query_type query_type);
+
+ class Print: public String
+ {
+ public:
+ Print(Item *item, enum_query_type type)
+ {
+ item->print(this, type);
+ }
+ };
+
void print_item_w_name(String *str, enum_query_type query_type);
void print_value(String *str);
@@ -1450,27 +1531,23 @@ public:
void split_sum_func2(THD *thd, Ref_ptr_array ref_pointer_array,
List<Item> &fields,
Item **ref, uint flags);
- virtual bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ virtual bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)= 0;
+ bool get_date_from_int(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool get_date_from_year(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool get_date_from_real(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool get_date_from_decimal(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool get_date_from_string(MYSQL_TIME *ltime, ulonglong fuzzydate);
bool get_time(MYSQL_TIME *ltime)
- { return get_date(ltime, TIME_TIME_ONLY | TIME_INVALID_DATES); }
- // Get date with automatic TIME->DATETIME conversion
- bool convert_time_to_datetime(THD *thd, MYSQL_TIME *ltime, ulonglong fuzzydate)
- {
- MYSQL_TIME tmp;
- if (time_to_datetime_with_warn(thd, ltime, &tmp, fuzzydate))
- return null_value= true;
- *ltime= tmp;
- return false;
- }
- bool get_date_with_conversion(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ { return get_date(ltime, Time::flags_for_get_date()); }
/*
- Get time with automatic DATE/DATETIME to TIME conversion.
+ Get time with automatic DATE/DATETIME to TIME conversion,
+ by subtracting CURRENT_DATE.
- Performes a reverse operation to get_date_with_conversion().
+ Performce a reverse operation to CAST(time AS DATETIME)
Suppose:
- we have a set of items (typically with the native MYSQL_TYPE_TIME type)
whose item->get_date() return TIME1 value, and
- - item->get_date_with_conversion() for the same Items return DATETIME1,
+ - CAST(AS DATETIME) for the same Items return DATETIME1,
after applying time-to-datetime conversion to TIME1.
then all items (typically of the native MYSQL_TYPE_{DATE|DATETIME} types)
@@ -1499,52 +1576,30 @@ public:
// Get a DATE or DATETIME value in numeric packed format for comparison
virtual longlong val_datetime_packed()
{
- MYSQL_TIME ltime;
- uint fuzzydate= TIME_FUZZY_DATES | TIME_INVALID_DATES;
- return get_date_with_conversion(&ltime, fuzzydate) ? 0 : pack_time(&ltime);
+ ulonglong fuzzydate= TIME_FUZZY_DATES | TIME_INVALID_DATES;
+ Datetime dt(current_thd, this, fuzzydate);
+ return dt.is_valid_datetime() ? pack_time(dt.get_mysql_time()) : 0;
}
// Get a TIME value in numeric packed format for comparison
virtual longlong val_time_packed()
{
- MYSQL_TIME ltime;
- uint fuzzydate= TIME_FUZZY_DATES | TIME_INVALID_DATES | TIME_TIME_ONLY;
- return get_date(&ltime, fuzzydate) ? 0 : pack_time(&ltime);
+ Time tm(this, Time::comparison_flags_for_get_date());
+ return tm.is_valid_time() ? pack_time(tm.get_mysql_time()) : 0;
}
longlong val_datetime_packed_result();
longlong val_time_packed_result()
{
MYSQL_TIME ltime;
- uint fuzzydate= TIME_TIME_ONLY | TIME_INVALID_DATES | TIME_FUZZY_DATES;
+ ulonglong fuzzydate= Time::comparison_flags_for_get_date();
return get_date_result(&ltime, fuzzydate) ? 0 : pack_time(&ltime);
}
+
// Get a temporal value in packed DATE/DATETIME or TIME format
longlong val_temporal_packed(enum_field_types f_type)
{
return f_type == MYSQL_TYPE_TIME ? val_time_packed() :
val_datetime_packed();
}
- enum_field_types field_type_for_temporal_comparison(const Item *other) const
- {
- if (cmp_type() == TIME_RESULT)
- {
- if (other->cmp_type() == TIME_RESULT)
- return Field::field_type_merge(field_type(), other->field_type());
- else
- return field_type();
- }
- else
- {
- if (other->cmp_type() == TIME_RESULT)
- return other->field_type();
- DBUG_ASSERT(0); // Two non-temporal data types, we should not get to here
- return MYSQL_TYPE_DATETIME;
- }
- }
- // Get a temporal value to compare to another Item
- longlong val_temporal_packed(const Item *other)
- {
- return val_temporal_packed(field_type_for_temporal_comparison(other));
- }
bool get_seconds(ulonglong *sec, ulong *sec_part);
virtual bool get_date_result(MYSQL_TIME *ltime, ulonglong fuzzydate)
{ return get_date(ltime,fuzzydate); }
@@ -1624,17 +1679,16 @@ public:
virtual Item *copy_andor_structure(THD *thd) { return this; }
virtual Item *real_item() { return this; }
virtual Item *get_tmp_table_item(THD *thd) { return copy_or_same(thd); }
+ virtual Item *make_odbc_literal(THD *thd, const LEX_CSTRING *typestr)
+ {
+ return this;
+ }
static CHARSET_INFO *default_charset();
- /*
- For backward compatibility, to make numeric
- data types return "binary" charset in client-side metadata.
- */
- virtual CHARSET_INFO *charset_for_protocol(void) const
+ CHARSET_INFO *charset_for_protocol(void) const
{
- return cmp_type() == STRING_RESULT ? collation.collation :
- &my_charset_bin;
+ return type_handler()->charset_for_protocol(this);
};
virtual bool walk(Item_processor processor, bool walk_subquery, void *arg)
@@ -1813,9 +1867,10 @@ public:
*/
virtual bool check_valid_arguments_processor(void *arg) { return 0; }
virtual bool update_vcol_processor(void *arg) { return 0; }
+ virtual bool set_fields_as_dependent_processor(void *arg) { return 0; }
/*============== End of Item processor list ======================*/
- virtual Item *get_copy(THD *thd, MEM_ROOT *mem_root)=0;
+ virtual Item *get_copy(THD *thd)=0;
bool cache_const_expr_analyzer(uchar **arg);
Item* cache_const_expr_transformer(THD *thd, uchar *arg);
@@ -1857,22 +1912,34 @@ public:
virtual Item **this_item_addr(THD *thd, Item **addr_arg) { return addr_arg; }
// Row emulation
- virtual uint cols() { return 1; }
+ virtual uint cols() const { return 1; }
virtual Item* element_index(uint i) { return this; }
virtual Item** addr(uint i) { return 0; }
virtual bool check_cols(uint c);
+ bool check_type_traditional_scalar(const char *opname) const;
+ bool check_type_scalar(const char *opname) const;
+ bool check_type_or_binary(const char *opname, const Type_handler *handler) const;
+ bool check_type_general_purpose_string(const char *opname) const;
+ bool check_type_can_return_int(const char *opname) const;
+ bool check_type_can_return_decimal(const char *opname) const;
+ bool check_type_can_return_real(const char *opname) const;
+ bool check_type_can_return_str(const char *opname) const;
+ bool check_type_can_return_text(const char *opname) const;
+ bool check_type_can_return_date(const char *opname) const;
+ bool check_type_can_return_time(const char *opname) const;
// It is not row => null inside is impossible
virtual bool null_inside() { return 0; }
// used in row subselects to get value of elements
virtual void bring_value() {}
+ const Type_handler *type_handler_long_or_longlong() const
+ {
+ return Type_handler::type_handler_long_or_longlong(max_char_length());
+ }
+
virtual Field *create_tmp_field(bool group, TABLE *table)
{
- /*
- Values with MY_INT32_NUM_DECIMAL_DIGITS digits may or may not fit into
- Field_long : make them Field_longlong.
- */
- return create_tmp_field(false, table, MY_INT32_NUM_DECIMAL_DIGITS - 2);
+ return tmp_table_field_from_field_type(table);
}
virtual Item_field *field_for_view_update() { return 0; }
@@ -1889,6 +1956,8 @@ public:
virtual Item *derived_grouping_field_transformer_for_where(THD *thd,
uchar *arg)
{ return this; }
+ virtual Item *in_predicate_to_in_subs_transformer(THD *thd, uchar *arg)
+ { return this; }
virtual bool expr_cache_is_needed(THD *) { return FALSE; }
virtual Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs);
bool needs_charset_converter(uint32 length, CHARSET_INFO *tocs) const
@@ -1961,7 +2030,7 @@ public:
Load_data_outvar *dst= get_load_data_outvar();
if (dst)
return dst;
- my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), name);
+ my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), name.str);
return NULL;
}
@@ -1989,10 +2058,14 @@ public:
}
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);
- uint32 max_char_length() const
- { return max_length / collation.collation->mbmaxlen; }
bool too_big_for_varchar() const
{ return max_char_length() > CONVERT_IF_BIGGER_TO_BLOB; }
void fix_length_and_charset(uint32 max_char_length_arg, CHARSET_INFO *cs)
@@ -2012,8 +2085,10 @@ public:
/**
Checks if this item or any of its descendents contains a subquery.
+ This is a replacement of the former Item::has_subquery() and
+ Item::with_subselect.
*/
- virtual bool has_subquery() const { return with_subselect; }
+ virtual bool with_subquery() const { DBUG_ASSERT(fixed); return false; }
Item* set_expr_cache(THD *thd);
@@ -2079,16 +2154,83 @@ public:
}
};
+MEM_ROOT *get_thd_memroot(THD *thd);
template <class T>
-inline Item* get_item_copy (THD *thd, MEM_ROOT *mem_root, T* item)
+inline Item* get_item_copy (THD *thd, T* item)
{
- Item *copy= new (mem_root) T(*item);
- copy->register_in(thd);
+ Item *copy= new (get_thd_memroot(thd)) T(*item);
+ if (likely(copy))
+ copy->register_in(thd);
return copy;
}
+/*
+ This class is a replacement for the former member Item::with_subselect.
+ Determines if the descendant Item is a subselect or some of
+ its arguments is or contains a subselect.
+*/
+class With_subquery_cache
+{
+protected:
+ bool m_with_subquery;
+public:
+ With_subquery_cache(): m_with_subquery(false) { }
+ void join(const Item *item) { m_with_subquery|= item->with_subquery(); }
+};
+
+
+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()
*/
@@ -2096,6 +2238,115 @@ inline Item* get_item_copy (THD *thd, MEM_ROOT *mem_root, T* item)
bool cmp_items(Item *a, Item *b);
+/**
+ Array of items, e.g. function or aggerate function arguments.
+*/
+class Item_args
+{
+protected:
+ Item **args, *tmp_arg[2];
+ uint arg_count;
+ void set_arguments(THD *thd, List<Item> &list);
+ bool walk_args(Item_processor processor, bool walk_subquery, void *arg)
+ {
+ for (uint i= 0; i < arg_count; i++)
+ {
+ if (args[i]->walk(processor, walk_subquery, arg))
+ return true;
+ }
+ return false;
+ }
+ bool transform_args(THD *thd, Item_transformer transformer, uchar *arg);
+ void propagate_equal_fields(THD *, const Item::Context &, COND_EQUAL *);
+ bool excl_dep_on_table(table_map tab_map)
+ {
+ for (uint i= 0; i < arg_count; i++)
+ {
+ if (args[i]->const_item())
+ continue;
+ if (!args[i]->excl_dep_on_table(tab_map))
+ return false;
+ }
+ return true;
+ }
+ bool excl_dep_on_grouping_fields(st_select_lex *sel)
+ {
+ for (uint i= 0; i < arg_count; i++)
+ {
+ if (args[i]->const_item())
+ continue;
+ if (!args[i]->excl_dep_on_grouping_fields(sel))
+ return false;
+ }
+ return true;
+ }
+ bool eq(const Item_args *other, bool binary_cmp) const
+ {
+ for (uint i= 0; i < arg_count ; i++)
+ {
+ if (!args[i]->eq(other->args[i], binary_cmp))
+ return false;
+ }
+ return true;
+ }
+public:
+ Item_args(void)
+ :args(NULL), arg_count(0)
+ { }
+ Item_args(Item *a)
+ :args(tmp_arg), arg_count(1)
+ {
+ args[0]= a;
+ }
+ Item_args(Item *a, Item *b)
+ :args(tmp_arg), arg_count(2)
+ {
+ args[0]= a; args[1]= b;
+ }
+ Item_args(THD *thd, Item *a, Item *b, Item *c)
+ {
+ arg_count= 0;
+ if (likely((args= (Item**) thd_alloc(thd, sizeof(Item*) * 3))))
+ {
+ arg_count= 3;
+ args[0]= a; args[1]= b; args[2]= c;
+ }
+ }
+ Item_args(THD *thd, Item *a, Item *b, Item *c, Item *d)
+ {
+ arg_count= 0;
+ if (likely((args= (Item**) thd_alloc(thd, sizeof(Item*) * 4))))
+ {
+ arg_count= 4;
+ args[0]= a; args[1]= b; args[2]= c; args[3]= d;
+ }
+ }
+ Item_args(THD *thd, Item *a, Item *b, Item *c, Item *d, Item* e)
+ {
+ arg_count= 5;
+ if (likely((args= (Item**) thd_alloc(thd, sizeof(Item*) * 5))))
+ {
+ arg_count= 5;
+ args[0]= a; args[1]= b; args[2]= c; args[3]= d; args[4]= e;
+ }
+ }
+ Item_args(THD *thd, List<Item> &list)
+ {
+ set_arguments(thd, list);
+ }
+ Item_args(THD *thd, const Item_args *other);
+ bool alloc_arguments(THD *thd, uint count);
+ void add_argument(Item *item)
+ {
+ args[arg_count++]= item;
+ }
+ inline Item **arguments() const { return args; }
+ inline uint argument_count() const { return arg_count; }
+ inline void remove_arguments() { arg_count=0; }
+ Sql_mode_dependency value_depends_on_sql_mode_bit_or() const;
+};
+
+
/*
Class to be used to enumerate all field references in an item tree. This
includes references to outside but not fields of the tables within a
@@ -2121,7 +2372,6 @@ public:
Field_enumerator() {} /* Remove gcc warning */
};
-class sp_head;
class Item_string;
@@ -2163,7 +2413,8 @@ protected:
uint repertoire() const { return MY_STRING_METADATA::repertoire; }
size_t char_length() const { return MY_STRING_METADATA::char_length; }
};
- void fix_charset_and_length_from_str_value(Derivation dv, Metadata metadata)
+ void fix_charset_and_length(CHARSET_INFO *cs,
+ Derivation dv, Metadata metadata)
{
/*
We have to have a different max_length than 'length' here to
@@ -2172,13 +2423,13 @@ protected:
number of chars for a string of this type because we in Create_field::
divide the max_length with mbmaxlen).
*/
- collation.set(str_value.charset(), dv, metadata.repertoire());
+ collation.set(cs, dv, metadata.repertoire());
fix_char_length(metadata.char_length());
decimals= NOT_FIXED_DEC;
}
- void fix_charset_and_length_from_str_value(Derivation dv)
+ void fix_charset_and_length_from_str_value(const String &str, Derivation dv)
{
- fix_charset_and_length_from_str_value(dv, Metadata(&str_value));
+ fix_charset_and_length(str.charset(), dv, Metadata(&str));
}
Item_basic_value(THD *thd): Item(thd) {}
/*
@@ -2221,6 +2472,12 @@ public:
void set_used_tables(table_map map) { used_table_map= map; }
table_map used_tables() const { return used_table_map; }
bool check_vcol_func_processor(void *arg) { return FALSE;}
+ virtual Item_basic_constant *make_string_literal_concat(THD *thd,
+ const LEX_CSTRING *)
+ {
+ DBUG_ASSERT(0);
+ return this;
+ }
/* to prevent drop fixed flag (no need parent cleanup call) */
void cleanup()
{
@@ -2230,7 +2487,10 @@ public:
done again between subsequent executions of a prepared statement.
*/
if (orig_name)
- name= orig_name;
+ {
+ name.str= orig_name;
+ name.length= strlen(orig_name);
+ }
}
};
@@ -2251,37 +2511,39 @@ protected:
*/
THD *m_thd;
+ bool fix_fields_from_item(THD *thd, Item **, const Item *);
public:
- LEX_STRING m_name;
+ LEX_CSTRING m_name;
public:
-#ifndef DBUG_OFF
+#ifdef DBUG_ASSERT_EXISTS
/*
Routine to which this Item_splocal belongs. Used for checking if correct
runtime context is used for variable handling.
*/
- sp_head *m_sp;
+ const sp_head *m_sp;
#endif
public:
- Item_sp_variable(THD *thd, char *sp_var_name_str, uint sp_var_name_length);
+ Item_sp_variable(THD *thd, const LEX_CSTRING *sp_var_name);
public:
- bool fix_fields(THD *thd, Item **);
+ bool fix_fields(THD *thd, Item **)= 0;
double val_real();
longlong val_int();
String *val_str(String *sp);
my_decimal *val_decimal(my_decimal *decimal_value);
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
bool is_null();
public:
- inline void make_field(THD *thd, Send_field *field);
+ void make_send_field(THD *thd, Send_field *field);
inline bool const_item() const;
inline int save_in_field(Field *field, bool no_conversions);
- inline bool send(Protocol *protocol, String *str);
+ inline bool send(Protocol *protocol, st_value *buffer);
bool check_vcol_func_processor(void *arg)
{
return mark_unsupported_function(m_name.str, arg, VCOL_IMPOSSIBLE);
@@ -2292,17 +2554,6 @@ public:
Item_sp_variable inline implementation.
*****************************************************************************/
-inline void Item_sp_variable::make_field(THD *thd, Send_field *field)
-{
- Item *it= this_item();
-
- if (name)
- it->set_name(thd, name, (uint) strlen(name), system_charset_info);
- else
- it->set_name(thd, m_name.str, (uint) m_name.length, system_charset_info);
- it->make_field(thd, field);
-}
-
inline bool Item_sp_variable::const_item() const
{
return TRUE;
@@ -2313,9 +2564,9 @@ inline int Item_sp_variable::save_in_field(Field *field, bool no_conversions)
return this_item()->save_in_field(field, no_conversions);
}
-inline bool Item_sp_variable::send(Protocol *protocol, String *str)
+inline bool Item_sp_variable::send(Protocol *protocol, st_value *buffer)
{
- return this_item()->send(protocol, str);
+ return this_item()->send(protocol, buffer);
}
@@ -2329,14 +2580,25 @@ class Item_splocal :public Item_sp_variable,
public Rewritable_query_parameter,
public Type_handler_hybrid_field_type
{
+protected:
+ const Sp_rcontext_handler *m_rcontext_handler;
+
uint m_var_idx;
Type m_type;
+
+ bool append_value_for_log(THD *thd, String *str);
+
+ sp_rcontext *get_rcontext(sp_rcontext *local_ctx) const;
+ Item_field *get_variable(sp_rcontext *ctx) const;
+
public:
- Item_splocal(THD *thd, const LEX_STRING &sp_var_name, uint sp_var_idx,
- enum_field_types sp_var_type,
+ Item_splocal(THD *thd, const Sp_rcontext_handler *rh,
+ const LEX_CSTRING *sp_var_name, uint sp_var_idx,
+ const Type_handler *handler,
uint pos_in_q= 0, uint len_in_q= 0);
+ bool fix_fields(THD *, Item **);
Item *this_item();
const Item *this_item() const;
Item **this_item_addr(THD *thd, Item **);
@@ -2344,17 +2606,17 @@ public:
virtual void print(String *str, enum_query_type query_type);
public:
- inline const LEX_STRING *my_name() const;
+ inline const LEX_CSTRING *my_name() const;
inline uint get_var_idx() const;
inline enum Type type() const;
- enum_field_types field_type() const
- { return Type_handler_hybrid_field_type::field_type(); }
- enum Item_result result_type () const
- { return Type_handler_hybrid_field_type::result_type(); }
- enum Item_result cmp_type () const
- { return Type_handler_hybrid_field_type::cmp_type(); }
+ const Type_handler *type_handler() const
+ { return Type_handler_hybrid_field_type::type_handler(); }
+ uint cols() const { return this_item()->cols(); }
+ Item* element_index(uint i) { return this_item()->element_index(i); }
+ Item** addr(uint i) { return this_item()->addr(i); }
+ bool check_cols(uint c);
private:
bool set_value(THD *thd, sp_rcontext *ctx, Item **it);
@@ -2370,14 +2632,97 @@ public:
bool append_for_log(THD *thd, String *str);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; }
+ Item *get_copy(THD *thd) { return 0; }
+
+ /*
+ Override the inherited create_field_for_create_select(),
+ because we want to preserve the exact data type for:
+ DECLARE a1 INT;
+ DECLARE a2 TYPE OF t1.a2;
+ CREATE TABLE t1 AS SELECT a1, a2;
+ 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); }
+};
+
+
+/**
+ An Item_splocal variant whose data type becomes known only at
+ sp_rcontext creation time, e.g. "DECLARE var1 t1.col1%TYPE".
+*/
+class Item_splocal_with_delayed_data_type: public Item_splocal
+{
+public:
+ Item_splocal_with_delayed_data_type(THD *thd,
+ const Sp_rcontext_handler *rh,
+ const LEX_CSTRING *sp_var_name,
+ uint sp_var_idx,
+ uint pos_in_q, uint len_in_q)
+ :Item_splocal(thd, rh, sp_var_name, sp_var_idx, &type_handler_null,
+ pos_in_q, len_in_q)
+ { }
+};
+
+
+/**
+ SP variables that are fields of a ROW.
+ DELCARE r ROW(a INT,b INT);
+ SELECT r.a; -- This is handled by Item_splocal_row_field
+*/
+class Item_splocal_row_field :public Item_splocal
+{
+protected:
+ LEX_CSTRING m_field_name;
+ uint m_field_idx;
+ bool set_value(THD *thd, sp_rcontext *ctx, Item **it);
+public:
+ Item_splocal_row_field(THD *thd,
+ const Sp_rcontext_handler *rh,
+ const LEX_CSTRING *sp_var_name,
+ const LEX_CSTRING *sp_field_name,
+ uint sp_var_idx, uint sp_field_idx,
+ const Type_handler *handler,
+ uint pos_in_q= 0, uint len_in_q= 0)
+ :Item_splocal(thd, rh, sp_var_name, sp_var_idx, handler, pos_in_q, len_in_q),
+ m_field_name(*sp_field_name),
+ m_field_idx(sp_field_idx)
+ { }
+ bool fix_fields(THD *thd, Item **);
+ Item *this_item();
+ const Item *this_item() const;
+ Item **this_item_addr(THD *thd, Item **);
+ bool append_for_log(THD *thd, String *str);
+ void print(String *str, enum_query_type query_type);
+};
+
+
+class Item_splocal_row_field_by_name :public Item_splocal_row_field
+{
+ bool set_value(THD *thd, sp_rcontext *ctx, Item **it);
+public:
+ Item_splocal_row_field_by_name(THD *thd,
+ const Sp_rcontext_handler *rh,
+ const LEX_CSTRING *sp_var_name,
+ const LEX_CSTRING *sp_field_name,
+ uint sp_var_idx,
+ const Type_handler *handler,
+ uint pos_in_q= 0, uint len_in_q= 0)
+ :Item_splocal_row_field(thd, rh, sp_var_name, sp_field_name,
+ sp_var_idx, 0 /* field index will be set later */,
+ handler, pos_in_q, len_in_q)
+ { }
+ bool fix_fields(THD *thd, Item **it);
+ void print(String *str, enum_query_type query_type);
};
+
/*****************************************************************************
Item_splocal inline implementation.
*****************************************************************************/
-inline const LEX_STRING *Item_splocal::my_name() const
+inline const LEX_CSTRING *Item_splocal::my_name() const
{
return &m_name;
}
@@ -2402,13 +2747,13 @@ public:
Item_case_expr(THD *thd, uint case_expr_id);
public:
+ bool fix_fields(THD *thd, Item **);
Item *this_item();
const Item *this_item() const;
Item **this_item_addr(THD *thd, Item **);
inline enum Type type() const;
- inline Item_result result_type() const;
- enum_field_types field_type() const { return this_item()->field_type(); }
+ const Type_handler *type_handler() const { return this_item()->type_handler(); }
public:
/*
@@ -2417,7 +2762,7 @@ public:
purposes.
*/
virtual void print(String *str, enum_query_type query_type);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; }
+ Item *get_copy(THD *thd) { return 0; }
private:
uint m_case_expr_id;
@@ -2432,12 +2777,6 @@ inline enum Item::Type Item_case_expr::type() const
return this_item()->type();
}
-inline Item_result Item_case_expr::result_type() const
-{
- return this_item()->result_type();
-}
-
-
/*
NAME_CONST(given_name, const_value).
This 'function' has all properties of the supplied const_value (which is
@@ -2467,17 +2806,13 @@ public:
longlong val_int();
String *val_str(String *sp);
my_decimal *val_decimal(my_decimal *);
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
bool is_null();
virtual void print(String *str, enum_query_type query_type);
- enum_field_types field_type() const
- {
- return value_item->field_type();
- }
-
- Item_result result_type() const
+ const Type_handler *type_handler() const
{
- return value_item->result_type();
+ return value_item->type_handler();
}
bool const_item() const
@@ -2490,16 +2825,16 @@ public:
return value_item->save_in_field(field, no_conversions);
}
- bool send(Protocol *protocol, String *str)
+ bool send(Protocol *protocol, st_value *buffer)
{
- return value_item->send(protocol, str);
+ return value_item->send(protocol, buffer);
}
bool check_vcol_func_processor(void *arg)
{
return mark_unsupported_function("name_const()", arg, VCOL_IMPOSSIBLE);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_name_const>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_name_const>(thd, this); }
};
class Item_num: public Item_basic_constant
@@ -2508,6 +2843,10 @@ public:
Item_num(THD *thd): Item_basic_constant(thd) { collation.set_numeric(); }
Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs);
bool check_partition_func_processor(void *int_arg) { return FALSE;}
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ {
+ return type_handler()->Item_get_date(this, ltime, fuzzydate);
+ }
};
#define NO_CACHED_FIELD_INDEX ((uint)(-1))
@@ -2554,13 +2893,13 @@ protected:
*/
const char *orig_db_name;
const char *orig_table_name;
- const char *orig_field_name;
+ LEX_CSTRING orig_field_name;
public:
Name_resolution_context *context;
const char *db_name;
const char *table_name;
- const char *field_name;
+ LEX_CSTRING field_name;
bool alias_name_used; /* true if item was resolved against alias */
/*
Cached value of index for this field in table->field array, used by prep.
@@ -2590,9 +2929,9 @@ 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 char *field_name_arg);
+ const LEX_CSTRING *field_name_arg);
Item_ident(THD *thd, Item_ident *item);
- Item_ident(THD *thd, TABLE_LIST *view_arg, const char *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;
@@ -2621,19 +2960,26 @@ public:
Item_ident_for_show(THD *thd, Field *par_field, const char *db_arg,
const char *table_name_arg):
Item(thd), field(par_field), db_name(db_arg), table_name(table_name_arg)
- {}
-
+ {
+ Type_std_attributes::set(par_field->type_std_attributes());
+ }
enum Type type() const { return FIELD_ITEM; }
double val_real() { return field->val_real(); }
longlong val_int() { return field->val_int(); }
String *val_str(String *str) { return field->val_str(str); }
my_decimal *val_decimal(my_decimal *dec) { return field->val_decimal(dec); }
- void make_field(THD *thd, Send_field *tmp_field);
- CHARSET_INFO *charset_for_protocol(void) const
- { return field->charset_for_protocol(); }
- enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; }
- Item* get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_ident_for_show>(thd, mem_root, this); }
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ {
+ return field->get_date(ltime, fuzzydate);
+ }
+ void make_send_field(THD *thd, Send_field *tmp_field);
+ const Type_handler *type_handler() const
+ {
+ const Type_handler *handler= field->type_handler();
+ return handler->type_handler_for_item_field();
+ }
+ Item* get_copy(THD *thd)
+ { return get_item_copy<Item_ident_for_show>(thd, this); }
};
@@ -2654,7 +3000,7 @@ public:
bool any_privileges;
Item_field(THD *thd, Name_resolution_context *context_arg,
const char *db_arg,const char *table_name_arg,
- const char *field_name_arg);
+ const LEX_CSTRING *field_name_arg);
/*
Constructor needed to process subselect with temporary tables (see Item)
*/
@@ -2684,7 +3030,7 @@ public:
my_decimal *val_decimal_result(my_decimal *);
bool val_bool_result();
bool is_null_result();
- bool send(Protocol *protocol, String *str_arg);
+ bool send(Protocol *protocol, st_value *buffer);
Load_data_outvar *get_load_data_outvar()
{
return this;
@@ -2712,24 +3058,28 @@ public:
void reset_field(Field *f);
bool fix_fields(THD *, Item **);
void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge);
- void make_field(THD *thd, Send_field *tmp_field);
+ void make_send_field(THD *thd, Send_field *tmp_field);
int save_in_field(Field *field,bool no_conversions);
void save_org_in_field(Field *field, fast_field_copier optimizer_data);
fast_field_copier setup_fast_field_copier(Field *field);
table_map used_tables() const;
table_map all_used_tables() const;
- enum Item_result result_type () const
+ const Type_handler *type_handler() const
{
- return field->result_type();
+ const Type_handler *handler= field->type_handler();
+ return handler->type_handler_for_item_field();
}
- Item_result cast_to_int_type() const
+ const Type_handler *cast_to_int_type_handler() const
{
- return field->cmp_type();
+ return field->type_handler()->cast_to_int_type_handler();
}
- enum_field_types field_type() const
+ const Type_handler *real_type_handler() const
{
- return field->type();
+ if (field->is_created_from_null_item)
+ return &type_handler_null;
+ return field->type_handler();
}
+ TYPELIB *get_typelib() const { return field->get_typelib(); }
enum_monotonicity_info get_monotonicity_info() const
{
return MONOTONIC_STRICT_INCREASING;
@@ -2785,8 +3135,7 @@ public:
cond_equal_ref);
}
bool is_result_field() { return false; }
- void set_result_field(Field *field_arg) {}
- void save_in_result_field(bool no_conversions) { }
+ void save_in_result_field(bool no_conversions);
Item *get_tmp_table_item(THD *thd);
bool collect_item_field_processor(void * arg);
bool add_field_to_set_processor(void * arg);
@@ -2809,9 +3158,18 @@ public:
if (field && (field->unireg_check == Field::NEXT_NUMBER))
{
// Auto increment fields are unsupported
- return mark_unsupported_function(field_name, arg, VCOL_FIELD_REF | VCOL_AUTO_INC);
+ 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 set_fields_as_dependent_processor(void *arg)
+ {
+ if (!(used_tables() & OUTER_REF_TABLE_BIT))
+ {
+ depended_from= (st_select_lex *) arg;
+ item_equal= NULL;
}
- return mark_unsupported_function(field_name, arg, VCOL_FIELD_REF);
+ return 0;
}
void cleanup();
Item_equal *get_item_equal() { return item_equal; }
@@ -2819,7 +3177,7 @@ public:
Item_equal *find_item_equal(COND_EQUAL *cond_equal);
Item* propagate_equal_fields(THD *, const Context &, COND_EQUAL *);
Item *replace_equal_field(THD *thd, uchar *arg);
- inline uint32 max_disp_length() { return field->max_display_length(); }
+ uint32 max_display_length() const { return field->max_display_length(); }
Item_field *field_for_view_update() { return this; }
int fix_outer_field(THD *thd, Field **field, Item **reference);
virtual Item *update_value_transformer(THD *thd, uchar *select_arg);
@@ -2834,8 +3192,8 @@ public:
bool cleanup_excluding_const_fields_processor(void *arg)
{ return field && const_item() ? 0 : cleanup_processor(arg); }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_field>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_field>(thd, this); }
bool is_outer_field() const
{
DBUG_ASSERT(fixed);
@@ -2846,14 +3204,43 @@ public:
DBUG_ASSERT(field_type() == MYSQL_TYPE_GEOMETRY);
return field->get_geometry_type();
}
- CHARSET_INFO *charset_for_protocol(void) const
- { return field->charset_for_protocol(); }
friend class Item_default_value;
friend class Item_insert_value;
friend class st_select_lex_unit;
};
+/**
+ Item_field for the ROW data type
+*/
+class Item_field_row: public Item_field,
+ public Item_args
+{
+public:
+ Item_field_row(THD *thd, Field *field)
+ :Item_field(thd, field),
+ Item_args()
+ { }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_field_row>(thd, this); }
+
+ const Type_handler *type_handler() const { return &type_handler_row; }
+ uint cols() const { return arg_count; }
+ Item* element_index(uint i) { return arg_count ? args[i] : this; }
+ Item** addr(uint i) { return arg_count ? args + i : NULL; }
+ bool check_cols(uint c)
+ {
+ if (cols() != c)
+ {
+ my_error(ER_OPERAND_COLUMNS, MYF(0), c);
+ return true;
+ }
+ return false;
+ }
+ bool row_create_items(THD *thd, List<Spvar_definition> *list);
+};
+
+
/*
@brief
Item_temptable_field is the same as Item_field, except that print()
@@ -2897,12 +3284,13 @@ public:
class Item_null :public Item_basic_constant
{
public:
- Item_null(THD *thd, char *name_par=0, CHARSET_INFO *cs= &my_charset_bin):
+ Item_null(THD *thd, const char *name_par=0, CHARSET_INFO *cs= &my_charset_bin):
Item_basic_constant(thd)
{
maybe_null= null_value= TRUE;
max_length= 0;
- name= name_par ? name_par : (char*) "NULL";
+ name.str= name_par ? name_par : "NULL";
+ name.length= strlen(name.str);
fixed= 1;
collation.set(cs, DERIVATION_IGNORABLE, MY_REPERTOIRE_ASCII);
}
@@ -2918,9 +3306,8 @@ public:
longlong val_time_packed();
int save_in_field(Field *field, bool no_conversions);
int save_safe_in_field(Field *field);
- bool send(Protocol *protocol, String *str);
- enum Item_result result_type () const { return STRING_RESULT; }
- enum_field_types field_type() const { return MYSQL_TYPE_NULL; }
+ bool send(Protocol *protocol, st_value *buffer);
+ const Type_handler *type_handler() const { return &type_handler_null; }
bool basic_const_item() const { return 1; }
Item *clone_item(THD *thd);
bool is_null() { return 1; }
@@ -2932,8 +3319,10 @@ public:
Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs);
bool check_partition_func_processor(void *int_arg) {return FALSE;}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_null>(thd, mem_root, this); }
+ Item_basic_constant *make_string_literal_concat(THD *thd,
+ const LEX_CSTRING *);
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_null>(thd, this); }
};
class Item_null_result :public Item_null
@@ -2942,21 +3331,10 @@ public:
Field *result_field;
Item_null_result(THD *thd): Item_null(thd), result_field(0) {}
bool is_result_field() { return result_field != 0; }
-#if MARIADB_VERSION_ID < 100300
enum_field_types field_type() const
{
return result_field->type();
}
- CHARSET_INFO *charset_for_protocol(void) const
- {
- return collation.collation;
- }
-#else
- const Type_handler *type_handler() const
- {
- return result_field->type_handler();
- }
-#endif
void save_in_result_field(bool no_conversions)
{
save_in_field(result_field, no_conversions);
@@ -2978,12 +3356,28 @@ public:
For example in case of 'SELECT ?' you'll get MYSQL_TYPE_STRING both
in result set and placeholders metadata, no matter what type you will
supply for this placeholder in mysql_stmt_execute.
+
+ Item_param has two Type_handler pointers,
+ which can point to different handlers:
+
+ 1. In the Type_handler_hybrid_field_type member
+ It's initialized in:
+ - Item_param::setup_conversion(), for client-server PS protocol,
+ according to the bind type.
+ - Item_param::set_from_item(), for EXECUTE and EXECUTE IMMEDIATE,
+ according to the actual parameter data type.
+
+ 2. In the "value" member.
+ It's initialized in:
+ - Item_param::set_param_func(), for client-server PS protocol.
+ - Item_param::set_from_item(), for EXECUTE and EXECUTE IMMEDIATE.
*/
class Item_param :public Item_basic_value,
private Settable_routine_parameter,
public Rewritable_query_parameter,
- public Type_handler_hybrid_field_type
+ private Type_handler_hybrid_field_type,
+ public Type_geometry_attributes
{
/*
NO_VALUE is a special value meaning that the parameter has not been
@@ -3028,9 +3422,8 @@ class Item_param :public Item_basic_value,
*/
enum enum_item_param_state
{
- NO_VALUE, NULL_VALUE, INT_VALUE, REAL_VALUE,
- STRING_VALUE, TIME_VALUE, LONG_DATA_VALUE,
- DECIMAL_VALUE, DEFAULT_VALUE, IGNORE_VALUE
+ NO_VALUE, NULL_VALUE, SHORT_DATA_VALUE, LONG_DATA_VALUE,
+ DEFAULT_VALUE, IGNORE_VALUE
} state;
enum Type item_type;
@@ -3043,7 +3436,6 @@ class Item_param :public Item_basic_value,
void fix_temporal(uint32 max_length_arg, uint decimals_arg);
-public:
struct CONVERSION_INFO
{
/*
@@ -3085,11 +3477,75 @@ public:
}
};
+ bool m_empty_string_is_null;
+
+ class PValue_simple
+ {
+ public:
+ union
+ {
+ longlong integer;
+ double real;
+ CONVERSION_INFO cs_info;
+ MYSQL_TIME time;
+ };
+ void swap(PValue_simple &other)
+ {
+ swap_variables(PValue_simple, *this, other);
+ }
+ };
+
+ class PValue: public Type_handler_hybrid_field_type,
+ public PValue_simple,
+ public Value_source
+ {
+ public:
+ PValue(): Type_handler_hybrid_field_type(&type_handler_null) {}
+ my_decimal m_decimal;
+ String m_string;
+ /*
+ A buffer for string and long data values. Historically all allocated
+ values returned from val_str() were treated as eligible to
+ modification. I. e. in some cases Item_func_concat can append it's
+ second argument to return value of the first one. Because of that we
+ can't return the original buffer holding string data from val_str(),
+ and have to have one buffer for data and another just pointing to
+ the data. This is the latter one and it's returned from val_str().
+ Can not be declared inside the union as it's not a POD type.
+ */
+ String m_string_ptr;
+
+ void swap(PValue &other)
+ {
+ Type_handler_hybrid_field_type::swap(other);
+ PValue_simple::swap(other);
+ m_decimal.swap(other.m_decimal);
+ m_string.swap(other.m_string);
+ m_string_ptr.swap(other.m_string_ptr);
+ }
+ double val_real() const;
+ longlong val_int(const Type_std_attributes *attr) const;
+ my_decimal *val_decimal(my_decimal *dec, const Type_std_attributes *attr);
+ String *val_str(String *str, const Type_std_attributes *attr);
+ };
+
+ PValue value;
+
+ const String *value_query_val_str(THD *thd, String* str) const;
+ bool value_eq(const Item *item, bool binary_cmp) const;
+ Item *value_clone_item(THD *thd);
+ bool is_evaluable_expression() const;
+ bool can_return_value() const;
+
+public:
/*
Used for bulk protocol only.
*/
enum enum_indicator_type indicator;
+ const Type_handler *type_handler() const
+ { return Type_handler_hybrid_field_type::type_handler(); }
+
bool vcol_assignment_allowed_value() const
{
switch (state) {
@@ -3098,55 +3554,43 @@ public:
case IGNORE_VALUE:
return true;
case NO_VALUE:
- case INT_VALUE:
- case REAL_VALUE:
- case STRING_VALUE:
- case TIME_VALUE:
+ case SHORT_DATA_VALUE:
case LONG_DATA_VALUE:
- case DECIMAL_VALUE:
break;
}
return false;
}
- /*
- A buffer for string and long data values. Historically all allocated
- values returned from val_str() were treated as eligible to
- modification. I. e. in some cases Item_func_concat can append it's
- second argument to return value of the first one. Because of that we
- can't return the original buffer holding string data from val_str(),
- and have to have one buffer for data and another just pointing to
- the data. This is the latter one and it's returned from val_str().
- Can not be declared inside the union as it's not a POD type.
- */
- String str_value_ptr;
- my_decimal decimal_value;
- union
- {
- longlong integer;
- double real;
- CONVERSION_INFO cs_info;
- MYSQL_TIME time;
- } value;
+ Field::geometry_type get_geometry_type() const
+ { return Type_geometry_attributes::get_geometry_type(); };
- enum_field_types field_type() const
- { return Type_handler_hybrid_field_type::field_type(); }
- enum Item_result result_type () const
- { return Type_handler_hybrid_field_type::result_type(); }
- enum Item_result cmp_type () const
- { return Type_handler_hybrid_field_type::cmp_type(); }
+ void set_geometry_type(uint type)
+ { Type_geometry_attributes::set_geometry_type(type); }
- Item_param(THD *thd, uint pos_in_query_arg);
+ Item_param(THD *thd, const LEX_CSTRING *name_arg,
+ uint pos_in_query_arg, uint len_in_query_arg);
enum Type type() const
{
return item_type;
}
- double val_real();
- longlong val_int();
- my_decimal *val_decimal(my_decimal*);
- String *val_str(String*);
+ double val_real()
+ {
+ return can_return_value() ? value.val_real() : 0e0;
+ }
+ longlong val_int()
+ {
+ return can_return_value() ? value.val_int(this) : 0;
+ }
+ my_decimal *val_decimal(my_decimal *dec)
+ {
+ return can_return_value() ? value.val_decimal(dec, this) : NULL;
+ }
+ String *val_str(String *str)
+ {
+ return can_return_value() ? value.val_str(str, this) : NULL;
+ }
bool get_date(MYSQL_TIME *tm, ulonglong fuzzydate);
int save_in_field(Field *field, bool no_conversions);
@@ -3157,20 +3601,64 @@ public:
void set_double(double i);
void set_decimal(const char *str, ulong length);
void set_decimal(const my_decimal *dv, bool unsigned_arg);
- bool set_str(const char *str, ulong length);
+ bool set_str(const char *str, ulong length,
+ CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
bool set_longdata(const char *str, ulong length);
void set_time(MYSQL_TIME *tm, timestamp_type type, uint32 max_length_arg);
void set_time(const MYSQL_TIME *tm, uint32 max_length_arg, uint decimals_arg);
bool set_from_item(THD *thd, Item *item);
void reset();
+
+ void set_param_tiny(uchar **pos, ulong len);
+ void set_param_short(uchar **pos, ulong len);
+ void set_param_int32(uchar **pos, ulong len);
+ void set_param_int64(uchar **pos, ulong len);
+ void set_param_float(uchar **pos, ulong len);
+ void set_param_double(uchar **pos, ulong len);
+ void set_param_decimal(uchar **pos, ulong len);
+ void set_param_time(uchar **pos, ulong len);
+ void set_param_datetime(uchar **pos, ulong len);
+ void set_param_date(uchar **pos, ulong len);
+ void set_param_str(uchar **pos, ulong len);
+
+ void setup_conversion(THD *thd, uchar param_type);
+ void setup_conversion_blob(THD *thd);
+ void setup_conversion_string(THD *thd, CHARSET_INFO *fromcs);
+
/*
Assign placeholder value from bind data.
Note, that 'len' has different semantics in embedded library (as we
don't need to check that packet is not broken there). See
sql_prepare.cc for details.
*/
- void (*set_param_func)(Item_param *param, uchar **pos, ulong len);
+ void set_param_func(uchar **pos, ulong len)
+ {
+ /*
+ To avoid Item_param::set_xxx() asserting on data type mismatch,
+ we set the value type handler here:
+ - It can not be initialized yet after Item_param::setup_conversion().
+ - Also, for LIMIT clause parameters, the value type handler might have
+ changed from the real type handler to type_handler_longlong.
+ So here we'll restore it.
+ */
+ const Type_handler *h= Item_param::type_handler();
+ value.set_handler(h);
+ h->Item_param_set_param_func(this, pos, len);
+ }
+
+ bool set_value(THD *thd, const Type_all_attributes *attr,
+ const st_value *val, const Type_handler *h)
+ {
+ value.set_handler(h); // See comments in set_param_func()
+ return h->Item_param_set_from_value(thd, this, attr, val);
+ }
+ bool set_limit_clause_param(longlong nr)
+ {
+ value.set_handler(&type_handler_longlong);
+ set_int(nr, MY_INT64_NUM_DECIMAL_DIGITS);
+ return !unsigned_flag && value.integer < 0;
+ }
const String *query_val_str(THD *thd, String *str) const;
bool convert_str_value(THD *thd);
@@ -3196,7 +3684,8 @@ public:
}
bool has_int_value() const
{
- return state == INT_VALUE;
+ return state == SHORT_DATA_VALUE &&
+ value.type_handler()->cmp_type() == INT_RESULT;
}
/*
This method is used to make a copy of a basic constant item when
@@ -3225,7 +3714,7 @@ public:
bool append_for_log(THD *thd, String *str);
bool check_vcol_func_processor(void *int_arg) {return FALSE;}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; }
+ Item *get_copy(THD *thd) { return 0; }
bool add_as_clone(THD *thd);
void sync_clones();
@@ -3241,7 +3730,9 @@ private:
public:
virtual const Send_field *get_out_param_info() const;
- virtual void make_field(THD *thd, Send_field *field);
+ Item_param *get_item_param() { return this; }
+
+ virtual void make_send_field(THD *thd, Send_field *field);
private:
Send_field *m_out_param_info;
@@ -3259,23 +3750,40 @@ class Item_int :public Item_num
{
public:
longlong value;
- Item_int(THD *thd, int32 i,uint length= MY_INT32_NUM_DECIMAL_DIGITS):
+ Item_int(THD *thd, int32 i,size_t length= MY_INT32_NUM_DECIMAL_DIGITS):
Item_num(thd), value((longlong) i)
- { max_length=length; fixed= 1; }
- Item_int(THD *thd, longlong i,uint length= MY_INT64_NUM_DECIMAL_DIGITS):
+ { max_length=(uint32)length; fixed= 1; }
+ Item_int(THD *thd, longlong i,size_t length= MY_INT64_NUM_DECIMAL_DIGITS):
Item_num(thd), value(i)
- { max_length=length; fixed= 1; }
- Item_int(THD *thd, ulonglong i, uint length= MY_INT64_NUM_DECIMAL_DIGITS):
+ { max_length=(uint32)length; fixed= 1; }
+ Item_int(THD *thd, ulonglong i, size_t length= MY_INT64_NUM_DECIMAL_DIGITS):
Item_num(thd), value((longlong)i)
- { max_length=length; fixed= 1; unsigned_flag= 1; }
- Item_int(THD *thd, const char *str_arg,longlong i,uint length):
+ { max_length=(uint32)length; fixed= 1; unsigned_flag= 1; }
+ Item_int(THD *thd, const char *str_arg,longlong i,size_t length):
+ Item_num(thd), value(i)
+ {
+ max_length=(uint32)length;
+ name.str= str_arg; name.length= safe_strlen(name.str);
+ fixed= 1;
+ }
+ Item_int(THD *thd, const char *str_arg,longlong i,size_t length, bool flag):
Item_num(thd), value(i)
- { max_length=length; name=(char*) str_arg; fixed= 1; }
- Item_int(THD *thd, const char *str_arg, uint length=64);
+ {
+ max_length=(uint32)length;
+ name.str= str_arg; name.length= safe_strlen(name.str);
+ fixed= 1;
+ unsigned_flag= flag;
+ }
+ Item_int(THD *thd, const char *str_arg, size_t length=64);
enum Type type() const { return INT_ITEM; }
- enum Item_result result_type () const { return INT_RESULT; }
- enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; }
+ const Type_handler *type_handler() const
+ { return type_handler_long_or_longlong(); }
+ Field *create_tmp_field(bool group, TABLE *table)
+ { return tmp_table_field_from_field_type(table); }
+ Field *create_field_for_create_select(TABLE *table)
+ { return tmp_table_field_from_field_type(table); }
longlong val_int() { DBUG_ASSERT(fixed == 1); return value; }
+ longlong val_int_min() const { DBUG_ASSERT(fixed == 1); return value; }
double val_real() { DBUG_ASSERT(fixed == 1); return (double) value; }
my_decimal *val_decimal(my_decimal *);
String *val_str(String*);
@@ -3288,8 +3796,8 @@ public:
{ return (uint) (max_length - MY_TEST(value < 0)); }
bool eq(const Item *item, bool binary_cmp) const
{ return int_eq(value, item); }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_int>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_int>(thd, this); }
};
@@ -3311,7 +3819,7 @@ public:
class Item_uint :public Item_int
{
public:
- Item_uint(THD *thd, const char *str_arg, uint length);
+ Item_uint(THD *thd, const char *str_arg, size_t length);
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()
@@ -3321,8 +3829,8 @@ public:
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, MEM_ROOT *mem_root)
- { return get_item_copy<Item_uint>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_uint>(thd, this); }
};
@@ -3335,7 +3843,12 @@ public:
int save_in_field(Field *field, bool no_conversions);
longlong val_int();
double val_real() { return (double)val_int(); }
- void set(longlong packed);
+ void set(longlong packed, enum_mysql_timestamp_type ts_type);
+ bool get_date(MYSQL_TIME *to, ulonglong fuzzydate)
+ {
+ *to= ltime;
+ return false;
+ }
};
@@ -3345,7 +3858,7 @@ class Item_decimal :public Item_num
protected:
my_decimal decimal_value;
public:
- Item_decimal(THD *thd, const char *str_arg, uint length,
+ Item_decimal(THD *thd, const char *str_arg, size_t length,
CHARSET_INFO *charset);
Item_decimal(THD *thd, const char *str, const my_decimal *val_arg,
uint decimal_par, uint length);
@@ -3355,8 +3868,7 @@ public:
Item_decimal(THD *thd, const uchar *bin, int precision, int scale);
enum Type type() const { return DECIMAL_ITEM; }
- enum Item_result result_type () const { return DECIMAL_RESULT; }
- enum_field_types field_type() const { return MYSQL_TYPE_NEWDECIMAL; }
+ const Type_handler *type_handler() const { return &type_handler_newdecimal; }
longlong val_int();
double val_real();
String *val_str(String*);
@@ -3369,23 +3881,24 @@ public:
uint decimal_precision() const { return decimal_value.precision(); }
bool eq(const Item *, bool binary_cmp) const;
void set_decimal_value(my_decimal *value_par);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_decimal>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_decimal>(thd, this); }
};
class Item_float :public Item_num
{
- char *presentation;
+ const char *presentation;
public:
double value;
- Item_float(THD *thd, const char *str_arg, uint length);
+ Item_float(THD *thd, const char *str_arg, size_t length);
Item_float(THD *thd, const char *str, double val_arg, uint decimal_par,
uint length): Item_num(thd), value(val_arg)
{
- presentation= name=(char*) str;
+ presentation= name.str= str;
+ name.length= safe_strlen(str);
decimals=(uint8) decimal_par;
- max_length=length;
+ max_length= length;
fixed= 1;
}
Item_float(THD *thd, double value_par, uint decimal_par):
@@ -3396,7 +3909,7 @@ public:
}
int save_in_field(Field *field, bool no_conversions);
enum Type type() const { return REAL_ITEM; }
- enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; }
+ const Type_handler *type_handler() const { return &type_handler_double; }
double val_real() { DBUG_ASSERT(fixed == 1); return value; }
longlong val_int()
{
@@ -3419,8 +3932,8 @@ public:
virtual void print(String *str, enum_query_type query_type);
bool eq(const Item *item, bool binary_cmp) const
{ return real_eq(value, item); }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_float>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_float>(thd, this); }
};
@@ -3450,7 +3963,7 @@ class Item_string :public Item_basic_constant
protected:
void fix_from_value(Derivation dv, const Metadata metadata)
{
- fix_charset_and_length_from_str_value(dv, metadata);
+ fix_charset_and_length(str_value.charset(), dv, metadata);
// it is constant => can be used without fix_fields (and frequently used)
fixed= 1;
}
@@ -3489,7 +4002,7 @@ public:
str_value.set_or_copy_aligned(str, length, cs);
fix_and_set_name_from_value(thd, dv, Metadata(&str_value, repertoire));
}
- Item_string(THD *thd, const char *str, uint length,
+ Item_string(THD *thd, const char *str, size_t length,
CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE):
Item_basic_constant(thd)
{
@@ -3505,21 +4018,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, uint length,
+ Item_string(THD *thd, const char *name_par, const char *str, size_t length,
CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE):
Item_basic_constant(thd)
{
str_value.set_or_copy_aligned(str, length, cs);
fix_from_value(dv, Metadata(&str_value));
- set_name(thd, name_par, 0, system_charset_info);
+ set_name(thd, name_par,safe_strlen(name_par), system_charset_info);
}
- Item_string(THD *thd, const char *name_par, const char *str, uint length,
+ Item_string(THD *thd, const char *name_par, const char *str, size_t length,
CHARSET_INFO *cs, Derivation dv, uint repertoire):
Item_basic_constant(thd)
{
str_value.set_or_copy_aligned(str, length, cs);
fix_from_value(dv, Metadata(&str_value, repertoire));
- set_name(thd, name_par, 0, system_charset_info);
+ set_name(thd, name_par, safe_strlen(name_par), system_charset_info);
}
void print_value(String *to) const
{
@@ -3534,9 +4047,12 @@ public:
return (String*) &str_value;
}
my_decimal *val_decimal(my_decimal *);
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ {
+ return get_date_from_string(ltime, fuzzydate);
+ }
int save_in_field(Field *field, bool no_conversions);
- enum Item_result result_type () const { return STRING_RESULT; }
- enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; }
+ const Type_handler *type_handler() const { return &type_handler_varchar; }
bool basic_const_item() const { return 1; }
bool eq(const Item *item, bool binary_cmp) const
{
@@ -3547,7 +4063,7 @@ public:
{
return const_charset_converter(thd, tocs, true);
}
- inline void append(char *str, uint length)
+ inline void append(const char *str, uint length)
{
str_value.append(str, length);
max_length= str_value.numchars() * collation.collation->mbmaxlen;
@@ -3582,7 +4098,7 @@ public:
String *check_well_formed_result(bool send_error)
{ return Item::check_well_formed_result(&str_value, send_error); }
- enum_field_types odbc_temporal_literal_type(const LEX_STRING *type_str) const
+ enum_field_types odbc_temporal_literal_type(const LEX_CSTRING *type_str) const
{
/*
If string is a reasonably short pure ASCII string literal,
@@ -3610,9 +4126,12 @@ public:
}
return MYSQL_TYPE_STRING; // Not a temporal literal
}
-
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_string>(thd, mem_root, this); }
+ Item_basic_constant *make_string_literal_concat(THD *thd,
+ const LEX_CSTRING *);
+ Item *make_odbc_literal(THD *thd, const LEX_CSTRING *typestr);
+
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_string>(thd, this); }
};
@@ -3655,7 +4174,7 @@ public:
DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII)
{ }
Item_string_ascii(THD *thd, const char *str):
- Item_string(thd, str, (uint)strlen(str), &my_charset_latin1,
+ Item_string(thd, str, (uint) strlen(str), &my_charset_latin1,
DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII)
{ }
};
@@ -3717,11 +4236,14 @@ 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):
+ 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= 0; }
- enum_field_types field_type() const { return date_time_field_type; }
+ { decimals= dec_arg; }
+ const Type_handler *type_handler() const
+ {
+ return Type_handler::get_handler_by_field_type(date_time_field_type);
+ }
};
@@ -3729,12 +4251,22 @@ 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) strlen(name_arg), &my_charset_bin)
+ 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; }
- enum_field_types field_type() const { return MYSQL_TYPE_BLOB; }
+ const Type_handler *type_handler() const
+ {
+ return Type_handler::blob_type_handler(max_length);
+ }
+ const Type_handler *real_type_handler() const
+ {
+ // Should not be called, Item_blob is used for SHOW purposes only.
+ DBUG_ASSERT(0);
+ return &type_handler_varchar;
+ }
Field *create_field_for_schema(THD *thd, TABLE *table)
- { return tmp_table_field_from_field_type(table, false, true); }
+ { return tmp_table_field_from_field_type(table); }
};
@@ -3751,8 +4283,12 @@ public:
CHARSET_INFO *cs= NULL):
Item_partition_func_safe_string(thd, "", 0,
cs ? cs : &my_charset_utf8_general_ci)
- { name=(char*) header; max_length= length * collation.collation->mbmaxlen; }
- void make_field(THD *thd, Send_field *field);
+ {
+ name.str= header;
+ name.length= strlen(name.str);
+ max_length= length * collation.collation->mbmaxlen;
+ }
+ void make_send_field(THD *thd, Send_field *field);
};
@@ -3766,7 +4302,10 @@ public:
{
unsigned_flag=1;
}
- enum_field_types field_type() const { return int_field_type; }
+ const Type_handler *type_handler() const
+ {
+ return Type_handler::get_handler_by_field_type(int_field_type);
+ }
};
@@ -3776,20 +4315,19 @@ public:
class Item_hex_constant: public Item_basic_constant
{
private:
- void hex_string_init(THD *thd, const char *str, uint str_length);
+ void hex_string_init(THD *thd, const char *str, size_t str_length);
public:
Item_hex_constant(THD *thd): Item_basic_constant(thd)
{
hex_string_init(thd, "", 0);
}
- Item_hex_constant(THD *thd, const char *str, uint str_length):
+ Item_hex_constant(THD *thd, const char *str, size_t str_length):
Item_basic_constant(thd)
{
hex_string_init(thd, str, str_length);
}
enum Type type() const { return VARBIN_ITEM; }
- enum Item_result result_type () const { return STRING_RESULT; }
- enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; }
+ const Type_handler *type_handler() const { return &type_handler_varchar; }
virtual Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs)
{
return const_charset_converter(thd, tocs, true);
@@ -3799,10 +4337,14 @@ public:
bool eq(const Item *item, bool binary_cmp) const
{
return item->basic_const_item() && item->type() == type() &&
- item->cast_to_int_type() == cast_to_int_type() &&
+ item->cast_to_int_type_handler() == cast_to_int_type_handler() &&
str_value.bin_eq(&((Item_hex_constant*)item)->str_value);
}
String *val_str(String*) { DBUG_ASSERT(fixed == 1); return &str_value; }
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ {
+ return type_handler()->Item_get_date(this, ltime, fuzzydate);
+ }
};
@@ -3815,8 +4357,9 @@ class Item_hex_hybrid: public Item_hex_constant
{
public:
Item_hex_hybrid(THD *thd): Item_hex_constant(thd) {}
- Item_hex_hybrid(THD *thd, const char *str, uint str_length):
+ Item_hex_hybrid(THD *thd, const char *str, size_t str_length):
Item_hex_constant(thd, str, str_length) {}
+ uint decimal_precision() const;
double val_real()
{
DBUG_ASSERT(fixed == 1);
@@ -3832,7 +4375,7 @@ public:
{
// following assert is redundant, because fixed=1 assigned in constructor
DBUG_ASSERT(fixed == 1);
- ulonglong value= (ulonglong) Item_hex_hybrid::val_int();
+ longlong value= Item_hex_hybrid::val_int();
int2my_decimal(E_DEC_FATAL_ERROR, value, TRUE, decimal_value);
return decimal_value;
}
@@ -3841,10 +4384,17 @@ public:
field->set_notnull();
return field->store_hex_hybrid(str_value.ptr(), str_value.length());
}
- enum Item_result cast_to_int_type() const { return INT_RESULT; }
+ const Type_handler *cast_to_int_type_handler() const
+ {
+ return &type_handler_longlong;
+ }
+ const Type_handler *type_handler_for_system_time() const
+ {
+ return &type_handler_longlong;
+ }
void print(String *str, enum_query_type query_type);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_hex_hybrid>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_hex_hybrid>(thd, this); }
};
@@ -3861,7 +4411,7 @@ class Item_hex_string: public Item_hex_constant
{
public:
Item_hex_string(THD *thd): Item_hex_constant(thd) {}
- Item_hex_string(THD *thd, const char *str, uint str_length):
+ Item_hex_string(THD *thd, const char *str, size_t str_length):
Item_hex_constant(thd, str, str_length) {}
longlong val_int()
{
@@ -3883,17 +4433,16 @@ public:
return field->store(str_value.ptr(), str_value.length(),
collation.collation);
}
- enum Item_result cast_to_int_type() const { return STRING_RESULT; }
void print(String *str, enum_query_type query_type);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_hex_string>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_hex_string>(thd, this); }
};
class Item_bin_string: public Item_hex_hybrid
{
public:
- Item_bin_string(THD *thd, const char *str,uint str_length);
+ Item_bin_string(THD *thd, const char *str, size_t str_length);
};
@@ -3906,13 +4455,14 @@ public:
Constructor for Item_date_literal.
@param ltime DATE value.
*/
- Item_temporal_literal(THD *thd, MYSQL_TIME *ltime): Item_basic_constant(thd)
+ Item_temporal_literal(THD *thd, const MYSQL_TIME *ltime)
+ :Item_basic_constant(thd)
{
collation.set(&my_charset_numeric, DERIVATION_NUMERIC, MY_REPERTOIRE_ASCII);
decimals= 0;
cached_time= *ltime;
}
- Item_temporal_literal(THD *thd, MYSQL_TIME *ltime, uint dec_arg):
+ Item_temporal_literal(THD *thd, const MYSQL_TIME *ltime, uint dec_arg):
Item_basic_constant(thd)
{
collation.set(&my_charset_numeric, DERIVATION_NUMERIC, MY_REPERTOIRE_ASCII);
@@ -3923,8 +4473,6 @@ public:
bool const_item() const { return true; }
enum Type type() const { return DATE_ITEM; }
bool eq(const Item *item, bool binary_cmp) const;
- enum Item_result result_type () const { return STRING_RESULT; }
- Item_result cmp_type() const { return TIME_RESULT; }
bool check_partition_func_processor(void *int_arg) {return FALSE;}
@@ -3940,7 +4488,7 @@ public:
my_decimal *val_decimal(my_decimal *decimal_value)
{ return val_decimal_from_date(decimal_value); }
int save_in_field(Field *field, bool no_conversions)
- { return save_date_in_field(field); }
+ { return save_date_in_field(field, no_conversions); }
};
@@ -3950,7 +4498,7 @@ public:
class Item_date_literal: public Item_temporal_literal
{
public:
- Item_date_literal(THD *thd, MYSQL_TIME *ltime)
+ Item_date_literal(THD *thd, const MYSQL_TIME *ltime)
:Item_temporal_literal(thd, ltime)
{
max_length= MAX_DATE_WIDTH;
@@ -3964,12 +4512,12 @@ public:
*/
maybe_null= !ltime->month || !ltime->day;
}
- enum_field_types field_type() const { return MYSQL_TYPE_DATE; }
+ const Type_handler *type_handler() const { return &type_handler_newdate; }
void print(String *str, enum_query_type query_type);
Item *clone_item(THD *thd);
bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_date_literal>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_date_literal>(thd, this); }
};
@@ -3979,18 +4527,18 @@ public:
class Item_time_literal: public Item_temporal_literal
{
public:
- Item_time_literal(THD *thd, MYSQL_TIME *ltime, uint dec_arg):
+ Item_time_literal(THD *thd, const MYSQL_TIME *ltime, uint dec_arg):
Item_temporal_literal(thd, ltime, dec_arg)
{
max_length= MIN_TIME_WIDTH + (decimals ? decimals + 1 : 0);
fixed= 1;
}
- enum_field_types field_type() const { return MYSQL_TYPE_TIME; }
+ const Type_handler *type_handler() const { return &type_handler_time2; }
void print(String *str, enum_query_type query_type);
Item *clone_item(THD *thd);
bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_time_literal>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_time_literal>(thd, this); }
};
@@ -4000,7 +4548,7 @@ public:
class Item_datetime_literal: public Item_temporal_literal
{
public:
- Item_datetime_literal(THD *thd, MYSQL_TIME *ltime, uint dec_arg):
+ Item_datetime_literal(THD *thd, const MYSQL_TIME *ltime, uint dec_arg):
Item_temporal_literal(thd, ltime, dec_arg)
{
max_length= MAX_DATETIME_WIDTH + (decimals ? decimals + 1 : 0);
@@ -4008,12 +4556,12 @@ public:
// See the comment on maybe_null in Item_date_literal
maybe_null= !ltime->month || !ltime->day;
}
- enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; }
+ const Type_handler *type_handler() const { return &type_handler_datetime2; }
void print(String *str, enum_query_type query_type);
Item *clone_item(THD *thd);
bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_datetime_literal>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_datetime_literal>(thd, this); }
};
@@ -4046,7 +4594,7 @@ class Item_date_literal_for_invalid_dates: public Item_date_literal
in sql_mode=TRADITIONAL.
*/
public:
- Item_date_literal_for_invalid_dates(THD *thd, MYSQL_TIME *ltime)
+ Item_date_literal_for_invalid_dates(THD *thd, const MYSQL_TIME *ltime)
:Item_date_literal(thd, ltime) { }
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
{
@@ -4064,7 +4612,7 @@ class Item_datetime_literal_for_invalid_dates: public Item_datetime_literal
{
public:
Item_datetime_literal_for_invalid_dates(THD *thd,
- MYSQL_TIME *ltime, uint dec_arg)
+ const MYSQL_TIME *ltime, uint dec_arg)
:Item_datetime_literal(thd, ltime, dec_arg) { }
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
{
@@ -4074,101 +4622,6 @@ public:
};
-/**
- Array of items, e.g. function or aggerate function arguments.
-*/
-class Item_args
-{
-protected:
- Item **args, *tmp_arg[2];
- uint arg_count;
- void set_arguments(THD *thd, List<Item> &list);
- bool walk_args(Item_processor processor, bool walk_subquery, void *arg)
- {
- for (uint i= 0; i < arg_count; i++)
- {
- if (args[i]->walk(processor, walk_subquery, arg))
- return true;
- }
- return false;
- }
- bool transform_args(THD *thd, Item_transformer transformer, uchar *arg);
- void propagate_equal_fields(THD *, const Item::Context &, COND_EQUAL *);
- bool excl_dep_on_table(table_map tab_map)
- {
- for (uint i= 0; i < arg_count; i++)
- {
- if (args[i]->const_item())
- continue;
- if (!args[i]->excl_dep_on_table(tab_map))
- return false;
- }
- return true;
- }
- bool excl_dep_on_grouping_fields(st_select_lex *sel)
- {
- for (uint i= 0; i < arg_count; i++)
- {
- if (args[i]->const_item())
- continue;
- if (!args[i]->excl_dep_on_grouping_fields(sel))
- return false;
- }
- return true;
- }
-public:
- Item_args(void)
- :args(NULL), arg_count(0)
- { }
- Item_args(Item *a)
- :args(tmp_arg), arg_count(1)
- {
- args[0]= a;
- }
- Item_args(Item *a, Item *b)
- :args(tmp_arg), arg_count(2)
- {
- args[0]= a; args[1]= b;
- }
- Item_args(THD *thd, Item *a, Item *b, Item *c)
- {
- arg_count= 0;
- if ((args= (Item**) thd_alloc(thd, sizeof(Item*) * 3)))
- {
- arg_count= 3;
- args[0]= a; args[1]= b; args[2]= c;
- }
- }
- Item_args(THD *thd, Item *a, Item *b, Item *c, Item *d)
- {
- arg_count= 0;
- if ((args= (Item**) thd_alloc(thd, sizeof(Item*) * 4)))
- {
- arg_count= 4;
- args[0]= a; args[1]= b; args[2]= c; args[3]= d;
- }
- }
- Item_args(THD *thd, Item *a, Item *b, Item *c, Item *d, Item* e)
- {
- arg_count= 5;
- if ((args= (Item**) thd_alloc(thd, sizeof(Item*) * 5)))
- {
- arg_count= 5;
- args[0]= a; args[1]= b; args[2]= c; args[3]= d; args[4]= e;
- }
- }
- Item_args(THD *thd, List<Item> &list)
- {
- set_arguments(thd, list);
- }
- Item_args(THD *thd, const Item_args *other);
- inline Item **arguments() const { return args; }
- inline uint argument_count() const { return arg_count; }
- inline void remove_arguments() { arg_count=0; }
- Sql_mode_dependency value_depends_on_sql_mode_bit_or() const;
-};
-
-
class Used_tables_and_const_cache
{
public:
@@ -4236,82 +4689,34 @@ public:
*/
class Item_func_or_sum: public Item_result_field,
public Item_args,
- public Used_tables_and_const_cache
+ public Used_tables_and_const_cache,
+ public With_subquery_cache
{
- bool agg_item_collations(DTCollation &c, const char *name,
- Item **items, uint nitems,
- uint flags, int item_sep);
- bool agg_item_set_converter(const DTCollation &coll, const char *fname,
- Item **args, uint nargs,
- uint flags, int item_sep);
protected:
- /*
- Collect arguments' character sets together.
- We allow to apply automatic character set conversion in some cases.
- The conditions when conversion is possible are:
- - arguments A and B have different charsets
- - A wins according to coercibility rules
- (i.e. a column is stronger than a string constant,
- an explicit COLLATE clause is stronger than a column)
- - character set of A is either superset for character set of B,
- or B is a string constant which can be converted into the
- character set of A without data loss.
-
- If all of the above is true, then it's possible to convert
- B into the character set of A, and then compare according
- to the collation of A.
-
- For functions with more than two arguments:
-
- collect(A,B,C) ::= collect(collect(A,B),C)
-
- Since this function calls THD::change_item_tree() on the passed Item **
- pointers, it is necessary to pass the original Item **'s, not copies.
- Otherwise their values will not be properly restored (see BUG#20769).
- If the items are not consecutive (eg. args[2] and args[5]), use the
- item_sep argument, ie.
-
- agg_item_charsets(coll, fname, &args[2], 2, flags, 3)
- */
bool agg_arg_charsets(DTCollation &c, Item **items, uint nitems,
uint flags, int item_sep)
{
- if (agg_item_collations(c, func_name(), items, nitems, flags, item_sep))
- return true;
-
- return agg_item_set_converter(c, func_name(), items, nitems,
- flags, item_sep);
+ return Type_std_attributes::agg_arg_charsets(c, func_name(),
+ items, nitems,
+ flags, item_sep);
}
- /*
- Aggregate arguments for string result, e.g: CONCAT(a,b)
- - convert to @@character_set_connection if all arguments are numbers
- - allow DERIVATION_NONE
- */
bool agg_arg_charsets_for_string_result(DTCollation &c,
Item **items, uint nitems,
int item_sep= 1)
{
- uint flags= MY_COLL_ALLOW_SUPERSET_CONV |
- MY_COLL_ALLOW_COERCIBLE_CONV |
- MY_COLL_ALLOW_NUMERIC_CONV;
- return agg_arg_charsets(c, items, nitems, flags, item_sep);
+ return Type_std_attributes::
+ agg_arg_charsets_for_string_result(c, func_name(),
+ items, nitems, item_sep);
}
- /*
- Aggregate arguments for string result, when some comparison
- is involved internally, e.g: REPLACE(a,b,c)
- - convert to @@character_set_connection if all arguments are numbers
- - disallow DERIVATION_NONE
- */
bool agg_arg_charsets_for_string_result_with_comparison(DTCollation &c,
Item **items,
uint nitems,
int item_sep= 1)
{
- uint flags= MY_COLL_ALLOW_SUPERSET_CONV |
- MY_COLL_ALLOW_COERCIBLE_CONV |
- MY_COLL_ALLOW_NUMERIC_CONV |
- MY_COLL_DISALLOW_NONE;
- return agg_arg_charsets(c, items, nitems, flags, item_sep);
+ return Type_std_attributes::
+ agg_arg_charsets_for_string_result_with_comparison(c, func_name(),
+ items, nitems,
+ item_sep);
}
/*
@@ -4323,13 +4728,10 @@ protected:
Item **items, uint nitems,
int item_sep= 1)
{
- uint flags= MY_COLL_ALLOW_SUPERSET_CONV |
- MY_COLL_ALLOW_COERCIBLE_CONV |
- MY_COLL_DISALLOW_NONE;
- return agg_arg_charsets(c, items, nitems, flags, item_sep);
+ return Type_std_attributes::
+ agg_arg_charsets_for_comparison(c, func_name(), items, nitems, item_sep);
}
-
public:
// This method is used by Arg_comparator
bool agg_arg_charsets_for_comparison(CHARSET_INFO **cs, Item **a, Item **b)
@@ -4371,6 +4773,7 @@ public:
Used_tables_and_const_cache(item) { }
Item_func_or_sum(THD *thd, List<Item> &list):
Item_result_field(thd), Item_args(thd, list) { }
+ bool with_subquery() const { DBUG_ASSERT(fixed); return m_with_subquery; }
bool walk(Item_processor processor, bool walk_subquery, void *arg)
{
if (walk_args(processor, walk_subquery, arg))
@@ -4397,13 +4800,43 @@ public:
virtual bool fix_length_and_dec()= 0;
bool const_item() const { return const_item_cache; }
table_map used_tables() const { return used_tables_cache; }
- Item* build_clone(THD *thd, MEM_ROOT *mem_root);
+ Item* build_clone(THD *thd);
Sql_mode_dependency value_depends_on_sql_mode() const
{
return Item_args::value_depends_on_sql_mode_bit_or().soft_to_hard();
}
};
+class sp_head;
+class sp_name;
+struct st_sp_security_context;
+
+class Item_sp
+{
+public:
+ Name_resolution_context *context;
+ sp_name *m_name;
+ sp_head *m_sp;
+ TABLE *dummy_table;
+ uchar result_buf[64];
+ sp_rcontext *func_ctx;
+ MEM_ROOT sp_mem_root;
+ Query_arena *sp_query_arena;
+
+ /*
+ The result field of the stored function.
+ */
+ Field *sp_result_field;
+ Item_sp(THD *thd, Name_resolution_context *context_arg, sp_name *name_arg);
+ Item_sp(THD *thd, Item_sp *item);
+ const char *func_name(THD *thd) const;
+ void cleanup();
+ bool sp_check_access(THD *thd);
+ bool execute(THD *thd, bool *null_value, Item **args, uint arg_count);
+ bool execute_impl(THD *thd, Item **args, uint arg_count);
+ bool init_result_field(THD *thd, uint max_length, uint maybe_null,
+ bool *null_value, LEX_CSTRING *name);
+};
class Item_ref :public Item_ident
{
@@ -4416,7 +4849,7 @@ public:
bool reference_trough_name;
Item_ref(THD *thd, Name_resolution_context *context_arg,
const char *db_arg, const char *table_name_arg,
- const char *field_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) {}
/*
@@ -4434,10 +4867,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 char *field_name_arg,
+ const char *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 char *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)
@@ -4467,22 +4900,17 @@ public:
my_decimal *val_decimal_result(my_decimal *);
bool val_bool_result();
bool is_null_result();
- bool send(Protocol *prot, String *tmp);
- void make_field(THD *thd, Send_field *field);
+ bool send(Protocol *prot, st_value *buffer);
+ void make_send_field(THD *thd, Send_field *field);
bool fix_fields(THD *, Item **);
void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge);
int save_in_field(Field *field, bool no_conversions);
void save_org_in_field(Field *field, fast_field_copier optimizer_data);
fast_field_copier setup_fast_field_copier(Field *field)
{ return (*ref)->setup_fast_field_copier(field); }
-#if MARIADB_VERSION_ID < 100300
- CHARSET_INFO *charset_for_protocol(void) const
- {
- return (*ref)->charset_for_protocol();
- }
-#endif
- enum Item_result result_type () const { return (*ref)->result_type(); }
- enum_field_types field_type() const { return (*ref)->field_type(); }
+ const Type_handler *type_handler() const { return (*ref)->type_handler(); }
+ const Type_handler *real_type_handler() const
+ { return (*ref)->real_type_handler(); }
Field *get_tmp_table_field()
{ return result_field ? result_field : (*ref)->get_tmp_table_field(); }
Item *get_tmp_table_item(THD *thd);
@@ -4520,6 +4948,11 @@ public:
{
return ref ? (*ref)->real_item() : this;
}
+ TYPELIB *get_typelib() const
+ {
+ return ref ? (*ref)->get_typelib() : NULL;
+ }
+
bool walk(Item_processor processor, bool walk_subquery, void *arg)
{
if (ref && *ref)
@@ -4552,7 +4985,7 @@ public:
virtual Ref_Type ref_type() { return REF; }
// Row emulation: forwarding of ROW-related calls to ref
- uint cols()
+ uint cols() const
{
return ref && result_type() == ROW_RESULT ? (*ref)->cols() : 1;
}
@@ -4590,17 +5023,17 @@ public:
return (*ref)->is_outer_field();
}
- Item* build_clone(THD *thd, MEM_ROOT *mem_root);
+ Item* build_clone(THD *thd);
/**
Checks if the item tree that ref points to contains a subquery.
*/
- virtual bool has_subquery() const
- {
- return (*ref)->has_subquery();
+ virtual bool with_subquery() const
+ {
+ return (*ref)->with_subquery();
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_ref>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_ref>(thd, this); }
bool excl_dep_on_table(table_map tab_map)
{
table_map used= used_tables();
@@ -4638,7 +5071,7 @@ 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 char *field_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)
@@ -4646,7 +5079,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 char *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)
@@ -4654,8 +5087,7 @@ public:
bool fix_fields(THD *thd, Item **it)
{
- if ((!(*ref)->fixed && (*ref)->fix_fields(thd, ref)) ||
- (*ref)->check_cols(1))
+ if ((*ref)->fix_fields_if_needed_for_scalar(thd, ref))
return TRUE;
return Item_ref::fix_fields(thd, it);
}
@@ -4668,8 +5100,8 @@ public:
bool is_null();
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
virtual Ref_Type ref_type() { return DIRECT_REF; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_direct_ref>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_direct_ref>(thd, this); }
};
@@ -4684,7 +5116,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;
@@ -4693,8 +5125,7 @@ public:
bool fix_fields(THD *thd, Item **it)
{
DBUG_ASSERT(ident->type() == FIELD_ITEM || ident->type() == REF_ITEM);
- if ((!ident->fixed && ident->fix_fields(thd, ref)) ||
- ident->check_cols(1))
+ if (ident->fix_fields_if_needed_for_scalar(thd, ref))
return TRUE;
set_properties();
return FALSE;
@@ -4714,7 +5145,8 @@ class Expression_cache_tracker;
The objects of this class can store its values in an expression cache.
*/
-class Item_cache_wrapper :public Item_result_field
+class Item_cache_wrapper :public Item_result_field,
+ public With_subquery_cache
{
private:
/* Pointer on the cached expression */
@@ -4741,6 +5173,7 @@ public:
enum Type type() const { return EXPR_CACHE_ITEM; }
enum Type real_type() const { return orig_item->type(); }
+ bool with_subquery() const { DBUG_ASSERT(fixed); return m_with_subquery; }
bool set_cache(THD *thd);
Expression_cache_tracker* init_tracker(MEM_ROOT *mem_root);
@@ -4759,7 +5192,7 @@ public:
bool val_bool();
bool is_null();
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
- bool send(Protocol *protocol, String *buffer);
+ bool send(Protocol *protocol, st_value *buffer);
void save_org_in_field(Field *field,
fast_field_copier data __attribute__ ((__unused__)))
{
@@ -4775,8 +5208,8 @@ public:
virtual void print(String *str, enum_query_type query_type);
virtual const char *full_name() const { return orig_item->full_name(); }
- virtual void make_field(THD *thd, Send_field *field)
- { orig_item->make_field(thd, field); }
+ virtual void make_send_field(THD *thd, Send_field *field)
+ { orig_item->make_send_field(thd, field); }
bool eq(const Item *item, bool binary_cmp) const
{
Item *it= ((Item *) item)->real_item();
@@ -4787,8 +5220,7 @@ public:
orig_item->fix_after_pullout(new_parent, &orig_item, merge);
}
int save_in_field(Field *to, bool no_conversions);
- enum Item_result result_type () const { return orig_item->result_type(); }
- enum_field_types field_type() const { return orig_item->field_type(); }
+ const Type_handler *type_handler() const { return orig_item->type_handler(); }
table_map used_tables() const { return orig_item->used_tables(); }
void update_used_tables()
{
@@ -4807,7 +5239,7 @@ public:
{ return orig_item->field_for_view_update(); }
/* Row emulation: forwarding of ROW-related calls to orig_item */
- uint cols()
+ uint cols() const
{ return result_type() == ROW_RESULT ? orig_item->cols() : 1; }
Item* element_index(uint i)
{ return result_type() == ROW_RESULT ? orig_item->element_index(i) : this; }
@@ -4833,9 +5265,9 @@ public:
{
return mark_unsupported_function("cache", arg, VCOL_IMPOSSIBLE);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_cache_wrapper>(thd, mem_root, this); }
- Item *build_clone(THD *thd, MEM_ROOT *mem_root) { return 0; }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_cache_wrapper>(thd, this); }
+ Item *build_clone(THD *thd) { return 0; }
};
@@ -4873,7 +5305,7 @@ public:
Item_direct_view_ref(THD *thd, Name_resolution_context *context_arg,
Item **item,
const char *table_name_arg,
- const char *field_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),
@@ -4981,7 +5413,7 @@ public:
}
return Item_direct_ref::get_date(ltime, fuzzydate);
}
- bool send(Protocol *protocol, String *buffer);
+ bool send(Protocol *protocol, st_value *buffer);
void save_org_in_field(Field *field,
fast_field_copier data __attribute__ ((__unused__)))
{
@@ -5013,8 +5445,8 @@ public:
my_decimal *val_decimal_result(my_decimal *val);
bool val_bool_result();
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_direct_view_ref>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_direct_view_ref>(thd, this); }
};
@@ -5044,7 +5476,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)
{
@@ -5053,7 +5485,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, const char *field_name_arg,
+ const char *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),
@@ -5094,7 +5526,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 char *field_name_arg):
+ const char *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);
@@ -5106,8 +5539,8 @@ public:
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
virtual void print(String *str, enum_query_type query_type);
table_map used_tables() const;
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_ref_null_helper>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_ref_null_helper>(thd, this); }
};
/*
@@ -5150,6 +5583,7 @@ public:
#include "item_xmlfunc.h"
#include "item_jsonfunc.h"
#include "item_create.h"
+#include "item_vers.h"
#endif
/**
@@ -5197,8 +5631,8 @@ protected:
item= i;
null_value=maybe_null=item->maybe_null;
Type_std_attributes::set(item);
- name=item->name;
- set_handler_by_field_type(item->field_type());
+ name= item->name;
+ set_handler(item->type_handler());
fixed= item->fixed;
}
@@ -5217,14 +5651,11 @@ public:
/** All of the subclasses should have the same type tag */
enum Type type() const { return COPY_STR_ITEM; }
- enum_field_types field_type() const
- { return Type_handler_hybrid_field_type::field_type(); }
- enum Item_result result_type () const
- { return Type_handler_hybrid_field_type::result_type(); }
- enum Item_result cmp_type () const
- { return Type_handler_hybrid_field_type::cmp_type(); }
+ const Type_handler *type_handler() const
+ { return Type_handler_hybrid_field_type::type_handler(); }
- void make_field(THD *thd, Send_field *field) { item->make_field(thd, field); }
+ void make_send_field(THD *thd, Send_field *field)
+ { item->make_send_field(thd, field); }
table_map used_tables() const { return (table_map) 1L; }
bool const_item() const { return 0; }
bool is_null() { return null_value; }
@@ -5264,10 +5695,12 @@ public:
my_decimal *val_decimal(my_decimal *);
double val_real();
longlong val_int();
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ { return get_date_from_string(ltime, fuzzydate); }
void copy();
int save_in_field(Field *field, bool no_conversions);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_copy_string>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_copy_string>(thd, this); }
};
@@ -5385,20 +5818,11 @@ class Item_default_value : public Item_field
public:
Item *arg;
Field *cached_field;
- Item_default_value(THD *thd, Name_resolution_context *context_arg)
- :Item_field(thd, context_arg, (const char *)NULL, (const char *)NULL,
- (const char *)NULL),
- arg(NULL), cached_field(NULL) {}
Item_default_value(THD *thd, Name_resolution_context *context_arg, Item *a)
:Item_field(thd, context_arg, (const char *)NULL, (const char *)NULL,
- (const char *)NULL),
- arg(a), cached_field(NULL) {}
- Item_default_value(THD *thd, Name_resolution_context *context_arg, Field *a)
- :Item_field(thd, context_arg, (const char *)NULL, (const char *)NULL,
- (const char *)NULL),
- arg(NULL),cached_field(NULL) {}
+ &null_clex_str),
+ arg(a), cached_field(NULL) {}
enum Type type() const { return DEFAULT_VALUE_ITEM; }
- bool vcol_assignment_allowed_value() const { return arg == NULL; }
bool eq(const Item *item, bool binary_cmp) const;
bool fix_fields(THD *, Item **);
void cleanup();
@@ -5408,12 +5832,12 @@ public:
longlong val_int();
my_decimal *val_decimal(my_decimal *decimal_value);
bool get_date(MYSQL_TIME *ltime,ulonglong fuzzydate);
- bool send(Protocol *protocol, String *buffer);
+ bool send(Protocol *protocol, st_value *buffer);
int save_in_field(Field *field_arg, bool no_conversions);
bool save_in_param(THD *thd, Item_param *param)
{
// It should not be possible to have "EXECUTE .. USING DEFAULT(a)"
- DBUG_ASSERT(arg == NULL);
+ DBUG_ASSERT(0);
param->set_default();
return false;
}
@@ -5438,34 +5862,124 @@ public:
Item *transform(THD *thd, Item_transformer transformer, uchar *args);
};
+
+class Item_contextually_typed_value_specification: public Item
+{
+public:
+ Item_contextually_typed_value_specification(THD *thd) :Item(thd)
+ { }
+ enum Type type() const { return CONTEXTUALLY_TYPED_VALUE_ITEM; }
+ bool vcol_assignment_allowed_value() const { return true; }
+ bool eq(const Item *item, bool binary_cmp) const
+ {
+ return false;
+ }
+ bool is_evaluable_expression() const { return false; }
+ bool fix_fields(THD *thd, Item **items)
+ {
+ fixed= true;
+ return false;
+ }
+ String *val_str(String *str)
+ {
+ DBUG_ASSERT(0); // never should be called
+ null_value= true;
+ return 0;
+ }
+ double val_real()
+ {
+ DBUG_ASSERT(0); // never should be called
+ null_value= true;
+ return 0.0;
+ }
+ longlong val_int()
+ {
+ DBUG_ASSERT(0); // never should be called
+ null_value= true;
+ return 0;
+ }
+ my_decimal *val_decimal(my_decimal *decimal_value)
+ {
+ DBUG_ASSERT(0); // never should be called
+ null_value= true;
+ return 0;
+ }
+ bool get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
+ {
+ DBUG_ASSERT(0); // never should be called
+ return null_value= true;
+ }
+ bool send(Protocol *protocol, st_value *buffer)
+ {
+ DBUG_ASSERT(0);
+ return true;
+ }
+ const Type_handler *type_handler() const
+ {
+ DBUG_ASSERT(0);
+ return &type_handler_null;
+ }
+};
+
+
+/*
+ <default specification> ::= DEFAULT
+*/
+class Item_default_specification:
+ public Item_contextually_typed_value_specification
+{
+public:
+ Item_default_specification(THD *thd)
+ :Item_contextually_typed_value_specification(thd)
+ { }
+ void print(String *str, enum_query_type query_type)
+ {
+ str->append(STRING_WITH_LEN("default"));
+ }
+ int save_in_field(Field *field_arg, bool no_conversions)
+ {
+ return field_arg->save_in_field_default_value(false);
+ }
+ bool save_in_param(THD *thd, Item_param *param)
+ {
+ param->set_default();
+ return false;
+ }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_default_specification>(thd, this); }
+};
+
+
/**
This class is used as bulk parameter INGNORE representation.
It just do nothing when assigned to a field
+ This is a non-standard MariaDB extension.
*/
-class Item_ignore_value : public Item_default_value
+class Item_ignore_specification:
+ public Item_contextually_typed_value_specification
{
public:
- Item_ignore_value(THD *thd, Name_resolution_context *context_arg)
- :Item_default_value(thd, context_arg)
- {};
-
- void print(String *str, enum_query_type query_type);
- int save_in_field(Field *field_arg, bool no_conversions);
+ Item_ignore_specification(THD *thd)
+ :Item_contextually_typed_value_specification(thd)
+ { }
+ void print(String *str, enum_query_type query_type)
+ {
+ str->append(STRING_WITH_LEN("ignore"));
+ }
+ int save_in_field(Field *field_arg, bool no_conversions)
+ {
+ return field_arg->save_in_field_ignore_value(false);
+ }
bool save_in_param(THD *thd, Item_param *param)
{
param->set_ignore();
return false;
}
-
- String *val_str(String *str);
- double val_real();
- longlong val_int();
- my_decimal *val_decimal(my_decimal *decimal_value);
- bool get_date(MYSQL_TIME *ltime,ulonglong fuzzydate);
- bool send(Protocol *protocol, String *buffer);
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_ignore_specification>(thd, this); }
};
@@ -5485,7 +5999,7 @@ 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,
- (const char *)NULL),
+ &null_clex_str),
arg(a) {}
bool eq(const Item *item, bool binary_cmp) const;
bool fix_fields(THD *, Item **);
@@ -5512,7 +6026,7 @@ public:
bool update_vcol_processor(void *arg) { return 0; }
bool check_vcol_func_processor(void *arg)
{
- return mark_unsupported_function("values()", arg, VCOL_IMPOSSIBLE);
+ return mark_unsupported_function("value()", arg, VCOL_IMPOSSIBLE);
}
};
@@ -5546,7 +6060,7 @@ public:
Item_trigger_field(THD *thd, Name_resolution_context *context_arg,
row_version_type row_ver_arg,
- const char *field_name_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),
@@ -5632,7 +6146,7 @@ protected:
public:
Item_cache(THD *thd):
Item_basic_constant(thd),
- Type_handler_hybrid_field_type(MYSQL_TYPE_STRING),
+ Type_handler_hybrid_field_type(&type_handler_string),
example(0), cached_field(0),
value_cached(0)
{
@@ -5641,9 +6155,9 @@ public:
null_value= 1;
}
protected:
- Item_cache(THD *thd, enum_field_types field_type_arg):
+ Item_cache(THD *thd, const Type_handler *handler):
Item_basic_constant(thd),
- Type_handler_hybrid_field_type(field_type_arg),
+ Type_handler_hybrid_field_type(handler),
example(0), cached_field(0),
value_cached(0)
{
@@ -5664,24 +6178,9 @@ public:
};
enum Type type() const { return CACHE_ITEM; }
- enum_field_types field_type() const
- { return Type_handler_hybrid_field_type::field_type(); }
- enum Item_result result_type () const
- { return Type_handler_hybrid_field_type::result_type(); }
- enum Item_result cmp_type () const
- { return Type_handler_hybrid_field_type::cmp_type(); }
+ const Type_handler *type_handler() const
+ { return Type_handler_hybrid_field_type::type_handler(); }
- static Item_cache* get_cache(THD *thd, const Item* item,
- const Item_result type, const enum_field_types f_type);
- static Item_cache* get_cache(THD *thd, const Item* item,
- const Item_result type)
- {
- return get_cache(thd, item, type, item->field_type());
- }
- static Item_cache* get_cache(THD *thd, const Item *item)
- {
- return get_cache(thd, item, item->cmp_type());
- }
virtual void keep_array() {}
virtual void print(String *str, enum_query_type query_type);
bool eq_def(const Field *field)
@@ -5724,6 +6223,7 @@ public:
}
virtual void store(Item *item);
+ virtual Item *get_item() { return example; }
virtual bool cache_value()= 0;
bool basic_const_item() const
{ return example && example->basic_const_item(); }
@@ -5772,28 +6272,39 @@ class Item_cache_int: public Item_cache
protected:
longlong value;
public:
- Item_cache_int(THD *thd): Item_cache(thd, MYSQL_TYPE_LONGLONG),
+ Item_cache_int(THD *thd): Item_cache(thd, &type_handler_longlong),
value(0) {}
- Item_cache_int(THD *thd, enum_field_types field_type_arg):
- Item_cache(thd, field_type_arg), value(0) {}
+ Item_cache_int(THD *thd, const Type_handler *handler):
+ Item_cache(thd, handler), value(0) {}
double val_real();
longlong val_int();
String* val_str(String *str);
my_decimal *val_decimal(my_decimal *);
- enum Item_result result_type() const { return INT_RESULT; }
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ { return get_date_from_int(ltime, fuzzydate); }
bool cache_value();
int save_in_field(Field *field, bool no_conversions);
Item *convert_to_basic_const_item(THD *thd);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_cache_int>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_cache_int>(thd, this); }
+};
+
+
+class Item_cache_year: public Item_cache_int
+{
+public:
+ Item_cache_year(THD *thd): Item_cache_int(thd, &type_handler_year) { }
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ { return get_date_from_year(ltime, fuzzydate); }
};
class Item_cache_temporal: public Item_cache_int
{
+protected:
+ Item_cache_temporal(THD *thd, const Type_handler *handler);
public:
- Item_cache_temporal(THD *thd, enum_field_types field_type_arg);
String* val_str(String *str);
my_decimal *val_decimal(my_decimal *);
longlong val_int();
@@ -5810,7 +6321,6 @@ public:
set_if_smaller(decimals, TIME_SECOND_PART_DIGITS);
return false;
}
- Item_result cmp_type() const { return TIME_RESULT; }
void store_packed(longlong val_arg, Item *example);
/*
Having a clone_item method tells optimizer that this object
@@ -5819,27 +6329,84 @@ public:
*/
Item *clone_item(THD *thd);
Item *convert_to_basic_const_item(THD *thd);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_cache_temporal>(thd, mem_root, this); }
+ virtual Item *make_literal(THD *) =0;
+};
+
+
+class Item_cache_time: public Item_cache_temporal
+{
+public:
+ Item_cache_time(THD *thd)
+ :Item_cache_temporal(thd, &type_handler_time2) { }
+ bool cache_value();
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_cache_time>(thd, this); }
+ Item *make_literal(THD *);
+};
+
+
+class Item_cache_datetime: public Item_cache_temporal
+{
+public:
+ Item_cache_datetime(THD *thd)
+ :Item_cache_temporal(thd, &type_handler_datetime2) { }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_cache_datetime>(thd, this); }
+ Item *make_literal(THD *);
+};
+
+
+class Item_cache_date: public Item_cache_temporal
+{
+public:
+ Item_cache_date(THD *thd)
+ :Item_cache_temporal(thd, &type_handler_newdate) { }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_cache_date>(thd, this); }
+ Item *make_literal(THD *);
};
class Item_cache_real: public Item_cache
{
+protected:
double value;
public:
- Item_cache_real(THD *thd): Item_cache(thd, MYSQL_TYPE_DOUBLE),
- value(0) {}
-
+ Item_cache_real(THD *thd, const Type_handler *h)
+ :Item_cache(thd, h),
+ value(0)
+ {}
double val_real();
longlong val_int();
- String* val_str(String *str);
my_decimal *val_decimal(my_decimal *);
- enum Item_result result_type() const { return REAL_RESULT; }
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ { return get_date_from_real(ltime, fuzzydate); }
bool cache_value();
Item *convert_to_basic_const_item(THD *thd);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_cache_real>(thd, mem_root, this); }
+};
+
+
+class Item_cache_double: public Item_cache_real
+{
+public:
+ Item_cache_double(THD *thd)
+ :Item_cache_real(thd, &type_handler_double)
+ { }
+ String* val_str(String *str);
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_cache_double>(thd, this); }
+};
+
+
+class Item_cache_float: public Item_cache_real
+{
+public:
+ Item_cache_float(THD *thd)
+ :Item_cache_real(thd, &type_handler_float)
+ { }
+ String* val_str(String *str);
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_cache_float>(thd, this); }
};
@@ -5848,17 +6415,18 @@ class Item_cache_decimal: public Item_cache
protected:
my_decimal decimal_value;
public:
- Item_cache_decimal(THD *thd): Item_cache(thd, MYSQL_TYPE_NEWDECIMAL) {}
+ Item_cache_decimal(THD *thd): Item_cache(thd, &type_handler_newdecimal) {}
double val_real();
longlong val_int();
String* val_str(String *str);
my_decimal *val_decimal(my_decimal *);
- enum Item_result result_type() const { return DECIMAL_RESULT; }
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ { return get_date_from_decimal(ltime, fuzzydate); }
bool cache_value();
Item *convert_to_basic_const_item(THD *thd);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_cache_decimal>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_cache_decimal>(thd, this); }
};
@@ -5870,7 +6438,7 @@ class Item_cache_str: public Item_cache
public:
Item_cache_str(THD *thd, const Item *item):
- Item_cache(thd, item->field_type()), value(0),
+ Item_cache(thd, item->type_handler()), value(0),
is_varbinary(item->type() == FIELD_ITEM &&
Item_cache_str::field_type() == MYSQL_TYPE_VARCHAR &&
!((const Item_field *) item)->field->has_charset())
@@ -5881,13 +6449,14 @@ public:
longlong val_int();
String* val_str(String *);
my_decimal *val_decimal(my_decimal *);
- enum Item_result result_type() const { return STRING_RESULT; }
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ { return get_date_from_string(ltime, fuzzydate); }
CHARSET_INFO *charset() const { return value->charset(); };
int save_in_field(Field *field, bool no_conversions);
bool cache_value();
Item *convert_to_basic_const_item(THD *thd);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_cache_str>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_cache_str>(thd, this); }
};
@@ -5911,8 +6480,8 @@ public:
*/
return Item::safe_charset_converter(thd, tocs);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_cache_str_for_nullif>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_cache_str_for_nullif>(thd, this); }
};
@@ -5938,9 +6507,9 @@ public:
bool setup(THD *thd, Item *item);
void store(Item *item);
void illegal_method_call(const char *);
- void make_field(THD *thd, Send_field *)
+ void make_send_field(THD *thd, Send_field *)
{
- illegal_method_call((const char*)"make_field");
+ illegal_method_call((const char*)"make_send_field");
};
double val_real()
{
@@ -5962,10 +6531,13 @@ public:
illegal_method_call((const char*)"val_decimal");
return 0;
};
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ {
+ illegal_method_call((const char*)"val_decimal");
+ return true;
+ }
- enum Item_result result_type() const { return ROW_RESULT; }
-
- uint cols() { return item_count; }
+ uint cols() const { return item_count; }
Item *element_index(uint i) { return values[i]; }
Item **addr(uint i) { return (Item **) (values + i); }
bool check_cols(uint c);
@@ -5984,8 +6556,8 @@ public:
}
bool cache_value();
virtual void set_null();
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_cache_row>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_cache_row>(thd, this); }
};
@@ -5997,51 +6569,85 @@ public:
single SP/PS execution.
*/
class Item_type_holder: public Item,
- public Type_handler_hybrid_real_field_type
+ public Type_handler_hybrid_field_type,
+ public Type_geometry_attributes
{
protected:
TYPELIB *enum_set_typelib;
- Field::geometry_type geometry_type;
-
- void get_full_info(Item *item);
-
- /* It is used to count decimal precision in join_types */
- int prev_decimal_int_part;
public:
- Item_type_holder(THD*, Item*);
+ Item_type_holder(THD *thd, Item *item)
+ :Item(thd, item),
+ Type_handler_hybrid_field_type(item->real_type_handler()),
+ enum_set_typelib(0)
+ {
+ DBUG_ASSERT(item->fixed);
+ maybe_null= item->maybe_null;
+ }
+ Item_type_holder(THD *thd,
+ Item *item,
+ const Type_handler *handler,
+ const Type_all_attributes *attr,
+ 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;
+ }
- enum_field_types field_type() const
- { return Type_handler_hybrid_real_field_type::field_type(); }
- enum_field_types real_field_type() const
- { return Type_handler_hybrid_real_field_type::real_field_type(); }
- enum Item_result result_type () const
+ const Type_handler *type_handler() const
{
- /*
- In 10.1 Item_type_holder::result_type() returned
- Field::result_merge_type(field_type()), which returned STRING_RESULT
- for the BIT data type. In 10.2 it returns INT_RESULT, similar
- to what Field_bit::result_type() does. This should not be
- important because Item_type_holder is a limited purpose Item
- and its result_type() should not be called from outside of
- Item_type_holder. It's called only internally from decimal_int_part()
- from join_types(), to calculate "decimals" of the result data type.
- As soon as we get BIT as one of the joined types, the result field
- type cannot be numeric: it's either BIT, or VARBINARY.
- */
- return Type_handler_hybrid_real_field_type::result_type();
+ return Type_handler_hybrid_field_type::type_handler()->
+ type_handler_for_item_field();
+ }
+ const Type_handler *real_type_handler() const
+ {
+ return Type_handler_hybrid_field_type::type_handler();
}
enum Type type() const { return TYPE_HOLDER; }
+ TYPELIB *get_typelib() const { return enum_set_typelib; }
+ /*
+ When handling a query like this:
+ VALUES ('') UNION VALUES( _utf16 0x0020 COLLATE utf16_bin);
+ Item_type_holder can be passed to
+ Type_handler_xxx::Item_hybrid_func_fix_attributes()
+ We don't want the latter to perform character set conversion of a
+ Item_type_holder by calling its val_str(), which calls DBUG_ASSERT(0).
+ Let's override const_item() and is_expensive() to avoid this.
+ Note, Item_hybrid_func_fix_attributes() could probably
+ have a new argument to distinguish what we need:
+ - (a) aggregate data type attributes only
+ - (b) install converters after attribute aggregation
+ So st_select_lex_unit::join_union_type_attributes() could
+ ask it to do (a) only, without (b).
+ */
+ bool const_item() const { return false; }
+ bool is_expensive() { return true; }
double val_real();
longlong val_int();
my_decimal *val_decimal(my_decimal *);
String *val_str(String*);
- bool join_types(THD *thd, Item *);
- Field *make_field_by_type(TABLE *table);
- static uint32 display_length(Item *item);
- static enum_field_types get_real_type(Item *);
- Field::geometry_type get_geometry_type() const { return geometry_type; };
- Item* get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; }
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ Field *create_tmp_field(bool group, TABLE *table)
+ {
+ return Item_type_holder::real_type_handler()->
+ make_and_init_table_field(&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_buff.cc b/sql/item_buff.cc
index bc888c2b679..a64e092a434 100644
--- a/sql/item_buff.cc
+++ b/sql/item_buff.cc
@@ -22,7 +22,7 @@
Buffers to save and compare item values
*/
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
/*
It is necessary to include set_var.h instead of item.h because there
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 03648a323d5..2d4501ed222 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -26,7 +26,7 @@
#pragma implementation // gcc: Class implementation
#endif
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include <m_ctype.h>
#include "sql_select.h"
@@ -34,21 +34,14 @@
#include "sql_time.h" // make_truncated_value_warning
#include "sql_base.h" // dynamic_column_error_message
-static Item** cache_converted_constant(THD *thd, Item **value,
- Item **cache_item, Item_result type,enum_field_types f_type);
-
/**
find an temporal type (item) that others will be converted to
for the purpose of comparison.
- for IN/CASE conversion only happens if the first item defines the
- comparison context.
-
this is the type that will be used in warnings like
"Incorrect <<TYPE>> value".
*/
-static Item *find_date_time_item(THD *thd, Item **args, uint nargs, uint col,
- bool in_case)
+static Item *find_date_time_item(Item **args, uint nargs, uint col)
{
Item *date_arg= 0, **arg, **arg_end;
for (arg= args, arg_end= args + nargs; arg != arg_end ; arg++)
@@ -56,22 +49,10 @@ static Item *find_date_time_item(THD *thd, Item **args, uint nargs, uint col,
Item *item= arg[0]->element_index(col);
if (item->cmp_type() != TIME_RESULT)
continue;
+ if (item->field_type() == MYSQL_TYPE_DATETIME)
+ return item;
if (!date_arg)
date_arg= item;
- if (item->field_type() == MYSQL_TYPE_DATETIME)
- break;
- }
- if (in_case ? date_arg == args[0]->element_index(col) : date_arg != NULL)
- {
- enum_field_types f_type= date_arg->field_type();
- for (arg= args, arg_end= args + nargs; arg != arg_end ; arg++)
- {
- Item *cache, **a= arg[0]->addr(col);
- if (!a)
- a= arg;
- if (cache_converted_constant(thd, a, &cache, TIME_RESULT, f_type) != a)
- thd->change_item_tree(a, cache);
- }
}
return date_arg;
}
@@ -116,37 +97,54 @@ static int cmp_row_type(Item* item1, Item* item2)
/**
Aggregates result types from the array of items.
- SYNOPSIS:
- agg_cmp_type()
- type [out] the aggregated type
- items array of items to aggregate the type from
- nitems number of items in the array
+ This method aggregates comparison handler from the array of items.
+ The result handler is used later for comparison of values of these items.
- DESCRIPTION
- This function aggregates result types from the array of items. Found type
- supposed to be used later for comparison of values of these items.
- Aggregation itself is performed by the item_cmp_type() function.
- @param[out] type the aggregated type
- @param items array of items to aggregate the type from
- @param nitems number of items in the array
+ aggregate_for_comparison()
+ funcname the function or operator name,
+ for error reporting
+ items array of items to aggregate the type from
+ nitems number of items in the array
+ int_uint_as_dec what to do when comparing INT to UINT:
+ set the comparison handler to decimal or int.
- @retval
- 1 type incompatibility has been detected
- @retval
- 0 otherwise
+ @retval true type incompatibility has been detected
+ @retval false otherwise
*/
-static int agg_cmp_type(Item_result *type,
- Item **items,
- uint nitems,
- bool int_uint_as_dec)
+bool
+Type_handler_hybrid_field_type::aggregate_for_comparison(const char *funcname,
+ Item **items,
+ uint nitems,
+ bool int_uint_as_dec)
{
uint unsigned_count= items[0]->unsigned_flag;
- type[0]= items[0]->cmp_type();
+ /*
+ Convert sub-type to super-type (e.g. DATE to DATETIME, INT to BIGINT, etc).
+ Otherwise Predicant_to_list_comparator will treat sub-types of the same
+ super-type as different data types and won't be able to use bisection in
+ many cases.
+ */
+ set_handler(items[0]->type_handler()->type_handler_for_comparison());
for (uint i= 1 ; i < nitems ; i++)
{
unsigned_count+= items[i]->unsigned_flag;
- type[0]= item_cmp_type(type[0], items[i]);
+ if (aggregate_for_comparison(items[i]->type_handler()->
+ type_handler_for_comparison()))
+ {
+ /*
+ For more precise error messages if aggregation failed on the first pair
+ {items[0],items[1]}, use the name of items[0]->data_handler().
+ Otherwise use the name of this->type_handler(), which is already a
+ result of aggregation for items[0]..items[i-1].
+ */
+ my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0),
+ i == 1 ? items[0]->type_handler()->name().ptr() :
+ type_handler()->name().ptr(),
+ items[i]->type_handler()->name().ptr(),
+ funcname);
+ return true;
+ }
/*
When aggregating types of two row expressions we have to check
that they have the same cardinality and that each component
@@ -154,102 +152,21 @@ static int agg_cmp_type(Item_result *type,
the signature of the corresponding component of the second row
expression.
*/
- if (type[0] == ROW_RESULT && cmp_row_type(items[0], items[i]))
- return 1; // error found: invalid usage of rows
+ if (cmp_type() == ROW_RESULT && cmp_row_type(items[0], items[i]))
+ return true; // error found: invalid usage of rows
}
/**
If all arguments are of INT type but have different unsigned_flag values,
switch to DECIMAL_RESULT.
*/
if (int_uint_as_dec &&
- type[0] == INT_RESULT &&
+ cmp_type() == INT_RESULT &&
unsigned_count != nitems && unsigned_count != 0)
- type[0]= DECIMAL_RESULT;
+ set_handler(&type_handler_newdecimal);
return 0;
}
-/**
- @brief Aggregates field types from the array of items.
-
- @param[in] items array of items to aggregate the type from
- @paran[in] nitems number of items in the array
- @param[in] treat_bit_as_number - if BIT should be aggregated to a non-BIT
- counterpart as a LONGLONG number or as a VARBINARY string.
-
- Currently behaviour depends on the function:
- - LEAST/GREATEST treat BIT as VARBINARY when
- aggregating with a non-BIT counterpart.
- Note, UNION also works this way.
-
- - CASE, COALESCE, IF, IFNULL treat BIT as LONGLONG when
- aggregating with a non-BIT counterpart;
-
- This inconsistency may be changed in the future. See MDEV-8867.
-
- Note, independently from "treat_bit_as_number":
- - a single BIT argument gives BIT as a result
- - two BIT couterparts give BIT as a result
-
- @details This function aggregates field types from the array of items.
- Found type is supposed to be used later as the result field type
- of a multi-argument function.
- Aggregation itself is performed by the Field::field_type_merge()
- function.
-
- @note The term "aggregation" is used here in the sense of inferring the
- result type of a function from its argument types.
-
- @return aggregated field type.
-*/
-
-enum_field_types agg_field_type(Item **items, uint nitems,
- bool treat_bit_as_number)
-{
- uint i;
- if (!nitems || items[0]->result_type() == ROW_RESULT)
- {
- DBUG_ASSERT(0);
- return MYSQL_TYPE_NULL;
- }
- enum_field_types res= items[0]->field_type();
- uint unsigned_count= items[0]->unsigned_flag;
- for (i= 1 ; i < nitems ; i++)
- {
- enum_field_types cur= items[i]->field_type();
- if (treat_bit_as_number &&
- ((res == MYSQL_TYPE_BIT) ^ (cur == MYSQL_TYPE_BIT)))
- {
- if (res == MYSQL_TYPE_BIT)
- res= MYSQL_TYPE_LONGLONG; // BIT + non-BIT
- else
- cur= MYSQL_TYPE_LONGLONG; // non-BIT + BIT
- }
- res= Field::field_type_merge(res, cur);
- unsigned_count+= items[i]->unsigned_flag;
- }
- switch (res) {
- 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:
- if (unsigned_count != 0 && unsigned_count != nitems)
- {
- /*
- If all arguments are of INT-alike type but have different
- unsigned_flag, then convert to DECIMAL.
- */
- return MYSQL_TYPE_NEWDECIMAL;
- }
- default:
- break;
- }
- return res;
-}
-
/*
Collects different types for comparison of first item with each other items
@@ -501,7 +418,8 @@ void Item_func::convert_const_compared_to_int_field(THD *thd)
args[field= 1]->real_item()->type() == FIELD_ITEM)
{
Item_field *field_item= (Item_field*) (args[field]->real_item());
- if ((field_item->field_type() == MYSQL_TYPE_LONGLONG ||
+ if (((field_item->field_type() == MYSQL_TYPE_LONGLONG &&
+ field_item->type_handler() != &type_handler_vers_trx_id) ||
field_item->field_type() == MYSQL_TYPE_YEAR))
convert_const_to_int(thd, field_item, &args[!field]);
}
@@ -560,77 +478,6 @@ bool Item_bool_rowready_func2::fix_length_and_dec()
}
-int Arg_comparator::set_compare_func(Item_func_or_sum *item, Item_result type)
-{
- owner= item;
- func= comparator_matrix[type][is_owner_equal_func()];
-
- switch (type) {
- case TIME_RESULT:
- m_compare_collation= &my_charset_numeric;
- break;
- case ROW_RESULT:
- {
- uint n= (*a)->cols();
- if (n != (*b)->cols())
- {
- my_error(ER_OPERAND_COLUMNS, MYF(0), n);
- comparators= 0;
- return 1;
- }
- if (!(comparators= new Arg_comparator[n]))
- return 1;
- for (uint i=0; i < n; i++)
- {
- if ((*a)->element_index(i)->cols() != (*b)->element_index(i)->cols())
- {
- my_error(ER_OPERAND_COLUMNS, MYF(0), (*a)->element_index(i)->cols());
- return 1;
- }
- if (comparators[i].set_cmp_func(owner, (*a)->addr(i),
- (*b)->addr(i), set_null))
- return 1;
- }
- break;
- }
- case INT_RESULT:
- {
- if (func == &Arg_comparator::compare_int_signed)
- {
- if ((*a)->unsigned_flag)
- func= (((*b)->unsigned_flag)?
- &Arg_comparator::compare_int_unsigned :
- &Arg_comparator::compare_int_unsigned_signed);
- else if ((*b)->unsigned_flag)
- func= &Arg_comparator::compare_int_signed_unsigned;
- }
- else if (func== &Arg_comparator::compare_e_int)
- {
- if ((*a)->unsigned_flag ^ (*b)->unsigned_flag)
- func= &Arg_comparator::compare_e_int_diff_signedness;
- }
- break;
- }
- case STRING_RESULT:
- case DECIMAL_RESULT:
- break;
- case REAL_RESULT:
- {
- if ((*a)->decimals < NOT_FIXED_DEC && (*b)->decimals < NOT_FIXED_DEC)
- {
- precision= 5 / log_10[MY_MAX((*a)->decimals, (*b)->decimals) + 1];
- if (func == &Arg_comparator::compare_real)
- func= &Arg_comparator::compare_real_fixed;
- else if (func == &Arg_comparator::compare_e_real)
- func= &Arg_comparator::compare_e_real_fixed;
- }
- break;
- }
- }
- return 0;
-}
-
-
/**
Prepare the comparator (set the comparison function) for comparing
items *a1 and *a2 in the context of 'type'.
@@ -647,14 +494,62 @@ int Arg_comparator::set_compare_func(Item_func_or_sum *item, Item_result type)
int Arg_comparator::set_cmp_func(Item_func_or_sum *owner_arg,
Item **a1, Item **a2)
{
- THD *thd= current_thd;
owner= owner_arg;
set_null= set_null && owner_arg;
a= a1;
b= a2;
- m_compare_type= item_cmp_type(*a1, *a2);
+ Item *tmp_args[2]= {*a1, *a2};
+ Type_handler_hybrid_field_type tmp;
+ if (tmp.aggregate_for_comparison(owner_arg->func_name(), tmp_args, 2, false))
+ {
+ DBUG_ASSERT(current_thd->is_error());
+ return 1;
+ }
+ m_compare_handler= tmp.type_handler();
+ return m_compare_handler->set_comparator_func(this);
+}
- if (m_compare_type == STRING_RESULT &&
+
+bool Arg_comparator::set_cmp_func_for_row_arguments()
+{
+ uint n= (*a)->cols();
+ if (n != (*b)->cols())
+ {
+ my_error(ER_OPERAND_COLUMNS, MYF(0), n);
+ comparators= 0;
+ return true;
+ }
+ if (!(comparators= new Arg_comparator[n]))
+ return true;
+ for (uint i=0; i < n; i++)
+ {
+ if ((*a)->element_index(i)->cols() != (*b)->element_index(i)->cols())
+ {
+ my_error(ER_OPERAND_COLUMNS, MYF(0), (*a)->element_index(i)->cols());
+ return true;
+ }
+ if (comparators[i].set_cmp_func(owner, (*a)->addr(i),
+ (*b)->addr(i), set_null))
+ return true;
+ }
+ return false;
+}
+
+
+bool Arg_comparator::set_cmp_func_row()
+{
+ func= is_owner_equal_func() ? &Arg_comparator::compare_e_row :
+ &Arg_comparator::compare_row;
+ return set_cmp_func_for_row_arguments();
+}
+
+
+bool Arg_comparator::set_cmp_func_string()
+{
+ THD *thd= current_thd;
+ func= is_owner_equal_func() ? &Arg_comparator::compare_e_string :
+ &Arg_comparator::compare_string;
+ if (compare_type() == STRING_RESULT &&
(*a)->result_type() == STRING_RESULT &&
(*b)->result_type() == STRING_RESULT)
{
@@ -663,7 +558,7 @@ int Arg_comparator::set_cmp_func(Item_func_or_sum *owner_arg,
generated item, like in natural join
*/
if (owner->agg_arg_charsets_for_comparison(&m_compare_collation, a, b))
- return 1;
+ return true;
if ((*a)->type() == Item::FUNC_ITEM &&
((Item_func *) (*a))->functype() == Item_func::JSON_EXTRACT_FUNC)
@@ -681,29 +576,73 @@ int Arg_comparator::set_cmp_func(Item_func_or_sum *owner_arg,
}
}
- if (m_compare_type == TIME_RESULT)
+ a= cache_converted_constant(thd, a, &a_cache, compare_type_handler());
+ b= cache_converted_constant(thd, b, &b_cache, compare_type_handler());
+ return false;
+}
+
+
+bool Arg_comparator::set_cmp_func_time()
+{
+ THD *thd= current_thd;
+ m_compare_collation= &my_charset_numeric;
+ func= is_owner_equal_func() ? &Arg_comparator::compare_e_time :
+ &Arg_comparator::compare_time;
+ a= cache_converted_constant(thd, a, &a_cache, compare_type_handler());
+ b= cache_converted_constant(thd, b, &b_cache, compare_type_handler());
+ return false;
+}
+
+
+bool Arg_comparator::set_cmp_func_datetime()
+{
+ THD *thd= current_thd;
+ m_compare_collation= &my_charset_numeric;
+ func= is_owner_equal_func() ? &Arg_comparator::compare_e_datetime :
+ &Arg_comparator::compare_datetime;
+ a= cache_converted_constant(thd, a, &a_cache, compare_type_handler());
+ b= cache_converted_constant(thd, b, &b_cache, compare_type_handler());
+ return false;
+}
+
+
+bool Arg_comparator::set_cmp_func_int()
+{
+ THD *thd= current_thd;
+ func= is_owner_equal_func() ? &Arg_comparator::compare_e_int :
+ &Arg_comparator::compare_int_signed;
+ if ((*a)->field_type() == MYSQL_TYPE_YEAR &&
+ (*b)->field_type() == MYSQL_TYPE_YEAR)
{
- enum_field_types f_type= a[0]->field_type_for_temporal_comparison(b[0]);
- if (f_type == MYSQL_TYPE_TIME)
- {
- func= is_owner_equal_func() ? &Arg_comparator::compare_e_time :
- &Arg_comparator::compare_time;
- }
- else
- {
- func= is_owner_equal_func() ? &Arg_comparator::compare_e_datetime :
- &Arg_comparator::compare_datetime;
- }
- a= cache_converted_constant(thd, a, &a_cache, m_compare_type, f_type);
- b= cache_converted_constant(thd, b, &b_cache, m_compare_type, f_type);
- return 0;
+ func= is_owner_equal_func() ? &Arg_comparator::compare_e_datetime :
+ &Arg_comparator::compare_datetime;
}
+ else if (func == &Arg_comparator::compare_int_signed)
+ {
+ if ((*a)->unsigned_flag)
+ func= (((*b)->unsigned_flag)?
+ &Arg_comparator::compare_int_unsigned :
+ &Arg_comparator::compare_int_unsigned_signed);
+ else if ((*b)->unsigned_flag)
+ func= &Arg_comparator::compare_int_signed_unsigned;
+ }
+ else if (func== &Arg_comparator::compare_e_int)
+ {
+ if ((*a)->unsigned_flag ^ (*b)->unsigned_flag)
+ func= &Arg_comparator::compare_e_int_diff_signedness;
+ }
+ a= cache_converted_constant(thd, a, &a_cache, compare_type_handler());
+ b= cache_converted_constant(thd, b, &b_cache, compare_type_handler());
+ return false;
+}
- if (m_compare_type == REAL_RESULT &&
- (((*a)->result_type() == DECIMAL_RESULT && !(*a)->const_item() &&
+
+bool Arg_comparator::set_cmp_func_real()
+{
+ if ((((*a)->result_type() == DECIMAL_RESULT && !(*a)->const_item() &&
(*b)->result_type() == STRING_RESULT && (*b)->const_item()) ||
- ((*b)->result_type() == DECIMAL_RESULT && !(*b)->const_item() &&
- (*a)->result_type() == STRING_RESULT && (*a)->const_item())))
+ ((*b)->result_type() == DECIMAL_RESULT && !(*b)->const_item() &&
+ (*a)->result_type() == STRING_RESULT && (*a)->const_item())))
{
/*
<non-const decimal expression> <cmp> <const string expression>
@@ -712,23 +651,34 @@ int Arg_comparator::set_cmp_func(Item_func_or_sum *owner_arg,
Do comparison as decimal rather than float, in order not to lose precision.
*/
- m_compare_type= DECIMAL_RESULT;
+ m_compare_handler= &type_handler_newdecimal;
+ return set_cmp_func_decimal();
}
- if (m_compare_type == INT_RESULT &&
- (*a)->field_type() == MYSQL_TYPE_YEAR &&
- (*b)->field_type() == MYSQL_TYPE_YEAR)
- {
- m_compare_type= TIME_RESULT;
- func= is_owner_equal_func() ? &Arg_comparator::compare_e_datetime :
- &Arg_comparator::compare_datetime;
- }
- else
- {
- a= cache_converted_constant(thd, a, &a_cache, m_compare_type, (*a)->field_type());
- b= cache_converted_constant(thd, b, &b_cache, m_compare_type, (*b)->field_type());
- }
- return set_compare_func(owner_arg, m_compare_type);
+ THD *thd= current_thd;
+ func= is_owner_equal_func() ? &Arg_comparator::compare_e_real :
+ &Arg_comparator::compare_real;
+ if ((*a)->decimals < NOT_FIXED_DEC && (*b)->decimals < NOT_FIXED_DEC)
+ {
+ precision= 5 / log_10[MY_MAX((*a)->decimals, (*b)->decimals) + 1];
+ if (func == &Arg_comparator::compare_real)
+ func= &Arg_comparator::compare_real_fixed;
+ else if (func == &Arg_comparator::compare_e_real)
+ func= &Arg_comparator::compare_e_real_fixed;
+ }
+ a= cache_converted_constant(thd, a, &a_cache, compare_type_handler());
+ b= cache_converted_constant(thd, b, &b_cache, compare_type_handler());
+ return false;
+}
+
+bool Arg_comparator::set_cmp_func_decimal()
+{
+ THD *thd= current_thd;
+ func= is_owner_equal_func() ? &Arg_comparator::compare_e_decimal :
+ &Arg_comparator::compare_decimal;
+ a= cache_converted_constant(thd, a, &a_cache, compare_type_handler());
+ b= cache_converted_constant(thd, b, &b_cache, compare_type_handler());
+ return false;
}
@@ -749,15 +699,20 @@ int Arg_comparator::set_cmp_func(Item_func_or_sum *owner_arg,
@return cache item or original value.
*/
-static Item** cache_converted_constant(THD *thd, Item **value,
- Item **cache_item, Item_result type, enum_field_types f_type)
+Item** Arg_comparator::cache_converted_constant(THD *thd_arg, Item **value,
+ Item **cache_item,
+ const Type_handler *handler)
{
- /* Don't need cache if doing context analysis only. */
- if (!thd->lex->is_ps_or_view_context_analysis() &&
- (*value)->const_item() && type != (*value)->result_type())
+ /*
+ Don't need cache if doing context analysis only.
+ */
+ if (!thd_arg->lex->is_ps_or_view_context_analysis() &&
+ (*value)->const_item() &&
+ handler->type_handler_for_comparison() !=
+ (*value)->type_handler_for_comparison())
{
- Item_cache *cache= Item_cache::get_cache(thd, *value, type, f_type);
- cache->setup(thd, *value);
+ Item_cache *cache= handler->Item_get_cache(thd_arg, *value);
+ cache->setup(thd_arg, *value);
*cache_item= cache;
return cache_item;
}
@@ -765,60 +720,57 @@ static Item** cache_converted_constant(THD *thd, Item **value,
}
-/*
- Compare items values as dates.
-
- SYNOPSIS
- Arg_comparator::compare_datetime()
-
- DESCRIPTION
- Compare items values as DATE/DATETIME for both EQUAL_FUNC and from other
- comparison functions.
-
- RETURN
- -1 a < b or at least one item is null
- 0 a == b
- 1 a > b
-*/
-
-int Arg_comparator::compare_temporal(enum_field_types type)
+int Arg_comparator::compare_time()
{
- longlong a_value, b_value;
-
+ longlong val1= (*a)->val_time_packed();
+ if (!(*a)->null_value)
+ {
+ longlong val2= (*b)->val_time_packed();
+ if (!(*b)->null_value)
+ return compare_not_null_values(val1, val2);
+ }
if (set_null)
- owner->null_value= 1;
+ owner->null_value= true;
+ return -1;
+}
- /* Get DATE/DATETIME/TIME value of the 'a' item. */
- a_value= (*a)->val_temporal_packed(type);
- if ((*a)->null_value)
- return -1;
- /* Get DATE/DATETIME/TIME value of the 'b' item. */
- b_value= (*b)->val_temporal_packed(type);
- if ((*b)->null_value)
- return -1;
+int Arg_comparator::compare_e_time()
+{
+ longlong val1= (*a)->val_time_packed();
+ longlong val2= (*b)->val_time_packed();
+ if ((*a)->null_value || (*b)->null_value)
+ return MY_TEST((*a)->null_value && (*b)->null_value);
+ return MY_TEST(val1 == val2);
+}
- /* Here we have two not-NULL values. */
- if (set_null)
- owner->null_value= 0;
- /* Compare values. */
- return a_value < b_value ? -1 : a_value > b_value ? 1 : 0;
-}
-int Arg_comparator::compare_e_temporal(enum_field_types type)
+int Arg_comparator::compare_datetime()
{
- longlong a_value, b_value;
+ longlong val1= (*a)->val_datetime_packed();
+ if (!(*a)->null_value)
+ {
+ longlong val2= (*b)->val_datetime_packed();
+ if (!(*b)->null_value)
+ return compare_not_null_values(val1, val2);
+ }
+ if (set_null)
+ owner->null_value= true;
+ return -1;
+}
- /* Get DATE/DATETIME/TIME value of the 'a' item. */
- a_value= (*a)->val_temporal_packed(type);
- /* Get DATE/DATETIME/TIME value of the 'b' item. */
- b_value= (*b)->val_temporal_packed(type);
- return (*a)->null_value || (*b)->null_value ?
- (*a)->null_value == (*b)->null_value : a_value == b_value;
+int Arg_comparator::compare_e_datetime()
+{
+ longlong val1= (*a)->val_datetime_packed();
+ longlong val2= (*b)->val_datetime_packed();
+ if ((*a)->null_value || (*b)->null_value)
+ return MY_TEST((*a)->null_value && (*b)->null_value);
+ return MY_TEST(val1 == val2);
}
+
int Arg_comparator::compare_string()
{
String *res1,*res2;
@@ -965,13 +917,7 @@ int Arg_comparator::compare_int_signed()
{
longlong val2= (*b)->val_int();
if (!(*b)->null_value)
- {
- if (set_null)
- owner->null_value= 0;
- if (val1 < val2) return -1;
- if (val1 == val2) return 0;
- return 1;
- }
+ return compare_not_null_values(val1, val2);
}
if (set_null)
owner->null_value= 1;
@@ -1230,6 +1176,8 @@ longlong Item_func_truth::val_int()
bool Item_in_optimizer::is_top_level_item()
{
+ if (invisible_mode())
+ return FALSE;
return ((Item_in_subselect *)args[1])->is_top_level_item();
}
@@ -1312,8 +1260,8 @@ bool Item_in_optimizer::fix_left(THD *thd)
ref0= &(((Item_in_subselect *)args[1])->left_expr);
args[0]= ((Item_in_subselect *)args[1])->left_expr;
}
- if ((!(*ref0)->fixed && (*ref0)->fix_fields(thd, ref0)) ||
- (!cache && !(cache= Item_cache::get_cache(thd, *ref0))))
+ if ((*ref0)->fix_fields_if_needed(thd, ref0) ||
+ (!cache && !(cache= (*ref0)->get_cache(thd))))
DBUG_RETURN(1);
/*
During fix_field() expression could be substituted.
@@ -1397,7 +1345,7 @@ bool Item_in_optimizer::fix_fields(THD *thd, Item **ref)
if (args[0]->maybe_null)
maybe_null=1;
- if (!args[1]->fixed && args[1]->fix_fields(thd, args+1))
+ if (args[1]->fix_fields_if_needed(thd, args + 1))
return TRUE;
if (!invisible_mode() &&
((sub && ((col= args[0]->cols()) != sub->engine->cols())) ||
@@ -1408,7 +1356,7 @@ bool Item_in_optimizer::fix_fields(THD *thd, Item **ref)
}
if (args[1]->maybe_null)
maybe_null=1;
- with_subselect= 1;
+ m_with_subquery= true;
with_sum_func= with_sum_func || args[1]->with_sum_func;
with_field= with_field || args[1]->with_field;
with_param= args[0]->with_param || args[1]->with_param;
@@ -1882,16 +1830,13 @@ bool Item_func_opt_neg::eq(const Item *item, bool binary_cmp) const
return 0;
if (negated != ((Item_func_opt_neg *) item_func)->negated)
return 0;
- for (uint i=0; i < arg_count ; i++)
- if (!args[i]->eq(item_func->arguments()[i], binary_cmp))
- return 0;
- return 1;
+ return Item_args::eq(item_func, binary_cmp);
}
bool Item_func_interval::fix_fields(THD *thd, Item **ref)
{
- if (Item_int_func::fix_fields(thd, ref))
+ if (Item_long_func::fix_fields(thd, ref))
return true;
for (uint i= 0 ; i < row->cols(); i++)
{
@@ -2130,9 +2075,7 @@ void Item_func_between::fix_after_pullout(st_select_lex *new_parent,
bool Item_func_between::fix_length_and_dec()
{
- THD *thd= current_thd;
max_length= 1;
- compare_as_dates= 0;
/*
As some compare functions are generated after sql_yacc,
@@ -2140,23 +2083,21 @@ bool Item_func_between::fix_length_and_dec()
*/
if (!args[0] || !args[1] || !args[2])
return TRUE;
- if (agg_cmp_type(&m_compare_type, args, 3, false))
+ if (m_comparator.aggregate_for_comparison(Item_func_between::func_name(),
+ args, 3, false))
+ {
+ DBUG_ASSERT(current_thd->is_error());
return TRUE;
+ }
- if (m_compare_type == STRING_RESULT &&
- agg_arg_charsets_for_comparison(cmp_collation, args, 3))
- return TRUE;
+ return m_comparator.type_handler()->
+ Item_func_between_fix_length_and_dec(this);
+}
- /*
- When comparing as date/time, we need to convert non-temporal values
- (e.g. strings) to MYSQL_TIME.
- For this to work, we need to know what date/time type we compare
- strings as.
- */
- if (m_compare_type == TIME_RESULT)
- compare_as_dates= find_date_time_item(thd, args, 3, 0, false);
- /* See the comment for Item_func::convert_const_compared_to_int_field */
+bool Item_func_between::fix_length_and_dec_numeric(THD *thd)
+{
+ /* See the comment about the similar block in Item_bool_func2 */
if (args[0]->real_item()->type() == FIELD_ITEM &&
!thd->lex->is_ps_or_view_context_analysis())
{
@@ -2167,13 +2108,56 @@ bool Item_func_between::fix_length_and_dec()
const bool cvt_arg1= convert_const_to_int(thd, field_item, &args[1]);
const bool cvt_arg2= convert_const_to_int(thd, field_item, &args[2]);
if (cvt_arg1 && cvt_arg2)
- m_compare_type= INT_RESULT; // Works for all types.
+ {
+ // Works for all types
+ m_comparator.set_handler(&type_handler_longlong);
+ }
}
}
return FALSE;
}
+bool Item_func_between::fix_length_and_dec_temporal(THD *thd)
+{
+ if (!thd->lex->is_ps_or_view_context_analysis())
+ {
+ for (uint i= 0; i < 3; i ++)
+ {
+ if (args[i]->const_item() &&
+ args[i]->type_handler_for_comparison() != m_comparator.type_handler())
+ {
+ Item_cache *cache= m_comparator.type_handler()->Item_get_cache(thd, args[i]);
+ if (!cache || cache->setup(thd, args[i]))
+ return true;
+ thd->change_item_tree(&args[i], cache);
+ }
+ }
+ }
+ return false;
+}
+
+
+longlong Item_func_between::val_int_cmp_temporal()
+{
+ enum_field_types f_type= m_comparator.type_handler()->field_type();
+ longlong value= args[0]->val_temporal_packed(f_type), a, b;
+ if ((null_value= args[0]->null_value))
+ return 0;
+ a= args[1]->val_temporal_packed(f_type);
+ b= args[2]->val_temporal_packed(f_type);
+ if (!args[1]->null_value && !args[2]->null_value)
+ return (longlong) ((value >= a && value <= b) != negated);
+ if (args[1]->null_value && args[2]->null_value)
+ null_value= true;
+ else if (args[1]->null_value)
+ null_value= value <= b; // not null if false range.
+ else
+ null_value= value >= a;
+ return (longlong) (!null_value && negated);
+}
+
+
longlong Item_func_between::val_int_cmp_string()
{
String *value,*a,*b;
@@ -2265,51 +2249,6 @@ longlong Item_func_between::val_int_cmp_real()
}
-longlong Item_func_between::val_int()
-{
- DBUG_ASSERT(fixed == 1);
-
- switch (m_compare_type) {
- case TIME_RESULT:
- {
- longlong value, a, b;
-
- enum_field_types f_type= field_type_for_temporal_comparison(compare_as_dates);
- value= args[0]->val_temporal_packed(f_type);
-
- if ((null_value= args[0]->null_value))
- return 0;
-
- a= args[1]->val_temporal_packed(f_type);
- b= args[2]->val_temporal_packed(f_type);
-
- if (!args[1]->null_value && !args[2]->null_value)
- return (longlong) ((value >= a && value <= b) != negated);
- if (args[1]->null_value && args[2]->null_value)
- null_value=1;
- else if (args[1]->null_value)
- null_value= value <= b; // not null if false range.
- else
- null_value= value >= a;
- break;
- }
- case STRING_RESULT:
- return val_int_cmp_string();
- case INT_RESULT:
- return val_int_cmp_int();
- case DECIMAL_RESULT:
- return val_int_cmp_decimal();
- case REAL_RESULT:
- return val_int_cmp_real();
- case ROW_RESULT:
- DBUG_ASSERT(0);
- null_value= 1;
- return 0;
- }
- return (longlong) (!null_value && negated);
-}
-
-
void Item_func_between::print(String *str, enum_query_type query_type)
{
args[0]->print_parenthesised(str, query_type, precedence());
@@ -2322,16 +2261,6 @@ void Item_func_between::print(String *str, enum_query_type query_type)
}
-uint Item_func_case_abbreviation2::decimal_precision2(Item **args) const
-{
- int arg0_int_part= args[0]->decimal_int_part();
- int arg1_int_part= args[1]->decimal_int_part();
- int max_int_part= MY_MAX(arg0_int_part, arg1_int_part);
- int precision= max_int_part + decimals;
- return MY_MIN(precision, DECIMAL_MAX_PRECISION);
-}
-
-
double
Item_func_ifnull::real_op()
{
@@ -2400,12 +2329,28 @@ Item_func_ifnull::str_op(String *str)
}
-bool Item_func_ifnull::date_op(MYSQL_TIME *ltime, uint fuzzydate)
+bool Item_func_ifnull::date_op(MYSQL_TIME *ltime, ulonglong fuzzydate)
+{
+ DBUG_ASSERT(fixed == 1);
+ for (uint i= 0; i < 2; i++)
+ {
+ Datetime dt(current_thd, args[i], fuzzydate & ~TIME_FUZZY_DATES);
+ if (!(dt.copy_to_mysql_time(ltime, mysql_timestamp_type())))
+ return (null_value= false);
+ }
+ return (null_value= true);
+}
+
+
+bool Item_func_ifnull::time_op(MYSQL_TIME *ltime)
{
DBUG_ASSERT(fixed == 1);
- if (!args[0]->get_date_with_conversion(ltime, fuzzydate & ~TIME_FUZZY_DATES))
- return (null_value= false);
- return (null_value= args[1]->get_date_with_conversion(ltime, fuzzydate & ~TIME_FUZZY_DATES));
+ for (uint i= 0; i < 2; i++)
+ {
+ if (!Time(args[i]).copy_to_mysql_time(ltime))
+ return (null_value= false);
+ }
+ return (null_value= true);
}
@@ -2471,91 +2416,6 @@ void Item_func_if::fix_after_pullout(st_select_lex *new_parent,
}
-void Item_func_if::cache_type_info(Item *source)
-{
- Type_std_attributes::set(source);
- set_handler_by_field_type(source->field_type());
- maybe_null= source->maybe_null;
-}
-
-
-bool
-Item_func_if::fix_length_and_dec()
-{
- // Let IF(cond, expr, NULL) and IF(cond, NULL, expr) inherit type from expr.
- if (args[1]->type() == NULL_ITEM)
- {
- cache_type_info(args[2]);
- maybe_null= true;
- // If both arguments are NULL, make resulting type BINARY(0).
- if (args[2]->type() == NULL_ITEM)
- set_handler_by_field_type(MYSQL_TYPE_STRING);
- return FALSE;
- }
- if (args[2]->type() == NULL_ITEM)
- {
- cache_type_info(args[1]);
- maybe_null= true;
- return FALSE;
- }
- return Item_func_case_abbreviation2::fix_length_and_dec2(args + 1);
-}
-
-
-double
-Item_func_if::real_op()
-{
- DBUG_ASSERT(fixed == 1);
- Item *arg= args[0]->val_bool() ? args[1] : args[2];
- double value= arg->val_real();
- null_value=arg->null_value;
- return value;
-}
-
-longlong
-Item_func_if::int_op()
-{
- DBUG_ASSERT(fixed == 1);
- Item *arg= args[0]->val_bool() ? args[1] : args[2];
- longlong value=arg->val_int();
- null_value=arg->null_value;
- return value;
-}
-
-String *
-Item_func_if::str_op(String *str)
-{
- DBUG_ASSERT(fixed == 1);
- Item *arg= args[0]->val_bool() ? args[1] : args[2];
- String *res=arg->val_str(str);
- if (res)
- res->set_charset(collation.collation);
- if ((null_value=arg->null_value))
- res= NULL;
- return res;
-}
-
-
-my_decimal *
-Item_func_if::decimal_op(my_decimal *decimal_value)
-{
- DBUG_ASSERT(fixed == 1);
- Item *arg= args[0]->val_bool() ? args[1] : args[2];
- my_decimal *value= arg->val_decimal(decimal_value);
- if ((null_value= arg->null_value))
- value= NULL;
- return value;
-}
-
-
-bool Item_func_if::date_op(MYSQL_TIME *ltime, uint fuzzydate)
-{
- DBUG_ASSERT(fixed == 1);
- Item *arg= args[0]->val_bool() ? args[1] : args[2];
- return (null_value= arg->get_date_with_conversion(ltime, fuzzydate));
-}
-
-
void Item_func_nullif::split_sum_func(THD *thd, Ref_ptr_array ref_pointer_array,
List<Item> &fields, uint flags)
{
@@ -2761,7 +2621,7 @@ Item_func_nullif::fix_length_and_dec()
*/
m_cache= args[0]->cmp_type() == STRING_RESULT ?
new (thd->mem_root) Item_cache_str_for_nullif(thd, args[0]) :
- Item_cache::get_cache(thd, args[0]);
+ args[0]->get_cache(thd);
if (!m_cache)
return TRUE;
m_cache->setup(thd, args[0]);
@@ -2770,7 +2630,7 @@ Item_func_nullif::fix_length_and_dec()
thd->change_item_tree(&args[0], m_cache);
thd->change_item_tree(&args[2], m_cache);
}
- set_handler_by_field_type(args[2]->field_type());
+ set_handler(args[2]->type_handler());
collation.set(args[2]->collation);
decimals= args[2]->decimals;
unsigned_flag= args[2]->unsigned_flag;
@@ -2969,59 +2829,56 @@ Item_func_nullif::decimal_op(my_decimal * decimal_value)
bool
-Item_func_nullif::date_op(MYSQL_TIME *ltime, uint fuzzydate)
+Item_func_nullif::date_op(MYSQL_TIME *ltime, ulonglong fuzzydate)
{
DBUG_ASSERT(fixed == 1);
if (!compare())
return (null_value= true);
- return (null_value= args[2]->get_date(ltime, fuzzydate));
+ Datetime dt(current_thd, args[2], fuzzydate);
+ return (null_value= dt.copy_to_mysql_time(ltime, mysql_timestamp_type()));
}
bool
-Item_func_nullif::is_null()
+Item_func_nullif::time_op(MYSQL_TIME *ltime)
{
- return (null_value= (!compare() ? 1 : args[2]->null_value));
+ DBUG_ASSERT(fixed == 1);
+ if (!compare())
+ return (null_value= true);
+ return (null_value= Time(args[2]).copy_to_mysql_time(ltime));
+
}
-Item_func_case::Item_func_case(THD *thd, List<Item> &list,
- Item *first_expr_arg, Item *else_expr_arg):
- Item_func_hybrid_field_type(thd), first_expr_num(-1), else_expr_num(-1),
- left_cmp_type(INT_RESULT), case_item(0), m_found_types(0)
+bool
+Item_func_nullif::is_null()
{
- DBUG_ASSERT(list.elements % 2 == 0);
- nwhens= list.elements / 2;
- if (first_expr_arg)
- {
- first_expr_num= 0;
- list.push_front(first_expr_arg, thd->mem_root);
- }
- if (else_expr_arg)
- {
- else_expr_num= list.elements;
- list.push_back(else_expr_arg, thd->mem_root);
- }
- set_arguments(thd, list);
+ return (null_value= (!compare() ? 1 : args[2]->is_null()));
+}
+void Item_func_case::reorder_args(uint start)
+{
/*
Reorder args, to have at first the optional CASE expression, then all WHEN
- expressions, then all THEN expressions. And the optional ELSE expression
+ expressions, then all THEN expressions. And the optional ELSE expression
at the end.
+
+ We reorder an even number of arguments, starting from start.
*/
- const size_t size= sizeof(Item*)*nwhens*2;
+ uint count = (arg_count - start) / 2;
+ const size_t size= sizeof(Item*) * count * 2;
Item **arg_buffer= (Item **)my_safe_alloca(size);
- memcpy(arg_buffer, args + first_expr_num + 1, size);
- for (uint i= 0; i < nwhens ; i++)
+ memcpy(arg_buffer, &args[start], size);
+ for (uint i= 0; i < count; i++)
{
- args[first_expr_num + 1 + i]= arg_buffer[i*2];
- args[first_expr_num + 1 + i + nwhens] = arg_buffer[i*2 + 1];
+ args[start + i]= arg_buffer[i*2];
+ args[start + i + count]= arg_buffer[i*2 + 1];
}
my_safe_afree(arg_buffer, size);
-
- bzero(&cmp_items, sizeof(cmp_items));
}
+
+
/**
Find and return matching items for CASE or ELSE item if all compares
are failed or NULL if ELSE item isn't defined.
@@ -3043,42 +2900,37 @@ Item_func_case::Item_func_case(THD *thd, List<Item> &list,
failed
*/
-Item *Item_func_case::find_item(String *str)
+Item *Item_func_case_searched::find_item()
{
- uint value_added_map= 0;
-
- if (first_expr_num == -1)
- {
- for (uint i=0 ; i < nwhens ; i++)
- {
- // No expression between CASE and the first WHEN
- if (args[i]->val_bool())
- return args[i+nwhens];
- }
- }
- else
+ uint count= when_count();
+ for (uint i= 0 ; i < count ; i++)
{
- /* Compare every WHEN argument with it and return the first match */
- for (uint i=1 ; i <= nwhens; i++)
- {
- if (args[i]->real_item()->type() == NULL_ITEM)
- continue;
- cmp_type= item_cmp_type(left_cmp_type, args[i]);
- DBUG_ASSERT(cmp_type != ROW_RESULT);
- DBUG_ASSERT(cmp_items[(uint)cmp_type]);
- if (!(value_added_map & (1U << (uint)cmp_type)))
- {
- cmp_items[(uint)cmp_type]->store_value(args[0]);
- if ((null_value= args[0]->null_value))
- return else_expr_num != -1 ? args[else_expr_num] : 0;
- value_added_map|= 1U << (uint)cmp_type;
- }
- if (cmp_items[(uint)cmp_type]->cmp(args[i]) == FALSE)
- return args[i + nwhens];
- }
+ if (args[i]->val_bool())
+ return args[i + count];
}
- // No, WHEN clauses all missed, return ELSE expression
- return else_expr_num != -1 ? args[else_expr_num] : 0;
+ Item **pos= Item_func_case_searched::else_expr_addr();
+ return pos ? pos[0] : 0;
+}
+
+
+Item *Item_func_case_simple::find_item()
+{
+ /* Compare every WHEN argument with it and return the first match */
+ uint idx;
+ if (!Predicant_to_list_comparator::cmp(this, &idx, NULL))
+ return args[idx + when_count()];
+ Item **pos= Item_func_case_simple::else_expr_addr();
+ return pos ? pos[0] : 0;
+}
+
+
+Item *Item_func_decode_oracle::find_item()
+{
+ uint idx;
+ if (!Predicant_to_list_comparator::cmp_nulls_equal(this, &idx))
+ return args[idx + when_count()];
+ Item **pos= Item_func_decode_oracle::else_expr_addr();
+ return pos ? pos[0] : 0;
}
@@ -3086,7 +2938,7 @@ String *Item_func_case::str_op(String *str)
{
DBUG_ASSERT(fixed == 1);
String *res;
- Item *item=find_item(str);
+ Item *item= find_item();
if (!item)
{
@@ -3103,9 +2955,7 @@ String *Item_func_case::str_op(String *str)
longlong Item_func_case::int_op()
{
DBUG_ASSERT(fixed == 1);
- char buff[MAX_FIELD_WIDTH];
- String dummy_str(buff,sizeof(buff),default_charset());
- Item *item=find_item(&dummy_str);
+ Item *item= find_item();
longlong res;
if (!item)
@@ -3121,9 +2971,7 @@ longlong Item_func_case::int_op()
double Item_func_case::real_op()
{
DBUG_ASSERT(fixed == 1);
- char buff[MAX_FIELD_WIDTH];
- String dummy_str(buff,sizeof(buff),default_charset());
- Item *item=find_item(&dummy_str);
+ Item *item= find_item();
double res;
if (!item)
@@ -3140,9 +2988,7 @@ double Item_func_case::real_op()
my_decimal *Item_func_case::decimal_op(my_decimal *decimal_value)
{
DBUG_ASSERT(fixed == 1);
- char buff[MAX_FIELD_WIDTH];
- String dummy_str(buff, sizeof(buff), default_charset());
- Item *item= find_item(&dummy_str);
+ Item *item= find_item();
my_decimal *res;
if (!item)
@@ -3157,33 +3003,34 @@ my_decimal *Item_func_case::decimal_op(my_decimal *decimal_value)
}
-bool Item_func_case::date_op(MYSQL_TIME *ltime, uint fuzzydate)
+bool Item_func_case::date_op(MYSQL_TIME *ltime, ulonglong fuzzydate)
{
DBUG_ASSERT(fixed == 1);
- char buff[MAX_FIELD_WIDTH];
- String dummy_str(buff, sizeof(buff), default_charset());
- Item *item= find_item(&dummy_str);
+ Item *item= find_item();
if (!item)
return (null_value= true);
- return (null_value= item->get_date_with_conversion(ltime, fuzzydate));
+ Datetime dt(current_thd, item, fuzzydate);
+ return (null_value= dt.copy_to_mysql_time(ltime, mysql_timestamp_type()));
}
-bool Item_func_case::fix_fields(THD *thd, Item **ref)
+bool Item_func_case::time_op(MYSQL_TIME *ltime)
{
- /*
- buff should match stack usage from
- Item_func_case::val_int() -> Item_func_case::find_item()
- */
- uchar buff[MAX_FIELD_WIDTH*2+sizeof(String)*2+sizeof(String*)*2+sizeof(double)*2+sizeof(longlong)*2];
+ DBUG_ASSERT(fixed == 1);
+ Item *item= find_item();
+ if (!item)
+ return (null_value= true);
+ return (null_value= Time(item).copy_to_mysql_time(ltime));
+}
+
+bool Item_func_case::fix_fields(THD *thd, Item **ref)
+{
bool res= Item_func::fix_fields(thd, ref);
- /*
- Call check_stack_overrun after fix_fields to be sure that stack variable
- is not optimized away
- */
- if (check_stack_overrun(thd, STACK_MIN_SIZE, buff))
- return TRUE; // Fatal error flag is set!
+
+ Item **pos= else_expr_addr();
+ if (!pos || pos[0]->maybe_null)
+ maybe_null= 1;
return res;
}
@@ -3193,108 +3040,151 @@ bool Item_func_case::fix_fields(THD *thd, Item **ref)
THD::change_item_tree() if needed.
*/
-static void change_item_tree_if_needed(THD *thd, Item **place, Item *new_value)
+static void propagate_and_change_item_tree(THD *thd, Item **place,
+ COND_EQUAL *cond,
+ const Item::Context &ctx)
{
+ Item *new_value= (*place)->propagate_equal_fields(thd, ctx, cond);
if (new_value && *place != new_value)
thd->change_item_tree(place, new_value);
}
-bool Item_func_case::fix_length_and_dec()
+bool Item_func_case_simple::prepare_predicant_and_values(THD *thd,
+ uint *found_types,
+ bool nulls_equal)
{
- m_found_types= 0;
- if (else_expr_num == -1 || args[else_expr_num]->maybe_null)
- maybe_null= 1;
-
- /*
- Aggregate all THEN and ELSE expression types
- and collations when string result
- */
- Item **rets= args + first_expr_num + 1 + nwhens;
- uint nrets= nwhens + (else_expr_num != -1);
- set_handler_by_field_type(agg_field_type(rets, nrets, true));
-
- if (Item_func_case::result_type() == STRING_RESULT)
+ bool have_null= false;
+ uint type_cnt;
+ Type_handler_hybrid_field_type tmp;
+ uint ncases= when_count();
+ add_predicant(this, 0);
+ for (uint i= 0 ; i < ncases; i++)
{
- if (count_string_result_length(Item_func_case::field_type(), rets, nrets))
- return TRUE;
+ if (nulls_equal ?
+ add_value("case..when", this, i + 1) :
+ add_value_skip_null("case..when", this, i + 1, &have_null))
+ return true;
}
- else
- fix_attributes(rets, nrets);
+ all_values_added(&tmp, &type_cnt, &m_found_types);
+#ifndef DBUG_OFF
+ Predicant_to_list_comparator::debug_print(thd);
+#endif
+ return false;
+}
+
+
+bool Item_func_case_searched::fix_length_and_dec()
+{
+ THD *thd= current_thd;
+ return aggregate_then_and_else_arguments(thd, when_count());
+}
+
+
+bool Item_func_case_simple::fix_length_and_dec()
+{
+ THD *thd= current_thd;
+ return (aggregate_then_and_else_arguments(thd, when_count() + 1) ||
+ aggregate_switch_and_when_arguments(thd, false));
+}
+
+
+bool Item_func_decode_oracle::fix_length_and_dec()
+{
+ THD *thd= current_thd;
+ return (aggregate_then_and_else_arguments(thd, when_count() + 1) ||
+ aggregate_switch_and_when_arguments(thd, true));
+}
+
+
+/*
+ Aggregate all THEN and ELSE expression types
+ and collations when string result
- /*
- Aggregate first expression and all WHEN expression types
- and collations when string comparison
- */
- if (first_expr_num != -1)
- {
- left_cmp_type= args[0]->cmp_type();
+ @param THD - current thd
+ @param start - an element in args to start aggregating from
+*/
+bool Item_func_case::aggregate_then_and_else_arguments(THD *thd, uint start)
+{
+ if (aggregate_for_result(func_name(), args + start, arg_count - start, true))
+ return true;
- if (!(m_found_types= collect_cmp_types(args, nwhens + 1)))
- return TRUE;
+ if (fix_attributes(args + start, arg_count - start))
+ return true;
- Item *date_arg= 0;
- if (m_found_types & (1U << TIME_RESULT))
- date_arg= find_date_time_item(current_thd, args, nwhens + 1, 0, true);
+ return false;
+}
- if (m_found_types & (1U << STRING_RESULT))
- {
- /*
- If we'll do string comparison, we also need to aggregate
- character set and collation for first/WHEN items and
- install converters for some of them to cmp_collation when necessary.
- This is done because cmp_item comparators cannot compare
- strings in two different character sets.
- Some examples when we install converters:
- 1. Converter installed for the first expression:
+/*
+ Aggregate the predicant expression and all WHEN expression types
+ and collations when string comparison
+*/
+bool Item_func_case_simple::aggregate_switch_and_when_arguments(THD *thd,
+ bool nulls_eq)
+{
+ uint ncases= when_count();
+ m_found_types= 0;
+ if (prepare_predicant_and_values(thd, &m_found_types, nulls_eq))
+ {
+ /*
+ If Predicant_to_list_comparator() fails to prepare components,
+ it must put an error into the diagnostics area. This is needed
+ to make fix_fields() catches such errors.
+ */
+ DBUG_ASSERT(thd->is_error());
+ return true;
+ }
- CASE latin1_item WHEN utf16_item THEN ... END
+ if (!(m_found_types= collect_cmp_types(args, ncases + 1)))
+ return true;
- is replaced to:
+ if (m_found_types & (1U << STRING_RESULT))
+ {
+ /*
+ If we'll do string comparison, we also need to aggregate
+ character set and collation for first/WHEN items and
+ install converters for some of them to cmp_collation when necessary.
+ This is done because cmp_item compatators cannot compare
+ strings in two different character sets.
+ Some examples when we install converters:
- CASE CONVERT(latin1_item USING utf16) WHEN utf16_item THEN ... END
+ 1. Converter installed for the first expression:
- 2. Converter installed for the left WHEN item:
+ CASE latin1_item WHEN utf16_item THEN ... END
- CASE utf16_item WHEN latin1_item THEN ... END
+ is replaced to:
- is replaced to:
+ CASE CONVERT(latin1_item USING utf16) WHEN utf16_item THEN ... END
- CASE utf16_item WHEN CONVERT(latin1_item USING utf16) THEN ... END
- */
- if (agg_arg_charsets_for_comparison(cmp_collation, args, nwhens + 1))
- return TRUE;
- }
+ 2. Converter installed for the left WHEN item:
- for (uint i= 0; i <= (uint)TIME_RESULT; i++)
- {
- if (m_found_types & (1U << i) && !cmp_items[i])
- {
- DBUG_ASSERT((Item_result)i != ROW_RESULT);
- if (!(cmp_items[i]=
- cmp_item::get_comparator((Item_result)i, date_arg,
- cmp_collation.collation)))
- return TRUE;
- }
- }
+ CASE utf16_item WHEN latin1_item THEN ... END
+
+ is replaced to:
+
+ CASE utf16_item WHEN CONVERT(latin1_item USING utf16) THEN ... END
+ */
+ if (agg_arg_charsets_for_comparison(cmp_collation, args, ncases + 1))
+ return true;
}
- return FALSE;
+
+ if (make_unique_cmp_items(thd, cmp_collation.collation))
+ return true;
+
+ return false;
}
-Item* Item_func_case::propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond)
+Item* Item_func_case_simple::propagate_equal_fields(THD *thd,
+ const Context &ctx,
+ COND_EQUAL *cond)
{
- if (first_expr_num == -1)
- {
- // None of the arguments are in a comparison context
- Item_args::propagate_equal_fields(thd, Context_identity(), cond);
- return this;
- }
+ const Type_handler *first_expr_cmp_handler;
+ first_expr_cmp_handler= args[0]->type_handler_for_comparison();
/*
- First, replace CASE expression.
- We cannot replace the CASE (the switch) argument if
+ Cannot replace the CASE (the switch) argument if
there are multiple comparison types were found, or found a single
comparison type that is not equal to args[0]->cmp_type().
@@ -3320,87 +3210,102 @@ Item* Item_func_case::propagate_equal_fields(THD *thd, const Context &ctx, COND_
WHEN 'str2' THEN TRUE
ELSE FALSE END;
*/
- if (m_found_types == (1UL << left_cmp_type))
- change_item_tree_if_needed(thd, args,
- args[0]->propagate_equal_fields(thd, Context(ANY_SUBST, left_cmp_type,
- cmp_collation.collation),
- cond));
- uint i= 1;
- for (; i <= nwhens ; i++) // WHEN expressions
+ if (m_found_types == (1UL << first_expr_cmp_handler->cmp_type()))
+ propagate_and_change_item_tree(thd, &args[0], cond,
+ Context(ANY_SUBST, first_expr_cmp_handler, cmp_collation.collation));
+
+ /*
+ These arguments are in comparison.
+ Allow invariants of the same value during propagation.
+ Note, as we pass ANY_SUBST, none of the WHEN arguments will be
+ replaced to zero-filled constants (only IDENTITY_SUBST allows this).
+ Such a change for WHEN arguments would require rebuilding cmp_items.
+ */
+ uint i, count= when_count();
+ for (i= 1; i <= count; i++)
{
- /*
- These arguments are in comparison.
- Allow invariants of the same value during propagation.
- Note, as we pass ANY_SUBST, none of the WHEN arguments will be
- replaced to zero-filled constants (only IDENTITY_SUBST allows this).
- Such a change for WHEN arguments would require rebuilding cmp_items.
- */
- Item_result tmp_cmp_type= item_cmp_type(args[first_expr_num], args[i]);
- change_item_tree_if_needed(thd, args + i,
- args[i]->propagate_equal_fields(thd, Context(ANY_SUBST, tmp_cmp_type,
- cmp_collation.collation),
- cond));
+ Type_handler_hybrid_field_type tmp(first_expr_cmp_handler);
+ if (!tmp.aggregate_for_comparison(args[i]->type_handler_for_comparison()))
+ propagate_and_change_item_tree(thd, &args[i], cond,
+ Context(ANY_SUBST, tmp.type_handler(), cmp_collation.collation));
}
- for (; i < arg_count ; i++) // THEN expressions and optional ELSE expression
+
+ // THEN and ELSE arguments (they are not in comparison)
+ for (; i < arg_count; i++)
+ propagate_and_change_item_tree(thd, &args[i], cond, Context_identity());
+
+ return this;
+}
+
+
+void Item_func_case::print_when_then_arguments(String *str,
+ enum_query_type query_type,
+ Item **items, uint count)
+{
+ for (uint i=0 ; i < count ; i++)
{
- change_item_tree_if_needed(thd, args + i,
- args[i]->propagate_equal_fields(thd, Context_identity(), cond));
+ str->append(STRING_WITH_LEN("when "));
+ items[i]->print_parenthesised(str, query_type, precedence());
+ str->append(STRING_WITH_LEN(" then "));
+ items[i + count]->print_parenthesised(str, query_type, precedence());
+ str->append(' ');
}
- return this;
}
-uint Item_func_case::decimal_precision() const
+void Item_func_case::print_else_argument(String *str,
+ enum_query_type query_type,
+ Item *item)
{
- int max_int_part=0;
- for (uint i=first_expr_num + 1 + nwhens ; i < arg_count; i++)
- set_if_bigger(max_int_part, args[i]->decimal_int_part());
- return MY_MIN(max_int_part + decimals, DECIMAL_MAX_PRECISION);
+ str->append(STRING_WITH_LEN("else "));
+ item->print_parenthesised(str, query_type, precedence());
+ str->append(' ');
}
-/**
- @todo
- Fix this so that it prints the whole CASE expression
-*/
+void Item_func_case_searched::print(String *str, enum_query_type query_type)
+{
+ Item **pos;
+ str->append(STRING_WITH_LEN("case "));
+ print_when_then_arguments(str, query_type, &args[0], when_count());
+ if ((pos= Item_func_case_searched::else_expr_addr()))
+ print_else_argument(str, query_type, pos[0]);
+ str->append(STRING_WITH_LEN("end"));
+}
+
-void Item_func_case::print(String *str, enum_query_type query_type)
+void Item_func_case_simple::print(String *str, enum_query_type query_type)
{
+ Item **pos;
str->append(STRING_WITH_LEN("case "));
- if (first_expr_num != -1)
- {
- args[0]->print_parenthesised(str, query_type, precedence());
- str->append(' ');
- }
- for (uint i= first_expr_num + 1 ; i < nwhens + first_expr_num + 1; i++)
- {
- str->append(STRING_WITH_LEN("when "));
- args[i]->print_parenthesised(str, query_type, precedence());
- str->append(STRING_WITH_LEN(" then "));
- args[i+nwhens]->print_parenthesised(str, query_type, precedence());
- str->append(' ');
- }
- if (else_expr_num != -1)
- {
- str->append(STRING_WITH_LEN("else "));
- args[else_expr_num]->print_parenthesised(str, query_type, precedence());
- str->append(' ');
- }
+ args[0]->print_parenthesised(str, query_type, precedence());
+ str->append(' ');
+ print_when_then_arguments(str, query_type, &args[1], when_count());
+ if ((pos= Item_func_case_simple::else_expr_addr()))
+ print_else_argument(str, query_type, pos[0]);
str->append(STRING_WITH_LEN("end"));
}
-void Item_func_case::cleanup()
+void Item_func_decode_oracle::print(String *str, enum_query_type query_type)
{
- uint i;
- DBUG_ENTER("Item_func_case::cleanup");
- Item_func::cleanup();
- for (i= 0; i <= (uint)TIME_RESULT; i++)
+ str->append(func_name());
+ str->append('(');
+ args[0]->print(str, query_type);
+ for (uint i= 1, count= when_count() ; i <= count; i++)
+ {
+ str->append(',');
+ args[i]->print(str, query_type);
+ str->append(',');
+ args[i+count]->print(str, query_type);
+ }
+ Item **else_expr= Item_func_case_simple::else_expr_addr();
+ if (else_expr)
{
- delete cmp_items[i];
- cmp_items[i]= 0;
+ str->append(',');
+ (*else_expr)->print(str, query_type);
}
- DBUG_VOID_RETURN;
+ str->append(')');
}
@@ -3451,12 +3356,25 @@ double Item_func_coalesce::real_op()
}
-bool Item_func_coalesce::date_op(MYSQL_TIME *ltime,uint fuzzydate)
+bool Item_func_coalesce::date_op(MYSQL_TIME *ltime, ulonglong fuzzydate)
+{
+ DBUG_ASSERT(fixed == 1);
+ for (uint i= 0; i < arg_count; i++)
+ {
+ Datetime dt(current_thd, args[i], fuzzydate & ~TIME_FUZZY_DATES);
+ if (!dt.copy_to_mysql_time(ltime, mysql_timestamp_type()))
+ return (null_value= false);
+ }
+ return (null_value= true);
+}
+
+
+bool Item_func_coalesce::time_op(MYSQL_TIME *ltime)
{
DBUG_ASSERT(fixed == 1);
for (uint i= 0; i < arg_count; i++)
{
- if (!args[i]->get_date_with_conversion(ltime, fuzzydate & ~TIME_FUZZY_DATES))
+ if (!Time(args[i]).copy_to_mysql_time(ltime))
return (null_value= false);
}
return (null_value= true);
@@ -3478,33 +3396,6 @@ my_decimal *Item_func_coalesce::decimal_op(my_decimal *decimal_value)
}
-void Item_hybrid_func::fix_attributes(Item **items, uint nitems)
-{
- switch (Item_hybrid_func::result_type()) {
- case STRING_RESULT:
- if (count_string_result_length(Item_hybrid_func::field_type(),
- items, nitems))
- return;
- break;
- case DECIMAL_RESULT:
- collation.set_numeric();
- count_decimal_length(items, nitems);
- break;
- case REAL_RESULT:
- collation.set_numeric();
- count_real_length(items, nitems);
- break;
- case INT_RESULT:
- collation.set_numeric();
- count_only_length(items, nitems);
- decimals= 0;
- break;
- case ROW_RESULT:
- case TIME_RESULT:
- DBUG_ASSERT(0);
- }
-}
-
/****************************************************************************
Classes and function for the IN operator
****************************************************************************/
@@ -3771,13 +3662,20 @@ void in_datetime::set(uint pos,Item *item)
{
struct packed_longlong *buff= &((packed_longlong*) base)[pos];
- buff->val= item->val_temporal_packed(warn_item);
+ buff->val= item->val_datetime_packed();
+ buff->unsigned_flag= 1L;
+}
+
+void in_time::set(uint pos,Item *item)
+{
+ struct packed_longlong *buff= &((packed_longlong*) base)[pos];
+
+ buff->val= item->val_time_packed();
buff->unsigned_flag= 1L;
}
-uchar *in_datetime::get_value(Item *item)
+uchar *in_temporal::get_value_internal(Item *item, enum_field_types f_type)
{
- enum_field_types f_type= item->field_type_for_temporal_comparison(warn_item);
tmp.val= item->val_temporal_packed(f_type);
if (item->null_value)
return 0;
@@ -3785,7 +3683,7 @@ uchar *in_datetime::get_value(Item *item)
return (uchar*) &tmp;
}
-Item *in_datetime::create_item(THD *thd)
+Item *in_temporal::create_item(THD *thd)
{
return new (thd->mem_root) Item_datetime(thd);
}
@@ -3846,25 +3744,95 @@ Item *in_decimal::create_item(THD *thd)
}
-cmp_item* cmp_item::get_comparator(Item_result type, Item *warn_item,
- CHARSET_INFO *cs)
+bool Predicant_to_list_comparator::alloc_comparators(THD *thd, uint nargs)
{
- switch (type) {
- case STRING_RESULT:
- return new cmp_item_sort_string(cs);
- case INT_RESULT:
- return new cmp_item_int;
- case REAL_RESULT:
- return new cmp_item_real;
- case ROW_RESULT:
- return new cmp_item_row;
- case DECIMAL_RESULT:
- return new cmp_item_decimal;
- case TIME_RESULT:
- DBUG_ASSERT(warn_item);
- return new cmp_item_datetime(warn_item);
+ size_t nbytes= sizeof(Predicant_to_value_comparator) * nargs;
+ if (!(m_comparators= (Predicant_to_value_comparator *) thd->alloc(nbytes)))
+ return true;
+ memset(m_comparators, 0, nbytes);
+ return false;
+}
+
+
+bool Predicant_to_list_comparator::add_value(const char *funcname,
+ Item_args *args,
+ uint value_index)
+{
+ DBUG_ASSERT(m_predicant_index < args->argument_count());
+ DBUG_ASSERT(value_index < args->argument_count());
+ Type_handler_hybrid_field_type tmp;
+ Item *tmpargs[2];
+ tmpargs[0]= args->arguments()[m_predicant_index];
+ tmpargs[1]= args->arguments()[value_index];
+ if (tmp.aggregate_for_comparison(funcname, tmpargs, 2, true))
+ {
+ DBUG_ASSERT(current_thd->is_error());
+ return true;
+ }
+ m_comparators[m_comparator_count].m_handler= tmp.type_handler();
+ m_comparators[m_comparator_count].m_arg_index= value_index;
+ m_comparator_count++;
+ return false;
+}
+
+
+bool Predicant_to_list_comparator::add_value_skip_null(const char *funcname,
+ Item_args *args,
+ uint value_index,
+ bool *nulls_found)
+{
+ /*
+ Skip explicit NULL constant items.
+ Using real_item() to correctly detect references to explicit NULLs
+ in HAVING clause, e.g. in this example "b" is skipped:
+ SELECT a,NULL AS b FROM t1 GROUP BY a HAVING 'A' IN (b,'A');
+ */
+ if (args->arguments()[value_index]->real_item()->type() == Item::NULL_ITEM)
+ {
+ *nulls_found= true;
+ return false;
+ }
+ return add_value(funcname, args, value_index);
+}
+
+
+void Predicant_to_list_comparator::
+ detect_unique_handlers(Type_handler_hybrid_field_type *compatible,
+ uint *unique_count,
+ uint *found_types)
+{
+ *unique_count= 0;
+ *found_types= 0;
+ for (uint i= 0; i < m_comparator_count; i++)
+ {
+ uint idx;
+ if (find_handler(&idx, m_comparators[i].m_handler, i))
+ {
+ m_comparators[i].m_handler_index= i; // New unique handler
+ (*unique_count)++;
+ (*found_types)|= 1U << m_comparators[i].m_handler->cmp_type();
+ compatible->set_handler(m_comparators[i].m_handler);
+ }
+ else
+ {
+ m_comparators[i].m_handler_index= idx; // Non-unique handler
+ }
+ }
+}
+
+
+bool Predicant_to_list_comparator::make_unique_cmp_items(THD *thd,
+ CHARSET_INFO *cs)
+{
+ for (uint i= 0; i < m_comparator_count; i++)
+ {
+ if (m_comparators[i].m_handler && // Skip implicit NULLs
+ m_comparators[i].m_handler_index == i && // Skip non-unuque
+ !(m_comparators[i].m_cmp_item=
+ m_comparators[i].m_handler->make_cmp_item(thd, cs)))
+ return true;
}
- return 0; // to satisfy compiler :)
+ return false;
}
@@ -3905,19 +3873,23 @@ cmp_item_row::~cmp_item_row()
}
-void cmp_item_row::alloc_comparators()
+bool cmp_item_row::alloc_comparators(THD *thd, uint cols)
{
- if (!comparators)
- comparators= (cmp_item **) current_thd->calloc(sizeof(cmp_item *)*n);
+ if (comparators)
+ {
+ DBUG_ASSERT(cols == n);
+ return false;
+ }
+ return
+ !(comparators= (cmp_item **) thd->calloc(sizeof(cmp_item *) * (n= cols)));
}
void cmp_item_row::store_value(Item *item)
{
DBUG_ENTER("cmp_item_row::store_value");
- n= item->cols();
- alloc_comparators();
- if (comparators)
+ THD *thd= current_thd;
+ if (!alloc_comparators(thd, item->cols()))
{
item->bring_value();
item->null_value= 0;
@@ -3925,10 +3897,25 @@ void cmp_item_row::store_value(Item *item)
{
if (!comparators[i])
{
- DBUG_ASSERT(item->element_index(i)->cmp_type() != TIME_RESULT);
+ /**
+ Comparators for the row elements that have temporal data types
+ are installed at initialization time by prepare_comparators().
+ Here we install comparators for the other data types.
+ There is a bug in the below code. See MDEV-11511.
+ When performing:
+ (predicant0,predicant1) IN ((value00,value01),(value10,value11))
+ It uses only the data type and the collation of the predicant
+ elements only. It should be fixed to aggregate the data type and
+ the collation for all elements at the N-th positions of the
+ predicate and all values:
+ - predicate0, value00, value01
+ - predicate1, value10, value11
+ */
+ Item *elem= item->element_index(i);
+ const Type_handler *handler= elem->type_handler();
+ DBUG_ASSERT(elem->cmp_type() != TIME_RESULT);
if (!(comparators[i]=
- cmp_item::get_comparator(item->element_index(i)->result_type(), 0,
- item->element_index(i)->collation.collation)))
+ handler->make_cmp_item(thd, elem->collation.collation)))
break; // new failed
}
comparators[i]->store_value(item->element_index(i));
@@ -4016,6 +4003,14 @@ void cmp_item_decimal::store_value(Item *item)
}
+int cmp_item_decimal::cmp_not_null(const Value *val)
+{
+ DBUG_ASSERT(!val->is_null());
+ DBUG_ASSERT(val->is_decimal());
+ return my_decimal_cmp(&value, &val->m_decimal);
+}
+
+
int cmp_item_decimal::cmp(Item *arg)
{
my_decimal tmp_buf, *tmp= arg->val_decimal(&tmp_buf);
@@ -4037,31 +4032,60 @@ cmp_item* cmp_item_decimal::make_same()
}
-void cmp_item_datetime::store_value(Item *item)
+void cmp_item_temporal::store_value_internal(Item *item,
+ enum_field_types f_type)
{
- enum_field_types f_type= item->field_type_for_temporal_comparison(warn_item);
value= item->val_temporal_packed(f_type);
m_null_value= item->null_value;
}
+int cmp_item_datetime::cmp_not_null(const Value *val)
+{
+ DBUG_ASSERT(!val->is_null());
+ DBUG_ASSERT(val->is_temporal());
+ return value != pack_time(&val->value.m_time);
+}
+
+
int cmp_item_datetime::cmp(Item *arg)
{
- const bool rc= value != arg->val_temporal_packed(warn_item);
+ const bool rc= value != arg->val_datetime_packed();
+ return (m_null_value || arg->null_value) ? UNKNOWN : rc;
+}
+
+
+int cmp_item_time::cmp_not_null(const Value *val)
+{
+ DBUG_ASSERT(!val->is_null());
+ DBUG_ASSERT(val->is_temporal());
+ return value != pack_time(&val->value.m_time);
+}
+
+
+int cmp_item_time::cmp(Item *arg)
+{
+ const bool rc= value != arg->val_time_packed();
return (m_null_value || arg->null_value) ? UNKNOWN : rc;
}
-int cmp_item_datetime::compare(cmp_item *ci)
+int cmp_item_temporal::compare(cmp_item *ci)
{
- cmp_item_datetime *l_cmp= (cmp_item_datetime *)ci;
+ cmp_item_temporal *l_cmp= (cmp_item_temporal *)ci;
return (value < l_cmp->value) ? -1 : ((value == l_cmp->value) ? 0 : 1);
}
cmp_item *cmp_item_datetime::make_same()
{
- return new cmp_item_datetime(warn_item);
+ return new cmp_item_datetime();
+}
+
+
+cmp_item *cmp_item_time::make_same()
+{
+ return new cmp_item_time();
}
@@ -4153,52 +4177,74 @@ void Item_func_in::fix_after_pullout(st_select_lex *new_parent, Item **ref,
eval_not_null_tables(NULL);
}
-static int srtcmp_in(CHARSET_INFO *cs, const String *x,const String *y)
+
+bool Item_func_in::prepare_predicant_and_values(THD *thd, uint *found_types)
{
- return cs->coll->strnncollsp(cs,
- (uchar *) x->ptr(),x->length(),
- (uchar *) y->ptr(),y->length());
+ uint type_cnt;
+ have_null= false;
+
+ add_predicant(this, 0);
+ for (uint i= 1 ; i < arg_count; i++)
+ {
+ if (add_value_skip_null(Item_func_in::func_name(), this, i, &have_null))
+ return true;
+ }
+ all_values_added(&m_comparator, &type_cnt, found_types);
+ arg_types_compatible= type_cnt < 2;
+
+#ifndef DBUG_OFF
+ Predicant_to_list_comparator::debug_print(thd);
+#endif
+ return false;
}
-/*
- Create 'array' for this IN predicate with the respect to its result type
- and put values from <in value list> in 'array'.
-*/
-bool Item_func_in::create_array(THD *thd)
+bool Item_func_in::fix_length_and_dec()
{
- Item *date_arg= 0;
+ THD *thd= current_thd;
+ uint found_types;
+ m_comparator.set_handler(type_handler_varchar.type_handler_for_comparison());
+ max_length= 1;
- switch (m_compare_type) {
- case STRING_RESULT:
- array=new (thd->mem_root) in_string(thd, arg_count - 1,
- (qsort2_cmp) srtcmp_in,
- cmp_collation.collation);
- break;
- case INT_RESULT:
- array= new (thd->mem_root) in_longlong(thd, arg_count - 1);
- break;
- case REAL_RESULT:
- array= new (thd->mem_root) in_double(thd, arg_count - 1);
- break;
- case ROW_RESULT:
- /*
- The row comparator was created at the beginning but only DATETIME
- items comparators were initialized. Call store_value() to setup
- others.
- */
- ((in_row*)array)->tmp.store_value(args[0]);
- break;
- case DECIMAL_RESULT:
- array= new (thd->mem_root) in_decimal(thd, arg_count - 1);
- break;
- case TIME_RESULT:
- date_arg= find_date_time_item(thd, args, arg_count, 0, true);
- array= new (thd->mem_root) in_datetime(thd, date_arg, arg_count - 1);
- break;
+ if (prepare_predicant_and_values(thd, &found_types))
+ {
+ DBUG_ASSERT(thd->is_error()); // Must set error
+ return TRUE;
}
- if (!array || thd->is_fatal_error) // OOM
- return true;
+
+ if (arg_types_compatible) // Bisection condition #1
+ {
+ if (m_comparator.type_handler()->
+ Item_func_in_fix_comparator_compatible_types(thd, this))
+ return TRUE;
+ }
+ else
+ {
+ DBUG_ASSERT(m_comparator.cmp_type() != ROW_RESULT);
+ if ( fix_for_scalar_comparison_using_cmp_items(thd, found_types))
+ return TRUE;
+ }
+
+ DBUG_EXECUTE_IF("Item_func_in",
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_UNKNOWN_ERROR, "DBUG: types_compatible=%s bisect=%s",
+ arg_types_compatible ? "yes" : "no",
+ array != NULL ? "yes" : "no"););
+ return FALSE;
+}
+
+
+/**
+ Populate Item_func_in::array with constant not-NULL arguments and sort them.
+
+ Sets "have_null" to true if some of the values appeared to be NULL.
+ Note, explicit NULLs were found during prepare_predicant_and_values().
+ So "have_null" can already be true before the fix_in_vector() call.
+ Here we additionally catch implicit NULLs.
+*/
+void Item_func_in::fix_in_vector()
+{
+ DBUG_ASSERT(array);
uint j=0;
for (uint i=1 ; i < arg_count ; i++)
{
@@ -4208,7 +4254,7 @@ bool Item_func_in::create_array(THD *thd)
else
{
/*
- We don't put NULL values in array to avoid erronous matches in
+ We don't put NULL values in array, to avoid erronous matches in
bisection.
*/
have_null= 1;
@@ -4216,134 +4262,33 @@ bool Item_func_in::create_array(THD *thd)
}
if ((array->used_count= j))
array->sort();
- return false;
}
-bool Item_func_in::fix_length_and_dec()
-{
- Item **arg, **arg_end;
- bool const_itm= 1;
- THD *thd= current_thd;
- /* TRUE <=> arguments values will be compared as DATETIMEs. */
- Item *date_arg= 0;
- uint found_types= 0;
- uint type_cnt= 0, i;
- m_compare_type= STRING_RESULT;
- left_cmp_type= args[0]->cmp_type();
- if (!(found_types= collect_cmp_types(args, arg_count, true)))
- return TRUE;
-
- for (arg= args + 1, arg_end= args + arg_count; arg != arg_end ; arg++)
- {
- if (!arg[0]->const_item())
- {
- const_itm= 0;
- break;
- }
- }
- for (i= 0; i <= (uint)TIME_RESULT; i++)
- {
- if (found_types & (1U << i))
- {
- (type_cnt)++;
- m_compare_type= (Item_result) i;
- }
- }
-
- /*
- First conditions for bisection to be possible:
- 1. All types are similar, and
- 2. All expressions in <in value list> are const
- */
- bool bisection_possible=
- type_cnt == 1 && // 1
- const_itm; // 2
- if (bisection_possible)
- {
- /*
- In the presence of NULLs, the correct result of evaluating this item
- must be UNKNOWN or FALSE. To achieve that:
- - If type is scalar, we can use bisection and the "have_null" boolean.
- - If type is ROW, we will need to scan all of <in value list> when
- searching, so bisection is impossible. Unless:
- 3. UNKNOWN and FALSE are equivalent results
- 4. Neither left expression nor <in value list> contain any NULL value
- */
-
- if (m_compare_type == ROW_RESULT &&
- ((!is_top_level_item() || negated) && // 3
- (list_contains_null() || args[0]->maybe_null))) // 4
- bisection_possible= false;
- }
-
- if (type_cnt == 1)
- {
- if (m_compare_type == STRING_RESULT &&
- agg_arg_charsets_for_comparison(cmp_collation, args, arg_count))
- return TRUE;
- arg_types_compatible= TRUE;
-
- if (m_compare_type == ROW_RESULT)
- {
- uint cols= args[0]->cols();
- cmp_item_row *cmp= 0;
-
- if (bisection_possible)
- {
- array= new (thd->mem_root) in_row(thd, arg_count-1, 0);
- if (!array)
- return TRUE;
- cmp= &((in_row*)array)->tmp;
- }
- else
- {
- if (!(cmp= new (thd->mem_root) cmp_item_row))
- return TRUE;
- cmp_items[ROW_RESULT]= cmp;
- }
- cmp->n= cols;
- cmp->alloc_comparators();
+/**
+ Convert all items in <in value list> to INT.
- for (uint col= 0; col < cols; col++)
- {
- date_arg= find_date_time_item(thd, args, arg_count, col, true);
- if (date_arg)
- {
- cmp_item **cmp= 0;
- if (array)
- cmp= ((in_row*)array)->tmp.comparators + col;
- else
- cmp= ((cmp_item_row*)cmp_items[ROW_RESULT])->comparators + col;
- *cmp= new (thd->mem_root) cmp_item_datetime(date_arg);
- if (!(*cmp))
- return TRUE;
- }
- }
- }
- }
+ IN must compare INT columns and constants as int values (the same
+ way as equality does).
+ So we must check here if the column on the left and all the constant
+ values on the right can be compared as integers and adjust the
+ comparison type accordingly.
- if (bisection_possible)
+ See the comment about the similar block in Item_bool_func2
+*/
+bool Item_func_in::value_list_convert_const_to_int(THD *thd)
+{
+ if (args[0]->real_item()->type() == FIELD_ITEM &&
+ !thd->lex->is_view_context_analysis())
{
- /*
- IN must compare INT columns and constants as int values (the same
- way as equality does).
- So we must check here if the column on the left and all the constant
- values on the right can be compared as integers and adjust the
- comparison type accordingly.
-
- And see the comment for Item_func::convert_const_compared_to_int_field
- */
- if (args[0]->real_item()->type() == FIELD_ITEM &&
- !thd->lex->is_view_context_analysis() && m_compare_type != INT_RESULT)
+ Item_field *field_item= (Item_field*) (args[0]->real_item());
+ if (field_item->field_type() == MYSQL_TYPE_LONGLONG ||
+ field_item->field_type() == MYSQL_TYPE_YEAR)
{
- Item_field *field_item= (Item_field*) (args[0]->real_item());
- if (field_item->field_type() == MYSQL_TYPE_LONGLONG ||
- field_item->field_type() == MYSQL_TYPE_YEAR)
+ bool all_converted= true;
+ Item **arg, **arg_end;
+ for (arg=args+1, arg_end=args+arg_count; arg != arg_end ; arg++)
{
- bool all_converted= true;
- for (arg=args+1, arg_end=args+arg_count; arg != arg_end ; arg++)
- {
/*
Explicit NULLs should not affect data cmp_type resolution:
- we ignore NULLs when calling collect_cmp_type()
@@ -4355,34 +4300,96 @@ bool Item_func_in::fix_length_and_dec()
if (arg[0]->type() != Item::NULL_ITEM &&
!convert_const_to_int(thd, field_item, &arg[0]))
all_converted= false;
- }
- if (all_converted)
- m_compare_type= INT_RESULT;
}
+ if (all_converted)
+ m_comparator.set_handler(&type_handler_longlong);
}
- if (create_array(thd))
- return TRUE;
}
- else
+ return thd->is_fatal_error; // Catch errrors in convert_const_to_int
+}
+
+
+/**
+ Historically this code installs comparators at initialization time
+ for temporal ROW elements only. All other comparators are installed later,
+ during the first store_value(). This causes the bug MDEV-11511.
+ See also comments in cmp_item_row::store_value().
+*/
+bool cmp_item_row::prepare_comparators(THD *thd, Item **args, uint arg_count)
+{
+ for (uint col= 0; col < n; col++)
{
- if (found_types & (1U << TIME_RESULT))
- date_arg= find_date_time_item(thd, args, arg_count, 0, true);
- if (found_types & (1U << STRING_RESULT) &&
- agg_arg_charsets_for_comparison(cmp_collation, args, arg_count))
- return TRUE;
- for (i= 0; i <= (uint) TIME_RESULT; i++)
+ Item *date_arg= find_date_time_item(args, arg_count, col);
+ if (date_arg)
{
- if (found_types & (1U << i) && !cmp_items[i])
- {
- if (!cmp_items[i] && !(cmp_items[i]=
- cmp_item::get_comparator((Item_result)i, date_arg,
- cmp_collation.collation)))
- return TRUE;
- }
+ // TODO: do like the scalar comparators do
+ const Type_handler *h= date_arg->type_handler();
+ comparators[col]= h->field_type() == MYSQL_TYPE_TIME ?
+ (cmp_item *) new (thd->mem_root) cmp_item_time() :
+ (cmp_item *) new (thd->mem_root) cmp_item_datetime();
+ if (!comparators[col])
+ return true;
}
}
- max_length= 1;
- return FALSE;
+ return false;
+}
+
+
+bool Item_func_in::fix_for_row_comparison_using_bisection(THD *thd)
+{
+ uint cols= args[0]->cols();
+ if (unlikely(!(array= new (thd->mem_root) in_row(thd, arg_count-1, 0))))
+ return true;
+ cmp_item_row *cmp= &((in_row*)array)->tmp;
+ if (cmp->alloc_comparators(thd, cols) ||
+ cmp->prepare_comparators(thd, args, arg_count))
+ return true;
+ /*
+ Only DATETIME items comparators were initialized.
+ Call store_value() to setup others.
+ */
+ cmp->store_value(args[0]);
+ if (unlikely(thd->is_fatal_error)) // OOM
+ return true;
+ fix_in_vector();
+ return false;
+}
+
+
+/**
+ This method is called for scalar data types when bisection is not possible,
+ for example:
+ - Some of args[1..arg_count] are not constants.
+ - args[1..arg_count] are constants, but pairs {args[0],args[1..arg_count]}
+ are compared by different data types, e.g.:
+ WHERE decimal_expr IN (1, 1e0)
+ The pair {args[0],args[1]} is compared by type_handler_decimal.
+ The pair {args[0],args[2]} is compared by type_handler_double.
+*/
+bool Item_func_in::fix_for_scalar_comparison_using_cmp_items(THD *thd,
+ uint found_types)
+{
+ if (found_types & (1U << STRING_RESULT) &&
+ agg_arg_charsets_for_comparison(cmp_collation, args, arg_count))
+ return true;
+ if (make_unique_cmp_items(thd, cmp_collation.collation))
+ return true;
+ return false;
+}
+
+
+/**
+ This method is called for the ROW data type when bisection is not possible.
+*/
+bool Item_func_in::fix_for_row_comparison_using_cmp_items(THD *thd)
+{
+ if (make_unique_cmp_items(thd, cmp_collation.collation))
+ return true;
+ DBUG_ASSERT(get_comparator_type_handler(0) == &type_handler_row);
+ DBUG_ASSERT(get_comparator_cmp_item(0));
+ cmp_item_row *cmp_row= (cmp_item_row*) get_comparator_cmp_item(0);
+ return cmp_row->alloc_comparators(thd, args[0]->cols()) ||
+ cmp_row->prepare_comparators(thd, args, arg_count);
}
@@ -4424,9 +4431,7 @@ void Item_func_in::print(String *str, enum_query_type query_type)
longlong Item_func_in::val_int()
{
- cmp_item *in_item;
DBUG_ASSERT(fixed == 1);
- uint value_added_map= 0;
if (array)
{
bool tmp=array->find(args[0]);
@@ -4444,42 +4449,34 @@ longlong Item_func_in::val_int()
if ((null_value= args[0]->real_item()->type() == NULL_ITEM))
return 0;
- have_null= 0;
- for (uint i= 1 ; i < arg_count ; i++)
+ null_value= have_null;
+ uint idx;
+ if (!Predicant_to_list_comparator::cmp(this, &idx, &null_value))
{
- if (args[i]->real_item()->type() == NULL_ITEM)
- {
- have_null= TRUE;
- continue;
- }
- Item_result cmp_type= item_cmp_type(left_cmp_type, args[i]);
- in_item= cmp_items[(uint)cmp_type];
- DBUG_ASSERT(in_item);
- if (!(value_added_map & (1U << (uint)cmp_type)))
- {
- in_item->store_value(args[0]);
- value_added_map|= 1U << (uint)cmp_type;
- }
- const int rc= in_item->cmp(args[i]);
- if (rc == FALSE)
- return (longlong) (!negated);
- have_null|= (rc == UNKNOWN);
+ null_value= false;
+ return (longlong) (!negated);
}
-
- null_value= have_null;
return (longlong) (!null_value && negated);
}
-Item *Item_func_in::build_clone(THD *thd, MEM_ROOT *mem_root)
+void Item_func_in::mark_as_condition_AND_part(TABLE_LIST *embedding)
{
- Item_func_in *clone= (Item_func_in *) Item_func::build_clone(thd, mem_root);
- if (clone)
+ THD *thd= current_thd;
+
+ Query_arena *arena, backup;
+ arena= thd->activate_stmt_arena_if_needed(&backup);
+
+ if (to_be_transformed_into_in_subq(thd))
{
- bzero(&clone->cmp_items, sizeof(cmp_items));
- clone->fix_length_and_dec();
+ transform_into_subq= true;
+ thd->lex->current_select->in_funcs.push_back(this, thd->mem_root);
}
- return clone;
+
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+
+ emb_on_expr_nest= embedding;
}
@@ -4622,11 +4619,9 @@ Item_cond::fix_fields(THD *thd, Item **ref)
thd->restore_active_arena(arena, &backup);
}
- // item can be substituted in fix_fields
- if ((!item->fixed &&
- item->fix_fields(thd, li.ref())) ||
- (item= *li.ref())->check_cols(1))
+ if (item->fix_fields_if_needed_for_bool(thd, li.ref()))
return TRUE; /* purecov: inspected */
+ item= *li.ref(); // item can be substituted in fix_fields
used_tables_cache|= item->used_tables();
if (item->const_item() && !item->with_param &&
!item->is_expensive() && !cond_has_datetime_is_null(item))
@@ -4668,7 +4663,7 @@ Item_cond::fix_fields(THD *thd, Item **ref)
with_sum_func|= item->with_sum_func;
with_param|= item->with_param;
with_field|= item->with_field;
- with_subselect|= item->has_subquery();
+ m_with_subquery|= item->with_subquery();
with_window_func|= item->with_window_func;
maybe_null|= item->maybe_null;
}
@@ -4865,17 +4860,14 @@ Item *Item_cond::propagate_equal_fields(THD *thd,
DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare());
DBUG_ASSERT(arg_count == 0);
List_iterator<Item> li(list);
- Item *item;
- while ((item= li++))
+ while (li++)
{
/*
- The exact value of the second parameter to propagate_equal_fields()
+ The exact value of the last parameter to propagate_and_change_item_tree()
is not important at this point. Item_func derivants will create and
pass their own context to the arguments.
*/
- Item *new_item= item->propagate_equal_fields(thd, Context_boolean(), cond);
- if (new_item && new_item != item)
- thd->change_item_tree(li.ref(), new_item);
+ propagate_and_change_item_tree(thd, li.ref(), cond, Context_boolean());
}
return this;
}
@@ -4986,23 +4978,23 @@ void Item_cond::neg_arguments(THD *thd)
@retval
clone of the item
- 0 if an error occured
+ 0 if an error occurred
*/
-Item *Item_cond::build_clone(THD *thd, MEM_ROOT *mem_root)
+Item *Item_cond::build_clone(THD *thd)
{
List_iterator_fast<Item> li(list);
Item *item;
- Item_cond *copy= (Item_cond *) get_copy(thd, mem_root);
+ Item_cond *copy= (Item_cond *) get_copy(thd);
if (!copy)
return 0;
copy->list.empty();
while ((item= li++))
{
- Item *arg_clone= item->build_clone(thd, mem_root);
+ Item *arg_clone= item->build_clone(thd);
if (!arg_clone)
return 0;
- if (copy->list.push_back(arg_clone, mem_root))
+ if (copy->list.push_back(arg_clone, thd->mem_root))
return 0;
}
return copy;
@@ -5399,12 +5391,11 @@ bool fix_escape_item(THD *thd, Item *escape_item, String *tmp_str,
return FALSE;
}
-
bool Item_func_like::fix_fields(THD *thd, Item **ref)
{
DBUG_ASSERT(fixed == 0);
if (Item_bool_func2::fix_fields(thd, ref) ||
- escape_item->fix_fields(thd, &escape_item) ||
+ escape_item->fix_fields_if_needed_for_scalar(thd, &escape_item) ||
fix_escape_item(thd, escape_item, &cmp_value1, escape_used_in_parsing,
cmp_collation.collation, &escape))
return TRUE;
@@ -5553,7 +5544,7 @@ bool Regexp_processor_pcre::compile(String *pattern, bool send_error)
m_pcre= pcre_compile(pattern->c_ptr_safe(), m_library_flags,
&pcreErrorStr, &pcreErrorOffset, NULL);
- if (m_pcre == NULL)
+ if (unlikely(m_pcre == NULL))
{
if (send_error)
{
@@ -5572,7 +5563,7 @@ bool Regexp_processor_pcre::compile(Item *item, bool send_error)
char buff[MAX_FIELD_WIDTH];
String tmp(buff, sizeof(buff), &my_charset_bin);
String *pattern= item->val_str(&tmp);
- if (item->null_value || compile(pattern, send_error))
+ if (unlikely(item->null_value) || (unlikely(compile(pattern, send_error))))
return true;
return false;
}
@@ -5691,15 +5682,15 @@ int Regexp_processor_pcre::pcre_exec_with_warn(const pcre *code,
int rc= pcre_exec(code, extra, subject, length,
startoffset, options, ovector, ovecsize);
DBUG_EXECUTE_IF("pcre_exec_error_123", rc= -123;);
- if (rc < PCRE_ERROR_NOMATCH)
+ if (unlikely(rc < PCRE_ERROR_NOMATCH))
pcre_exec_warn(rc);
return rc;
}
-bool Regexp_processor_pcre::exec(const char *str, int length, int offset)
+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, length, offset, 0,
+ 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));
return false;
}
@@ -5810,6 +5801,7 @@ Item_func_regexp_instr::fix_length_and_dec()
re.init(cmp_collation.collation, 0);
re.fix_owner(this, args[0], args[1]);
+ max_length= MY_INT32_NUM_DECIMAL_DIGITS; // See also Item_func_locate
return FALSE;
}
@@ -6290,10 +6282,11 @@ Item *Item_bool_rowready_func2::negated_item(THD *thd)
of the type Item_field or Item_direct_view_ref(Item_field).
*/
-Item_equal::Item_equal(THD *thd, Item *f1, Item *f2, bool with_const_item):
+Item_equal::Item_equal(THD *thd, const Type_handler *handler,
+ Item *f1, Item *f2, bool with_const_item):
Item_bool_func(thd), eval_item(0), cond_false(0), cond_true(0),
context_field(NULL), link_equal_fields(FALSE),
- m_compare_type(item_cmp_type(f1, f2)),
+ m_compare_handler(handler),
m_compare_collation(f2->collation.collation)
{
const_item_cache= 0;
@@ -6319,7 +6312,7 @@ Item_equal::Item_equal(THD *thd, Item *f1, Item *f2, bool with_const_item):
Item_equal::Item_equal(THD *thd, Item_equal *item_equal):
Item_bool_func(thd), eval_item(0), cond_false(0), cond_true(0),
context_field(NULL), link_equal_fields(FALSE),
- m_compare_type(item_equal->m_compare_type),
+ m_compare_handler(item_equal->m_compare_handler),
m_compare_collation(item_equal->m_compare_collation)
{
const_item_cache= 0;
@@ -6362,7 +6355,7 @@ void Item_equal::add_const(THD *thd, Item *c)
return;
}
Item *const_item= get_const();
- switch (Item_equal::compare_type()) {
+ switch (Item_equal::compare_type_handler()->cmp_type()) {
case TIME_RESULT:
{
enum_field_types f_type= context_field->field_type();
@@ -6735,7 +6728,7 @@ bool Item_equal::fix_fields(THD *thd, Item **ref)
used_tables_cache|= item->used_tables();
tmp_table_map= item->not_null_tables();
not_null_tables_cache|= tmp_table_map;
- DBUG_ASSERT(!item->with_sum_func && !item->with_subselect);
+ DBUG_ASSERT(!item->with_sum_func && !item->with_subquery());
if (item->maybe_null)
maybe_null= 1;
if (!item->get_item_equal())
@@ -6842,9 +6835,9 @@ longlong Item_equal::val_int()
bool Item_equal::fix_length_and_dec()
{
Item *item= get_first(NO_PARTICULAR_TAB, NULL);
- eval_item= cmp_item::get_comparator(item->cmp_type(), item,
- item->collation.collation);
- return FALSE;
+ const Type_handler *handler= item->type_handler();
+ eval_item= handler->make_cmp_item(current_thd, item->collation.collation);
+ return eval_item == NULL;
}
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index cad179dff74..f22ef0a6c3c 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -47,7 +47,7 @@ typedef int (*Item_field_cmpfunc)(Item *f1, Item *f2, void *arg);
class Arg_comparator: public Sql_alloc
{
Item **a, **b;
- Item_result m_compare_type;
+ const Type_handler *m_compare_handler;
CHARSET_INFO *m_compare_collation;
arg_cmp_func func;
Item_func_or_sum *owner;
@@ -57,27 +57,42 @@ class Arg_comparator: public Sql_alloc
/* Fields used in DATE/DATETIME comparison. */
Item *a_cache, *b_cache; // Cached values of a and b items
// when one of arguments is NULL.
- int set_compare_func(Item_func_or_sum *owner, Item_result type);
- int set_cmp_func(Item_func_or_sum *owner_arg, Item **a1, Item **a2);
- int compare_temporal(enum_field_types type);
- int compare_e_temporal(enum_field_types type);
+ int set_cmp_func(Item_func_or_sum *owner_arg, Item **a1, Item **a2);
+ int compare_not_null_values(longlong val1, longlong val2)
+ {
+ if (set_null)
+ owner->null_value= false;
+ if (val1 < val2) return -1;
+ if (val1 == val2) return 0;
+ return 1;
+ }
public:
/* Allow owner function to use string buffers. */
String value1, value2;
- Arg_comparator(): m_compare_type(STRING_RESULT),
+ Arg_comparator():
+ m_compare_handler(&type_handler_null),
m_compare_collation(&my_charset_bin),
set_null(TRUE), comparators(0),
a_cache(0), b_cache(0) {};
Arg_comparator(Item **a1, Item **a2): a(a1), b(a2),
- m_compare_type(STRING_RESULT),
+ m_compare_handler(&type_handler_null),
m_compare_collation(&my_charset_bin),
set_null(TRUE), comparators(0),
a_cache(0), b_cache(0) {};
public:
+ bool set_cmp_func_for_row_arguments();
+ bool set_cmp_func_row();
+ bool set_cmp_func_string();
+ bool set_cmp_func_time();
+ bool set_cmp_func_datetime();
+ bool set_cmp_func_int();
+ bool set_cmp_func_real();
+ bool set_cmp_func_decimal();
+
inline int set_cmp_func(Item_func_or_sum *owner_arg,
Item **a1, Item **a2, bool set_null_arg)
{
@@ -102,10 +117,10 @@ public:
int compare_e_row(); // compare args[0] & args[1]
int compare_real_fixed();
int compare_e_real_fixed();
- int compare_datetime() { return compare_temporal(MYSQL_TYPE_DATETIME); }
- int compare_e_datetime() { return compare_e_temporal(MYSQL_TYPE_DATETIME); }
- int compare_time() { return compare_temporal(MYSQL_TYPE_TIME); }
- int compare_e_time() { return compare_e_temporal(MYSQL_TYPE_TIME); }
+ int compare_datetime();
+ int compare_e_datetime();
+ int compare_time();
+ int compare_e_time();
int compare_json_str_basic(Item *j, Item *s);
int compare_json_str();
int compare_str_json();
@@ -113,13 +128,15 @@ public:
int compare_e_json_str();
int compare_e_str_json();
- static arg_cmp_func comparator_matrix [6][2];
+ Item** cache_converted_constant(THD *thd, Item **value, Item **cache,
+ const Type_handler *type);
inline bool is_owner_equal_func()
{
return (owner->type() == Item::FUNC_ITEM &&
((Item_func*)owner)->functype() == Item_func::EQUAL_FUNC);
}
- Item_result compare_type() const { return m_compare_type; }
+ const Type_handler *compare_type_handler() const { return m_compare_handler; }
+ Item_result compare_type() const { return m_compare_handler->cmp_type(); }
CHARSET_INFO *compare_collation() const { return m_compare_collation; }
Arg_comparator *subcomparators() const { return comparators; }
void cleanup()
@@ -198,6 +215,7 @@ public:
Item_bool_func(THD *thd, Item *a, Item *b, Item *c): Item_int_func(thd, a, b, c) {}
Item_bool_func(THD *thd, List<Item> &list): Item_int_func(thd, list) { }
Item_bool_func(THD *thd, Item_bool_func *item) :Item_int_func(thd, item) {}
+ const Type_handler *type_handler() const { return &type_handler_long; }
bool is_bool_type() { return true; }
virtual CHARSET_INFO *compare_collation() const { return NULL; }
bool fix_length_and_dec() { decimals=0; max_length=1; return FALSE; }
@@ -250,8 +268,8 @@ public:
Item_func_istrue(THD *thd, Item *a): Item_func_truth(thd, a, true, true) {}
~Item_func_istrue() {}
virtual const char* func_name() const { return "istrue"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_istrue>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_istrue>(thd, this); }
};
@@ -266,10 +284,9 @@ public:
Item_func_truth(thd, a, true, false) {}
~Item_func_isnottrue() {}
virtual const char* func_name() const { return "isnottrue"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_isnottrue>(thd, mem_root, this); }
- bool eval_not_null_tables(void *opt_arg)
- { not_null_tables_cache= 0; 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; }
};
@@ -283,8 +300,8 @@ public:
Item_func_isfalse(THD *thd, Item *a): Item_func_truth(thd, a, false, true) {}
~Item_func_isfalse() {}
virtual const char* func_name() const { return "isfalse"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_isfalse>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_isfalse>(thd, this); }
};
@@ -299,10 +316,9 @@ public:
Item_func_truth(thd, a, false, false) {}
~Item_func_isnotfalse() {}
virtual const char* func_name() const { return "isnotfalse"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_isnotfalse>(thd, mem_root, this); }
- bool eval_not_null_tables(void *opt_arg)
- { not_null_tables_cache= 0; 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; }
};
@@ -342,7 +358,7 @@ public:
Item_in_optimizer(THD *thd, Item *a, Item *b):
Item_bool_func(thd, a, b), cache(0), expr_cache(0),
save_cache(0), result_for_null_param(UNKNOWN)
- { with_subselect= true; }
+ { m_with_subquery= true; }
bool fix_fields(THD *, Item **);
bool fix_left(THD *thd);
table_map not_null_tables() const { return 0; }
@@ -369,8 +385,8 @@ public:
void restore_first_argument();
Item* get_wrapped_in_subselect_item()
{ return args[1]; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_in_optimizer>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_in_optimizer>(thd, this); }
};
@@ -395,7 +411,7 @@ public:
Specifies which result type the function uses to compare its arguments.
This method is used in equal field propagation.
*/
- virtual Item_result compare_type() const
+ virtual const Type_handler *compare_type_handler() const
{
/*
Have STRING_RESULT by default, which means the function compares
@@ -403,7 +419,7 @@ public:
and for Item_func_spatial_rel.
Note, Item_bool_rowready_func2 overrides this default behaviour.
*/
- return STRING_RESULT;
+ return &type_handler_varchar;
}
SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr)
{
@@ -483,12 +499,14 @@ class Item_bool_rowready_func2 :public Item_bool_func2_with_rev
{
protected:
Arg_comparator cmp;
+ bool check_arguments() const
+ {
+ return check_argument_types_like_args0();
+ }
public:
Item_bool_rowready_func2(THD *thd, Item *a, Item *b):
Item_bool_func2_with_rev(thd, a, b), cmp(tmp_arg, tmp_arg + 1)
- {
- allowed_arg_cols= 0; // Fetch this value from first argument
- }
+ { }
Sql_mode_dependency value_depends_on_sql_mode() const;
void print(String *str, enum_query_type query_type)
{
@@ -501,7 +519,7 @@ public:
{
Item_args::propagate_equal_fields(thd,
Context(ANY_SUBST,
- cmp.compare_type(),
+ cmp.compare_type_handler(),
compare_collation()),
cond);
return this;
@@ -512,7 +530,10 @@ public:
return cmp.set_cmp_func(this, tmp_arg, tmp_arg + 1, true);
}
CHARSET_INFO *compare_collation() const { return cmp.compare_collation(); }
- Item_result compare_type() const { return cmp.compare_type(); }
+ const Type_handler *compare_type_handler() const
+ {
+ return cmp.compare_type_handler();
+ }
Arg_comparator *get_comparator() { return &cmp; }
void cleanup()
{
@@ -526,10 +547,10 @@ public:
return add_key_fields_optimize_op(join, key_fields, and_level,
usable_tables, sargables, false);
}
- Item *build_clone(THD *thd, MEM_ROOT *mem_root)
+ Item *build_clone(THD *thd)
{
Item_bool_rowready_func2 *clone=
- (Item_bool_rowready_func2 *) Item_func::build_clone(thd, mem_root);
+ (Item_bool_rowready_func2 *) Item_func::build_clone(thd);
if (clone)
{
clone->cmp.comparators= 0;
@@ -559,8 +580,8 @@ public:
Item_args::propagate_equal_fields(thd, Context_boolean(), cond);
return this;
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_xor>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_xor>(thd, this); }
};
class Item_func_not :public Item_bool_func
@@ -578,8 +599,8 @@ public:
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, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_not>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_not>(thd, this); }
};
class Item_maxmin_subselect;
@@ -627,14 +648,14 @@ public:
void add_key_fields(JOIN *join, KEY_FIELD **key_fields,
uint *and_level, table_map usable_tables,
SARGABLE_PARAM **sargables);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_trig_cond>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_trig_cond>(thd, this); }
};
class Item_func_not_all :public Item_func_not
{
/* allow to check presence of values in max/min optimization */
- Item_sum_hybrid *test_sum_item;
+ Item_sum_min_max *test_sum_item;
Item_maxmin_subselect *test_sub_item;
public:
@@ -650,7 +671,7 @@ public:
bool fix_fields(THD *thd, Item **ref)
{return Item_func::fix_fields(thd, ref);}
virtual void print(String *str, enum_query_type query_type);
- void set_sum_test(Item_sum_hybrid *item) { test_sum_item= item; test_sub_item= 0; };
+ void set_sum_test(Item_sum_min_max *item) { test_sum_item= item; test_sub_item= 0; };
void set_sub_test(Item_maxmin_subselect *item) { test_sub_item= item; test_sum_item= 0;};
bool empty_underlying_subquery();
Item *neg_transformer(THD *thd);
@@ -665,8 +686,8 @@ public:
longlong val_int();
const char *func_name() const { return "<nop>"; }
Item *neg_transformer(THD *thd);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_nop_all>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_nop_all>(thd, this); }
};
@@ -706,8 +727,8 @@ public:
uint in_equality_no;
virtual uint exists2in_reserved_items() { return 1; };
friend class Arg_comparator;
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_eq>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_eq>(thd, this); }
};
class Item_func_equal :public Item_bool_rowready_func2
@@ -730,8 +751,8 @@ public:
return add_key_fields_optimize_op(join, key_fields, and_level,
usable_tables, sargables, true);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_equal>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_equal>(thd, this); }
};
@@ -746,8 +767,8 @@ public:
cond_result eq_cmp_result() const { return COND_TRUE; }
const char *func_name() const { return ">="; }
Item *negated_item(THD *thd);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_ge>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_ge>(thd, this); }
};
@@ -762,8 +783,8 @@ public:
cond_result eq_cmp_result() const { return COND_FALSE; }
const char *func_name() const { return ">"; }
Item *negated_item(THD *thd);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_gt>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_gt>(thd, this); }
};
@@ -778,8 +799,8 @@ public:
cond_result eq_cmp_result() const { return COND_TRUE; }
const char *func_name() const { return "<="; }
Item *negated_item(THD *thd);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_le>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_le>(thd, this); }
};
@@ -794,8 +815,8 @@ public:
cond_result eq_cmp_result() const { return COND_FALSE; }
const char *func_name() const { return "<"; }
Item *negated_item(THD *thd);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_lt>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_lt>(thd, this); }
};
@@ -819,8 +840,8 @@ public:
Item *negated_item(THD *thd);
void add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level,
table_map usable_tables, SARGABLE_PARAM **sargables);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_ne>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_ne>(thd, this); }
};
@@ -837,10 +858,10 @@ class Item_func_opt_neg :public Item_bool_func
{
protected:
/*
- The result type that will be used for comparison.
- cmp_type() of all arguments are collected to here.
+ The data type handler that will be used for comparison.
+ Data type handlers of all arguments are mixed to here.
*/
- Item_result m_compare_type;
+ Type_handler_hybrid_field_type m_comparator;
/*
The collation that will be used for comparison in case
when m_compare_type is STRING_RESULT.
@@ -875,15 +896,23 @@ protected:
Field *field, Item *value);
public:
String value0,value1,value2;
- /* TRUE <=> arguments will be compared as dates. */
- Item *compare_as_dates;
Item_func_between(THD *thd, Item *a, Item *b, Item *c):
- Item_func_opt_neg(thd, a, b, c), compare_as_dates(FALSE) { }
- longlong val_int();
+ Item_func_opt_neg(thd, a, b, c) { }
+ longlong val_int()
+ {
+ DBUG_ASSERT(fixed);
+ return m_comparator.type_handler()->Item_func_between_val_int(this);
+ }
enum Functype functype() const { return BETWEEN; }
const char *func_name() const { return "between"; }
enum precedence precedence() const { return BETWEEN_PRECEDENCE; }
bool fix_length_and_dec();
+ bool fix_length_and_dec_string(THD *)
+ {
+ return agg_arg_charsets_for_comparison(cmp_collation, args, 3);
+ }
+ bool fix_length_and_dec_temporal(THD *);
+ 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);
void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge);
@@ -896,28 +925,31 @@ public:
{
Item_args::propagate_equal_fields(thd,
Context(ANY_SUBST,
- m_compare_type,
+ m_comparator.type_handler(),
compare_collation()),
cond);
return this;
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_between>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_between>(thd, this); }
longlong val_int_cmp_string();
+ longlong val_int_cmp_temporal();
longlong val_int_cmp_int();
longlong val_int_cmp_real();
longlong val_int_cmp_decimal();
};
-class Item_func_strcmp :public Item_int_func
+class Item_func_strcmp :public Item_long_func
{
+ bool check_arguments() const
+ { return check_argument_types_can_return_str(0, 2); }
String value1, value2;
DTCollation cmp_collation;
public:
Item_func_strcmp(THD *thd, Item *a, Item *b):
- Item_int_func(thd, a, b) {}
+ Item_long_func(thd, a, b) {}
longlong val_int();
uint decimal_precision() const { return 1; }
const char *func_name() const { return "strcmp"; }
@@ -928,8 +960,8 @@ public:
fix_char_length(2);
return FALSE;
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_strcmp>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_strcmp>(thd, this); }
};
@@ -940,17 +972,19 @@ struct interval_range
my_decimal dec;
};
-class Item_func_interval :public Item_int_func
+class Item_func_interval :public Item_long_func
{
Item_row *row;
bool use_decimal_comparison;
interval_range *intervals;
-public:
- Item_func_interval(THD *thd, Item_row *a):
- Item_int_func(thd, a), row(a), intervals(0)
+ bool check_arguments() const
{
- allowed_arg_cols= 0; // Fetch this value from first argument
+ return check_argument_types_like_args0();
}
+public:
+ Item_func_interval(THD *thd, Item_row *a):
+ Item_long_func(thd, a), row(a), intervals(0)
+ { }
bool fix_fields(THD *, Item **);
longlong val_int();
bool fix_length_and_dec();
@@ -961,33 +995,35 @@ public:
str->append(func_name());
print_args(str, 0, query_type);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_interval>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_interval>(thd, this); }
};
-class Item_func_coalesce :public Item_func_hybrid_field_type
+class Item_func_coalesce :public Item_func_case_expression
{
public:
Item_func_coalesce(THD *thd, Item *a, Item *b):
- Item_func_hybrid_field_type(thd, a, b) {}
+ Item_func_case_expression(thd, a, b) {}
Item_func_coalesce(THD *thd, List<Item> &list):
- Item_func_hybrid_field_type(thd, list) {}
+ Item_func_case_expression(thd, list) {}
double real_op();
longlong int_op();
String *str_op(String *);
my_decimal *decimal_op(my_decimal *);
- bool date_op(MYSQL_TIME *ltime,uint fuzzydate);
+ bool date_op(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool time_op(MYSQL_TIME *ltime);
bool fix_length_and_dec()
{
- set_handler_by_field_type(agg_field_type(args, arg_count, true));
+ if (aggregate_for_result(func_name(), args, arg_count, true))
+ return TRUE;
fix_attributes(args, arg_count);
return FALSE;
}
const char *func_name() const { return "coalesce"; }
table_map not_null_tables() const { return 0; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_coalesce>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_coalesce>(thd, this); }
};
@@ -995,22 +1031,53 @@ public:
Case abbreviations that aggregate its result field type by two arguments:
IFNULL(arg1, arg2)
IF(switch, arg1, arg2)
+ NVL2(switch, arg1, arg2)
*/
-class Item_func_case_abbreviation2 :public Item_func_hybrid_field_type
+class Item_func_case_abbreviation2 :public Item_func_case_expression
{
protected:
bool fix_length_and_dec2(Item **items)
{
- set_handler_by_field_type(agg_field_type(items, 2, true));
+ if (aggregate_for_result(func_name(), items, 2, true))
+ return TRUE;
fix_attributes(items, 2);
return FALSE;
}
- uint decimal_precision2(Item **args) const;
+
+ void cache_type_info(const Item *source, bool maybe_null_arg)
+ {
+ Type_std_attributes::set(source);
+ set_handler(source->type_handler());
+ maybe_null= maybe_null_arg;
+ }
+
+ bool fix_length_and_dec2_eliminate_null(Item **items)
+ {
+ // Let IF(cond, expr, NULL) and IF(cond, NULL, expr) inherit type from expr.
+ if (items[0]->type() == NULL_ITEM)
+ {
+ cache_type_info(items[1], true);
+ // If both arguments are NULL, make resulting type BINARY(0).
+ if (items[1]->type() == NULL_ITEM)
+ set_handler(&type_handler_string);
+ }
+ else if (items[1]->type() == NULL_ITEM)
+ {
+ cache_type_info(items[0], true);
+ }
+ else
+ {
+ if (fix_length_and_dec2(items))
+ return TRUE;
+ }
+ return FALSE;
+ }
+
public:
Item_func_case_abbreviation2(THD *thd, Item *a, Item *b):
- Item_func_hybrid_field_type(thd, a, b) { }
+ Item_func_case_expression(thd, a, b) { }
Item_func_case_abbreviation2(THD *thd, Item *a, Item *b, Item *c):
- Item_func_hybrid_field_type(thd, a, b, c) { }
+ Item_func_case_expression(thd, a, b, c) { }
};
@@ -1023,7 +1090,8 @@ public:
longlong int_op();
String *str_op(String *str);
my_decimal *decimal_op(my_decimal *);
- bool date_op(MYSQL_TIME *ltime,uint fuzzydate);
+ bool date_op(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool time_op(MYSQL_TIME *ltime);
bool fix_length_and_dec()
{
if (Item_func_case_abbreviation2::fix_length_and_dec2(args))
@@ -1032,47 +1100,103 @@ public:
return FALSE;
}
const char *func_name() const { return "ifnull"; }
- Field *create_field_for_create_select(TABLE *table)
- { return tmp_table_field_from_field_type(table, false, false); }
table_map not_null_tables() const { return 0; }
- uint decimal_precision() const
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_ifnull>(thd, this); }
+};
+
+
+/**
+ Case abbreviations that have a switch argument and
+ two return arguments to choose from. Returns the value
+ of either of the two return arguments depending on the switch argument value.
+
+ IF(switch, arg1, arg2)
+ NVL(switch, arg1, arg2)
+*/
+class Item_func_case_abbreviation2_switch: public Item_func_case_abbreviation2
+{
+protected:
+ virtual Item *find_item() const= 0;
+
+public:
+ Item_func_case_abbreviation2_switch(THD *thd, Item *a, Item *b, Item *c)
+ :Item_func_case_abbreviation2(thd, a, b, c)
+ { }
+
+ bool date_op(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ {
+ Datetime dt(current_thd, find_item(), fuzzydate);
+ return (null_value= dt.copy_to_mysql_time(ltime, mysql_timestamp_type()));
+ }
+ bool time_op(MYSQL_TIME *ltime)
+ {
+ return (null_value= Time(find_item()).copy_to_mysql_time(ltime));
+ }
+ longlong int_op()
{
- return Item_func_case_abbreviation2::decimal_precision2(args);
+ return val_int_from_item(find_item());
+ }
+ double real_op()
+ {
+ return val_real_from_item(find_item());
+ }
+ my_decimal *decimal_op(my_decimal *decimal_value)
+ {
+ return val_decimal_from_item(find_item(), decimal_value);
+ }
+ String *str_op(String *str)
+ {
+ return val_str_from_item(find_item(), str);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_ifnull>(thd, mem_root, this); }
};
-class Item_func_if :public Item_func_case_abbreviation2
+class Item_func_if :public Item_func_case_abbreviation2_switch
{
+protected:
+ Item *find_item() const { return args[0]->val_bool() ? args[1] : args[2]; }
+
public:
Item_func_if(THD *thd, Item *a, Item *b, Item *c):
- Item_func_case_abbreviation2(thd, a, b, c)
+ Item_func_case_abbreviation2_switch(thd, a, b, c)
{}
- bool date_op(MYSQL_TIME *ltime, uint fuzzydate);
- longlong int_op();
- double real_op();
- my_decimal *decimal_op(my_decimal *);
- String *str_op(String *);
bool fix_fields(THD *, Item **);
- bool fix_length_and_dec();
- uint decimal_precision() const
+ bool fix_length_and_dec()
{
- return Item_func_case_abbreviation2::decimal_precision2(args + 1);
+ return fix_length_and_dec2_eliminate_null(args + 1);
}
const char *func_name() const { return "if"; }
bool eval_not_null_tables(void *opt_arg);
void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_if>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_if>(thd, this); }
private:
void cache_type_info(Item *source);
};
-class Item_func_nullif :public Item_func_hybrid_field_type
+class Item_func_nvl2 :public Item_func_case_abbreviation2_switch
+{
+protected:
+ Item *find_item() const { return args[0]->is_null() ? args[2] : args[1]; }
+
+public:
+ Item_func_nvl2(THD *thd, Item *a, Item *b, Item *c):
+ Item_func_case_abbreviation2_switch(thd, a, b, c)
+ {}
+ const char *func_name() const { return "nvl2"; }
+ bool fix_length_and_dec()
+ {
+ return fix_length_and_dec2_eliminate_null(args + 1);
+ }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_nvl2>(thd, this); }
+};
+
+
+class Item_func_nullif :public Item_func_case_expression
{
Arg_comparator cmp;
/*
@@ -1110,7 +1234,7 @@ public:
See also Item_func_nullif::fix_length_and_dec().
*/
Item_func_nullif(THD *thd, Item *a, Item *b):
- Item_func_hybrid_field_type(thd, a, b, a),
+ Item_func_case_expression(thd, a, b, a),
m_cache(NULL),
m_arg0(NULL)
{ arg_count--; }
@@ -1119,14 +1243,14 @@ public:
Item_func_hybrid_field_type::cleanup();
arg_count= 2; // See the comment to the constructor
}
- bool date_op(MYSQL_TIME *ltime, uint fuzzydate);
+ bool date_op(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool time_op(MYSQL_TIME *ltime);
double real_op();
longlong int_op();
String *str_op(String *str);
my_decimal *decimal_op(my_decimal *);
bool fix_length_and_dec();
bool walk(Item_processor processor, bool walk_subquery, void *arg);
- uint decimal_precision() const { return args[2]->decimal_precision(); }
const char *func_name() const { return "nullif"; }
void print(String *str, enum_query_type query_type);
void split_sum_func(THD *thd, Ref_ptr_array ref_pointer_array,
@@ -1136,7 +1260,8 @@ public:
bool is_null();
Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond)
{
- Context cmpctx(ANY_SUBST, cmp.compare_type(), cmp.compare_collation());
+ Context cmpctx(ANY_SUBST, cmp.compare_type_handler(),
+ cmp.compare_collation());
const Item *old0= args[0];
args[0]->propagate_equal_fields_and_change_item_tree(thd, cmpctx,
cond, &args[0]);
@@ -1154,8 +1279,8 @@ public:
cond, &args[2]);
return this;
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_nullif>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_nullif>(thd, this); }
Item *derived_field_transformer_for_having(THD *thd, uchar *arg)
{ reset_first_arg_if_needed(); return this; }
Item *derived_field_transformer_for_where(THD *thd, uchar *arg)
@@ -1219,7 +1344,7 @@ public:
{
return MY_TEST(compare(collation, base + pos1 * size, base + pos2 * size));
}
- virtual Item_result result_type()= 0;
+ virtual const Type_handler *type_handler() const= 0;
};
class in_string :public in_vector
@@ -1250,7 +1375,7 @@ public:
Item_string_for_in_vector *to= (Item_string_for_in_vector*) item;
to->set_value(str);
}
- Item_result result_type() { return STRING_RESULT; }
+ const Type_handler *type_handler() const { return &type_handler_varchar; }
};
class in_longlong :public in_vector
@@ -1277,7 +1402,7 @@ public:
((Item_int*) item)->unsigned_flag= (bool)
((packed_longlong*) base)[pos].unsigned_flag;
}
- Item_result result_type() { return INT_RESULT; }
+ const Type_handler *type_handler() const { return &type_handler_longlong; }
friend int cmp_longlong(void *cmp_arg, packed_longlong *a,packed_longlong *b);
};
@@ -1286,27 +1411,50 @@ public:
/*
Class to represent a vector of constant DATE/DATETIME values.
*/
-class in_datetime :public in_longlong
+class in_temporal :public in_longlong
{
+protected:
+ uchar *get_value_internal(Item *item, enum_field_types f_type);
public:
- /* An item used to issue warnings. */
- Item *warn_item;
+ /* Cache for the left item. */
- in_datetime(THD *thd, Item *warn_item_arg, uint elements)
- :in_longlong(thd, elements), warn_item(warn_item_arg) {}
- void set(uint pos,Item *item);
- uchar *get_value(Item *item);
+ in_temporal(THD *thd, uint elements)
+ :in_longlong(thd, elements) {};
Item *create_item(THD *thd);
void value_to_item(uint pos, Item *item)
{
packed_longlong *val= reinterpret_cast<packed_longlong*>(base)+pos;
- Item_datetime *dt= reinterpret_cast<Item_datetime*>(item);
- dt->set(val->val);
+ Item_datetime *dt= static_cast<Item_datetime*>(item);
+ dt->set(val->val, type_handler()->mysql_timestamp_type());
}
+ uchar *get_value(Item *item)
+ { return get_value_internal(item, type_handler()->field_type()); }
friend int cmp_longlong(void *cmp_arg, packed_longlong *a,packed_longlong *b);
};
+class in_datetime :public in_temporal
+{
+public:
+ in_datetime(THD *thd, uint elements)
+ :in_temporal(thd, elements)
+ {}
+ void set(uint pos,Item *item);
+ const Type_handler *type_handler() const { return &type_handler_datetime2; }
+};
+
+
+class in_time :public in_temporal
+{
+public:
+ in_time(THD *thd, uint elements)
+ :in_temporal(thd, elements)
+ {}
+ void set(uint pos,Item *item);
+ const Type_handler *type_handler() const { return &type_handler_time2; }
+};
+
+
class in_double :public in_vector
{
double tmp;
@@ -1319,7 +1467,7 @@ public:
{
((Item_float*)item)->value= ((double*) base)[pos];
}
- Item_result result_type() { return REAL_RESULT; }
+ const Type_handler *type_handler() const { return &type_handler_double; }
};
@@ -1337,7 +1485,7 @@ public:
Item_decimal *item_dec= (Item_decimal*)item;
item_dec->set_decimal_value(dec);
}
- Item_result result_type() { return DECIMAL_RESULT; }
+ const Type_handler *type_handler() const { return &type_handler_newdecimal; }
};
@@ -1357,10 +1505,9 @@ public:
"stored argument's value <> item's value"
*/
virtual int cmp(Item *item)= 0;
+ virtual int cmp_not_null(const Value *value)= 0;
// for optimized IN with row
virtual int compare(cmp_item *item)= 0;
- static cmp_item* get_comparator(Item_result type, Item * warn_item,
- CHARSET_INFO *cs);
virtual cmp_item *make_same()= 0;
virtual void store_value_by_template(THD *thd, cmp_item *tmpl, Item *item)
{
@@ -1410,6 +1557,12 @@ public:
value_res= &value;
}
}
+ int cmp_not_null(const Value *val)
+ {
+ DBUG_ASSERT(!val->is_null());
+ DBUG_ASSERT(val->is_string());
+ return sortcmp(value_res, &val->m_string, cmp_charset) != 0;
+ }
int cmp(Item *arg)
{
char buff[STRING_BUFFER_USUAL_SIZE];
@@ -1446,6 +1599,12 @@ public:
value= item->val_int();
m_null_value= item->null_value;
}
+ int cmp_not_null(const Value *val)
+ {
+ DBUG_ASSERT(!val->is_null());
+ DBUG_ASSERT(val->is_longlong());
+ return value != val->value.m_longlong;
+ }
int cmp(Item *arg)
{
const bool rc= value != arg->val_int();
@@ -1462,18 +1621,45 @@ public:
/*
Compare items in the DATETIME context.
*/
-class cmp_item_datetime : public cmp_item_scalar
+class cmp_item_temporal: public cmp_item_scalar
{
+protected:
longlong value;
+ void store_value_internal(Item *item, enum_field_types type);
public:
- /* Item used for issuing warnings. */
- Item *warn_item;
+ cmp_item_temporal() {}
+ int compare(cmp_item *ci);
+};
- cmp_item_datetime(Item *warn_item_arg)
- : warn_item(warn_item_arg) {}
- void store_value(Item *item);
+
+class cmp_item_datetime: public cmp_item_temporal
+{
+public:
+ cmp_item_datetime()
+ :cmp_item_temporal()
+ { }
+ void store_value(Item *item)
+ {
+ store_value_internal(item, MYSQL_TYPE_DATETIME);
+ }
+ int cmp_not_null(const Value *val);
+ int cmp(Item *arg);
+ cmp_item *make_same();
+};
+
+
+class cmp_item_time: public cmp_item_temporal
+{
+public:
+ cmp_item_time()
+ :cmp_item_temporal()
+ { }
+ void store_value(Item *item)
+ {
+ store_value_internal(item, MYSQL_TYPE_TIME);
+ }
+ int cmp_not_null(const Value *val);
int cmp(Item *arg);
- int compare(cmp_item *ci);
cmp_item *make_same();
};
@@ -1487,6 +1673,12 @@ public:
value= item->val_real();
m_null_value= item->null_value;
}
+ int cmp_not_null(const Value *val)
+ {
+ DBUG_ASSERT(!val->is_null());
+ DBUG_ASSERT(val->is_double());
+ return value != val->value.m_double;
+ }
int cmp(Item *arg)
{
const bool rc= value != arg->val_real();
@@ -1508,6 +1700,7 @@ public:
cmp_item_decimal() {} /* Remove gcc warning */
void store_value(Item *item);
int cmp(Item *arg);
+ int cmp_not_null(const Value *val);
int compare(cmp_item *c);
cmp_item *make_same();
};
@@ -1530,6 +1723,11 @@ public:
value_res= item->val_str(&value);
m_null_value= item->null_value;
}
+ int cmp_not_null(const Value *val)
+ {
+ DBUG_ASSERT(false);
+ return TRUE;
+ }
int cmp(Item *item)
{
// Should never be called
@@ -1548,69 +1746,504 @@ public:
};
+/**
+ A helper class to handle situations when some item "pred" (the predicant)
+ is consequently compared to a list of other items value0..valueN (the values).
+ Currently used to handle:
+ - <in predicate>
+ pred IN (value0, value1, value2)
+ - <simple case>
+ CASE pred WHEN value0 .. WHEN value1 .. WHEN value2 .. END
+
+ Every pair {pred,valueN} can be compared by its own Type_handler.
+ Some pairs can use the same Type_handler.
+ In cases when all pairs use exactly the same Type_handler,
+ we say "all types are compatible".
+
+ For example, for an expression
+ 1 IN (1, 1e0, 1.0, 2)
+ - pred is 1
+ - value0 is 1
+ - value1 is 1e0
+ - value2 is 1.1
+ - value3 is 2
+
+ Pairs (pred,valueN) are compared as follows:
+ N expr1 Type
+ - ----- ----
+ 0 1 INT
+ 1 1e0 DOUBLE
+ 2 1.0 DECIMAL
+ 3 2 INT
+
+ Types are not compatible in this example.
+
+ During add_value() calls, each pair {pred,valueN} is analysed:
+ - If valueN is an explicit NULL, it can be ignored in the caller asks to do so
+ - If valueN is not an explicit NULL (or if the caller didn't ask to skip
+ NULLs), then the value add an element in the array m_comparators[].
+
+ Every element m_comparators[] stores the following information:
+ 1. m_arg_index - the position of the value expression in the original
+ argument array, e.g. in Item_func_in::args[] or Item_func_case::args[].
+
+ 2. m_handler - the pointer to the data type handler that the owner
+ will use to compare the pair {args[m_predicate_index],args[m_arg_index]}.
+
+ 3. m_handler_index - the index of an m_comparators[] element corresponding
+ to the leftmost pair that uses exactly the same Type_handler for
+ comparison. m_handler_index helps to maintain unique data type handlers.
+ - m_comparators[i].m_handler_index==i means that this is the
+ leftmost pair that uses the Type_handler m_handler for comparision.
+ - If m_comparators[i].m_handlex_index!=i, it means that some earlier
+ element m_comparators[j<i] is already using this Type_handler
+ pointed by m_handler.
+
+ 4. m_cmp_item - the pointer to a cmp_item instance to handle comparison
+ for this pair. Only unique type handlers have m_cmp_item!=NULL.
+ Non-unique type handlers share the same cmp_item instance.
+ For all m_comparators[] elements the following assersion it true:
+ (m_handler_index==i) == (m_cmp_item!=NULL)
+*/
+class Predicant_to_list_comparator
+{
+ // Allocate memory on thd memory root for "nvalues" values.
+ bool alloc_comparators(THD *thd, uint nvalues);
+
+ /**
+ Look up m_comparators[] for a comparator using the given data type handler.
+ @param [OUT] idx - the index of the found comparator is returned here
+ @param [IN] handler - the data type handler to find
+ @param [IN] count - search in the range [0,count) only
+ @retval true - this type handler was not found
+ (*idx is not defined in this case).
+ @retval false - this type handler was found (the position of the
+ found handler is returned in idx).
+ */
+ bool find_handler(uint *idx, const Type_handler *handler, uint count)
+ {
+ DBUG_ASSERT(count < m_comparator_count);
+ for (uint i= 0 ; i < count; i++)
+ {
+ if (m_comparators[i].m_handler == handler)
+ {
+ *idx= i;
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ Populate m_comparators[i].m_handler_index for all elements in
+ m_comparators using the information in m_comparators[i].m_handlers,
+ which was previously populated by a add_predicant() call and a number
+ of add_value() calls.
+ @param [OUT] compatible - If all comparator types are compatible,
+ their data type handler is returned here.
+ @param [OUT] unuque_cnt - The number of unique data type handlers found.
+ If the value returned in *unique_cnt is 0,
+ it means all values were explicit NULLs:
+ expr0 IN (NULL,NULL,..,NULL)
+ @param [OUT] found_type - The bit mask for all found cmp_type()'s.
+ */
+ void detect_unique_handlers(Type_handler_hybrid_field_type *compatible,
+ uint *unique_cnt, uint *found_types);
+ /**
+ Creates a cmp_item instances for all unique handlers and stores
+ them into m_comparators[i].m_cmp_item, using the information previously
+ populated by add_predicant(), add_value(), detect_unque_handlers().
+ */
+
+ /*
+ Compare the predicant to the value pointed by m_comparators[i].
+ @param args - the same argument array which was previously used
+ with add_predicant() and add_value().
+ @param i - which pair to check.
+ @retval true - the predicant is not equal to the value.
+ @retval false - the predicant is equal to the value.
+ @retval UNKNOWN - the result is uncertain yet because the predicant
+ and/or the value returned NULL,
+ more pairs {pred,valueN} should be checked.
+ */
+ int cmp_arg(Item_args *args, uint i)
+ {
+ Predicant_to_value_comparator *cmp=
+ &m_comparators[m_comparators[i].m_handler_index];
+ cmp_item *in_item= cmp->m_cmp_item;
+ DBUG_ASSERT(in_item);
+ /*
+ If this is the leftmost pair that uses the data type handler
+ pointed by m_comparators[i].m_handler, then we need to cache
+ the predicant value representation used by this handler.
+ */
+ if (m_comparators[i].m_handler_index == i)
+ in_item->store_value(args->arguments()[m_predicant_index]);
+ /*
+ If the predicant item has null_value==true then:
+ - In case of scalar expression we can returns UNKNOWN immediately.
+ No needs to check the result of the value item.
+ - In case of ROW, null_value==true means that *some* row elements
+ returned NULL, but *some* elements can still be non-NULL!
+ We need to get the result of the value item and test
+ if non-NULL elements in the predicant and the value produce
+ TRUE (not equal), or UNKNOWN.
+ */
+ if (args->arguments()[m_predicant_index]->null_value &&
+ m_comparators[i].m_handler != &type_handler_row)
+ return UNKNOWN;
+ return in_item->cmp(args->arguments()[m_comparators[i].m_arg_index]);
+ }
+ int cmp_args_nulls_equal(Item_args *args, uint i)
+ {
+ Predicant_to_value_comparator *cmp=
+ &m_comparators[m_comparators[i].m_handler_index];
+ cmp_item *in_item= cmp->m_cmp_item;
+ DBUG_ASSERT(in_item);
+ Item *predicant= args->arguments()[m_predicant_index];
+ Item *arg= args->arguments()[m_comparators[i].m_arg_index];
+ ValueBuffer<MAX_FIELD_WIDTH> val;
+ if (m_comparators[i].m_handler_index == i)
+ in_item->store_value(predicant);
+ m_comparators[i].m_handler->Item_save_in_value(arg, &val);
+ if (predicant->null_value && val.is_null())
+ return FALSE; // Two nulls are equal
+ if (predicant->null_value || val.is_null())
+ return UNKNOWN;
+ return in_item->cmp_not_null(&val);
+ }
+ /**
+ Predicant_to_value_comparator - a comparator for one pair (pred,valueN).
+ See comments above.
+ */
+ struct Predicant_to_value_comparator
+ {
+ const Type_handler *m_handler;
+ cmp_item *m_cmp_item;
+ uint m_arg_index;
+ uint m_handler_index;
+ void cleanup()
+ {
+ if (m_cmp_item)
+ delete m_cmp_item;
+ memset(this, 0, sizeof(*this));
+ }
+ };
+
+ Predicant_to_value_comparator *m_comparators; // The comparator array
+ uint m_comparator_count;// The number of elements in m_comparators[]
+ uint m_predicant_index; // The position of the predicant in its argument list,
+ // e.g. for Item_func_in m_predicant_index is 0,
+ // as predicant is stored in Item_func_in::args[0].
+ // For Item_func_case m_predicant_index is
+ // set to Item_func_case::first_expr_num.
+
+public:
+ Predicant_to_list_comparator(THD *thd, uint nvalues)
+ :m_comparator_count(0),
+ m_predicant_index(0)
+ {
+ alloc_comparators(thd, nvalues);
+ }
+
+ uint comparator_count() const { return m_comparator_count; }
+ const Type_handler *get_comparator_type_handler(uint i) const
+ {
+ DBUG_ASSERT(i < m_comparator_count);
+ return m_comparators[i].m_handler;
+ }
+ uint get_comparator_arg_index(uint i) const
+ {
+ DBUG_ASSERT(i < m_comparator_count);
+ return m_comparators[i].m_arg_index;
+ }
+ cmp_item *get_comparator_cmp_item(uint i) const
+ {
+ DBUG_ASSERT(i < m_comparator_count);
+ return m_comparators[i].m_cmp_item;
+ }
+
+#ifndef DBUG_OFF
+ void debug_print(THD *thd)
+ {
+ for (uint i= 0; i < m_comparator_count; i++)
+ {
+ DBUG_EXECUTE_IF("Predicant_to_list_comparator",
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_UNKNOWN_ERROR, "DBUG: [%d] arg=%d handler=%d (%s)", i,
+ m_comparators[i].m_arg_index,
+ m_comparators[i].m_handler_index,
+ m_comparators[m_comparators[i].m_handler_index].
+ m_handler->name().ptr()););
+ }
+ }
+#endif
+
+ void add_predicant(Item_args *args, uint predicant_index)
+ {
+ DBUG_ASSERT(m_comparator_count == 0); // Set in constructor
+ DBUG_ASSERT(m_predicant_index == 0); // Set in constructor
+ DBUG_ASSERT(predicant_index < args->argument_count());
+ m_predicant_index= predicant_index;
+ }
+ /**
+ Add a new element into m_comparators[], using a {pred,valueN} pair.
+
+ @param funcname - the name of the operation, for error reporting
+ @param args - the owner function's argument list
+ @param value_index - the value position in args
+ @retval true - could not add an element because of non-comparable
+ arguments (e.g. ROWs with size)
+ @retval false - a new element was successfully added.
+ */
+ bool add_value(const char *funcname, Item_args *args, uint value_index);
+
+ /**
+ Add a new element into m_comparators[], ignoring explicit NULL values.
+ If the value appeared to be an explicit NULL, nulls_found[0] is set to true.
+ */
+ bool add_value_skip_null(const char *funcname,
+ Item_args *args, uint value_index,
+ bool *nulls_found);
+
+ /**
+ Signal "this" that there will be no new add_value*() calls,
+ so it can prepare its internal structures for comparison.
+
+ @param [OUT] compatible - If all comparators are compatible,
+ their data type handler is returned here.
+ @param [OUT] unuque_cnt - The number of unique data type handlers found.
+ If the value returned in *unique_cnt is 0,
+ it means all values were explicit NULLs:
+ expr0 IN (NULL,NULL,..,NULL)
+ @param [OUT] found_type - The bit mask for all found cmp_type()'s.
+ */
+ void all_values_added(Type_handler_hybrid_field_type *compatible,
+ uint *unique_cnt, uint *found_types)
+ {
+ detect_unique_handlers(compatible, unique_cnt, found_types);
+ }
+ /**
+ Creates cmp_item instances for all unique handlers and stores
+ them into m_comparators[].m_cmp_item, using the information previously
+ populated by add_predicant(), add_value() and detect_unque_handlers().
+ */
+ bool make_unique_cmp_items(THD *thd, CHARSET_INFO *cs);
+ void cleanup()
+ {
+ DBUG_ASSERT(m_comparators);
+ for (uint i= 0; i < m_comparator_count; i++)
+ m_comparators[i].cleanup();
+ memset(m_comparators, 0, sizeof(m_comparators[0]) * m_comparator_count);
+ m_comparator_count= 0;
+ m_predicant_index= 0;
+ }
+ bool init_clone(THD *thd, uint nvalues)
+ {
+ m_comparator_count= 0;
+ m_predicant_index= 0;
+ return alloc_comparators(thd, nvalues);
+ }
+ /**
+ @param [IN] args - The argument list that was previously used with
+ add_predicant() and add_value().
+ @param [OUT] idx - In case if a value that is equal to the predicant
+ was found, the index of the matching value is returned
+ here. Otherwise, *idx is not changed.
+ @param [IN/OUT] found_unknown_values - how to handle UNKNOWN results.
+ If found_unknown_values is NULL (e.g. Item_func_case),
+ cmp() returns immediately when the first UNKNOWN
+ result is found.
+ If found_unknown_values is non-NULL (Item_func_in),
+ cmp() does not return when an UNKNOWN result is found,
+ sets *found_unknown_values to true, and continues
+ to compare the remaining pairs to find FALSE
+ (i.e. the value that is equal to the predicant).
+
+ @retval false - Found a value that is equal to the predicant
+ @retval true - Didn't find an equal value
+ */
+ bool cmp(Item_args *args, uint *idx, bool *found_unknown_values)
+ {
+ for (uint i= 0 ; i < m_comparator_count ; i++)
+ {
+ DBUG_ASSERT(m_comparators[i].m_handler != NULL);
+ const int rc= cmp_arg(args, i);
+ if (rc == FALSE)
+ {
+ *idx= m_comparators[i].m_arg_index;
+ return false; // Found a matching value
+ }
+ if (rc == UNKNOWN)
+ {
+ if (!found_unknown_values)
+ return true;
+ *found_unknown_values= true;
+ }
+ }
+ return true; // Not found
+ }
+ /*
+ Same as above, but treats two NULLs as equal, e.g. as in DECODE_ORACLE().
+ */
+ bool cmp_nulls_equal(Item_args *args, uint *idx)
+ {
+ for (uint i= 0 ; i < m_comparator_count ; i++)
+ {
+ DBUG_ASSERT(m_comparators[i].m_handler != NULL);
+ if (cmp_args_nulls_equal(args, i) == FALSE)
+ {
+ *idx= m_comparators[i].m_arg_index;
+ return false; // Found a matching value
+ }
+ }
+ return true; // Not found
+ }
+};
+
+
/*
The class Item_func_case is the CASE ... WHEN ... THEN ... END function
implementation.
-
- When there is no expression between CASE and the first WHEN
- (the CASE expression) then this function simple checks all WHEN expressions
- one after another. When some WHEN expression evaluated to TRUE then the
- value of the corresponding THEN expression is returned.
-
- When the CASE expression is specified then it is compared to each WHEN
- expression individually. When an equal WHEN expression is found
- corresponding THEN expression is returned.
- In order to do correct comparisons several comparators are used. One for
- each result type. Different result types that are used in particular
- CASE ... END expression are collected in the fix_length_and_dec() member
- function and only comparators for there result types are used.
*/
-class Item_func_case :public Item_func_hybrid_field_type
+class Item_func_case :public Item_func_case_expression
{
- int first_expr_num, else_expr_num;
- enum Item_result left_cmp_type;
+protected:
String tmp_value;
- uint nwhens;
- Item_result cmp_type;
DTCollation cmp_collation;
- cmp_item *cmp_items[6]; /* For all result types */
- cmp_item *case_item;
- uint m_found_types;
-public:
- Item_func_case(THD *thd, List<Item> &list, Item *first_expr_arg,
- Item *else_expr_arg);
+ bool aggregate_then_and_else_arguments(THD *thd, uint count);
+ virtual Item **else_expr_addr() const= 0;
+ virtual Item *find_item()= 0;
+ void print_when_then_arguments(String *str, enum_query_type query_type,
+ Item **items, uint count);
+ void print_else_argument(String *str, enum_query_type query_type, Item *item);
+ void reorder_args(uint start);
+public:
+ Item_func_case(THD *thd, List<Item> &list)
+ :Item_func_case_expression(thd, list)
+ { }
double real_op();
longlong int_op();
String *str_op(String *);
my_decimal *decimal_op(my_decimal *);
- bool date_op(MYSQL_TIME *ltime, uint fuzzydate);
+ bool date_op(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool time_op(MYSQL_TIME *ltime);
bool fix_fields(THD *thd, Item **ref);
- bool fix_length_and_dec();
- uint decimal_precision() const;
table_map not_null_tables() const { return 0; }
const char *func_name() const { return "case"; }
enum precedence precedence() const { return BETWEEN_PRECEDENCE; }
- virtual void print(String *str, enum_query_type query_type);
- Item *find_item(String *str);
CHARSET_INFO *compare_collation() const { return cmp_collation.collation; }
- void cleanup();
- Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond);
bool need_parentheses_in_default() { return true; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_case>(thd, mem_root, this); }
- Item *build_clone(THD *thd, MEM_ROOT *mem_root)
+};
+
+
+/*
+ CASE WHEN cond THEN res [WHEN cond THEN res...] [ELSE res] END
+
+ Searched CASE checks all WHEN expressions one after another.
+ When some WHEN expression evaluated to TRUE then the
+ value of the corresponding THEN expression is returned.
+*/
+class Item_func_case_searched: public Item_func_case
+{
+ uint when_count() const { return arg_count / 2; }
+ bool with_else() const { return arg_count % 2; }
+ Item **else_expr_addr() const { return with_else() ? &args[arg_count - 1] : 0; }
+public:
+ Item_func_case_searched(THD *thd, List<Item> &list)
+ :Item_func_case(thd, list)
{
- Item_func_case *clone= (Item_func_case *) Item_func::build_clone(thd, mem_root);
- if (clone)
- {
- clone->case_item= 0;
- bzero(&clone->cmp_items, sizeof(cmp_items));
- }
+ DBUG_ASSERT(arg_count >= 2);
+ reorder_args(0);
+ }
+ enum Functype functype() const { return CASE_SEARCHED_FUNC; }
+ void print(String *str, enum_query_type query_type);
+ bool fix_length_and_dec();
+ Item *propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond)
+ {
+ // None of the arguments are in a comparison context
+ Item_args::propagate_equal_fields(thd, Context_identity(), cond);
+ return this;
+ }
+ Item *find_item();
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_case_searched>(thd, this); }
+};
+
+
+/*
+ CASE pred WHEN value THEN res [WHEN value THEN res...] [ELSE res] END
+
+ When the predicant expression is specified then it is compared to each WHEN
+ expression individually. When an equal WHEN expression is found
+ the corresponding THEN expression is returned.
+ In order to do correct comparisons several comparators are used. One for
+ each result type. Different result types that are used in particular
+ CASE ... END expression are collected in the fix_length_and_dec() member
+ function and only comparators for there result types are used.
+*/
+class Item_func_case_simple: public Item_func_case,
+ public Predicant_to_list_comparator
+{
+protected:
+ uint m_found_types;
+ uint when_count() const { return (arg_count - 1) / 2; }
+ bool with_else() const { return arg_count % 2 == 0; }
+ Item **else_expr_addr() const { return with_else() ? &args[arg_count - 1] : 0; }
+ bool aggregate_switch_and_when_arguments(THD *thd, bool nulls_equal);
+ bool prepare_predicant_and_values(THD *thd, uint *found_types,
+ bool nulls_equal);
+public:
+ Item_func_case_simple(THD *thd, List<Item> &list)
+ :Item_func_case(thd, list),
+ Predicant_to_list_comparator(thd, arg_count),
+ m_found_types(0)
+ {
+ DBUG_ASSERT(arg_count >= 3);
+ reorder_args(1);
+ }
+ void cleanup()
+ {
+ DBUG_ENTER("Item_func_case_simple::cleanup");
+ Item_func::cleanup();
+ Predicant_to_list_comparator::cleanup();
+ DBUG_VOID_RETURN;
+ }
+ enum Functype functype() const { return CASE_SIMPLE_FUNC; }
+ void print(String *str, enum_query_type query_type);
+ bool fix_length_and_dec();
+ Item *propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond);
+ Item *find_item();
+ Item *build_clone(THD *thd)
+ {
+ Item_func_case_simple *clone= (Item_func_case_simple *)
+ Item_func_case::build_clone(thd);
+ uint ncases= when_count();
+ if (clone && clone->Predicant_to_list_comparator::init_clone(thd, ncases))
+ return NULL;
return clone;
}
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_case_simple>(thd, this); }
+};
+
+
+class Item_func_decode_oracle: public Item_func_case_simple
+{
+public:
+ Item_func_decode_oracle(THD *thd, List<Item> &list)
+ :Item_func_case_simple(thd, list)
+ { }
+ const char *func_name() const { return "decode_oracle"; }
+ void print(String *str, enum_query_type query_type);
+ bool fix_length_and_dec();
+ Item *find_item();
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_decode_oracle>(thd, this); }
};
+
/*
The Item_func_in class implements
in_expr IN (<in value list>)
@@ -1619,14 +2252,27 @@ public:
The current implementation distinguishes 2 cases:
1) all items in <in value list> are constants and have the same
- result type. This case is handled by in_vector class.
+ result type. This case is handled by in_vector class,
+ implementing fast bisection search.
2) otherwise Item_func_in employs several cmp_item objects to perform
comparisons of in_expr and an item from <in value list>. One cmp_item
object for each result type. Different result types are collected in the
fix_length_and_dec() member function by means of collect_cmp_types()
function.
+
+ Bisection is possible when:
+ 1. All types are similar
+ 2. All expressions in <in value list> are const
+ In the presence of NULLs, the correct result of evaluating this item
+ must be UNKNOWN or FALSE. To achieve that:
+ - If type is scalar, we can use bisection and the "have_null" boolean.
+ - If type is ROW, we will need to scan all of <in value list> when
+ searching, so bisection is impossible. Unless:
+ 3. UNKNOWN and FALSE are equivalent results
+ 4. Neither left expression nor <in value list> contain any NULL value
*/
-class Item_func_in :public Item_func_opt_neg
+class Item_func_in :public Item_func_opt_neg,
+ public Predicant_to_list_comparator
{
/**
Usable if <in value list> is made only of constants. Returns true if one
@@ -1634,9 +2280,24 @@ class Item_func_in :public Item_func_opt_neg
IN ( (-5, (12,NULL)), ... ).
*/
bool list_contains_null();
+ bool all_items_are_consts(Item **items, uint nitems) const
+ {
+ for (uint i= 0; i < nitems; i++)
+ {
+ if (!items[i]->const_item() || items[i]->is_expensive())
+ return false;
+ }
+ return true;
+ }
+ bool prepare_predicant_and_values(THD *thd, uint *found_types);
+ bool check_arguments() const
+ {
+ return check_argument_types_like_args0();
+ }
protected:
SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param,
Field *field, Item *value);
+ bool transform_into_subq;
public:
/// An array of values, created when the bisection lookup method is used
in_vector *array;
@@ -1652,32 +2313,57 @@ public:
and can be used safely as comparisons for key conditions
*/
bool arg_types_compatible;
- Item_result left_cmp_type;
- cmp_item *cmp_items[6]; /* One cmp_item for each result type */
+
+ TABLE_LIST *emb_on_expr_nest;
Item_func_in(THD *thd, List<Item> &list):
- Item_func_opt_neg(thd, list), array(0), have_null(0),
- arg_types_compatible(FALSE)
- {
- bzero(&cmp_items, sizeof(cmp_items));
- allowed_arg_cols= 0; // Fetch this value from first argument
- }
+ Item_func_opt_neg(thd, list),
+ Predicant_to_list_comparator(thd, arg_count - 1),
+ transform_into_subq(false),
+ array(0), have_null(0),
+ arg_types_compatible(FALSE), emb_on_expr_nest(0)
+ { }
longlong val_int();
bool fix_fields(THD *, Item **);
- bool create_array(THD *thd);
bool fix_length_and_dec();
+ bool compatible_types_scalar_bisection_possible()
+ {
+ DBUG_ASSERT(m_comparator.cmp_type() != ROW_RESULT);
+ return all_items_are_consts(args + 1, arg_count - 1); // Bisection #2
+ }
+ bool compatible_types_row_bisection_possible()
+ {
+ DBUG_ASSERT(m_comparator.cmp_type() == ROW_RESULT);
+ return all_items_are_consts(args + 1, arg_count - 1) && // Bisection #2
+ ((is_top_level_item() && !negated) || // Bisection #3
+ (!list_contains_null() && !args[0]->maybe_null)); // Bisection #4
+ }
+ bool agg_all_arg_charsets_for_comparison()
+ {
+ return agg_arg_charsets_for_comparison(cmp_collation, args, arg_count);
+ }
+ void fix_in_vector();
+ bool value_list_convert_const_to_int(THD *thd);
+ bool fix_for_scalar_comparison_using_bisection(THD *thd)
+ {
+ array= m_comparator.type_handler()->make_in_vector(thd, this, arg_count - 1);
+ if (!array) // OOM
+ return true;
+ fix_in_vector();
+ return false;
+ }
+ bool fix_for_scalar_comparison_using_cmp_items(THD *thd, uint found_types);
+
+ bool fix_for_row_comparison_using_cmp_items(THD *thd);
+ bool fix_for_row_comparison_using_bisection(THD *thd);
+
void cleanup()
{
- uint i;
DBUG_ENTER("Item_func_in::cleanup");
Item_int_func::cleanup();
delete array;
array= 0;
- for (i= 0; i <= (uint)TIME_RESULT; i++)
- {
- delete cmp_items[i];
- cmp_items[i]= 0;
- }
+ Predicant_to_list_comparator::cleanup();
DBUG_VOID_RETURN;
}
void add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level,
@@ -1691,13 +2377,20 @@ public:
will be replaced to a zero-filled Item_string.
Such a change would require rebuilding of cmp_items.
*/
- Context cmpctx(ANY_SUBST, m_compare_type,
- Item_func_in::compare_collation());
- for (uint i= 0; i < arg_count; i++)
+ if (arg_types_compatible)
+ {
+ Context cmpctx(ANY_SUBST, m_comparator.type_handler(),
+ Item_func_in::compare_collation());
+ args[0]->propagate_equal_fields_and_change_item_tree(thd, cmpctx,
+ cond, &args[0]);
+ }
+ for (uint i= 0; i < comparator_count(); i++)
{
- if (arg_types_compatible || i > 0)
- args[i]->propagate_equal_fields_and_change_item_tree(thd, cmpctx,
- cond, &args[i]);
+ Context cmpctx(ANY_SUBST, get_comparator_type_handler(i),
+ Item_func_in::compare_collation());
+ uint idx= get_comparator_arg_index(i);
+ args[idx]->propagate_equal_fields_and_change_item_tree(thd, cmpctx,
+ cond, &args[idx]);
}
return this;
}
@@ -1708,9 +2401,24 @@ public:
bool eval_not_null_tables(void *opt_arg);
void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge);
bool count_sargable_conds(void *arg);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_in>(thd, mem_root, this); }
- Item *build_clone(THD *thd, MEM_ROOT *mem_root);
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_in>(thd, this); }
+ Item *build_clone(THD *thd)
+ {
+ Item_func_in *clone= (Item_func_in *) Item_func::build_clone(thd);
+ if (clone)
+ {
+ clone->array= 0;
+ if (clone->Predicant_to_list_comparator::init_clone(thd, arg_count - 1))
+ return NULL;
+ }
+ return clone;
+ }
+ void mark_as_condition_AND_part(TABLE_LIST *embedding);
+ bool to_be_transformed_into_in_subq(THD *thd);
+ bool create_value_list_for_tvc(THD *thd, List< List<Item> > *values);
+ Item *in_predicate_to_in_subs_transformer(THD *thd, uchar *arg);
+ uint32 max_length_of_left_expr();
};
class cmp_item_row :public cmp_item
@@ -1721,12 +2429,18 @@ public:
cmp_item_row(): comparators(0), n(0) {}
~cmp_item_row();
void store_value(Item *item);
- inline void alloc_comparators();
+ bool alloc_comparators(THD *thd, uint n);
+ bool prepare_comparators(THD *, Item **args, uint arg_count);
int cmp(Item *arg);
+ int cmp_not_null(const Value *val)
+ {
+ DBUG_ASSERT(false);
+ return TRUE;
+ }
int compare(cmp_item *arg);
cmp_item *make_same();
void store_value_by_template(THD *thd, cmp_item *tmpl, Item *);
- friend bool Item_func_in::fix_length_and_dec();
+ friend class Item_func_in;
cmp_item *get_comparator(uint i) { return comparators[i]; }
};
@@ -1739,9 +2453,8 @@ public:
~in_row();
void set(uint pos,Item *item);
uchar *get_value(Item *item);
- friend bool Item_func_in::create_array(THD *thd);
- friend bool Item_func_in::fix_length_and_dec();
- Item_result result_type() { return ROW_RESULT; }
+ friend class Item_func_in;
+ const Type_handler *type_handler() const { return &type_handler_row; }
cmp_item *get_cmp_item() { return &tmp; }
};
@@ -1825,8 +2538,8 @@ public:
bool top_level);
table_map not_null_tables() const { return 0; }
Item *neg_transformer(THD *thd);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_isnull>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_isnull>(thd, this); }
};
/* Functions used by HAVING for rewriting IN subquery */
@@ -1873,8 +2586,8 @@ public:
Item *neg_transformer(THD *thd);
void print(String *str, enum_query_type query_type);
void top_level_item() { abort_on_null=1; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_isnotnull>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_isnotnull>(thd, this); }
};
@@ -1993,7 +2706,7 @@ public:
if ((flags & MY_CS_NOPAD) && !(flags & MY_CS_NON1TO1))
Item_args::propagate_equal_fields(thd,
Context(ANY_SUBST,
- STRING_RESULT,
+ &type_handler_long_blob,
compare_collation()),
cond);
return this;
@@ -2016,8 +2729,8 @@ public:
bool find_selective_predicates_list_processor(void *arg);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_like>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_like>(thd, this); }
};
@@ -2075,7 +2788,7 @@ public:
{
return !m_is_const && compile(item, false);
}
- bool exec(const char *str, int length, int offset);
+ bool exec(const char *str, size_t length, size_t offset);
bool exec(String *str, int offset, uint n_result_offsets_to_convert);
bool exec(Item *item, int offset, uint n_result_offsets_to_convert);
bool match() const { return m_pcre_exec_rc < 0 ? 0 : 1; }
@@ -2128,7 +2841,7 @@ public:
bool fix_length_and_dec();
const char *func_name() const { return "regexp"; }
enum precedence precedence() const { return CMP_PRECEDENCE; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; }
+ Item *get_copy(THD *) { return 0; }
void print(String *str, enum_query_type query_type)
{
print_op(str, query_type);
@@ -2138,12 +2851,24 @@ public:
};
-class Item_func_regexp_instr :public Item_int_func
+/*
+ In the corner case REGEXP_INSTR could return (2^32 + 1),
+ which would not fit into Item_long_func range.
+ But string lengths are limited with max_allowed_packet,
+ which cannot be bigger than 1024*1024*1024.
+*/
+class Item_func_regexp_instr :public Item_long_func
{
+ bool check_arguments() const
+ {
+ return args[0]->check_type_can_return_str(func_name()) ||
+ args[1]->check_type_can_return_text(func_name());
+ }
Regexp_processor_pcre re;
DTCollation cmp_collation;
public:
- Item_func_regexp_instr(THD *thd, Item *a, Item *b): Item_int_func(thd, a, b)
+ Item_func_regexp_instr(THD *thd, Item *a, Item *b)
+ :Item_long_func(thd, a, b)
{}
void cleanup()
{
@@ -2156,7 +2881,7 @@ public:
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, MEM_ROOT *mem_root) { return 0; }
+ Item *get_copy(THD *thd) { return 0; }
};
@@ -2229,12 +2954,11 @@ public:
Item *transform(THD *thd, Item_transformer transformer, uchar *arg);
void traverse_cond(Cond_traverser, void *arg, traverse_order order);
void neg_arguments(THD *thd);
- enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; }
Item* propagate_equal_fields(THD *, const Context &, COND_EQUAL *);
Item *compile(THD *thd, Item_analyzer analyzer, uchar **arg_p,
Item_transformer transformer, uchar *arg_t);
bool eval_not_null_tables(void *opt_arg);
- Item *build_clone(THD *thd, MEM_ROOT *mem_root);
+ Item *build_clone(THD *thd);
bool excl_dep_on_table(table_map tab_map);
bool excl_dep_on_grouping_fields(st_select_lex *sel);
};
@@ -2364,14 +3088,15 @@ class Item_equal: public Item_bool_func
bool link_equal_fields;
- Item_result m_compare_type;
+ const Type_handler *m_compare_handler;
CHARSET_INFO *m_compare_collation;
String cmp_value1, cmp_value2;
public:
COND_EQUAL *upper_levels; /* multiple equalities of upper and levels */
- Item_equal(THD *thd, Item *f1, Item *f2, bool with_const_item);
+ Item_equal(THD *thd, const Type_handler *handler,
+ Item *f1, Item *f2, bool with_const_item);
Item_equal(THD *thd, Item_equal *item_equal);
/* Currently the const item is always the first in the list of equal items */
inline Item* get_const() { return with_const ? equal_items.head() : NULL; }
@@ -2409,12 +3134,12 @@ public:
bool walk(Item_processor processor, bool walk_subquery, void *arg);
Item *transform(THD *thd, Item_transformer transformer, uchar *arg);
virtual void print(String *str, enum_query_type query_type);
- Item_result compare_type() const { return m_compare_type; }
+ const Type_handler *compare_type_handler() const { return m_compare_handler; }
CHARSET_INFO *compare_collation() const { return m_compare_collation; }
void set_context_field(Item_field *ctx_field) { context_field= ctx_field; }
void set_link_equal_fields(bool flag) { link_equal_fields= flag; }
- Item* get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; }
+ Item* get_copy(THD *thd) { return 0; }
/*
This does not comply with the specification of the virtual method,
but Item_equal items are processed distinguishly anyway
@@ -2568,8 +3293,8 @@ public:
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);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_cond_and>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_cond_and>(thd, this); }
};
inline bool is_cond_and(Item *item)
@@ -2595,8 +3320,8 @@ public:
table_map not_null_tables() const { return and_tables_cache; }
Item *copy_andor_structure(THD *thd);
Item *neg_transformer(THD *thd);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_cond_or>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_cond_or>(thd, this); }
};
class Item_func_dyncol_check :public Item_bool_func
@@ -2606,8 +3331,8 @@ public:
longlong val_int();
const char *func_name() const { return "column_check"; }
bool need_parentheses_in_default() { return false; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_dyncol_check>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_dyncol_check>(thd, this); }
};
class Item_func_dyncol_exists :public Item_bool_func
@@ -2618,10 +3343,65 @@ public:
longlong val_int();
const char *func_name() const { return "column_exists"; }
bool need_parentheses_in_default() { return false; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_dyncol_exists>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_dyncol_exists>(thd, this); }
+};
+
+
+class Item_func_cursor_bool_attr: public Item_bool_func, public Cursor_ref
+{
+public:
+ Item_func_cursor_bool_attr(THD *thd, const LEX_CSTRING *name, uint offset)
+ :Item_bool_func(thd), Cursor_ref(name, offset)
+ { }
+ bool check_vcol_func_processor(void *arg)
+ {
+ return mark_unsupported_function(func_name(), arg, VCOL_SESSION_FUNC);
+ }
+ void print(String *str, enum_query_type query_type)
+ {
+ Cursor_ref::print_func(str, func_name());
+ }
+};
+
+
+class Item_func_cursor_isopen: public Item_func_cursor_bool_attr
+{
+public:
+ Item_func_cursor_isopen(THD *thd, const LEX_CSTRING *name, uint offset)
+ :Item_func_cursor_bool_attr(thd, name, offset) { }
+ const char *func_name() const { return "%ISOPEN"; }
+ longlong val_int();
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_cursor_isopen>(thd, this); }
+};
+
+
+class Item_func_cursor_found: public Item_func_cursor_bool_attr
+{
+public:
+ Item_func_cursor_found(THD *thd, const LEX_CSTRING *name, uint offset)
+ :Item_func_cursor_bool_attr(thd, name, offset) { maybe_null= true; }
+ const char *func_name() const { return "%FOUND"; }
+ longlong val_int();
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_cursor_found>(thd, this); }
};
+
+class Item_func_cursor_notfound: public Item_func_cursor_bool_attr
+{
+public:
+ Item_func_cursor_notfound(THD *thd, const LEX_CSTRING *name, uint offset)
+ :Item_func_cursor_bool_attr(thd, name, offset) { maybe_null= true; }
+ const char *func_name() const { return "%NOTFOUND"; }
+ longlong val_int();
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_cursor_notfound>(thd, this); }
+};
+
+
+
inline bool is_cond_or(Item *item)
{
if (item->type() != Item::COND_ITEM)
diff --git a/sql/item_create.cc b/sql/item_create.cc
index 1cf5a06a3a4..eb361b8222f 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -22,7 +22,7 @@
Functions to create an item. Used by sql_yac.yy
*/
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
/*
It is necessary to include set_var.h instead of item.h because there
@@ -38,112 +38,19 @@
/*
=============================================================================
- HELPER FUNCTIONS
-=============================================================================
-*/
-
-static const char* item_name(Item *a, String *str)
-{
- if (a->name)
- return a->name;
- str->length(0);
- a->print(str, QT_ORDINARY);
- return str->c_ptr_safe();
-}
-
-
-static void wrong_precision_error(uint errcode, Item *a,
- ulonglong number, uint maximum)
-{
- char buff[1024];
- String buf(buff, sizeof(buff), system_charset_info);
-
- my_error(errcode, MYF(0), number, item_name(a, &buf), maximum);
-}
-
-
-/**
- Get precision and scale for a declaration
-
- return
- 0 ok
- 1 error
-*/
-
-bool get_length_and_scale(ulonglong length, ulonglong decimals,
- uint *out_length, uint *out_decimals,
- uint max_precision, uint max_scale,
- Item *a)
-{
- if (length > (ulonglong) max_precision)
- {
- wrong_precision_error(ER_TOO_BIG_PRECISION, a, length, max_precision);
- return 1;
- }
- if (decimals > (ulonglong) max_scale)
- {
- wrong_precision_error(ER_TOO_BIG_SCALE, a, decimals, max_scale);
- return 1;
- }
-
- *out_decimals= (uint) decimals;
- my_decimal_trim(&length, out_decimals);
- *out_length= (uint) length;
-
-
- if (*out_length < *out_decimals)
- {
- my_error(ER_M_BIGGER_THAN_D, MYF(0), "");
- return 1;
- }
- return 0;
-}
-
-/*
-=============================================================================
LOCAL DECLARATIONS
=============================================================================
*/
/**
- 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>
- which are syntactically correct (the syntax can refer to a UDF),
- but semantically invalid for native functions.
-*/
-
-class Create_native_func : public Create_func
-{
-public:
- virtual Item *create_func(THD *thd, LEX_STRING name, List<Item> *item_list);
-
- /**
- Builder method, with no arguments.
- @param thd The current thread
- @param name The native function name
- @param item_list The function parameters, none of which are named
- @return An item representing the function call
- */
- virtual Item *create_native(THD *thd, LEX_STRING name,
- List<Item> *item_list) = 0;
-
-protected:
- /** Constructor. */
- Create_native_func() {}
- /** Destructor. */
- virtual ~Create_native_func() {}
-};
-
-
-/**
Adapter for functions that takes exactly zero arguments.
*/
class Create_func_arg0 : public Create_func
{
public:
- virtual Item *create_func(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_func(THD *thd, LEX_CSTRING *name,
+ List<Item> *item_list);
/**
Builder method, with no arguments.
@@ -167,7 +74,7 @@ protected:
class Create_func_arg1 : public Create_func
{
public:
- virtual Item *create_func(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
/**
Builder method, with one argument.
@@ -192,7 +99,7 @@ protected:
class Create_func_arg2 : public Create_func
{
public:
- virtual Item *create_func(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
/**
Builder method, with two arguments.
@@ -218,7 +125,7 @@ protected:
class Create_func_arg3 : public Create_func
{
public:
- virtual Item *create_func(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
/**
Builder method, with three arguments.
@@ -245,7 +152,7 @@ protected:
class Create_sp_func : public Create_qfunc
{
public:
- virtual Item *create_with_db(THD *thd, LEX_STRING db, LEX_STRING name,
+ virtual Item *create_with_db(THD *thd, LEX_CSTRING *db, LEX_CSTRING *name,
bool use_explicit_name, List<Item> *item_list);
static Create_sp_func s_singleton;
@@ -268,7 +175,7 @@ protected:
class Create_func_no_geom : public Create_func
{
public:
- virtual Item *create_func(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
/** Singleton. */
static Create_func_no_geom s_singleton;
@@ -414,7 +321,7 @@ protected:
class Create_func_atan : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_atan s_singleton;
@@ -516,6 +423,19 @@ protected:
};
+class Create_func_chr : public Create_func_arg1
+{
+public:
+ virtual Item *create_1_arg(THD *thd, Item *arg1);
+
+ static Create_func_chr s_singleton;
+
+protected:
+ Create_func_chr() {}
+ virtual ~Create_func_chr() {}
+};
+
+
class Create_func_convexhull : public Create_func_arg1
{
public:
@@ -635,7 +555,7 @@ protected:
class Create_func_concat : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_concat s_singleton;
@@ -645,6 +565,19 @@ protected:
};
+class Create_func_concat_operator_oracle : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
+
+ static Create_func_concat_operator_oracle s_singleton;
+
+protected:
+ Create_func_concat_operator_oracle() {}
+ virtual ~Create_func_concat_operator_oracle() {}
+};
+
+
class Create_func_decode_histogram : public Create_func_arg2
{
public:
@@ -658,10 +591,23 @@ protected:
};
+class Create_func_decode_oracle : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
+
+ static Create_func_decode_oracle s_singleton;
+
+protected:
+ Create_func_decode_oracle() {}
+ virtual ~Create_func_decode_oracle() {}
+};
+
+
class Create_func_concat_ws : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_concat_ws s_singleton;
@@ -712,6 +658,19 @@ protected:
#endif
+class Create_func_nvl2 : public Create_func_arg3
+{
+public:
+ virtual Item *create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3);
+
+ static Create_func_nvl2 s_singleton;
+
+protected:
+ Create_func_nvl2() {}
+ virtual ~Create_func_nvl2() {}
+};
+
+
class Create_func_conv : public Create_func_arg3
{
public:
@@ -792,19 +751,6 @@ protected:
#endif
-class Create_func_date_format : public Create_func_arg2
-{
-public:
- virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
-
- static Create_func_date_format s_singleton;
-
-protected:
- Create_func_date_format() {}
- virtual ~Create_func_date_format() {}
-};
-
-
class Create_func_datediff : public Create_func_arg2
{
public:
@@ -870,19 +816,6 @@ protected:
};
-class Create_func_decode : public Create_func_arg2
-{
-public:
- virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
-
- static Create_func_decode s_singleton;
-
-protected:
- Create_func_decode() {}
- virtual ~Create_func_decode() {}
-};
-
-
class Create_func_degrees : public Create_func_arg1
{
public:
@@ -899,7 +832,7 @@ protected:
class Create_func_des_decrypt : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_des_decrypt s_singleton;
@@ -912,7 +845,7 @@ protected:
class Create_func_des_encrypt : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_des_encrypt s_singleton;
@@ -981,7 +914,7 @@ class Create_func_distance : public Create_func_arg2
class Create_func_elt : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_elt s_singleton;
@@ -1007,7 +940,7 @@ protected:
class Create_func_encrypt : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_encrypt s_singleton;
@@ -1103,7 +1036,7 @@ protected:
class Create_func_export_set : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_export_set s_singleton;
@@ -1131,7 +1064,7 @@ protected:
class Create_func_field : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_field s_singleton;
@@ -1170,7 +1103,7 @@ protected:
class Create_func_format : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_format s_singleton;
@@ -1222,7 +1155,7 @@ protected:
class Create_func_from_unixtime : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_from_unixtime s_singleton;
@@ -1236,7 +1169,7 @@ protected:
class Create_func_geometry_from_text : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_geometry_from_text s_singleton;
@@ -1251,7 +1184,7 @@ protected:
class Create_func_geometry_from_wkb : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_geometry_from_wkb s_singleton;
@@ -1266,7 +1199,7 @@ protected:
class Create_func_geometry_from_json : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_geometry_from_json s_singleton;
@@ -1279,7 +1212,7 @@ protected:
class Create_func_as_geojson : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_as_geojson s_singleton;
@@ -1366,7 +1299,7 @@ protected:
class Create_func_greatest : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_greatest s_singleton;
@@ -1792,7 +1725,7 @@ protected:
class Create_func_json_detailed: public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_json_detailed s_singleton;
@@ -1857,7 +1790,7 @@ protected:
class Create_func_json_keys: public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_json_keys s_singleton;
@@ -1870,7 +1803,7 @@ protected:
class Create_func_json_contains: public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_json_contains s_singleton;
@@ -1883,7 +1816,7 @@ protected:
class Create_func_json_contains_path : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_json_contains_path s_singleton;
@@ -1896,7 +1829,7 @@ protected:
class Create_func_json_extract : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_json_extract s_singleton;
@@ -1909,7 +1842,7 @@ protected:
class Create_func_json_search : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_json_search s_singleton;
@@ -1922,7 +1855,7 @@ protected:
class Create_func_json_array : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_json_array s_singleton;
@@ -1935,7 +1868,7 @@ protected:
class Create_func_json_array_append : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_json_array_append s_singleton;
@@ -1948,7 +1881,7 @@ protected:
class Create_func_json_array_insert : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_json_array_insert s_singleton;
@@ -1961,7 +1894,7 @@ protected:
class Create_func_json_insert : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_json_insert s_singleton;
@@ -1974,7 +1907,7 @@ protected:
class Create_func_json_set : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_json_set s_singleton;
@@ -1987,7 +1920,7 @@ protected:
class Create_func_json_replace : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_json_replace s_singleton;
@@ -2000,7 +1933,7 @@ protected:
class Create_func_json_remove : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_json_remove s_singleton;
@@ -2013,7 +1946,7 @@ protected:
class Create_func_json_object : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_json_object s_singleton;
@@ -2026,7 +1959,7 @@ protected:
class Create_func_json_length : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_json_length s_singleton;
@@ -2039,7 +1972,7 @@ protected:
class Create_func_json_merge : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_json_merge s_singleton;
@@ -2052,7 +1985,7 @@ protected:
class Create_func_json_merge_patch : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_json_merge_patch s_singleton;
@@ -2104,7 +2037,7 @@ protected:
class Create_func_last_insert_id : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_last_insert_id s_singleton;
@@ -2130,7 +2063,7 @@ protected:
class Create_func_least : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_least s_singleton;
@@ -2152,6 +2085,18 @@ protected:
virtual ~Create_func_length() {}
};
+class Create_func_octet_length : public Create_func_arg1
+{
+public:
+ virtual Item *create_1_arg(THD *thd, Item *arg1);
+
+ static Create_func_octet_length s_singleton;
+
+protected:
+ Create_func_octet_length() {}
+ virtual ~Create_func_octet_length() {}
+};
+
#ifndef DBUG_OFF
class Create_func_like_range_min : public Create_func_arg2
@@ -2210,7 +2155,7 @@ protected:
class Create_func_locate : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_locate s_singleton;
@@ -2223,7 +2168,7 @@ protected:
class Create_func_log : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_log s_singleton;
@@ -2259,16 +2204,34 @@ protected:
};
-class Create_func_lpad : public Create_func_arg3
+class Create_func_lpad : public Create_native_func
{
public:
- virtual Item *create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3);
-
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name,
+ List<Item> *item_list)
+ {
+ return thd->variables.sql_mode & MODE_ORACLE ?
+ create_native_oracle(thd, name, item_list) :
+ create_native_std(thd, name, item_list);
+ }
static Create_func_lpad s_singleton;
protected:
Create_func_lpad() {}
virtual ~Create_func_lpad() {}
+ Item *create_native_std(THD *thd, LEX_CSTRING *name, List<Item> *items);
+ Item *create_native_oracle(THD *thd, LEX_CSTRING *name, List<Item> *items);
+};
+
+
+class Create_func_lpad_oracle : public Create_func_lpad
+{
+public:
+ Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list)
+ {
+ return create_native_oracle(thd, name, item_list);
+ }
+ static Create_func_lpad_oracle s_singleton;
};
@@ -2285,6 +2248,19 @@ protected:
};
+class Create_func_ltrim_oracle : public Create_func_arg1
+{
+public:
+ virtual Item *create_1_arg(THD *thd, Item *arg1);
+
+ static Create_func_ltrim_oracle s_singleton;
+
+protected:
+ Create_func_ltrim_oracle() {}
+ virtual ~Create_func_ltrim_oracle() {}
+};
+
+
class Create_func_makedate : public Create_func_arg2
{
public:
@@ -2314,7 +2290,7 @@ protected:
class Create_func_make_set : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_make_set s_singleton;
@@ -2327,7 +2303,7 @@ protected:
class Create_func_master_pos_wait : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_master_pos_wait s_singleton;
@@ -2340,7 +2316,7 @@ protected:
class Create_func_master_gtid_wait : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_master_gtid_wait s_singleton;
@@ -2636,7 +2612,7 @@ protected:
class Create_func_rand : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_rand s_singleton;
@@ -2659,6 +2635,19 @@ protected:
};
+class Create_func_replace_oracle : public Create_func_arg3
+{
+public:
+ virtual Item *create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3);
+
+ static Create_func_replace_oracle s_singleton;
+
+protected:
+ Create_func_replace_oracle() {}
+ virtual ~Create_func_replace_oracle() {}
+};
+
+
class Create_func_reverse : public Create_func_arg1
{
public:
@@ -2675,7 +2664,7 @@ protected:
class Create_func_round : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_round s_singleton;
@@ -2685,16 +2674,34 @@ protected:
};
-class Create_func_rpad : public Create_func_arg3
+class Create_func_rpad : public Create_native_func
{
public:
- virtual Item *create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3);
-
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name,
+ List<Item> *item_list)
+ {
+ return thd->variables.sql_mode & MODE_ORACLE ?
+ create_native_oracle(thd, name, item_list) :
+ create_native_std(thd, name, item_list);
+ }
static Create_func_rpad s_singleton;
protected:
Create_func_rpad() {}
virtual ~Create_func_rpad() {}
+ Item *create_native_std(THD *thd, LEX_CSTRING *name, List<Item> *items);
+ Item *create_native_oracle(THD *thd, LEX_CSTRING *name, List<Item> *items);
+};
+
+
+class Create_func_rpad_oracle : public Create_func_rpad
+{
+public:
+ Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list)
+ {
+ return create_native_oracle(thd, name, item_list);
+ }
+ static Create_func_rpad_oracle s_singleton;
};
@@ -2711,6 +2718,19 @@ protected:
};
+class Create_func_rtrim_oracle : public Create_func_arg1
+{
+public:
+ virtual Item *create_1_arg(THD *thd, Item *arg1);
+
+ static Create_func_rtrim_oracle s_singleton;
+
+protected:
+ Create_func_rtrim_oracle() {}
+ virtual ~Create_func_rtrim_oracle() {}
+};
+
+
class Create_func_sec_to_time : public Create_func_arg1
{
public:
@@ -2897,6 +2917,20 @@ protected:
};
+class Create_func_substr_oracle : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name,
+ List<Item> *item_list);
+
+ static Create_func_substr_oracle s_singleton;
+
+protected:
+ Create_func_substr_oracle() {}
+ virtual ~Create_func_substr_oracle() {}
+};
+
+
class Create_func_subtime : public Create_func_arg2
{
public:
@@ -3070,7 +3104,7 @@ protected:
class Create_func_unix_timestamp : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_unix_timestamp s_singleton;
@@ -3232,7 +3266,7 @@ protected:
class Create_func_year_week : public Create_native_func
{
public:
- virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_year_week s_singleton;
@@ -3276,7 +3310,7 @@ Create_func_no_geom Create_func_no_geom::s_singleton;
Item*
Create_func_no_geom::create_func(THD * /* unused */,
- LEX_STRING /* unused */,
+ LEX_CSTRING /* unused */,
List<Item> * /* unused */)
{
/* FIXME: error message can't be translated. */
@@ -3288,11 +3322,11 @@ Create_func_no_geom::create_func(THD * /* unused */,
Item*
-Create_qfunc::create_func(THD *thd, LEX_STRING name, List<Item> *item_list)
+Create_qfunc::create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list)
{
- LEX_STRING db;
+ LEX_CSTRING db;
- if (! thd->db && ! thd->lex->sphead)
+ if (unlikely(! thd->db.str && ! thd->lex->sphead))
{
/*
The proper error message should be in the lines of:
@@ -3306,14 +3340,14 @@ Create_qfunc::create_func(THD *thd, LEX_STRING name, List<Item> *item_list)
the case when a default database exist, see Create_sp_func::create().
*/
my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
- "FUNCTION", name.str);
+ "FUNCTION", name->str);
return NULL;
}
- if (thd->lex->copy_db_to(&db.str, &db.length))
+ if (thd->lex->copy_db_to(&db))
return NULL;
- return create_with_db(thd, db, name, false, item_list);
+ return create_with_db(thd, &db, name, false, item_list);
}
@@ -3321,9 +3355,9 @@ Create_qfunc::create_func(THD *thd, LEX_STRING name, List<Item> *item_list)
Create_udf_func Create_udf_func::s_singleton;
Item*
-Create_udf_func::create_func(THD *thd, LEX_STRING name, List<Item> *item_list)
+Create_udf_func::create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list)
{
- udf_func *udf= find_udf(name.str, (uint)name.length);
+ udf_func *udf= find_udf(name->str, name->length);
DBUG_ASSERT(udf);
return create(thd, udf, item_list);
}
@@ -3431,15 +3465,17 @@ Create_udf_func::create(THD *thd, udf_func *udf, List<Item> *item_list)
Create_sp_func Create_sp_func::s_singleton;
Item*
-Create_sp_func::create_with_db(THD *thd, LEX_STRING db, LEX_STRING name,
+Create_sp_func::create_with_db(THD *thd, LEX_CSTRING *db, LEX_CSTRING *name,
bool use_explicit_name, List<Item> *item_list)
{
int arg_count= 0;
Item *func= NULL;
LEX *lex= thd->lex;
sp_name *qname;
+ const Sp_handler *sph= &sp_handler_function;
+ Database_qualified_name pkgname(&null_clex_str, &null_clex_str);
- if (has_named_parameters(item_list))
+ if (unlikely(has_named_parameters(item_list)))
{
/*
The syntax "db.foo(expr AS p1, expr AS p2, ...) is invalid,
@@ -3450,7 +3486,7 @@ Create_sp_func::create_with_db(THD *thd, LEX_STRING db, LEX_STRING name,
because it can refer to a User Defined Function call.
For a Stored Function however, this has no semantic.
*/
- my_error(ER_WRONG_PARAMETERS_TO_STORED_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMETERS_TO_STORED_FCT, MYF(0), name->str);
return NULL;
}
@@ -3458,14 +3494,18 @@ Create_sp_func::create_with_db(THD *thd, LEX_STRING db, LEX_STRING name,
arg_count= item_list->elements;
qname= new (thd->mem_root) sp_name(db, name, use_explicit_name);
- qname->init_qname(thd);
- sp_add_used_routine(lex, thd, qname, TYPE_ENUM_FUNCTION);
-
+ if (unlikely(sph->sp_resolve_package_routine(thd, thd->lex->sphead,
+ qname, &sph, &pkgname)))
+ return NULL;
+ sph->add_used_routine(lex, thd, qname);
+ if (pkgname.m_name.length)
+ sp_handler_package_body.add_used_routine(lex, thd, &pkgname);
if (arg_count > 0)
- func= new (thd->mem_root) Item_func_sp(thd, lex->current_context(), qname,
- *item_list);
+ func= new (thd->mem_root) Item_func_sp(thd, lex->current_context(),
+ qname, sph, *item_list);
else
- func= new (thd->mem_root) Item_func_sp(thd, lex->current_context(), qname);
+ func= new (thd->mem_root) Item_func_sp(thd, lex->current_context(),
+ qname, sph);
lex->safe_to_cache_query= 0;
return func;
@@ -3473,11 +3513,11 @@ Create_sp_func::create_with_db(THD *thd, LEX_STRING db, LEX_STRING name,
Item*
-Create_native_func::create_func(THD *thd, LEX_STRING name, List<Item> *item_list)
+Create_native_func::create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list)
{
- if (has_named_parameters(item_list))
+ if (unlikely(has_named_parameters(item_list)))
{
- my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), name->str);
return NULL;
}
@@ -3486,16 +3526,16 @@ Create_native_func::create_func(THD *thd, LEX_STRING name, List<Item> *item_list
Item*
-Create_func_arg0::create_func(THD *thd, LEX_STRING name, List<Item> *item_list)
+Create_func_arg0::create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list)
{
int arg_count= 0;
if (item_list != NULL)
arg_count= item_list->elements;
- if (arg_count != 0)
+ if (unlikely(arg_count != 0))
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
return NULL;
}
@@ -3504,24 +3544,24 @@ Create_func_arg0::create_func(THD *thd, LEX_STRING name, List<Item> *item_list)
Item*
-Create_func_arg1::create_func(THD *thd, LEX_STRING name, List<Item> *item_list)
+Create_func_arg1::create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list)
{
int arg_count= 0;
if (item_list)
arg_count= item_list->elements;
- if (arg_count != 1)
+ if (unlikely(arg_count != 1))
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
return NULL;
}
Item *param_1= item_list->pop();
- if (! param_1->is_autogenerated_name)
+ if (unlikely(! param_1->is_autogenerated_name))
{
- my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), name->str);
return NULL;
}
@@ -3530,26 +3570,26 @@ Create_func_arg1::create_func(THD *thd, LEX_STRING name, List<Item> *item_list)
Item*
-Create_func_arg2::create_func(THD *thd, LEX_STRING name, List<Item> *item_list)
+Create_func_arg2::create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list)
{
int arg_count= 0;
if (item_list)
arg_count= item_list->elements;
- if (arg_count != 2)
+ if (unlikely(arg_count != 2))
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
return NULL;
}
Item *param_1= item_list->pop();
Item *param_2= item_list->pop();
- if ( (! 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);
+ my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), name->str);
return NULL;
}
@@ -3558,16 +3598,16 @@ Create_func_arg2::create_func(THD *thd, LEX_STRING name, List<Item> *item_list)
Item*
-Create_func_arg3::create_func(THD *thd, LEX_STRING name, List<Item> *item_list)
+Create_func_arg3::create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list)
{
int arg_count= 0;
if (item_list)
arg_count= item_list->elements;
- if (arg_count != 3)
+ if (unlikely(arg_count != 3))
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
return NULL;
}
@@ -3575,11 +3615,11 @@ Create_func_arg3::create_func(THD *thd, LEX_STRING name, List<Item> *item_list)
Item *param_2= item_list->pop();
Item *param_3= item_list->pop();
- if ( (! 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);
+ my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), name->str);
return NULL;
}
@@ -3677,7 +3717,7 @@ Create_func_asin::create_1_arg(THD *thd, Item *arg1)
Create_func_atan Create_func_atan::s_singleton;
Item*
-Create_func_atan::create_native(THD *thd, LEX_STRING name,
+Create_func_atan::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item* func= NULL;
@@ -3702,7 +3742,7 @@ Create_func_atan::create_native(THD *thd, LEX_STRING name,
}
default:
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
break;
}
}
@@ -3738,7 +3778,7 @@ Item*
Create_func_binlog_gtid_pos::create_2_arg(THD *thd, Item *arg1, Item *arg2)
{
#ifdef HAVE_REPLICATION
- if (!mysql_bin_log.is_open())
+ if (unlikely(!mysql_bin_log.is_open()))
#endif
{
my_error(ER_NO_BINARY_LOGGING, MYF(0));
@@ -3786,6 +3826,16 @@ Create_func_centroid::create_1_arg(THD *thd, Item *arg1)
}
+Create_func_chr Create_func_chr::s_singleton;
+
+Item*
+Create_func_chr::create_1_arg(THD *thd, Item *arg1)
+{
+ CHARSET_INFO *cs_db= thd->variables.collation_database;
+ return new (thd->mem_root) Item_func_chr(thd, arg1, cs_db);
+}
+
+
Create_func_convexhull Create_func_convexhull::s_singleton;
Item*
@@ -3858,7 +3908,7 @@ Create_func_dyncol_json::create_1_arg(THD *thd, Item *arg1)
Create_func_concat Create_func_concat::s_singleton;
Item*
-Create_func_concat::create_native(THD *thd, LEX_STRING name,
+Create_func_concat::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
int arg_count= 0;
@@ -3866,13 +3916,36 @@ Create_func_concat::create_native(THD *thd, LEX_STRING name,
if (item_list != NULL)
arg_count= item_list->elements;
- if (arg_count < 1)
+ if (unlikely(arg_count < 1))
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
return NULL;
}
- return new (thd->mem_root) Item_func_concat(thd, *item_list);
+ return thd->variables.sql_mode & MODE_ORACLE ?
+ new (thd->mem_root) Item_func_concat_operator_oracle(thd, *item_list) :
+ new (thd->mem_root) Item_func_concat(thd, *item_list);
+}
+
+Create_func_concat_operator_oracle
+ Create_func_concat_operator_oracle::s_singleton;
+
+Item*
+Create_func_concat_operator_oracle::create_native(THD *thd, LEX_CSTRING *name,
+ List<Item> *item_list)
+{
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ if (unlikely(arg_count < 1))
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
+ return NULL;
+ }
+
+ return new (thd->mem_root) Item_func_concat_operator_oracle(thd, *item_list);
}
Create_func_decode_histogram Create_func_decode_histogram::s_singleton;
@@ -3883,10 +3956,25 @@ Create_func_decode_histogram::create_2_arg(THD *thd, Item *arg1, Item *arg2)
return new (thd->mem_root) Item_func_decode_histogram(thd, arg1, arg2);
}
+Create_func_decode_oracle Create_func_decode_oracle::s_singleton;
+
+Item*
+Create_func_decode_oracle::create_native(THD *thd, LEX_CSTRING *name,
+ List<Item> *item_list)
+{
+ uint arg_count= item_list ? item_list->elements : 0;
+ if (unlikely(arg_count < 3))
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
+ return NULL;
+ }
+ return new (thd->mem_root) Item_func_decode_oracle(thd, *item_list);
+}
+
Create_func_concat_ws Create_func_concat_ws::s_singleton;
Item*
-Create_func_concat_ws::create_native(THD *thd, LEX_STRING name,
+Create_func_concat_ws::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
int arg_count= 0;
@@ -3895,9 +3983,9 @@ Create_func_concat_ws::create_native(THD *thd, LEX_STRING name,
arg_count= item_list->elements;
/* "WS" stands for "With Separator": this function takes 2+ arguments */
- if (arg_count < 2)
+ if (unlikely(arg_count < 2))
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
return NULL;
}
@@ -3946,6 +4034,15 @@ Create_func_contains::create_2_arg(THD *thd, Item *arg1, Item *arg2)
#endif
+Create_func_nvl2 Create_func_nvl2::s_singleton;
+
+Item*
+Create_func_nvl2::create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3)
+{
+ return new (thd->mem_root) Item_func_nvl2(thd, arg1, arg2, arg3);
+}
+
+
Create_func_conv Create_func_conv::s_singleton;
Item*
@@ -4003,15 +4100,6 @@ Create_func_crosses::create_2_arg(THD *thd, Item *arg1, Item *arg2)
#endif
-Create_func_date_format Create_func_date_format::s_singleton;
-
-Item*
-Create_func_date_format::create_2_arg(THD *thd, Item *arg1, Item *arg2)
-{
- return new (thd->mem_root) Item_func_date_format(thd, arg1, arg2, 0);
-}
-
-
Create_func_datediff Create_func_datediff::s_singleton;
Item*
@@ -4060,15 +4148,6 @@ Create_func_dayofyear::create_1_arg(THD *thd, Item *arg1)
}
-Create_func_decode Create_func_decode::s_singleton;
-
-Item*
-Create_func_decode::create_2_arg(THD *thd, Item *arg1, Item *arg2)
-{
- return new (thd->mem_root) Item_func_decode(thd, arg1, arg2);
-}
-
-
Create_func_degrees Create_func_degrees::s_singleton;
Item*
@@ -4082,7 +4161,7 @@ Create_func_degrees::create_1_arg(THD *thd, Item *arg1)
Create_func_des_decrypt Create_func_des_decrypt::s_singleton;
Item*
-Create_func_des_decrypt::create_native(THD *thd, LEX_STRING name,
+Create_func_des_decrypt::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func= NULL;
@@ -4107,7 +4186,7 @@ Create_func_des_decrypt::create_native(THD *thd, LEX_STRING name,
}
default:
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
break;
}
}
@@ -4119,7 +4198,7 @@ Create_func_des_decrypt::create_native(THD *thd, LEX_STRING name,
Create_func_des_encrypt Create_func_des_encrypt::s_singleton;
Item*
-Create_func_des_encrypt::create_native(THD *thd, LEX_STRING name,
+Create_func_des_encrypt::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func= NULL;
@@ -4144,7 +4223,7 @@ Create_func_des_encrypt::create_native(THD *thd, LEX_STRING name,
}
default:
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
break;
}
}
@@ -4198,7 +4277,7 @@ Create_func_distance::create_2_arg(THD *thd, Item *arg1, Item *arg2)
Create_func_elt Create_func_elt::s_singleton;
Item*
-Create_func_elt::create_native(THD *thd, LEX_STRING name,
+Create_func_elt::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
int arg_count= 0;
@@ -4206,9 +4285,9 @@ Create_func_elt::create_native(THD *thd, LEX_STRING name,
if (item_list != NULL)
arg_count= item_list->elements;
- if (arg_count < 2)
+ if (unlikely(arg_count < 2))
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
return NULL;
}
@@ -4228,7 +4307,7 @@ Create_func_encode::create_2_arg(THD *thd, Item *arg1, Item *arg2)
Create_func_encrypt Create_func_encrypt::s_singleton;
Item*
-Create_func_encrypt::create_native(THD *thd, LEX_STRING name,
+Create_func_encrypt::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func= NULL;
@@ -4254,7 +4333,7 @@ Create_func_encrypt::create_native(THD *thd, LEX_STRING name,
}
default:
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
break;
}
}
@@ -4329,7 +4408,7 @@ Create_func_exp::create_1_arg(THD *thd, Item *arg1)
Create_func_export_set Create_func_export_set::s_singleton;
Item*
-Create_func_export_set::create_native(THD *thd, LEX_STRING name,
+Create_func_export_set::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func= NULL;
@@ -4370,7 +4449,7 @@ Create_func_export_set::create_native(THD *thd, LEX_STRING name,
}
default:
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
break;
}
}
@@ -4394,7 +4473,7 @@ Create_func_exteriorring::create_1_arg(THD *thd, Item *arg1)
Create_func_field Create_func_field::s_singleton;
Item*
-Create_func_field::create_native(THD *thd, LEX_STRING name,
+Create_func_field::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
int arg_count= 0;
@@ -4402,9 +4481,9 @@ Create_func_field::create_native(THD *thd, LEX_STRING name,
if (item_list != NULL)
arg_count= item_list->elements;
- if (arg_count < 2)
+ if (unlikely(arg_count < 2))
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
return NULL;
}
@@ -4433,7 +4512,7 @@ Create_func_floor::create_1_arg(THD *thd, Item *arg1)
Create_func_format Create_func_format::s_singleton;
Item*
-Create_func_format::create_native(THD *thd, LEX_STRING name,
+Create_func_format::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func= NULL;
@@ -4456,7 +4535,7 @@ Create_func_format::create_native(THD *thd, LEX_STRING name,
break;
}
default:
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
break;
}
@@ -4498,7 +4577,7 @@ Create_func_from_days::create_1_arg(THD *thd, Item *arg1)
Create_func_from_unixtime Create_func_from_unixtime::s_singleton;
Item*
-Create_func_from_unixtime::create_native(THD *thd, LEX_STRING name,
+Create_func_from_unixtime::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func= NULL;
@@ -4519,12 +4598,12 @@ Create_func_from_unixtime::create_native(THD *thd, LEX_STRING name,
Item *param_1= item_list->pop();
Item *param_2= item_list->pop();
Item *ut= new (thd->mem_root) Item_func_from_unixtime(thd, param_1);
- func= new (thd->mem_root) Item_func_date_format(thd, ut, param_2, 0);
+ func= new (thd->mem_root) Item_func_date_format(thd, ut, param_2);
break;
}
default:
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
break;
}
}
@@ -4537,7 +4616,7 @@ Create_func_from_unixtime::create_native(THD *thd, LEX_STRING name,
Create_func_geometry_from_text Create_func_geometry_from_text::s_singleton;
Item*
-Create_func_geometry_from_text::create_native(THD *thd, LEX_STRING name,
+Create_func_geometry_from_text::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func= NULL;
@@ -4563,7 +4642,7 @@ Create_func_geometry_from_text::create_native(THD *thd, LEX_STRING name,
}
default:
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
break;
}
}
@@ -4577,7 +4656,7 @@ Create_func_geometry_from_text::create_native(THD *thd, LEX_STRING name,
Create_func_geometry_from_wkb Create_func_geometry_from_wkb::s_singleton;
Item*
-Create_func_geometry_from_wkb::create_native(THD *thd, LEX_STRING name,
+Create_func_geometry_from_wkb::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func= NULL;
@@ -4603,7 +4682,7 @@ Create_func_geometry_from_wkb::create_native(THD *thd, LEX_STRING name,
}
default:
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
break;
}
}
@@ -4617,7 +4696,7 @@ Create_func_geometry_from_wkb::create_native(THD *thd, LEX_STRING name,
Create_func_geometry_from_json Create_func_geometry_from_json::s_singleton;
Item*
-Create_func_geometry_from_json::create_native(THD *thd, LEX_STRING name,
+Create_func_geometry_from_json::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func= NULL;
@@ -4652,7 +4731,7 @@ Create_func_geometry_from_json::create_native(THD *thd, LEX_STRING name,
}
default:
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
break;
}
}
@@ -4664,7 +4743,7 @@ Create_func_geometry_from_json::create_native(THD *thd, LEX_STRING name,
Create_func_as_geojson Create_func_as_geojson::s_singleton;
Item*
-Create_func_as_geojson::create_native(THD *thd, LEX_STRING name,
+Create_func_as_geojson::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func= NULL;
@@ -4698,7 +4777,7 @@ Create_func_as_geojson::create_native(THD *thd, LEX_STRING name,
}
default:
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
break;
}
}
@@ -4767,7 +4846,7 @@ Create_func_glength::create_1_arg(THD *thd, Item *arg1)
Create_func_greatest Create_func_greatest::s_singleton;
Item*
-Create_func_greatest::create_native(THD *thd, LEX_STRING name,
+Create_func_greatest::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
int arg_count= 0;
@@ -4775,9 +4854,9 @@ Create_func_greatest::create_native(THD *thd, LEX_STRING name,
if (item_list != NULL)
arg_count= item_list->elements;
- if (arg_count < 2)
+ if (unlikely(arg_count < 2))
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
return NULL;
}
@@ -5052,6 +5131,7 @@ Create_func_json_exists Create_func_json_exists::s_singleton;
Item*
Create_func_json_exists::create_2_arg(THD *thd, Item *arg1, Item *arg2)
{
+ status_var_increment(current_thd->status_var.feature_json);
return new (thd->mem_root) Item_func_json_exists(thd, arg1, arg2);
}
@@ -5059,7 +5139,7 @@ Create_func_json_exists::create_2_arg(THD *thd, Item *arg1, Item *arg2)
Create_func_json_detailed Create_func_json_detailed::s_singleton;
Item*
-Create_func_json_detailed::create_native(THD *thd, LEX_STRING name,
+Create_func_json_detailed::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func= NULL;
@@ -5068,15 +5148,16 @@ Create_func_json_detailed::create_native(THD *thd, LEX_STRING name,
if (item_list != NULL)
arg_count= item_list->elements;
- if (arg_count < 1 || arg_count > 2 /* json_doc, [path]...*/)
+ if (unlikely(arg_count < 1 || arg_count > 2 /* json_doc, [path]...*/))
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
}
else
{
func= new (thd->mem_root) Item_func_json_format(thd, *item_list);
}
+ status_var_increment(current_thd->status_var.feature_json);
return func;
}
@@ -5086,6 +5167,7 @@ Create_func_json_loose Create_func_json_loose::s_singleton;
Item*
Create_func_json_loose::create_1_arg(THD *thd, Item *arg1)
{
+ status_var_increment(current_thd->status_var.feature_json);
return new (thd->mem_root) Item_func_json_format(thd, arg1,
Item_func_json_format::LOOSE);
}
@@ -5096,6 +5178,7 @@ Create_func_json_compact Create_func_json_compact::s_singleton;
Item*
Create_func_json_compact::create_1_arg(THD *thd, Item *arg1)
{
+ status_var_increment(current_thd->status_var.feature_json);
return new (thd->mem_root) Item_func_json_format(thd, arg1,
Item_func_json_format::COMPACT);
}
@@ -5106,6 +5189,7 @@ Create_func_json_valid Create_func_json_valid::s_singleton;
Item*
Create_func_json_valid::create_1_arg(THD *thd, Item *arg1)
{
+ status_var_increment(current_thd->status_var.feature_json);
return new (thd->mem_root) Item_func_json_valid(thd, arg1);
}
@@ -5115,6 +5199,7 @@ Create_func_json_type Create_func_json_type::s_singleton;
Item*
Create_func_json_type::create_1_arg(THD *thd, Item *arg1)
{
+ status_var_increment(current_thd->status_var.feature_json);
return new (thd->mem_root) Item_func_json_type(thd, arg1);
}
@@ -5124,6 +5209,7 @@ Create_func_json_depth Create_func_json_depth::s_singleton;
Item*
Create_func_json_depth::create_1_arg(THD *thd, Item *arg1)
{
+ status_var_increment(current_thd->status_var.feature_json);
return new (thd->mem_root) Item_func_json_depth(thd, arg1);
}
@@ -5133,6 +5219,7 @@ Create_func_json_value Create_func_json_value::s_singleton;
Item*
Create_func_json_value::create_2_arg(THD *thd, Item *arg1, Item *arg2)
{
+ status_var_increment(current_thd->status_var.feature_json);
return new (thd->mem_root) Item_func_json_value(thd, arg1, arg2);
}
@@ -5142,6 +5229,7 @@ Create_func_json_query Create_func_json_query::s_singleton;
Item*
Create_func_json_query::create_2_arg(THD *thd, Item *arg1, Item *arg2)
{
+ status_var_increment(current_thd->status_var.feature_json);
return new (thd->mem_root) Item_func_json_query(thd, arg1, arg2);
}
@@ -5151,6 +5239,7 @@ Create_func_json_quote Create_func_json_quote::s_singleton;
Item*
Create_func_json_quote::create_1_arg(THD *thd, Item *arg1)
{
+ status_var_increment(current_thd->status_var.feature_json);
return new (thd->mem_root) Item_func_json_quote(thd, arg1);
}
@@ -5160,6 +5249,7 @@ Create_func_json_unquote Create_func_json_unquote::s_singleton;
Item*
Create_func_json_unquote::create_1_arg(THD *thd, Item *arg1)
{
+ status_var_increment(current_thd->status_var.feature_json);
return new (thd->mem_root) Item_func_json_unquote(thd, arg1);
}
@@ -5176,7 +5266,7 @@ Create_func_last_day::create_1_arg(THD *thd, Item *arg1)
Create_func_json_array Create_func_json_array::s_singleton;
Item*
-Create_func_json_array::create_native(THD *thd, LEX_STRING name,
+Create_func_json_array::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func;
@@ -5190,6 +5280,7 @@ Create_func_json_array::create_native(THD *thd, LEX_STRING name,
func= new (thd->mem_root) Item_func_json_array(thd);
}
+ status_var_increment(current_thd->status_var.feature_json);
return func;
}
@@ -5197,7 +5288,7 @@ Create_func_json_array::create_native(THD *thd, LEX_STRING name,
Create_func_json_array_append Create_func_json_array_append::s_singleton;
Item*
-Create_func_json_array_append::create_native(THD *thd, LEX_STRING name,
+Create_func_json_array_append::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func= NULL;
@@ -5206,15 +5297,16 @@ Create_func_json_array_append::create_native(THD *thd, LEX_STRING name,
if (item_list != NULL)
arg_count= item_list->elements;
- if (arg_count < 3 || (arg_count & 1) == 0 /*is even*/)
+ if (unlikely(arg_count < 3 || (arg_count & 1) == 0 /*is even*/))
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
}
else
{
func= new (thd->mem_root) Item_func_json_array_append(thd, *item_list);
}
+ status_var_increment(current_thd->status_var.feature_json);
return func;
}
@@ -5222,7 +5314,7 @@ Create_func_json_array_append::create_native(THD *thd, LEX_STRING name,
Create_func_json_array_insert Create_func_json_array_insert::s_singleton;
Item*
-Create_func_json_array_insert::create_native(THD *thd, LEX_STRING name,
+Create_func_json_array_insert::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func= NULL;
@@ -5231,15 +5323,16 @@ Create_func_json_array_insert::create_native(THD *thd, LEX_STRING name,
if (item_list != NULL)
arg_count= item_list->elements;
- if (arg_count < 3 || (arg_count & 1) == 0 /*is even*/)
+ if (unlikely(arg_count < 3 || (arg_count & 1) == 0 /*is even*/))
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
}
else
{
func= new (thd->mem_root) Item_func_json_array_insert(thd, *item_list);
}
+ status_var_increment(current_thd->status_var.feature_json);
return func;
}
@@ -5247,7 +5340,7 @@ Create_func_json_array_insert::create_native(THD *thd, LEX_STRING name,
Create_func_json_insert Create_func_json_insert::s_singleton;
Item*
-Create_func_json_insert::create_native(THD *thd, LEX_STRING name,
+Create_func_json_insert::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func= NULL;
@@ -5256,9 +5349,9 @@ Create_func_json_insert::create_native(THD *thd, LEX_STRING name,
if (item_list != NULL)
arg_count= item_list->elements;
- if (arg_count < 3 || (arg_count & 1) == 0 /*is even*/)
+ if (unlikely(arg_count < 3 || (arg_count & 1) == 0 /*is even*/))
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
}
else
{
@@ -5266,6 +5359,7 @@ Create_func_json_insert::create_native(THD *thd, LEX_STRING name,
thd, *item_list);
}
+ status_var_increment(current_thd->status_var.feature_json);
return func;
}
@@ -5273,7 +5367,7 @@ Create_func_json_insert::create_native(THD *thd, LEX_STRING name,
Create_func_json_set Create_func_json_set::s_singleton;
Item*
-Create_func_json_set::create_native(THD *thd, LEX_STRING name,
+Create_func_json_set::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func= NULL;
@@ -5282,9 +5376,9 @@ Create_func_json_set::create_native(THD *thd, LEX_STRING name,
if (item_list != NULL)
arg_count= item_list->elements;
- if (arg_count < 3 || (arg_count & 1) == 0 /*is even*/)
+ if (unlikely(arg_count < 3 || (arg_count & 1) == 0 /*is even*/))
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
}
else
{
@@ -5292,6 +5386,7 @@ Create_func_json_set::create_native(THD *thd, LEX_STRING name,
thd, *item_list);
}
+ status_var_increment(current_thd->status_var.feature_json);
return func;
}
@@ -5299,7 +5394,7 @@ Create_func_json_set::create_native(THD *thd, LEX_STRING name,
Create_func_json_replace Create_func_json_replace::s_singleton;
Item*
-Create_func_json_replace::create_native(THD *thd, LEX_STRING name,
+Create_func_json_replace::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func= NULL;
@@ -5308,9 +5403,9 @@ Create_func_json_replace::create_native(THD *thd, LEX_STRING name,
if (item_list != NULL)
arg_count= item_list->elements;
- if (arg_count < 3 || (arg_count & 1) == 0 /*is even*/)
+ if (unlikely(arg_count < 3 || (arg_count & 1) == 0 /*is even*/))
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
}
else
{
@@ -5318,6 +5413,7 @@ Create_func_json_replace::create_native(THD *thd, LEX_STRING name,
thd, *item_list);
}
+ status_var_increment(current_thd->status_var.feature_json);
return func;
}
@@ -5325,7 +5421,7 @@ Create_func_json_replace::create_native(THD *thd, LEX_STRING name,
Create_func_json_remove Create_func_json_remove::s_singleton;
Item*
-Create_func_json_remove::create_native(THD *thd, LEX_STRING name,
+Create_func_json_remove::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func= NULL;
@@ -5334,15 +5430,16 @@ Create_func_json_remove::create_native(THD *thd, LEX_STRING name,
if (item_list != NULL)
arg_count= item_list->elements;
- if (arg_count < 2 /*json_doc, path [,path]*/)
+ if (unlikely(arg_count < 2 /*json_doc, path [,path]*/))
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
}
else
{
func= new (thd->mem_root) Item_func_json_remove(thd, *item_list);
}
+ status_var_increment(current_thd->status_var.feature_json);
return func;
}
@@ -5350,7 +5447,7 @@ Create_func_json_remove::create_native(THD *thd, LEX_STRING name,
Create_func_json_object Create_func_json_object::s_singleton;
Item*
-Create_func_json_object::create_native(THD *thd, LEX_STRING name,
+Create_func_json_object::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func;
@@ -5359,9 +5456,9 @@ Create_func_json_object::create_native(THD *thd, LEX_STRING name,
if (item_list != NULL)
{
arg_count= item_list->elements;
- if ((arg_count & 1) != 0 /*is odd*/)
+ if (unlikely((arg_count & 1) != 0 /*is odd*/))
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
func= NULL;
}
else
@@ -5375,6 +5472,7 @@ Create_func_json_object::create_native(THD *thd, LEX_STRING name,
func= new (thd->mem_root) Item_func_json_object(thd);
}
+ status_var_increment(current_thd->status_var.feature_json);
return func;
}
@@ -5382,16 +5480,16 @@ Create_func_json_object::create_native(THD *thd, LEX_STRING name,
Create_func_json_length Create_func_json_length::s_singleton;
Item*
-Create_func_json_length::create_native(THD *thd, LEX_STRING name,
+Create_func_json_length::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func;
int arg_count;
- if (item_list == NULL ||
- (arg_count= item_list->elements) == 0)
+ if (unlikely(item_list == NULL ||
+ (arg_count= item_list->elements) == 0))
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
func= NULL;
}
else
@@ -5399,6 +5497,7 @@ Create_func_json_length::create_native(THD *thd, LEX_STRING name,
func= new (thd->mem_root) Item_func_json_length(thd, *item_list);
}
+ status_var_increment(current_thd->status_var.feature_json);
return func;
}
@@ -5406,16 +5505,16 @@ Create_func_json_length::create_native(THD *thd, LEX_STRING name,
Create_func_json_merge Create_func_json_merge::s_singleton;
Item*
-Create_func_json_merge::create_native(THD *thd, LEX_STRING name,
+Create_func_json_merge::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func;
int arg_count;
- if (item_list == NULL ||
- (arg_count= item_list->elements) < 2) // json, json
+ if (unlikely(item_list == NULL ||
+ (arg_count= item_list->elements) < 2)) // json, json
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
func= NULL;
}
else
@@ -5423,6 +5522,7 @@ Create_func_json_merge::create_native(THD *thd, LEX_STRING name,
func= new (thd->mem_root) Item_func_json_merge(thd, *item_list);
}
+ status_var_increment(current_thd->status_var.feature_json);
return func;
}
@@ -5430,7 +5530,7 @@ Create_func_json_merge::create_native(THD *thd, LEX_STRING name,
Create_func_json_merge_patch Create_func_json_merge_patch::s_singleton;
Item*
-Create_func_json_merge_patch::create_native(THD *thd, LEX_STRING name,
+Create_func_json_merge_patch::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func;
@@ -5439,7 +5539,7 @@ Create_func_json_merge_patch::create_native(THD *thd, LEX_STRING name,
if (item_list == NULL ||
(arg_count= item_list->elements) < 2) // json, json
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
func= NULL;
}
else
@@ -5454,7 +5554,7 @@ Create_func_json_merge_patch::create_native(THD *thd, LEX_STRING name,
Create_func_json_contains Create_func_json_contains::s_singleton;
Item*
-Create_func_json_contains::create_native(THD *thd, LEX_STRING name,
+Create_func_json_contains::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func= NULL;
@@ -5463,15 +5563,16 @@ Create_func_json_contains::create_native(THD *thd, LEX_STRING name,
if (item_list != NULL)
arg_count= item_list->elements;
- if (arg_count == 2 || arg_count == 3/* json_doc, val, [path] */)
+ if (unlikely(arg_count == 2 || arg_count == 3/* json_doc, val, [path] */))
{
func= new (thd->mem_root) Item_func_json_contains(thd, *item_list);
}
else
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
}
+ status_var_increment(current_thd->status_var.feature_json);
return func;
}
@@ -5479,7 +5580,7 @@ Create_func_json_contains::create_native(THD *thd, LEX_STRING name,
Create_func_json_keys Create_func_json_keys::s_singleton;
Item*
-Create_func_json_keys::create_native(THD *thd, LEX_STRING name,
+Create_func_json_keys::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func= NULL;
@@ -5488,15 +5589,16 @@ Create_func_json_keys::create_native(THD *thd, LEX_STRING name,
if (item_list != NULL)
arg_count= item_list->elements;
- if (arg_count < 1 || arg_count > 2 /* json_doc, [path]...*/)
+ if (unlikely(arg_count < 1 || arg_count > 2 /* json_doc, [path]...*/))
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
}
else
{
func= new (thd->mem_root) Item_func_json_keys(thd, *item_list);
}
+ status_var_increment(current_thd->status_var.feature_json);
return func;
}
@@ -5504,7 +5606,7 @@ Create_func_json_keys::create_native(THD *thd, LEX_STRING name,
Create_func_json_contains_path Create_func_json_contains_path::s_singleton;
Item*
-Create_func_json_contains_path::create_native(THD *thd, LEX_STRING name,
+Create_func_json_contains_path::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func= NULL;
@@ -5513,15 +5615,16 @@ Create_func_json_contains_path::create_native(THD *thd, LEX_STRING name,
if (item_list != NULL)
arg_count= item_list->elements;
- if (arg_count < 3 /* json_doc, one_or_all, path, [path]...*/)
+ if (unlikely(arg_count < 3 /* json_doc, one_or_all, path, [path]...*/))
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
}
else
{
func= new (thd->mem_root) Item_func_json_contains_path(thd, *item_list);
}
+ status_var_increment(current_thd->status_var.feature_json);
return func;
}
@@ -5529,7 +5632,7 @@ Create_func_json_contains_path::create_native(THD *thd, LEX_STRING name,
Create_func_json_extract Create_func_json_extract::s_singleton;
Item*
-Create_func_json_extract::create_native(THD *thd, LEX_STRING name,
+Create_func_json_extract::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func= NULL;
@@ -5538,15 +5641,16 @@ Create_func_json_extract::create_native(THD *thd, LEX_STRING name,
if (item_list != NULL)
arg_count= item_list->elements;
- if (arg_count < 2 /* json_doc, path, [path]...*/)
+ if (unlikely(arg_count < 2 /* json_doc, path, [path]...*/))
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
}
else
{
func= new (thd->mem_root) Item_func_json_extract(thd, *item_list);
}
+ status_var_increment(current_thd->status_var.feature_json);
return func;
}
@@ -5554,7 +5658,7 @@ Create_func_json_extract::create_native(THD *thd, LEX_STRING name,
Create_func_json_search Create_func_json_search::s_singleton;
Item*
-Create_func_json_search::create_native(THD *thd, LEX_STRING name,
+Create_func_json_search::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func= NULL;
@@ -5563,15 +5667,16 @@ Create_func_json_search::create_native(THD *thd, LEX_STRING name,
if (item_list != NULL)
arg_count= item_list->elements;
- if (arg_count < 3 /* json_doc, one_or_all, search_str, [escape_char[, path]...*/)
+ if (unlikely(arg_count < 3 /* json_doc, one_or_all, search_str, [escape_char[, path]...*/))
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
}
else
{
func= new (thd->mem_root) Item_func_json_search(thd, *item_list);
}
+ status_var_increment(current_thd->status_var.feature_json);
return func;
}
@@ -5579,7 +5684,7 @@ Create_func_json_search::create_native(THD *thd, LEX_STRING name,
Create_func_last_insert_id Create_func_last_insert_id::s_singleton;
Item*
-Create_func_last_insert_id::create_native(THD *thd, LEX_STRING name,
+Create_func_last_insert_id::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func= NULL;
@@ -5604,7 +5709,7 @@ Create_func_last_insert_id::create_native(THD *thd, LEX_STRING name,
}
default:
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
break;
}
}
@@ -5625,7 +5730,7 @@ Create_func_lcase::create_1_arg(THD *thd, Item *arg1)
Create_func_least Create_func_least::s_singleton;
Item*
-Create_func_least::create_native(THD *thd, LEX_STRING name,
+Create_func_least::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
int arg_count= 0;
@@ -5633,9 +5738,9 @@ Create_func_least::create_native(THD *thd, LEX_STRING name,
if (item_list != NULL)
arg_count= item_list->elements;
- if (arg_count < 2)
+ if (unlikely(arg_count < 2))
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
return NULL;
}
@@ -5648,7 +5753,18 @@ Create_func_length Create_func_length::s_singleton;
Item*
Create_func_length::create_1_arg(THD *thd, Item *arg1)
{
- return new (thd->mem_root) Item_func_length(thd, arg1);
+ if (thd->variables.sql_mode & MODE_ORACLE)
+ return new (thd->mem_root) Item_func_char_length(thd, arg1);
+ else
+ return new (thd->mem_root) Item_func_octet_length(thd, arg1);
+}
+
+Create_func_octet_length Create_func_octet_length::s_singleton;
+
+Item*
+Create_func_octet_length::create_1_arg(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_octet_length(thd, arg1);
}
@@ -5696,7 +5812,7 @@ Create_func_load_file::create_1_arg(THD *thd, Item *arg1)
Create_func_locate Create_func_locate::s_singleton;
Item*
-Create_func_locate::create_native(THD *thd, LEX_STRING name,
+Create_func_locate::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func= NULL;
@@ -5725,7 +5841,7 @@ Create_func_locate::create_native(THD *thd, LEX_STRING name,
}
default:
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
break;
}
}
@@ -5737,7 +5853,7 @@ Create_func_locate::create_native(THD *thd, LEX_STRING name,
Create_func_log Create_func_log::s_singleton;
Item*
-Create_func_log::create_native(THD *thd, LEX_STRING name,
+Create_func_log::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func= NULL;
@@ -5762,7 +5878,7 @@ Create_func_log::create_native(THD *thd, LEX_STRING name,
}
default:
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
break;
}
}
@@ -5791,10 +5907,65 @@ Create_func_log2::create_1_arg(THD *thd, Item *arg1)
Create_func_lpad Create_func_lpad::s_singleton;
+Create_func_lpad_oracle Create_func_lpad_oracle::s_singleton;
+
Item*
-Create_func_lpad::create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3)
+Create_func_lpad::create_native_std(THD *thd, LEX_CSTRING *name,
+ List<Item> *item_list)
{
- return new (thd->mem_root) Item_func_lpad(thd, arg1, arg2, arg3);
+ Item *func= NULL;
+ int arg_count= item_list ? item_list->elements : 0;
+
+ switch (arg_count) {
+ case 2:
+ {
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+ func= new (thd->mem_root) Item_func_lpad(thd, param_1, param_2);
+ break;
+ }
+ case 3:
+ {
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+ Item *param_3= item_list->pop();
+ func= new (thd->mem_root) Item_func_lpad(thd, param_1, param_2, param_3);
+ break;
+ }
+ default:
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
+ break;
+ }
+
+ return func;
+}
+
+
+Item*
+Create_func_lpad::create_native_oracle(THD *thd, LEX_CSTRING *name,
+ List<Item> *item_list)
+{
+ int arg_count= item_list ? item_list->elements : 0;
+ switch (arg_count) {
+ case 2:
+ {
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+ return new (thd->mem_root) Item_func_lpad_oracle(thd, param_1, param_2);
+ }
+ case 3:
+ {
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+ Item *param_3= item_list->pop();
+ return new (thd->mem_root) Item_func_lpad_oracle(thd, param_1,
+ param_2, param_3);
+ }
+ default:
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
+ break;
+ }
+ return NULL;
}
@@ -5803,7 +5974,16 @@ Create_func_ltrim Create_func_ltrim::s_singleton;
Item*
Create_func_ltrim::create_1_arg(THD *thd, Item *arg1)
{
- return new (thd->mem_root) Item_func_ltrim(thd, arg1);
+ return Lex_trim(TRIM_LEADING, arg1).make_item_func_trim(thd);
+}
+
+
+Create_func_ltrim_oracle Create_func_ltrim_oracle::s_singleton;
+
+Item*
+Create_func_ltrim_oracle::create_1_arg(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_ltrim_oracle(thd, arg1);
}
@@ -5828,7 +6008,7 @@ Create_func_maketime::create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3)
Create_func_make_set Create_func_make_set::s_singleton;
Item*
-Create_func_make_set::create_native(THD *thd, LEX_STRING name,
+Create_func_make_set::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
int arg_count= 0;
@@ -5836,9 +6016,9 @@ Create_func_make_set::create_native(THD *thd, LEX_STRING name,
if (item_list != NULL)
arg_count= item_list->elements;
- if (arg_count < 2)
+ if (unlikely(arg_count < 2))
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
return NULL;
}
@@ -5849,7 +6029,7 @@ Create_func_make_set::create_native(THD *thd, LEX_STRING name,
Create_func_master_pos_wait Create_func_master_pos_wait::s_singleton;
Item*
-Create_func_master_pos_wait::create_native(THD *thd, LEX_STRING name,
+Create_func_master_pos_wait::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
@@ -5861,9 +6041,9 @@ Create_func_master_pos_wait::create_native(THD *thd, LEX_STRING name,
if (item_list != NULL)
arg_count= item_list->elements;
- if (arg_count < 2 || arg_count > 4)
+ if (unlikely(arg_count < 2 || arg_count > 4))
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
return func;
}
@@ -5900,7 +6080,7 @@ Create_func_master_pos_wait::create_native(THD *thd, LEX_STRING name,
Create_func_master_gtid_wait Create_func_master_gtid_wait::s_singleton;
Item*
-Create_func_master_gtid_wait::create_native(THD *thd, LEX_STRING name,
+Create_func_master_gtid_wait::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func= NULL;
@@ -5911,9 +6091,9 @@ Create_func_master_gtid_wait::create_native(THD *thd, LEX_STRING name,
if (item_list != NULL)
arg_count= item_list->elements;
- if (arg_count < 1 || arg_count > 2)
+ if (unlikely(arg_count < 1 || arg_count > 2))
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
return func;
}
@@ -6146,7 +6326,7 @@ Create_func_radians::create_1_arg(THD *thd, Item *arg1)
Create_func_rand Create_func_rand::s_singleton;
Item*
-Create_func_rand::create_native(THD *thd, LEX_STRING name,
+Create_func_rand::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func= NULL;
@@ -6184,7 +6364,7 @@ Create_func_rand::create_native(THD *thd, LEX_STRING name,
}
default:
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
break;
}
}
@@ -6204,6 +6384,16 @@ Create_func_release_lock::create_1_arg(THD *thd, Item *arg1)
}
+Create_func_replace_oracle Create_func_replace_oracle::s_singleton;
+
+Item*
+Create_func_replace_oracle::create_3_arg(THD *thd, Item *arg1, Item *arg2,
+ Item *arg3)
+{
+ return new (thd->mem_root) Item_func_replace_oracle(thd, arg1, arg2, arg3);
+}
+
+
Create_func_reverse Create_func_reverse::s_singleton;
Item*
@@ -6216,7 +6406,7 @@ Create_func_reverse::create_1_arg(THD *thd, Item *arg1)
Create_func_round Create_func_round::s_singleton;
Item*
-Create_func_round::create_native(THD *thd, LEX_STRING name,
+Create_func_round::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func= NULL;
@@ -6242,7 +6432,7 @@ Create_func_round::create_native(THD *thd, LEX_STRING name,
}
default:
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
break;
}
}
@@ -6253,10 +6443,65 @@ Create_func_round::create_native(THD *thd, LEX_STRING name,
Create_func_rpad Create_func_rpad::s_singleton;
+Create_func_rpad_oracle Create_func_rpad_oracle::s_singleton;
+
+Item*
+Create_func_rpad::create_native_std(THD *thd, LEX_CSTRING *name,
+ List<Item> *item_list)
+{
+ Item *func= NULL;
+ int arg_count= item_list ? item_list->elements : 0;
+
+ switch (arg_count) {
+ case 2:
+ {
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+ func= new (thd->mem_root) Item_func_rpad(thd, param_1, param_2);
+ break;
+ }
+ case 3:
+ {
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+ Item *param_3= item_list->pop();
+ func= new (thd->mem_root) Item_func_rpad(thd, param_1, param_2, param_3);
+ break;
+ }
+ default:
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
+ break;
+ }
+
+ return func;
+}
+
+
Item*
-Create_func_rpad::create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3)
+Create_func_rpad::create_native_oracle(THD *thd, LEX_CSTRING *name,
+ List<Item> *item_list)
{
- return new (thd->mem_root) Item_func_rpad(thd, arg1, arg2, arg3);
+ int arg_count= item_list ? item_list->elements : 0;
+ switch (arg_count) {
+ case 2:
+ {
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+ return new (thd->mem_root) Item_func_rpad_oracle(thd, param_1, param_2);
+ }
+ case 3:
+ {
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+ Item *param_3= item_list->pop();
+ return new (thd->mem_root) Item_func_rpad_oracle(thd, param_1,
+ param_2, param_3);
+ }
+ default:
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
+ break;
+ }
+ return NULL;
}
@@ -6265,7 +6510,16 @@ Create_func_rtrim Create_func_rtrim::s_singleton;
Item*
Create_func_rtrim::create_1_arg(THD *thd, Item *arg1)
{
- return new (thd->mem_root) Item_func_rtrim(thd, arg1);
+ return Lex_trim(TRIM_TRAILING, arg1).make_item_func_trim(thd);
+}
+
+
+Create_func_rtrim_oracle Create_func_rtrim_oracle::s_singleton;
+
+Item*
+Create_func_rtrim_oracle::create_1_arg(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_rtrim_oracle(thd, arg1);
}
@@ -6402,6 +6656,40 @@ Create_func_substr_index::create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *a
}
+Create_func_substr_oracle Create_func_substr_oracle::s_singleton;
+
+Item*
+Create_func_substr_oracle::create_native(THD *thd, LEX_CSTRING *name,
+ List<Item> *item_list)
+{
+ Item *func= NULL;
+ int arg_count= item_list ? item_list->elements : 0;
+
+ switch (arg_count) {
+ case 2:
+ {
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+ func= new (thd->mem_root) Item_func_substr_oracle(thd, param_1, param_2);
+ break;
+ }
+ case 3:
+ {
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+ Item *param_3= item_list->pop();
+ func= new (thd->mem_root) Item_func_substr_oracle(thd, param_1, param_2, param_3);
+ break;
+ }
+ default:
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
+ break;
+ }
+
+ return func;
+}
+
+
Create_func_subtime Create_func_subtime::s_singleton;
Item*
@@ -6425,7 +6713,7 @@ Create_func_time_format Create_func_time_format::s_singleton;
Item*
Create_func_time_format::create_2_arg(THD *thd, Item *arg1, Item *arg2)
{
- return new (thd->mem_root) Item_func_date_format(thd, arg1, arg2, 1);
+ return new (thd->mem_root) Item_func_time_format(thd, arg1, arg2);
}
@@ -6525,7 +6813,7 @@ Create_func_unhex::create_1_arg(THD *thd, Item *arg1)
Create_func_unix_timestamp Create_func_unix_timestamp::s_singleton;
Item*
-Create_func_unix_timestamp::create_native(THD *thd, LEX_STRING name,
+Create_func_unix_timestamp::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func= NULL;
@@ -6549,7 +6837,7 @@ Create_func_unix_timestamp::create_native(THD *thd, LEX_STRING name,
}
default:
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
break;
}
}
@@ -6680,7 +6968,7 @@ Create_func_y::create_1_arg(THD *thd, Item *arg1)
Create_func_year_week Create_func_year_week::s_singleton;
Item*
-Create_func_year_week::create_native(THD *thd, LEX_STRING name,
+Create_func_year_week::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func= NULL;
@@ -6706,7 +6994,7 @@ Create_func_year_week::create_native(THD *thd, LEX_STRING name,
}
default:
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
break;
}
}
@@ -6715,12 +7003,6 @@ Create_func_year_week::create_native(THD *thd, LEX_STRING name,
}
-struct Native_func_registry
-{
- LEX_STRING name;
- Create_func *builder;
-};
-
#define BUILDER(F) & F::s_singleton
#ifdef HAVE_SPATIAL
@@ -6742,341 +7024,353 @@ struct Native_func_registry
static Native_func_registry func_array[] =
{
- { { C_STRING_WITH_LEN("ABS") }, BUILDER(Create_func_abs)},
- { { C_STRING_WITH_LEN("ACOS") }, BUILDER(Create_func_acos)},
- { { C_STRING_WITH_LEN("ADDTIME") }, BUILDER(Create_func_addtime)},
- { { C_STRING_WITH_LEN("AES_DECRYPT") }, BUILDER(Create_func_aes_decrypt)},
- { { C_STRING_WITH_LEN("AES_ENCRYPT") }, BUILDER(Create_func_aes_encrypt)},
- { { C_STRING_WITH_LEN("AREA") }, GEOM_BUILDER(Create_func_area)},
- { { C_STRING_WITH_LEN("ASBINARY") }, GEOM_BUILDER(Create_func_as_wkb)},
- { { C_STRING_WITH_LEN("ASIN") }, BUILDER(Create_func_asin)},
- { { C_STRING_WITH_LEN("ASTEXT") }, GEOM_BUILDER(Create_func_as_wkt)},
- { { C_STRING_WITH_LEN("ASWKB") }, GEOM_BUILDER(Create_func_as_wkb)},
- { { C_STRING_WITH_LEN("ASWKT") }, GEOM_BUILDER(Create_func_as_wkt)},
- { { C_STRING_WITH_LEN("ATAN") }, BUILDER(Create_func_atan)},
- { { C_STRING_WITH_LEN("ATAN2") }, BUILDER(Create_func_atan)},
- { { C_STRING_WITH_LEN("BENCHMARK") }, BUILDER(Create_func_benchmark)},
- { { C_STRING_WITH_LEN("BIN") }, BUILDER(Create_func_bin)},
- { { C_STRING_WITH_LEN("BINLOG_GTID_POS") }, BUILDER(Create_func_binlog_gtid_pos)},
- { { C_STRING_WITH_LEN("BIT_COUNT") }, BUILDER(Create_func_bit_count)},
- { { C_STRING_WITH_LEN("BIT_LENGTH") }, BUILDER(Create_func_bit_length)},
- { { C_STRING_WITH_LEN("BOUNDARY") }, GEOM_BUILDER(Create_func_boundary)},
- { { C_STRING_WITH_LEN("BUFFER") }, GEOM_BUILDER(Create_func_buffer)},
- { { C_STRING_WITH_LEN("CEIL") }, BUILDER(Create_func_ceiling)},
- { { C_STRING_WITH_LEN("CEILING") }, BUILDER(Create_func_ceiling)},
- { { C_STRING_WITH_LEN("CENTROID") }, GEOM_BUILDER(Create_func_centroid)},
- { { C_STRING_WITH_LEN("CHARACTER_LENGTH") }, BUILDER(Create_func_char_length)},
- { { C_STRING_WITH_LEN("CHAR_LENGTH") }, BUILDER(Create_func_char_length)},
- { { C_STRING_WITH_LEN("COERCIBILITY") }, BUILDER(Create_func_coercibility)},
- { { C_STRING_WITH_LEN("COLUMN_CHECK") }, BUILDER(Create_func_dyncol_check)},
- { { C_STRING_WITH_LEN("COLUMN_EXISTS") }, BUILDER(Create_func_dyncol_exists)},
- { { C_STRING_WITH_LEN("COLUMN_LIST") }, BUILDER(Create_func_dyncol_list)},
- { { C_STRING_WITH_LEN("COLUMN_JSON") }, BUILDER(Create_func_dyncol_json)},
- { { C_STRING_WITH_LEN("COMPRESS") }, BUILDER(Create_func_compress)},
- { { C_STRING_WITH_LEN("CONCAT") }, BUILDER(Create_func_concat)},
- { { C_STRING_WITH_LEN("CONCAT_WS") }, BUILDER(Create_func_concat_ws)},
- { { C_STRING_WITH_LEN("CONNECTION_ID") }, BUILDER(Create_func_connection_id)},
- { { C_STRING_WITH_LEN("CONV") }, BUILDER(Create_func_conv)},
- { { C_STRING_WITH_LEN("CONVERT_TZ") }, BUILDER(Create_func_convert_tz)},
- { { C_STRING_WITH_LEN("CONVEXHULL") }, GEOM_BUILDER(Create_func_convexhull)},
- { { C_STRING_WITH_LEN("COS") }, BUILDER(Create_func_cos)},
- { { C_STRING_WITH_LEN("COT") }, BUILDER(Create_func_cot)},
- { { C_STRING_WITH_LEN("CRC32") }, BUILDER(Create_func_crc32)},
- { { C_STRING_WITH_LEN("CROSSES") }, GEOM_BUILDER(Create_func_crosses)},
- { { C_STRING_WITH_LEN("DATEDIFF") }, BUILDER(Create_func_datediff)},
- { { C_STRING_WITH_LEN("DATE_FORMAT") }, BUILDER(Create_func_date_format)},
- { { C_STRING_WITH_LEN("DAYNAME") }, BUILDER(Create_func_dayname)},
- { { C_STRING_WITH_LEN("DAYOFMONTH") }, BUILDER(Create_func_dayofmonth)},
- { { C_STRING_WITH_LEN("DAYOFWEEK") }, BUILDER(Create_func_dayofweek)},
- { { C_STRING_WITH_LEN("DAYOFYEAR") }, BUILDER(Create_func_dayofyear)},
- { { C_STRING_WITH_LEN("DECODE") }, BUILDER(Create_func_decode)},
- { { C_STRING_WITH_LEN("DEGREES") }, BUILDER(Create_func_degrees)},
- { { C_STRING_WITH_LEN("DECODE_HISTOGRAM") }, BUILDER(Create_func_decode_histogram)},
- { { C_STRING_WITH_LEN("DES_DECRYPT") }, BUILDER(Create_func_des_decrypt)},
- { { C_STRING_WITH_LEN("DES_ENCRYPT") }, BUILDER(Create_func_des_encrypt)},
- { { C_STRING_WITH_LEN("DIMENSION") }, GEOM_BUILDER(Create_func_dimension)},
- { { C_STRING_WITH_LEN("DISJOINT") }, GEOM_BUILDER(Create_func_mbr_disjoint)},
- { { C_STRING_WITH_LEN("ELT") }, BUILDER(Create_func_elt)},
- { { C_STRING_WITH_LEN("ENCODE") }, BUILDER(Create_func_encode)},
- { { C_STRING_WITH_LEN("ENCRYPT") }, BUILDER(Create_func_encrypt)},
- { { C_STRING_WITH_LEN("ENDPOINT") }, GEOM_BUILDER(Create_func_endpoint)},
- { { C_STRING_WITH_LEN("ENVELOPE") }, GEOM_BUILDER(Create_func_envelope)},
- { { C_STRING_WITH_LEN("EQUALS") }, GEOM_BUILDER(Create_func_equals)},
- { { C_STRING_WITH_LEN("EXP") }, BUILDER(Create_func_exp)},
- { { C_STRING_WITH_LEN("EXPORT_SET") }, BUILDER(Create_func_export_set)},
- { { C_STRING_WITH_LEN("EXTERIORRING") }, GEOM_BUILDER(Create_func_exteriorring)},
- { { C_STRING_WITH_LEN("EXTRACTVALUE") }, BUILDER(Create_func_xml_extractvalue)},
- { { C_STRING_WITH_LEN("FIELD") }, BUILDER(Create_func_field)},
- { { C_STRING_WITH_LEN("FIND_IN_SET") }, BUILDER(Create_func_find_in_set)},
- { { C_STRING_WITH_LEN("FLOOR") }, BUILDER(Create_func_floor)},
- { { C_STRING_WITH_LEN("FORMAT") }, BUILDER(Create_func_format)},
- { { C_STRING_WITH_LEN("FOUND_ROWS") }, BUILDER(Create_func_found_rows)},
- { { C_STRING_WITH_LEN("FROM_BASE64") }, BUILDER(Create_func_from_base64)},
- { { C_STRING_WITH_LEN("FROM_DAYS") }, BUILDER(Create_func_from_days)},
- { { C_STRING_WITH_LEN("FROM_UNIXTIME") }, BUILDER(Create_func_from_unixtime)},
- { { C_STRING_WITH_LEN("GEOMCOLLFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { C_STRING_WITH_LEN("GEOMCOLLFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { C_STRING_WITH_LEN("GEOMETRYCOLLECTIONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { C_STRING_WITH_LEN("GEOMETRYCOLLECTIONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { C_STRING_WITH_LEN("GEOMETRYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { C_STRING_WITH_LEN("GEOMETRYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { C_STRING_WITH_LEN("GEOMETRYN") }, GEOM_BUILDER(Create_func_geometryn)},
- { { C_STRING_WITH_LEN("GEOMETRYTYPE") }, GEOM_BUILDER(Create_func_geometry_type)},
- { { C_STRING_WITH_LEN("GEOMFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { C_STRING_WITH_LEN("GEOMFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { C_STRING_WITH_LEN("GET_LOCK") }, BUILDER(Create_func_get_lock)},
- { { C_STRING_WITH_LEN("GLENGTH") }, GEOM_BUILDER(Create_func_glength)},
- { { C_STRING_WITH_LEN("GREATEST") }, BUILDER(Create_func_greatest)},
- { { C_STRING_WITH_LEN("HEX") }, BUILDER(Create_func_hex)},
- { { C_STRING_WITH_LEN("IFNULL") }, BUILDER(Create_func_ifnull)},
- { { C_STRING_WITH_LEN("INET_ATON") }, BUILDER(Create_func_inet_aton)},
- { { C_STRING_WITH_LEN("INET_NTOA") }, BUILDER(Create_func_inet_ntoa)},
- { { C_STRING_WITH_LEN("INET6_ATON") }, BUILDER(Create_func_inet6_aton)},
- { { C_STRING_WITH_LEN("INET6_NTOA") }, BUILDER(Create_func_inet6_ntoa)},
- { { C_STRING_WITH_LEN("IS_IPV4") }, BUILDER(Create_func_is_ipv4)},
- { { C_STRING_WITH_LEN("IS_IPV6") }, BUILDER(Create_func_is_ipv6)},
- { { C_STRING_WITH_LEN("IS_IPV4_COMPAT") }, BUILDER(Create_func_is_ipv4_compat)},
- { { C_STRING_WITH_LEN("IS_IPV4_MAPPED") }, BUILDER(Create_func_is_ipv4_mapped)},
- { { C_STRING_WITH_LEN("INSTR") }, BUILDER(Create_func_instr)},
- { { C_STRING_WITH_LEN("INTERIORRINGN") }, GEOM_BUILDER(Create_func_interiorringn)},
- { { C_STRING_WITH_LEN("INTERSECTS") }, GEOM_BUILDER(Create_func_mbr_intersects)},
- { { C_STRING_WITH_LEN("ISCLOSED") }, GEOM_BUILDER(Create_func_isclosed)},
- { { C_STRING_WITH_LEN("ISEMPTY") }, GEOM_BUILDER(Create_func_isempty)},
- { { C_STRING_WITH_LEN("ISNULL") }, BUILDER(Create_func_isnull)},
- { { C_STRING_WITH_LEN("ISRING") }, GEOM_BUILDER(Create_func_isring)},
- { { C_STRING_WITH_LEN("ISSIMPLE") }, GEOM_BUILDER(Create_func_issimple)},
- { { C_STRING_WITH_LEN("IS_FREE_LOCK") }, BUILDER(Create_func_is_free_lock)},
- { { C_STRING_WITH_LEN("IS_USED_LOCK") }, BUILDER(Create_func_is_used_lock)},
- { { C_STRING_WITH_LEN("JSON_ARRAY") }, BUILDER(Create_func_json_array)},
- { { C_STRING_WITH_LEN("JSON_ARRAY_APPEND") }, BUILDER(Create_func_json_array_append)},
- { { C_STRING_WITH_LEN("JSON_ARRAY_INSERT") }, BUILDER(Create_func_json_array_insert)},
- { { C_STRING_WITH_LEN("JSON_COMPACT") }, BUILDER(Create_func_json_compact)},
- { { C_STRING_WITH_LEN("JSON_CONTAINS") }, BUILDER(Create_func_json_contains)},
- { { C_STRING_WITH_LEN("JSON_CONTAINS_PATH") }, BUILDER(Create_func_json_contains_path)},
- { { C_STRING_WITH_LEN("JSON_DEPTH") }, BUILDER(Create_func_json_depth)},
- { { C_STRING_WITH_LEN("JSON_DETAILED") }, BUILDER(Create_func_json_detailed)},
- { { C_STRING_WITH_LEN("JSON_EXISTS") }, BUILDER(Create_func_json_exists)},
- { { C_STRING_WITH_LEN("JSON_EXTRACT") }, BUILDER(Create_func_json_extract)},
- { { C_STRING_WITH_LEN("JSON_INSERT") }, BUILDER(Create_func_json_insert)},
- { { C_STRING_WITH_LEN("JSON_KEYS") }, BUILDER(Create_func_json_keys)},
- { { C_STRING_WITH_LEN("JSON_LENGTH") }, BUILDER(Create_func_json_length)},
- { { C_STRING_WITH_LEN("JSON_LOOSE") }, BUILDER(Create_func_json_loose)},
- { { C_STRING_WITH_LEN("JSON_MERGE") }, BUILDER(Create_func_json_merge)},
- { { C_STRING_WITH_LEN("JSON_MERGE_PATCH") }, BUILDER(Create_func_json_merge_patch)},
- { { C_STRING_WITH_LEN("JSON_MERGE_PRESERVE") }, BUILDER(Create_func_json_merge)},
- { { C_STRING_WITH_LEN("JSON_QUERY") }, BUILDER(Create_func_json_query)},
- { { C_STRING_WITH_LEN("JSON_QUOTE") }, BUILDER(Create_func_json_quote)},
- { { C_STRING_WITH_LEN("JSON_OBJECT") }, BUILDER(Create_func_json_object)},
- { { C_STRING_WITH_LEN("JSON_REMOVE") }, BUILDER(Create_func_json_remove)},
- { { C_STRING_WITH_LEN("JSON_REPLACE") }, BUILDER(Create_func_json_replace)},
- { { C_STRING_WITH_LEN("JSON_SET") }, BUILDER(Create_func_json_set)},
- { { C_STRING_WITH_LEN("JSON_SEARCH") }, BUILDER(Create_func_json_search)},
- { { C_STRING_WITH_LEN("JSON_TYPE") }, BUILDER(Create_func_json_type)},
- { { C_STRING_WITH_LEN("JSON_UNQUOTE") }, BUILDER(Create_func_json_unquote)},
- { { C_STRING_WITH_LEN("JSON_VALID") }, BUILDER(Create_func_json_valid)},
- { { C_STRING_WITH_LEN("JSON_VALUE") }, BUILDER(Create_func_json_value)},
- { { C_STRING_WITH_LEN("LAST_DAY") }, BUILDER(Create_func_last_day)},
- { { C_STRING_WITH_LEN("LAST_INSERT_ID") }, BUILDER(Create_func_last_insert_id)},
- { { C_STRING_WITH_LEN("LCASE") }, BUILDER(Create_func_lcase)},
- { { C_STRING_WITH_LEN("LEAST") }, BUILDER(Create_func_least)},
- { { C_STRING_WITH_LEN("LENGTH") }, BUILDER(Create_func_length)},
+ { { STRING_WITH_LEN("ABS") }, BUILDER(Create_func_abs)},
+ { { STRING_WITH_LEN("ACOS") }, BUILDER(Create_func_acos)},
+ { { 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)},
+ { { STRING_WITH_LEN("BIN") }, BUILDER(Create_func_bin)},
+ { { 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)},
+ { { STRING_WITH_LEN("COERCIBILITY") }, BUILDER(Create_func_coercibility)},
+ { { STRING_WITH_LEN("COLUMN_CHECK") }, BUILDER(Create_func_dyncol_check)},
+ { { STRING_WITH_LEN("COLUMN_EXISTS") }, BUILDER(Create_func_dyncol_exists)},
+ { { STRING_WITH_LEN("COLUMN_LIST") }, BUILDER(Create_func_dyncol_list)},
+ { { STRING_WITH_LEN("COLUMN_JSON") }, BUILDER(Create_func_dyncol_json)},
+ { { STRING_WITH_LEN("COMPRESS") }, BUILDER(Create_func_compress)},
+ { { STRING_WITH_LEN("CONCAT") }, BUILDER(Create_func_concat)},
+ { { STRING_WITH_LEN("CONCAT_OPERATOR_ORACLE") }, BUILDER(Create_func_concat_operator_oracle)},
+ { { STRING_WITH_LEN("CONCAT_WS") }, BUILDER(Create_func_concat_ws)},
+ { { 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)},
+ { { STRING_WITH_LEN("DAYOFWEEK") }, BUILDER(Create_func_dayofweek)},
+ { { STRING_WITH_LEN("DAYOFYEAR") }, BUILDER(Create_func_dayofyear)},
+ { { STRING_WITH_LEN("DEGREES") }, BUILDER(Create_func_degrees)},
+ { { STRING_WITH_LEN("DECODE_HISTOGRAM") }, BUILDER(Create_func_decode_histogram)},
+ { { 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)},
+ { { STRING_WITH_LEN("FLOOR") }, BUILDER(Create_func_floor)},
+ { { STRING_WITH_LEN("FORMAT") }, BUILDER(Create_func_format)},
+ { { STRING_WITH_LEN("FOUND_ROWS") }, BUILDER(Create_func_found_rows)},
+ { { 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)},
+ { { STRING_WITH_LEN("JSON_ARRAY_APPEND") }, BUILDER(Create_func_json_array_append)},
+ { { STRING_WITH_LEN("JSON_ARRAY_INSERT") }, BUILDER(Create_func_json_array_insert)},
+ { { STRING_WITH_LEN("JSON_COMPACT") }, BUILDER(Create_func_json_compact)},
+ { { STRING_WITH_LEN("JSON_CONTAINS") }, BUILDER(Create_func_json_contains)},
+ { { STRING_WITH_LEN("JSON_CONTAINS_PATH") }, BUILDER(Create_func_json_contains_path)},
+ { { STRING_WITH_LEN("JSON_DEPTH") }, BUILDER(Create_func_json_depth)},
+ { { STRING_WITH_LEN("JSON_DETAILED") }, BUILDER(Create_func_json_detailed)},
+ { { STRING_WITH_LEN("JSON_EXISTS") }, BUILDER(Create_func_json_exists)},
+ { { STRING_WITH_LEN("JSON_EXTRACT") }, BUILDER(Create_func_json_extract)},
+ { { STRING_WITH_LEN("JSON_INSERT") }, BUILDER(Create_func_json_insert)},
+ { { STRING_WITH_LEN("JSON_KEYS") }, BUILDER(Create_func_json_keys)},
+ { { STRING_WITH_LEN("JSON_LENGTH") }, BUILDER(Create_func_json_length)},
+ { { STRING_WITH_LEN("JSON_LOOSE") }, BUILDER(Create_func_json_loose)},
+ { { STRING_WITH_LEN("JSON_MERGE") }, BUILDER(Create_func_json_merge)},
+ { { STRING_WITH_LEN("JSON_MERGE_PATCH") }, BUILDER(Create_func_json_merge_patch)},
+ { { STRING_WITH_LEN("JSON_MERGE_PRESERVE") }, BUILDER(Create_func_json_merge)},
+ { { STRING_WITH_LEN("JSON_QUERY") }, BUILDER(Create_func_json_query)},
+ { { STRING_WITH_LEN("JSON_QUOTE") }, BUILDER(Create_func_json_quote)},
+ { { STRING_WITH_LEN("JSON_OBJECT") }, BUILDER(Create_func_json_object)},
+ { { STRING_WITH_LEN("JSON_REMOVE") }, BUILDER(Create_func_json_remove)},
+ { { STRING_WITH_LEN("JSON_REPLACE") }, BUILDER(Create_func_json_replace)},
+ { { STRING_WITH_LEN("JSON_SET") }, BUILDER(Create_func_json_set)},
+ { { STRING_WITH_LEN("JSON_SEARCH") }, BUILDER(Create_func_json_search)},
+ { { STRING_WITH_LEN("JSON_TYPE") }, BUILDER(Create_func_json_type)},
+ { { STRING_WITH_LEN("JSON_UNQUOTE") }, BUILDER(Create_func_json_unquote)},
+ { { STRING_WITH_LEN("JSON_VALID") }, BUILDER(Create_func_json_valid)},
+ { { STRING_WITH_LEN("JSON_VALUE") }, BUILDER(Create_func_json_value)},
+ { { STRING_WITH_LEN("LAST_DAY") }, BUILDER(Create_func_last_day)},
+ { { STRING_WITH_LEN("LAST_INSERT_ID") }, BUILDER(Create_func_last_insert_id)},
+ { { STRING_WITH_LEN("LCASE") }, BUILDER(Create_func_lcase)},
+ { { STRING_WITH_LEN("LEAST") }, BUILDER(Create_func_least)},
+ { { STRING_WITH_LEN("LENGTH") }, BUILDER(Create_func_length)},
+ { { STRING_WITH_LEN("LENGTHB") }, BUILDER(Create_func_octet_length)},
#ifndef DBUG_OFF
- { { C_STRING_WITH_LEN("LIKE_RANGE_MIN") }, BUILDER(Create_func_like_range_min)},
- { { C_STRING_WITH_LEN("LIKE_RANGE_MAX") }, BUILDER(Create_func_like_range_max)},
+ { { 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
- { { C_STRING_WITH_LEN("LINEFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { C_STRING_WITH_LEN("LINEFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { C_STRING_WITH_LEN("LINESTRINGFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { C_STRING_WITH_LEN("LINESTRINGFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { C_STRING_WITH_LEN("LN") }, BUILDER(Create_func_ln)},
- { { C_STRING_WITH_LEN("LOAD_FILE") }, BUILDER(Create_func_load_file)},
- { { C_STRING_WITH_LEN("LOCATE") }, BUILDER(Create_func_locate)},
- { { C_STRING_WITH_LEN("LOG") }, BUILDER(Create_func_log)},
- { { C_STRING_WITH_LEN("LOG10") }, BUILDER(Create_func_log10)},
- { { C_STRING_WITH_LEN("LOG2") }, BUILDER(Create_func_log2)},
- { { C_STRING_WITH_LEN("LOWER") }, BUILDER(Create_func_lcase)},
- { { C_STRING_WITH_LEN("LPAD") }, BUILDER(Create_func_lpad)},
- { { C_STRING_WITH_LEN("LTRIM") }, BUILDER(Create_func_ltrim)},
- { { C_STRING_WITH_LEN("MAKEDATE") }, BUILDER(Create_func_makedate)},
- { { C_STRING_WITH_LEN("MAKETIME") }, BUILDER(Create_func_maketime)},
- { { C_STRING_WITH_LEN("MAKE_SET") }, BUILDER(Create_func_make_set)},
- { { C_STRING_WITH_LEN("MASTER_GTID_WAIT") }, BUILDER(Create_func_master_gtid_wait)},
- { { C_STRING_WITH_LEN("MASTER_POS_WAIT") }, BUILDER(Create_func_master_pos_wait)},
- { { C_STRING_WITH_LEN("MBRCONTAINS") }, GEOM_BUILDER(Create_func_mbr_contains)},
- { { C_STRING_WITH_LEN("MBRDISJOINT") }, GEOM_BUILDER(Create_func_mbr_disjoint)},
- { { C_STRING_WITH_LEN("MBREQUAL") }, GEOM_BUILDER(Create_func_mbr_equals)},
- { { C_STRING_WITH_LEN("MBREQUALS") }, GEOM_BUILDER(Create_func_mbr_equals)},
- { { C_STRING_WITH_LEN("MBRINTERSECTS") }, GEOM_BUILDER(Create_func_mbr_intersects)},
- { { C_STRING_WITH_LEN("MBROVERLAPS") }, GEOM_BUILDER(Create_func_mbr_overlaps)},
- { { C_STRING_WITH_LEN("MBRTOUCHES") }, GEOM_BUILDER(Create_func_touches)},
- { { C_STRING_WITH_LEN("MBRWITHIN") }, GEOM_BUILDER(Create_func_mbr_within)},
- { { C_STRING_WITH_LEN("MD5") }, BUILDER(Create_func_md5)},
- { { C_STRING_WITH_LEN("MLINEFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { C_STRING_WITH_LEN("MLINEFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { C_STRING_WITH_LEN("MONTHNAME") }, BUILDER(Create_func_monthname)},
- { { C_STRING_WITH_LEN("MPOINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { C_STRING_WITH_LEN("MPOINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { C_STRING_WITH_LEN("MPOLYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { C_STRING_WITH_LEN("MPOLYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { C_STRING_WITH_LEN("MULTILINESTRINGFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { C_STRING_WITH_LEN("MULTILINESTRINGFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { C_STRING_WITH_LEN("MULTIPOINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { C_STRING_WITH_LEN("MULTIPOINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { C_STRING_WITH_LEN("MULTIPOLYGONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { C_STRING_WITH_LEN("MULTIPOLYGONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { C_STRING_WITH_LEN("NAME_CONST") }, BUILDER(Create_func_name_const)},
- { { C_STRING_WITH_LEN("NULLIF") }, BUILDER(Create_func_nullif)},
- { { C_STRING_WITH_LEN("NUMGEOMETRIES") }, GEOM_BUILDER(Create_func_numgeometries)},
- { { C_STRING_WITH_LEN("NUMINTERIORRINGS") }, GEOM_BUILDER(Create_func_numinteriorring)},
- { { C_STRING_WITH_LEN("NUMPOINTS") }, GEOM_BUILDER(Create_func_numpoints)},
- { { C_STRING_WITH_LEN("OCT") }, BUILDER(Create_func_oct)},
- { { C_STRING_WITH_LEN("OCTET_LENGTH") }, BUILDER(Create_func_length)},
- { { C_STRING_WITH_LEN("ORD") }, BUILDER(Create_func_ord)},
- { { C_STRING_WITH_LEN("OVERLAPS") }, GEOM_BUILDER(Create_func_mbr_overlaps)},
- { { C_STRING_WITH_LEN("PERIOD_ADD") }, BUILDER(Create_func_period_add)},
- { { C_STRING_WITH_LEN("PERIOD_DIFF") }, BUILDER(Create_func_period_diff)},
- { { C_STRING_WITH_LEN("PI") }, BUILDER(Create_func_pi)},
- { { C_STRING_WITH_LEN("POINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { C_STRING_WITH_LEN("POINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { C_STRING_WITH_LEN("POINTN") }, GEOM_BUILDER(Create_func_pointn)},
- { { C_STRING_WITH_LEN("POINTONSURFACE") }, GEOM_BUILDER(Create_func_pointonsurface)},
- { { C_STRING_WITH_LEN("POLYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { C_STRING_WITH_LEN("POLYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { C_STRING_WITH_LEN("POLYGONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { C_STRING_WITH_LEN("POLYGONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { C_STRING_WITH_LEN("POW") }, BUILDER(Create_func_pow)},
- { { C_STRING_WITH_LEN("POWER") }, BUILDER(Create_func_pow)},
- { { C_STRING_WITH_LEN("QUOTE") }, BUILDER(Create_func_quote)},
- { { C_STRING_WITH_LEN("REGEXP_INSTR") }, BUILDER(Create_func_regexp_instr)},
- { { C_STRING_WITH_LEN("REGEXP_REPLACE") }, BUILDER(Create_func_regexp_replace)},
- { { C_STRING_WITH_LEN("REGEXP_SUBSTR") }, BUILDER(Create_func_regexp_substr)},
- { { C_STRING_WITH_LEN("RADIANS") }, BUILDER(Create_func_radians)},
- { { C_STRING_WITH_LEN("RAND") }, BUILDER(Create_func_rand)},
- { { C_STRING_WITH_LEN("RELEASE_LOCK") }, BUILDER(Create_func_release_lock)},
- { { C_STRING_WITH_LEN("REVERSE") }, BUILDER(Create_func_reverse)},
- { { C_STRING_WITH_LEN("ROUND") }, BUILDER(Create_func_round)},
- { { C_STRING_WITH_LEN("RPAD") }, BUILDER(Create_func_rpad)},
- { { C_STRING_WITH_LEN("RTRIM") }, BUILDER(Create_func_rtrim)},
- { { C_STRING_WITH_LEN("SEC_TO_TIME") }, BUILDER(Create_func_sec_to_time)},
- { { C_STRING_WITH_LEN("SHA") }, BUILDER(Create_func_sha)},
- { { C_STRING_WITH_LEN("SHA1") }, BUILDER(Create_func_sha)},
- { { C_STRING_WITH_LEN("SHA2") }, BUILDER(Create_func_sha2)},
- { { C_STRING_WITH_LEN("SIGN") }, BUILDER(Create_func_sign)},
- { { C_STRING_WITH_LEN("SIN") }, BUILDER(Create_func_sin)},
- { { C_STRING_WITH_LEN("SLEEP") }, BUILDER(Create_func_sleep)},
- { { C_STRING_WITH_LEN("SOUNDEX") }, BUILDER(Create_func_soundex)},
- { { C_STRING_WITH_LEN("SPACE") }, BUILDER(Create_func_space)},
- { { C_STRING_WITH_LEN("SQRT") }, BUILDER(Create_func_sqrt)},
- { { C_STRING_WITH_LEN("SRID") }, GEOM_BUILDER(Create_func_srid)},
- { { C_STRING_WITH_LEN("STARTPOINT") }, GEOM_BUILDER(Create_func_startpoint)},
- { { C_STRING_WITH_LEN("STRCMP") }, BUILDER(Create_func_strcmp)},
- { { C_STRING_WITH_LEN("STR_TO_DATE") }, BUILDER(Create_func_str_to_date)},
- { { C_STRING_WITH_LEN("ST_AREA") }, GEOM_BUILDER(Create_func_area)},
- { { C_STRING_WITH_LEN("ST_ASBINARY") }, GEOM_BUILDER(Create_func_as_wkb)},
- { { C_STRING_WITH_LEN("ST_ASGEOJSON") }, GEOM_BUILDER(Create_func_as_geojson)},
- { { C_STRING_WITH_LEN("ST_ASTEXT") }, GEOM_BUILDER(Create_func_as_wkt)},
- { { C_STRING_WITH_LEN("ST_ASWKB") }, GEOM_BUILDER(Create_func_as_wkb)},
- { { C_STRING_WITH_LEN("ST_ASWKT") }, GEOM_BUILDER(Create_func_as_wkt)},
- { { C_STRING_WITH_LEN("ST_BOUNDARY") }, GEOM_BUILDER(Create_func_boundary)},
- { { C_STRING_WITH_LEN("ST_BUFFER") }, GEOM_BUILDER(Create_func_buffer)},
- { { C_STRING_WITH_LEN("ST_CENTROID") }, GEOM_BUILDER(Create_func_centroid)},
- { { C_STRING_WITH_LEN("ST_CONTAINS") }, GEOM_BUILDER(Create_func_contains)},
- { { C_STRING_WITH_LEN("ST_CONVEXHULL") }, GEOM_BUILDER(Create_func_convexhull)},
- { { C_STRING_WITH_LEN("ST_CROSSES") }, GEOM_BUILDER(Create_func_crosses)},
- { { C_STRING_WITH_LEN("ST_DIFFERENCE") }, GEOM_BUILDER(Create_func_difference)},
- { { C_STRING_WITH_LEN("ST_DIMENSION") }, GEOM_BUILDER(Create_func_dimension)},
- { { C_STRING_WITH_LEN("ST_DISJOINT") }, GEOM_BUILDER(Create_func_disjoint)},
- { { C_STRING_WITH_LEN("ST_DISTANCE") }, GEOM_BUILDER(Create_func_distance)},
- { { C_STRING_WITH_LEN("ST_ENDPOINT") }, GEOM_BUILDER(Create_func_endpoint)},
- { { C_STRING_WITH_LEN("ST_ENVELOPE") }, GEOM_BUILDER(Create_func_envelope)},
- { { C_STRING_WITH_LEN("ST_EQUALS") }, GEOM_BUILDER(Create_func_equals)},
- { { C_STRING_WITH_LEN("ST_EXTERIORRING") }, GEOM_BUILDER(Create_func_exteriorring)},
- { { C_STRING_WITH_LEN("ST_GEOMCOLLFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { C_STRING_WITH_LEN("ST_GEOMCOLLFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { C_STRING_WITH_LEN("ST_GEOMETRYCOLLECTIONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { C_STRING_WITH_LEN("ST_GEOMETRYCOLLECTIONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { C_STRING_WITH_LEN("ST_GEOMETRYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { C_STRING_WITH_LEN("ST_GEOMETRYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { C_STRING_WITH_LEN("ST_GEOMETRYN") }, GEOM_BUILDER(Create_func_geometryn)},
- { { C_STRING_WITH_LEN("ST_GEOMETRYTYPE") }, GEOM_BUILDER(Create_func_geometry_type)},
- { { C_STRING_WITH_LEN("ST_GEOMFROMGEOJSON") }, GEOM_BUILDER(Create_func_geometry_from_json)},
- { { C_STRING_WITH_LEN("ST_GEOMFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { C_STRING_WITH_LEN("ST_GEOMFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { 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)},
+ { { STRING_WITH_LEN("LOG") }, BUILDER(Create_func_log)},
+ { { STRING_WITH_LEN("LOG10") }, BUILDER(Create_func_log10)},
+ { { STRING_WITH_LEN("LOG2") }, BUILDER(Create_func_log2)},
+ { { STRING_WITH_LEN("LOWER") }, BUILDER(Create_func_lcase)},
+ { { STRING_WITH_LEN("LPAD") }, BUILDER(Create_func_lpad)},
+ { { STRING_WITH_LEN("LPAD_ORACLE") }, BUILDER(Create_func_lpad_oracle)},
+ { { STRING_WITH_LEN("LTRIM") }, BUILDER(Create_func_ltrim)},
+ { { STRING_WITH_LEN("LTRIM_ORACLE") }, BUILDER(Create_func_ltrim_oracle)},
+ { { STRING_WITH_LEN("MAKEDATE") }, BUILDER(Create_func_makedate)},
+ { { STRING_WITH_LEN("MAKETIME") }, BUILDER(Create_func_maketime)},
+ { { 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)},
+ { { STRING_WITH_LEN("REGEXP_INSTR") }, BUILDER(Create_func_regexp_instr)},
+ { { STRING_WITH_LEN("REGEXP_REPLACE") }, BUILDER(Create_func_regexp_replace)},
+ { { 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_LOCK") }, BUILDER(Create_func_release_lock)},
+ { { STRING_WITH_LEN("REPLACE_ORACLE") },
+ BUILDER(Create_func_replace_oracle)},
+ { { STRING_WITH_LEN("REVERSE") }, BUILDER(Create_func_reverse)},
+ { { STRING_WITH_LEN("ROUND") }, BUILDER(Create_func_round)},
+ { { STRING_WITH_LEN("RPAD") }, BUILDER(Create_func_rpad)},
+ { { STRING_WITH_LEN("RPAD_ORACLE") }, BUILDER(Create_func_rpad_oracle)},
+ { { STRING_WITH_LEN("RTRIM") }, BUILDER(Create_func_rtrim)},
+ { { STRING_WITH_LEN("RTRIM_ORACLE") }, BUILDER(Create_func_rtrim_oracle)},
+ { { STRING_WITH_LEN("SEC_TO_TIME") }, BUILDER(Create_func_sec_to_time)},
+ { { STRING_WITH_LEN("SHA") }, BUILDER(Create_func_sha)},
+ { { STRING_WITH_LEN("SHA1") }, BUILDER(Create_func_sha)},
+ { { STRING_WITH_LEN("SHA2") }, BUILDER(Create_func_sha2)},
+ { { STRING_WITH_LEN("SIGN") }, BUILDER(Create_func_sign)},
+ { { STRING_WITH_LEN("SIN") }, BUILDER(Create_func_sin)},
+ { { STRING_WITH_LEN("SLEEP") }, BUILDER(Create_func_sleep)},
+ { { 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
- { { C_STRING_WITH_LEN("ST_GIS_DEBUG") }, GEOM_BUILDER(Create_func_gis_debug)},
+ { { STRING_WITH_LEN("ST_GIS_DEBUG") }, GEOM_BUILDER(Create_func_gis_debug)},
#endif
- { { C_STRING_WITH_LEN("ST_EQUALS") }, GEOM_BUILDER(Create_func_equals)},
- { { C_STRING_WITH_LEN("ST_INTERIORRINGN") }, GEOM_BUILDER(Create_func_interiorringn)},
- { { C_STRING_WITH_LEN("ST_INTERSECTS") }, GEOM_BUILDER(Create_func_intersects)},
- { { C_STRING_WITH_LEN("ST_INTERSECTION") }, GEOM_BUILDER(Create_func_intersection)},
- { { C_STRING_WITH_LEN("ST_ISCLOSED") }, GEOM_BUILDER(Create_func_isclosed)},
- { { C_STRING_WITH_LEN("ST_ISEMPTY") }, GEOM_BUILDER(Create_func_isempty)},
- { { C_STRING_WITH_LEN("ST_ISRING") }, GEOM_BUILDER(Create_func_isring)},
- { { C_STRING_WITH_LEN("ST_ISSIMPLE") }, GEOM_BUILDER(Create_func_issimple)},
- { { C_STRING_WITH_LEN("ST_LENGTH") }, GEOM_BUILDER(Create_func_glength)},
- { { C_STRING_WITH_LEN("ST_LINEFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { C_STRING_WITH_LEN("ST_LINEFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { C_STRING_WITH_LEN("ST_LINESTRINGFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { C_STRING_WITH_LEN("ST_LINESTRINGFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { C_STRING_WITH_LEN("ST_MLINEFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { C_STRING_WITH_LEN("ST_MLINEFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { C_STRING_WITH_LEN("ST_MPOINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { C_STRING_WITH_LEN("ST_MPOINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { C_STRING_WITH_LEN("ST_MPOLYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { C_STRING_WITH_LEN("ST_MPOLYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { C_STRING_WITH_LEN("ST_MULTILINESTRINGFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { C_STRING_WITH_LEN("ST_MULTILINESTRINGFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { C_STRING_WITH_LEN("ST_MULTIPOINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { C_STRING_WITH_LEN("ST_MULTIPOINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { C_STRING_WITH_LEN("ST_MULTIPOLYGONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { C_STRING_WITH_LEN("ST_MULTIPOLYGONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { C_STRING_WITH_LEN("ST_NUMGEOMETRIES") }, GEOM_BUILDER(Create_func_numgeometries)},
- { { C_STRING_WITH_LEN("ST_NUMINTERIORRINGS") }, GEOM_BUILDER(Create_func_numinteriorring)},
- { { C_STRING_WITH_LEN("ST_NUMPOINTS") }, GEOM_BUILDER(Create_func_numpoints)},
- { { C_STRING_WITH_LEN("ST_OVERLAPS") }, GEOM_BUILDER(Create_func_overlaps)},
- { { C_STRING_WITH_LEN("ST_POINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { C_STRING_WITH_LEN("ST_POINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { C_STRING_WITH_LEN("ST_POINTN") }, GEOM_BUILDER(Create_func_pointn)},
- { { C_STRING_WITH_LEN("ST_POINTONSURFACE") }, GEOM_BUILDER(Create_func_pointonsurface)},
- { { C_STRING_WITH_LEN("ST_POLYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { C_STRING_WITH_LEN("ST_POLYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { C_STRING_WITH_LEN("ST_POLYGONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { C_STRING_WITH_LEN("ST_POLYGONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { C_STRING_WITH_LEN("ST_RELATE") }, GEOM_BUILDER(Create_func_relate)},
- { { C_STRING_WITH_LEN("ST_SRID") }, GEOM_BUILDER(Create_func_srid)},
- { { C_STRING_WITH_LEN("ST_STARTPOINT") }, GEOM_BUILDER(Create_func_startpoint)},
- { { C_STRING_WITH_LEN("ST_SYMDIFFERENCE") }, GEOM_BUILDER(Create_func_symdifference)},
- { { C_STRING_WITH_LEN("ST_TOUCHES") }, GEOM_BUILDER(Create_func_touches)},
- { { C_STRING_WITH_LEN("ST_UNION") }, GEOM_BUILDER(Create_func_union)},
- { { C_STRING_WITH_LEN("ST_WITHIN") }, GEOM_BUILDER(Create_func_within)},
- { { C_STRING_WITH_LEN("ST_X") }, GEOM_BUILDER(Create_func_x)},
- { { C_STRING_WITH_LEN("ST_Y") }, GEOM_BUILDER(Create_func_y)},
- { { C_STRING_WITH_LEN("SUBSTRING_INDEX") }, BUILDER(Create_func_substr_index)},
- { { C_STRING_WITH_LEN("SUBTIME") }, BUILDER(Create_func_subtime)},
- { { C_STRING_WITH_LEN("TAN") }, BUILDER(Create_func_tan)},
- { { C_STRING_WITH_LEN("TIMEDIFF") }, BUILDER(Create_func_timediff)},
- { { C_STRING_WITH_LEN("TIME_FORMAT") }, BUILDER(Create_func_time_format)},
- { { C_STRING_WITH_LEN("TIME_TO_SEC") }, BUILDER(Create_func_time_to_sec)},
- { { C_STRING_WITH_LEN("TOUCHES") }, GEOM_BUILDER(Create_func_touches)},
- { { C_STRING_WITH_LEN("TO_BASE64") }, BUILDER(Create_func_to_base64)},
- { { C_STRING_WITH_LEN("TO_DAYS") }, BUILDER(Create_func_to_days)},
- { { C_STRING_WITH_LEN("TO_SECONDS") }, BUILDER(Create_func_to_seconds)},
- { { C_STRING_WITH_LEN("UCASE") }, BUILDER(Create_func_ucase)},
- { { C_STRING_WITH_LEN("UNCOMPRESS") }, BUILDER(Create_func_uncompress)},
- { { C_STRING_WITH_LEN("UNCOMPRESSED_LENGTH") }, BUILDER(Create_func_uncompressed_length)},
- { { C_STRING_WITH_LEN("UNHEX") }, BUILDER(Create_func_unhex)},
- { { C_STRING_WITH_LEN("UNIX_TIMESTAMP") }, BUILDER(Create_func_unix_timestamp)},
- { { C_STRING_WITH_LEN("UPDATEXML") }, BUILDER(Create_func_xml_update)},
- { { C_STRING_WITH_LEN("UPPER") }, BUILDER(Create_func_ucase)},
- { { C_STRING_WITH_LEN("UUID") }, BUILDER(Create_func_uuid)},
- { { C_STRING_WITH_LEN("UUID_SHORT") }, BUILDER(Create_func_uuid_short)},
- { { C_STRING_WITH_LEN("VERSION") }, BUILDER(Create_func_version)},
- { { C_STRING_WITH_LEN("WEEKDAY") }, BUILDER(Create_func_weekday)},
- { { C_STRING_WITH_LEN("WEEKOFYEAR") }, BUILDER(Create_func_weekofyear)},
- { { C_STRING_WITH_LEN("WITHIN") }, GEOM_BUILDER(Create_func_within)},
- { { C_STRING_WITH_LEN("X") }, GEOM_BUILDER(Create_func_x)},
- { { C_STRING_WITH_LEN("Y") }, GEOM_BUILDER(Create_func_y)},
- { { C_STRING_WITH_LEN("YEARWEEK") }, BUILDER(Create_func_year_week)},
+ { { 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)},
+ { { STRING_WITH_LEN("SUBTIME") }, BUILDER(Create_func_subtime)},
+ { { STRING_WITH_LEN("TAN") }, BUILDER(Create_func_tan)},
+ { { 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)},
+ { { STRING_WITH_LEN("UCASE") }, BUILDER(Create_func_ucase)},
+ { { STRING_WITH_LEN("UNCOMPRESS") }, BUILDER(Create_func_uncompress)},
+ { { STRING_WITH_LEN("UNCOMPRESSED_LENGTH") }, BUILDER(Create_func_uncompressed_length)},
+ { { STRING_WITH_LEN("UNHEX") }, BUILDER(Create_func_unhex)},
+ { { STRING_WITH_LEN("UNIX_TIMESTAMP") }, BUILDER(Create_func_unix_timestamp)},
+ { { STRING_WITH_LEN("UPDATEXML") }, BUILDER(Create_func_xml_update)},
+ { { STRING_WITH_LEN("UPPER") }, BUILDER(Create_func_ucase)},
+ { { STRING_WITH_LEN("UUID") }, BUILDER(Create_func_uuid)},
+ { { STRING_WITH_LEN("UUID_SHORT") }, BUILDER(Create_func_uuid_short)},
+ { { 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)},
+ { { 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}
};
@@ -7100,8 +7394,6 @@ get_native_fct_hash_key(const uchar *buff, size_t *length,
int item_create_init()
{
- Native_func_registry *func;
-
DBUG_ENTER("item_create_init");
if (my_hash_init(& native_functions_hash,
@@ -7114,7 +7406,16 @@ int item_create_init()
MYF(0)))
DBUG_RETURN(1);
- for (func= func_array; func->builder != NULL; func++)
+ DBUG_RETURN(item_create_append(func_array));
+}
+
+int item_create_append(Native_func_registry array[])
+{
+ Native_func_registry *func;
+
+ DBUG_ENTER("item_create_append");
+
+ for (func= array; func->builder != NULL; func++)
{
if (my_hash_insert(& native_functions_hash, (uchar*) func))
DBUG_RETURN(1);
@@ -7146,15 +7447,15 @@ void item_create_cleanup()
}
Create_func *
-find_native_function_builder(THD *thd, LEX_STRING name)
+find_native_function_builder(THD *thd, const LEX_CSTRING *name)
{
Native_func_registry *func;
Create_func *builder= NULL;
/* Thread safe */
- func= (Native_func_registry*) my_hash_search(& native_functions_hash,
- (uchar*) name.str,
- name.length);
+ func= (Native_func_registry*) my_hash_search(&native_functions_hash,
+ (uchar*) name->str,
+ name->length);
if (func)
{
@@ -7171,112 +7472,6 @@ find_qualified_function_builder(THD *thd)
}
-Item *
-create_func_cast(THD *thd, Item *a, Cast_target cast_type,
- const char *c_len, const char *c_dec,
- CHARSET_INFO *cs)
-{
- Item *UNINIT_VAR(res);
- ulonglong length= 0, decimals= 0;
- int error;
-
- /*
- We don't have to check for error here as sql_yacc.yy has guaranteed
- that the values are in range of ulonglong
- */
- if (c_len)
- length= (ulonglong) my_strtoll10(c_len, NULL, &error);
- if (c_dec)
- decimals= (ulonglong) my_strtoll10(c_dec, NULL, &error);
-
- switch (cast_type) {
- case ITEM_CAST_BINARY:
- res= new (thd->mem_root) Item_func_binary(thd, a);
- break;
- case ITEM_CAST_SIGNED_INT:
- res= new (thd->mem_root) Item_func_signed(thd, a);
- break;
- case ITEM_CAST_UNSIGNED_INT:
- res= new (thd->mem_root) Item_func_unsigned(thd, a);
- break;
- case ITEM_CAST_DATE:
- res= new (thd->mem_root) Item_date_typecast(thd, a);
- break;
- case ITEM_CAST_TIME:
- if (decimals > MAX_DATETIME_PRECISION)
- {
- wrong_precision_error(ER_TOO_BIG_PRECISION, a, decimals,
- MAX_DATETIME_PRECISION);
- return 0;
- }
- res= new (thd->mem_root) Item_time_typecast(thd, a, (uint) decimals);
- break;
- case ITEM_CAST_DATETIME:
- if (decimals > MAX_DATETIME_PRECISION)
- {
- wrong_precision_error(ER_TOO_BIG_PRECISION, a, decimals,
- MAX_DATETIME_PRECISION);
- return 0;
- }
- res= new (thd->mem_root) Item_datetime_typecast(thd, a, (uint) decimals);
- break;
- case ITEM_CAST_DECIMAL:
- {
- uint len;
- uint dec;
- if (get_length_and_scale(length, decimals, &len, &dec,
- DECIMAL_MAX_PRECISION, DECIMAL_MAX_SCALE,
- a))
- return NULL;
- res= new (thd->mem_root) Item_decimal_typecast(thd, a, len, dec);
- break;
- }
- case ITEM_CAST_DOUBLE:
- {
- uint len, dec;
- if (!c_len)
- {
- length= DBL_DIG+7;
- decimals= NOT_FIXED_DEC;
- }
- else if (get_length_and_scale(length, decimals, &len, &dec,
- DECIMAL_MAX_PRECISION, NOT_FIXED_DEC-1,
- a))
- return NULL;
- res= new (thd->mem_root) Item_double_typecast(thd, a, (uint) length,
- (uint) decimals);
- break;
- }
- case ITEM_CAST_CHAR:
- {
- int len= -1;
- CHARSET_INFO *real_cs= (cs ? cs : thd->variables.collation_connection);
- if (c_len)
- {
- if (length > MAX_FIELD_BLOBLENGTH)
- {
- char buff[1024];
- String buf(buff, sizeof(buff), system_charset_info);
- my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), item_name(a, &buf),
- MAX_FIELD_BLOBLENGTH);
- return NULL;
- }
- len= (int) length;
- }
- res= new (thd->mem_root) Item_char_typecast(thd, a, len, real_cs);
- break;
- }
- default:
- {
- DBUG_ASSERT(0);
- res= 0;
- break;
- }
- }
- return res;
-}
-
-
static bool
have_important_literal_warnings(const MYSQL_TIME_STATUS *status)
{
@@ -7295,7 +7490,7 @@ have_important_literal_warnings(const MYSQL_TIME_STATUS *status)
*/
Item *create_temporal_literal(THD *thd,
- const char *str, uint length,
+ const char *str, size_t length,
CHARSET_INFO *cs,
enum_field_types type,
bool send_error)
@@ -7331,7 +7526,7 @@ Item *create_temporal_literal(THD *thd,
DBUG_ASSERT(0);
}
- if (item)
+ if (likely(item))
{
if (status.warnings) // e.g. a note on nanosecond truncation
{
@@ -7434,13 +7629,14 @@ Item *create_func_dyncol_delete(THD *thd, Item *str, List<Item> &nums)
Item *create_func_dyncol_get(THD *thd, Item *str, Item *num,
- Cast_target cast_type,
+ const Type_handler *handler,
const char *c_len, const char *c_dec,
CHARSET_INFO *cs)
{
Item *res;
- if (!(res= new (thd->mem_root) Item_dyncol_get(thd, str, num)))
+ if (likely(!(res= new (thd->mem_root) Item_dyncol_get(thd, str, num))))
return res; // Return NULL
- return create_func_cast(thd, res, cast_type, c_len, c_dec, cs);
+ return handler->create_typecast_item(thd, res,
+ Type_cast_attributes(c_len, c_dec, cs));
}
diff --git a/sql/item_create.h b/sql/item_create.h
index 97dc594b11c..b9d5ab9f377 100644
--- a/sql/item_create.h
+++ b/sql/item_create.h
@@ -19,6 +19,8 @@
#ifndef ITEM_CREATE_H
#define ITEM_CREATE_H
+#include "item_func.h" // Cast_target
+
typedef struct st_udf_func udf_func;
/**
@@ -56,7 +58,7 @@ public:
@param item_list The list of arguments to the function, can be NULL
@return An item representing the parsed function call, or NULL
*/
- virtual Item *create_func(THD *thd, LEX_STRING name, List<Item> *item_list) = 0;
+ virtual Item *create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list) = 0;
protected:
/** Constructor */
@@ -67,6 +69,38 @@ protected:
/**
+ 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>
+ which are syntactically correct (the syntax can refer to a UDF),
+ but semantically invalid for native functions.
+*/
+
+class Create_native_func : 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
+ @param name The native function name
+ @param item_list The function parameters, none of which are named
+ @return An item representing the function call
+ */
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name,
+ List<Item> *item_list) = 0;
+
+protected:
+ /** Constructor. */
+ Create_native_func() {}
+ /** Destructor. */
+ virtual ~Create_native_func() {}
+};
+
+
+/**
Function builder for qualified functions.
This builder is used with functions call using a qualified function name
syntax, as in <code>db.func(expr, expr, ...)</code>.
@@ -83,7 +117,8 @@ public:
@param item_list The list of arguments to the function, can be NULL
@return An item representing the parsed function call
*/
- virtual Item *create_func(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_func(THD *thd, LEX_CSTRING *name,
+ List<Item> *item_list);
/**
The builder create method, for qualified functions.
@@ -94,7 +129,7 @@ public:
@param item_list The list of arguments to the function, can be NULL
@return An item representing the parsed function call
*/
- virtual Item *create_with_db(THD *thd, LEX_STRING db, LEX_STRING name,
+ virtual Item *create_with_db(THD *thd, LEX_CSTRING *db, LEX_CSTRING *name,
bool use_explicit_name,
List<Item> *item_list) = 0;
@@ -112,7 +147,8 @@ protected:
@param name The native function name
@return The native function builder associated with the name, or NULL
*/
-extern Create_func * find_native_function_builder(THD *thd, LEX_STRING name);
+extern Create_func *find_native_function_builder(THD *thd,
+ const LEX_CSTRING *name);
/**
@@ -131,7 +167,8 @@ extern Create_qfunc * find_qualified_function_builder(THD *thd);
class Create_udf_func : public Create_func
{
public:
- virtual Item *create_func(THD *thd, LEX_STRING name, List<Item> *item_list);
+ virtual Item *create_func(THD *thd, LEX_CSTRING *name,
+ List<Item> *item_list);
/**
The builder create method, for User Defined Functions.
@@ -154,22 +191,8 @@ protected:
#endif
-/**
- Builder for cast expressions.
- @param thd The current thread
- @param a The item to cast
- @param cast_type the type casted into
- @param len TODO
- @param dec TODO
- @param cs The character set
-*/
-Item *
-create_func_cast(THD *thd, Item *a, Cast_target cast_type,
- const char *len, const char *dec,
- CHARSET_INFO *cs);
-
Item *create_temporal_literal(THD *thd,
- const char *str, uint length,
+ const char *str, size_t length,
CHARSET_INFO *cs,
enum_field_types type,
bool send_error);
@@ -183,7 +206,14 @@ Item *create_temporal_literal(THD *thd, const String *str,
type, send_error);
}
+struct Native_func_registry
+{
+ LEX_CSTRING name;
+ Create_func *builder;
+};
+
int item_create_init();
+int item_create_append(Native_func_registry array[]);
void item_create_cleanup();
Item *create_func_dyncol_create(THD *thd, List<DYNCALL_CREATE_DEF> &list);
@@ -191,7 +221,7 @@ Item *create_func_dyncol_add(THD *thd, Item *str,
List<DYNCALL_CREATE_DEF> &list);
Item *create_func_dyncol_delete(THD *thd, Item *str, List<Item> &nums);
Item *create_func_dyncol_get(THD *thd, Item *num, Item *str,
- Cast_target cast_type,
+ const Type_handler *handler,
const char *c_len, const char *c_dec,
CHARSET_INFO *cs);
Item *create_func_dyncol_json(THD *thd, Item *str);
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 9d588ce0eb1..c343fe26248 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -53,17 +53,18 @@
#include "sp.h"
#include "set_var.h"
#include "debug_sync.h"
+#include "sql_base.h"
#include "sql_cte.h"
#ifdef NO_EMBEDDED_ACCESS_CHECKS
#define sp_restore_security_context(A,B) while (0) {}
#endif
-bool check_reserved_words(LEX_STRING *name)
+bool check_reserved_words(const LEX_CSTRING *name)
{
- if (!my_strcasecmp(system_charset_info, name->str, "GLOBAL") ||
- !my_strcasecmp(system_charset_info, name->str, "LOCAL") ||
- !my_strcasecmp(system_charset_info, name->str, "SESSION"))
+ if (lex_string_eq(name, STRING_WITH_LEN("GLOBAL")) ||
+ lex_string_eq(name, STRING_WITH_LEN("LOCAL")) ||
+ lex_string_eq(name, STRING_WITH_LEN("SESSION")))
return TRUE;
return FALSE;
}
@@ -78,23 +79,35 @@ static inline bool test_if_sum_overflows_ull(ulonglong arg1, ulonglong arg2)
}
-void Item_args::set_arguments(THD *thd, List<Item> &list)
+/**
+ Allocate memory for arguments using tmp_args or thd->alloc().
+ @retval false - success
+ @retval true - error (arg_count is set to 0 for conveniece)
+*/
+bool Item_args::alloc_arguments(THD *thd, uint count)
{
- arg_count= list.elements;
- if (arg_count <= 2)
+ if (count <= 2)
{
args= tmp_arg;
+ return false;
}
- else if (!(args= (Item**) thd->alloc(sizeof(Item*) * arg_count)))
+ if ((args= (Item**) thd->alloc(sizeof(Item*) * count)) == NULL)
{
arg_count= 0;
- return;
+ return true;
}
- uint i= 0;
+ return false;
+}
+
+
+void Item_args::set_arguments(THD *thd, List<Item> &list)
+{
+ if (alloc_arguments(thd, list.elements))
+ return;
List_iterator_fast<Item> li(list);
Item *item;
- while ((item= li++))
- args[i++]= item;
+ for (arg_count= 0; (item= li++); )
+ args[arg_count++]= item;
}
@@ -129,6 +142,144 @@ void Item_func::sync_with_sum_func_and_with_field(List<Item> &list)
}
+bool Item_func::check_argument_types_like_args0() const
+{
+ if (arg_count < 2)
+ return false;
+ uint cols= args[0]->cols();
+ bool is_scalar= args[0]->type_handler()->is_scalar_type();
+ for (uint i= 1; i < arg_count; i++)
+ {
+ if (is_scalar != args[i]->type_handler()->is_scalar_type())
+ {
+ my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0),
+ args[0]->type_handler()->name().ptr(),
+ args[i]->type_handler()->name().ptr(), func_name());
+ return true;
+ }
+ if (args[i]->check_cols(cols))
+ return true;
+ }
+ return false;
+}
+
+
+bool Item_func::check_argument_types_or_binary(const Type_handler *handler,
+ uint start, uint end) const
+{
+ for (uint i= start; i < end ; i++)
+ {
+ DBUG_ASSERT(i < arg_count);
+ if (args[i]->check_type_or_binary(func_name(), handler))
+ return true;
+ }
+ return false;
+}
+
+
+bool Item_func::check_argument_types_traditional_scalar(uint start,
+ uint end) const
+{
+ for (uint i= start; i < end ; i++)
+ {
+ DBUG_ASSERT(i < arg_count);
+ if (args[i]->check_type_traditional_scalar(func_name()))
+ return true;
+ }
+ return false;
+}
+
+
+bool Item_func::check_argument_types_can_return_int(uint start,
+ uint end) const
+{
+ for (uint i= start; i < end ; i++)
+ {
+ DBUG_ASSERT(i < arg_count);
+ if (args[i]->check_type_can_return_int(func_name()))
+ return true;
+ }
+ return false;
+}
+
+
+bool Item_func::check_argument_types_can_return_real(uint start,
+ uint end) const
+{
+ for (uint i= start; i < end ; i++)
+ {
+ DBUG_ASSERT(i < arg_count);
+ if (args[i]->check_type_can_return_real(func_name()))
+ return true;
+ }
+ return false;
+}
+
+
+bool Item_func::check_argument_types_can_return_text(uint start,
+ uint end) const
+{
+ for (uint i= start; i < end ; i++)
+ {
+ DBUG_ASSERT(i < arg_count);
+ if (args[i]->check_type_can_return_text(func_name()))
+ return true;
+ }
+ return false;
+}
+
+
+bool Item_func::check_argument_types_can_return_str(uint start,
+ uint end) const
+{
+ for (uint i= start; i < end ; i++)
+ {
+ DBUG_ASSERT(i < arg_count);
+ if (args[i]->check_type_can_return_str(func_name()))
+ return true;
+ }
+ return false;
+}
+
+
+bool Item_func::check_argument_types_can_return_date(uint start,
+ uint end) const
+{
+ for (uint i= start; i < end ; i++)
+ {
+ DBUG_ASSERT(i < arg_count);
+ if (args[i]->check_type_can_return_date(func_name()))
+ return true;
+ }
+ return false;
+}
+
+
+bool Item_func::check_argument_types_can_return_time(uint start,
+ uint end) const
+{
+ for (uint i= start; i < end ; i++)
+ {
+ DBUG_ASSERT(i < arg_count);
+ if (args[i]->check_type_can_return_time(func_name()))
+ return true;
+ }
+ return false;
+}
+
+
+bool Item_func::check_argument_types_scalar(uint start, uint end) const
+{
+ for (uint i= start; i < end; i++)
+ {
+ DBUG_ASSERT(i < arg_count);
+ if (args[i]->check_type_scalar(func_name()))
+ return true;
+ }
+ return false;
+}
+
+
/*
Resolve references to table column for a function and its argument
@@ -198,23 +349,10 @@ Item_func::fix_fields(THD *thd, Item **ref)
We can't yet set item to *arg as fix_fields may change *arg
We shouldn't call fix_fields() twice, so check 'fixed' field first
*/
- if ((!(*arg)->fixed && (*arg)->fix_fields(thd, arg)))
+ if ((*arg)->fix_fields_if_needed(thd, arg))
return TRUE; /* purecov: inspected */
item= *arg;
- if (allowed_arg_cols)
- {
- if (item->check_cols(allowed_arg_cols))
- return 1;
- }
- else
- {
- /* we have to fetch allowed_arg_cols from first argument */
- DBUG_ASSERT(arg == args); // it is first argument
- allowed_arg_cols= item->cols();
- DBUG_ASSERT(allowed_arg_cols); // Can't be 0 any more
- }
-
if (item->maybe_null)
maybe_null=1;
@@ -224,9 +362,11 @@ Item_func::fix_fields(THD *thd, Item **ref)
with_field= with_field || item->with_field;
used_tables_and_const_cache_join(item);
not_null_tables_cache|= item->not_null_tables();
- with_subselect|= item->has_subquery();
+ m_with_subquery|= item->with_subquery();
}
}
+ if (check_arguments())
+ return true;
if (fix_length_and_dec())
return TRUE;
fixed= 1;
@@ -506,10 +646,7 @@ bool Item_func::eq(const Item *item, bool binary_cmp) const
(func_type == Item_func::FUNC_SP &&
my_strcasecmp(system_charset_info, func_name(), item_func->func_name())))
return 0;
- for (uint i=0; i < arg_count ; i++)
- if (!args[i]->eq(item_func->args[i], binary_cmp))
- return 0;
- return 1;
+ return Item_args::eq(item_func, binary_cmp);
}
@@ -531,6 +668,17 @@ my_decimal *Item_func::val_decimal(my_decimal *decimal_value)
}
+bool Item_hybrid_func::fix_attributes(Item **items, uint nitems)
+{
+ bool rc= Item_hybrid_func::type_handler()->
+ Item_hybrid_func_fix_attributes(current_thd,
+ func_name(), this, this,
+ items, nitems);
+ DBUG_ASSERT(!rc || current_thd->is_error());
+ return rc;
+}
+
+
String *Item_real_func::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
@@ -573,128 +721,6 @@ void Item_udf_func::fix_num_length_and_dec()
#endif
-/**
- Count max_length and decimals for temporal functions.
-
- @param item Argument array
- @param nitems Number of arguments in the array.
-
- @retval False on success, true on error.
-*/
-void Item_func::count_datetime_length(enum_field_types field_type_arg,
- Item **item, uint nitems)
-{
- unsigned_flag= 0;
- decimals= 0;
- if (field_type_arg != MYSQL_TYPE_DATE)
- {
- for (uint i= 0; i < nitems; i++)
- set_if_bigger(decimals, item[i]->decimals);
- }
- set_if_smaller(decimals, TIME_SECOND_PART_DIGITS);
- uint len= decimals ? (decimals + 1) : 0;
- len+= mysql_temporal_int_part_length(field_type_arg);
- fix_char_length(len);
-}
-
-/**
- Set max_length/decimals of function if function is fixed point and
- result length/precision depends on argument ones.
-*/
-
-void Item_func::count_decimal_length(Item **item, uint nitems)
-{
- int max_int_part= 0;
- decimals= 0;
- unsigned_flag= 1;
- 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);
- }
- int precision= MY_MIN(max_int_part + decimals, DECIMAL_MAX_PRECISION);
- fix_char_length(my_decimal_precision_to_length_no_truncation(precision,
- decimals,
- unsigned_flag));
-}
-
-
-/**
- Set max_length of if it is maximum length of its arguments.
-*/
-
-void Item_func::count_only_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);
-}
-
-
-/**
- Set max_length/decimals of function if function is floating point and
- result length/precision depends on argument ones.
-*/
-
-void Item_func::count_real_length(Item **items, uint nitems)
-{
- uint32 length= 0;
- decimals= 0;
- max_length= 0;
- unsigned_flag= false;
- for (uint i=0 ; i < nitems ; i++)
- {
- if (decimals < FLOATING_POINT_DECIMALS)
- {
- set_if_bigger(decimals, items[i]->decimals);
- /* Will be ignored if items[i]->decimals >= FLOATING_POINT_DECIMALS */
- set_if_bigger(length, (items[i]->max_length - items[i]->decimals));
- }
- set_if_bigger(max_length, items[i]->max_length);
- }
- if (decimals < FLOATING_POINT_DECIMALS)
- {
- max_length= length;
- length+= decimals;
- if (length < max_length) // If previous operation gave overflow
- max_length= UINT_MAX32;
- else
- max_length= length;
- }
-}
-
-
-/**
- Calculate max_length and decimals for STRING_RESULT functions.
-
- @param field_type Field type.
- @param items Argument array.
- @param nitems Number of arguments.
-
- @retval False on success, true on error.
-*/
-bool Item_func::count_string_result_length(enum_field_types field_type_arg,
- Item **items, uint nitems)
-{
- if (agg_arg_charsets_for_string_result(collation, items, nitems, 1))
- return true;
- if (is_temporal_type(field_type_arg))
- count_datetime_length(field_type_arg, items, nitems);
- else
- {
- count_only_length(items, nitems);
- decimals= max_length ? NOT_FIXED_DEC : 0;
- }
- return false;
-}
-
-
void Item_func::signal_divide_by_null()
{
THD *thd= current_thd;
@@ -733,7 +759,7 @@ String *Item_int_func::val_str(String *str)
bool Item_func_connection_id::fix_length_and_dec()
{
- if (Item_int_func::fix_length_and_dec())
+ if (Item_long_func::fix_length_and_dec())
return TRUE;
max_length= 10;
return FALSE;
@@ -750,407 +776,277 @@ bool Item_func_connection_id::fix_fields(THD *thd, Item **ref)
}
-/**
- Check arguments here to determine result's type for a numeric
- function of two arguments.
-*/
-
-bool Item_num_op::fix_length_and_dec(void)
+bool Item_num_op::fix_type_handler(const Type_aggregator *aggregator)
{
- DBUG_ENTER("Item_num_op::fix_length_and_dec");
- DBUG_PRINT("info", ("name %s", func_name()));
DBUG_ASSERT(arg_count == 2);
- Item_result r0= args[0]->cast_to_int_type();
- Item_result r1= args[1]->cast_to_int_type();
-
- if (r0 == REAL_RESULT || r1 == REAL_RESULT ||
- r0 == STRING_RESULT || r1 ==STRING_RESULT)
- {
- count_real_length(args, arg_count);
- max_length= float_length(decimals);
- set_handler_by_result_type(REAL_RESULT);
- }
- else if (r0 == DECIMAL_RESULT || r1 == DECIMAL_RESULT ||
- r0 == TIME_RESULT || r1 == TIME_RESULT)
- {
- set_handler_by_result_type(DECIMAL_RESULT);
- result_precision();
- fix_decimals();
- if ((r0 == TIME_RESULT || r1 == TIME_RESULT) && decimals == 0)
- set_handler_by_result_type(INT_RESULT);
- }
- else
- {
- DBUG_ASSERT(r0 == INT_RESULT && r1 == INT_RESULT);
- set_handler_by_result_type(INT_RESULT);
- result_precision();
- decimals= 0;
- }
- DBUG_PRINT("info", ("Type: %s",
- (result_type() == REAL_RESULT ? "REAL_RESULT" :
- result_type() == DECIMAL_RESULT ? "DECIMAL_RESULT" :
- result_type() == INT_RESULT ? "INT_RESULT" :
- "--ILLEGAL!!!--")));
- DBUG_RETURN(FALSE);
+ const Type_handler *h0= args[0]->cast_to_int_type_handler();
+ const Type_handler *h1= args[1]->cast_to_int_type_handler();
+ if (!aggregate_for_num_op(aggregator, h0, h1))
+ return false;
+ my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0),
+ h0->name().ptr(), h1->name().ptr(), func_name());
+ return true;
}
-/**
- Set result type for a numeric function of one argument
- (can be also used by a numeric function of many arguments, if the result
- type depends only on the first argument)
-*/
-
-bool Item_func_num1::fix_length_and_dec()
+bool Item_func_plus::fix_length_and_dec(void)
{
- DBUG_ENTER("Item_func_num1::fix_length_and_dec");
+ DBUG_ENTER("Item_func_plus::fix_length_and_dec");
DBUG_PRINT("info", ("name %s", func_name()));
- // Note, cast_to_int_type() can return TIME_RESULT
- switch (args[0]->cast_to_int_type()) {
- case INT_RESULT:
- set_handler_by_result_type(INT_RESULT);
- max_length= args[0]->max_length;
- unsigned_flag= args[0]->unsigned_flag;
- break;
- case STRING_RESULT:
- case REAL_RESULT:
- set_handler_by_result_type(REAL_RESULT);
- decimals= args[0]->decimals; // Preserve NOT_FIXED_DEC
- max_length= float_length(decimals);
- break;
- case TIME_RESULT:
- case DECIMAL_RESULT:
- set_handler_by_result_type(DECIMAL_RESULT);
- decimals= args[0]->decimal_scale(); // Do not preserve NOT_FIXED_DEC
- max_length= args[0]->max_length;
- break;
- case ROW_RESULT:
- DBUG_ASSERT(0);
- }
- DBUG_PRINT("info", ("Type: %s",
- (result_type() == REAL_RESULT ? "REAL_RESULT" :
- result_type() == DECIMAL_RESULT ? "DECIMAL_RESULT" :
- result_type() == INT_RESULT ? "INT_RESULT" :
- "--ILLEGAL!!!--")));
+ const Type_aggregator *aggregator= &type_handler_data->m_type_aggregator_for_plus;
+ DBUG_EXECUTE_IF("num_op", aggregator= &type_handler_data->m_type_aggregator_for_result;);
+ DBUG_ASSERT(aggregator->is_commutative());
+ if (fix_type_handler(aggregator))
+ DBUG_RETURN(TRUE);
+ if (Item_func_plus::type_handler()->Item_func_plus_fix_length_and_dec(this))
+ DBUG_RETURN(TRUE);
+ DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr()));
DBUG_RETURN(FALSE);
}
-String *Item_func_hybrid_field_type::val_str(String *str)
+String *Item_func_hybrid_field_type::val_str_from_decimal_op(String *str)
{
- DBUG_ASSERT(fixed == 1);
- switch (Item_func_hybrid_field_type::cmp_type()) {
- case DECIMAL_RESULT:
- {
- my_decimal decimal_value, *val;
- if (!(val= decimal_op_with_null_check(&decimal_value)))
- return 0; // null is set
- my_decimal_round(E_DEC_FATAL_ERROR, val, decimals, FALSE, val);
- str->set_charset(collation.collation);
- my_decimal2string(E_DEC_FATAL_ERROR, val, 0, 0, 0, str);
- break;
- }
- case INT_RESULT:
- {
- longlong nr= int_op();
- if (null_value)
- return 0; /* purecov: inspected */
- str->set_int(nr, unsigned_flag, collation.collation);
- break;
- }
- case REAL_RESULT:
- {
- double nr= real_op();
- if (null_value)
- return 0; /* purecov: inspected */
- str->set_real(nr, decimals, collation.collation);
- break;
- }
- case TIME_RESULT:
- {
- MYSQL_TIME ltime;
- if (date_op_with_null_check(&ltime) ||
- (null_value= str->alloc(MAX_DATE_STRING_REP_LENGTH)))
- return (String *) 0;
- ltime.time_type= mysql_type_to_time_type(field_type());
- str->length(my_TIME_to_str(&ltime, const_cast<char*>(str->ptr()), decimals));
- str->set_charset(&my_charset_bin);
- DBUG_ASSERT(!null_value);
- return str;
- }
- case STRING_RESULT:
- return str_op_with_null_check(&str_value);
- case ROW_RESULT:
- DBUG_ASSERT(0);
- }
- DBUG_ASSERT(!null_value || (str == NULL));
+ my_decimal decimal_value, *val;
+ if (!(val= decimal_op_with_null_check(&decimal_value)))
+ return 0; // null is set
+ DBUG_ASSERT(!null_value);
+ my_decimal_round(E_DEC_FATAL_ERROR, val, decimals, FALSE, val);
+ str->set_charset(collation.collation);
+ my_decimal2string(E_DEC_FATAL_ERROR, val, 0, 0, 0, str);
return str;
}
+double Item_func_hybrid_field_type::val_real_from_decimal_op()
+{
+ my_decimal decimal_value, *val;
+ if (!(val= decimal_op_with_null_check(&decimal_value)))
+ return 0.0; // null is set
+ double result;
+ my_decimal2double(E_DEC_FATAL_ERROR, val, &result);
+ return result;
+}
-double Item_func_hybrid_field_type::val_real()
+longlong Item_func_hybrid_field_type::val_int_from_decimal_op()
{
- DBUG_ASSERT(fixed == 1);
- switch (Item_func_hybrid_field_type::cmp_type()) {
- case DECIMAL_RESULT:
- {
- my_decimal decimal_value, *val;
- double result;
- if (!(val= decimal_op_with_null_check(&decimal_value)))
- return 0.0; // null is set
- my_decimal2double(E_DEC_FATAL_ERROR, val, &result);
- return result;
- }
- case INT_RESULT:
- {
- longlong result= int_op();
- return unsigned_flag ? (double) ((ulonglong) result) : (double) result;
- }
- case REAL_RESULT:
- return real_op();
- case TIME_RESULT:
- {
- MYSQL_TIME ltime;
- if (date_op_with_null_check(&ltime))
- return 0;
- ltime.time_type= mysql_type_to_time_type(field_type());
- return TIME_to_double(&ltime);
- }
- case STRING_RESULT:
- {
- String *res= str_op_with_null_check(&str_value);
- return res ? double_from_string_with_check(res) : 0.0;
- }
- case ROW_RESULT:
- DBUG_ASSERT(0);
- }
- return 0.0;
+ my_decimal decimal_value, *val;
+ if (!(val= decimal_op_with_null_check(&decimal_value)))
+ return 0; // null is set
+ longlong result;
+ my_decimal2int(E_DEC_FATAL_ERROR, val, unsigned_flag, &result);
+ return result;
+}
+
+bool Item_func_hybrid_field_type::get_date_from_decimal_op(MYSQL_TIME *ltime,
+ ulonglong fuzzydate)
+{
+ my_decimal value, *res;
+ if (!(res= decimal_op_with_null_check(&value)) ||
+ decimal_to_datetime_with_warn(res, ltime, fuzzydate,
+ field_table_or_null(),
+ field_name_or_null()))
+ return make_zero_mysql_time(ltime, fuzzydate);
+ return (null_value= 0);
}
-longlong Item_func_hybrid_field_type::val_int()
+String *Item_func_hybrid_field_type::val_str_from_int_op(String *str)
{
- DBUG_ASSERT(fixed == 1);
- switch (Item_func_hybrid_field_type::cmp_type()) {
- case DECIMAL_RESULT:
- {
- my_decimal decimal_value, *val;
- if (!(val= decimal_op_with_null_check(&decimal_value)))
- return 0; // null is set
- longlong result;
- my_decimal2int(E_DEC_FATAL_ERROR, val, unsigned_flag, &result);
- return result;
- }
- case INT_RESULT:
- return int_op();
- case REAL_RESULT:
- return Converter_double_to_longlong(real_op(), unsigned_flag).result();
- case TIME_RESULT:
- {
- MYSQL_TIME ltime;
- if (date_op_with_null_check(&ltime))
- return 0;
- ltime.time_type= mysql_type_to_time_type(field_type());
- return TIME_to_ulonglong(&ltime);
- }
- case STRING_RESULT:
- {
- String *res= str_op_with_null_check(&str_value);
- return res ? longlong_from_string_with_check(res) : 0;
- }
- case ROW_RESULT:
- DBUG_ASSERT(0);
- }
- return 0;
+ longlong nr= int_op();
+ if (null_value)
+ return 0; /* purecov: inspected */
+ str->set_int(nr, unsigned_flag, collation.collation);
+ return str;
}
+double Item_func_hybrid_field_type::val_real_from_int_op()
+{
+ longlong result= int_op();
+ return unsigned_flag ? (double) ((ulonglong) result) : (double) result;
+}
-my_decimal *Item_func_hybrid_field_type::val_decimal(my_decimal *decimal_value)
+my_decimal *
+Item_func_hybrid_field_type::val_decimal_from_int_op(my_decimal *dec)
{
- my_decimal *val= decimal_value;
- DBUG_ASSERT(fixed == 1);
- switch (Item_func_hybrid_field_type::cmp_type()) {
- case DECIMAL_RESULT:
- val= decimal_op_with_null_check(decimal_value);
- break;
- case INT_RESULT:
- {
- longlong result= int_op();
- if (null_value)
- return NULL;
- int2my_decimal(E_DEC_FATAL_ERROR, result, unsigned_flag, decimal_value);
- break;
- }
- case REAL_RESULT:
- {
- double result= (double)real_op();
- if (null_value)
- return NULL;
- double2my_decimal(E_DEC_FATAL_ERROR, result, decimal_value);
- break;
- }
- case TIME_RESULT:
- {
- MYSQL_TIME ltime;
- if (date_op_with_null_check(&ltime))
- {
- my_decimal_set_zero(decimal_value);
- return 0;
- }
- ltime.time_type= mysql_type_to_time_type(field_type());
- return date2my_decimal(&ltime, decimal_value);
- }
- case STRING_RESULT:
- {
- String *res= str_op_with_null_check(&str_value);
- return res ? decimal_from_string_with_check(decimal_value, res) : 0;
- }
- case ROW_RESULT:
- DBUG_ASSERT(0);
- }
- return val;
+ longlong result= int_op();
+ if (null_value)
+ return NULL;
+ int2my_decimal(E_DEC_FATAL_ERROR, result, unsigned_flag, dec);
+ return dec;
+}
+
+bool Item_func_hybrid_field_type::get_date_from_int_op(MYSQL_TIME *ltime,
+ ulonglong fuzzydate)
+{
+ longlong value= int_op();
+ bool neg= !unsigned_flag && value < 0;
+ if (null_value || int_to_datetime_with_warn(neg, neg ? -value : value,
+ ltime, fuzzydate,
+ field_table_or_null(),
+ field_name_or_null()))
+ return make_zero_mysql_time(ltime, fuzzydate);
+ return (null_value= 0);
}
-bool Item_func_hybrid_field_type::get_date(MYSQL_TIME *ltime,
- ulonglong fuzzydate)
+String *Item_func_hybrid_field_type::val_str_from_real_op(String *str)
{
- DBUG_ASSERT(fixed == 1);
- switch (Item_func_hybrid_field_type::cmp_type()) {
- case DECIMAL_RESULT:
- {
- my_decimal value, *res;
- if (!(res= decimal_op_with_null_check(&value)) ||
- decimal_to_datetime_with_warn(res, ltime, fuzzydate,
- field_table_or_null(),
- field_name_or_null()))
- goto err;
- break;
- }
- case INT_RESULT:
- {
- longlong value= int_op();
- bool neg= !unsigned_flag && value < 0;
- if (null_value || int_to_datetime_with_warn(neg, neg ? -value : value,
- ltime, fuzzydate,
- field_table_or_null(),
- field_name_or_null()))
- goto err;
- break;
- }
- case REAL_RESULT:
- {
- double value= real_op();
- if (null_value || double_to_datetime_with_warn(value, ltime, fuzzydate,
- field_table_or_null(),
- field_name_or_null()))
- goto err;
- break;
- }
- case TIME_RESULT:
- return date_op(ltime,
- (uint)(fuzzydate |
- (field_type() == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0)));
- case STRING_RESULT:
- {
- char buff[40];
- String tmp(buff,sizeof(buff), &my_charset_bin),*res;
- if (!(res= str_op_with_null_check(&tmp)) ||
- str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(),
- ltime, fuzzydate))
- goto err;
- break;
- }
- case ROW_RESULT:
- DBUG_ASSERT(0);
- }
+ double nr= real_op();
+ if (null_value)
+ return 0; /* purecov: inspected */
+ str->set_real(nr, decimals, collation.collation);
+ return str;
+}
+
+longlong Item_func_hybrid_field_type::val_int_from_real_op()
+{
+ return Converter_double_to_longlong(real_op(), unsigned_flag).result();
+}
+
+my_decimal *
+Item_func_hybrid_field_type::val_decimal_from_real_op(my_decimal *dec)
+{
+ double result= (double) real_op();
+ if (null_value)
+ return NULL;
+ double2my_decimal(E_DEC_FATAL_ERROR, result, dec);
+ return dec;
+}
+bool Item_func_hybrid_field_type::get_date_from_real_op(MYSQL_TIME *ltime,
+ ulonglong fuzzydate)
+{
+ double value= real_op();
+ if (null_value || double_to_datetime_with_warn(value, ltime, fuzzydate,
+ field_table_or_null(),
+ field_name_or_null()))
+ return make_zero_mysql_time(ltime, fuzzydate);
return (null_value= 0);
+}
-err:
- bzero(ltime, sizeof(*ltime));
- return null_value|= !(fuzzydate & TIME_FUZZY_DATES);
+
+String *Item_func_hybrid_field_type::val_str_from_date_op(String *str)
+{
+ MYSQL_TIME ltime;
+ if (date_op_with_null_check(&ltime) ||
+ (null_value= str->alloc(MAX_DATE_STRING_REP_LENGTH)))
+ return (String *) 0;
+ str->length(my_TIME_to_str(&ltime, const_cast<char*>(str->ptr()), decimals));
+ str->set_charset(&my_charset_bin);
+ DBUG_ASSERT(!null_value);
+ return str;
}
+double Item_func_hybrid_field_type::val_real_from_date_op()
+{
+ MYSQL_TIME ltime;
+ if (date_op_with_null_check(&ltime))
+ return 0;
+ return TIME_to_double(&ltime);
+}
-void Item_func_signed::print(String *str, enum_query_type query_type)
+longlong Item_func_hybrid_field_type::val_int_from_date_op()
{
- str->append(STRING_WITH_LEN("cast("));
- args[0]->print(str, query_type);
- str->append(STRING_WITH_LEN(" as signed)"));
+ MYSQL_TIME ltime;
+ if (date_op_with_null_check(&ltime))
+ return 0;
+ return TIME_to_ulonglong(&ltime);
+}
+my_decimal *
+Item_func_hybrid_field_type::val_decimal_from_date_op(my_decimal *dec)
+{
+ MYSQL_TIME ltime;
+ if (date_op_with_null_check(&ltime))
+ {
+ my_decimal_set_zero(dec);
+ return 0;
+ }
+ return date2my_decimal(&ltime, dec);
}
-longlong Item::val_int_from_str(int *error)
+String *Item_func_hybrid_field_type::val_str_from_time_op(String *str)
{
- char buff[MAX_FIELD_WIDTH];
- String tmp(buff,sizeof(buff), &my_charset_bin), *res;
+ MYSQL_TIME ltime;
+ if (time_op_with_null_check(&ltime) ||
+ (null_value= my_TIME_to_str(&ltime, str, decimals)))
+ return NULL;
+ return str;
+}
- /*
- For a string result, we must first get the string and then convert it
- to a longlong
- */
+double Item_func_hybrid_field_type::val_real_from_time_op()
+{
+ MYSQL_TIME ltime;
+ return time_op_with_null_check(&ltime) ? 0 : TIME_to_double(&ltime);
+}
+
+longlong Item_func_hybrid_field_type::val_int_from_time_op()
+{
+ MYSQL_TIME ltime;
+ return time_op_with_null_check(&ltime) ? 0 : TIME_to_ulonglong(&ltime);
+}
- if (!(res= val_str(&tmp)))
+my_decimal *
+Item_func_hybrid_field_type::val_decimal_from_time_op(my_decimal *dec)
+{
+ MYSQL_TIME ltime;
+ if (time_op_with_null_check(&ltime))
{
- *error= 0;
+ my_decimal_set_zero(dec);
return 0;
}
- Converter_strtoll10_with_warn cnv(NULL, Warn_filter_all(),
- res->charset(), res->ptr(), res->length());
- *error= cnv.error();
- return cnv.result();
+ return date2my_decimal(&ltime, dec);
}
-longlong Item::val_int_signed_typecast()
+double Item_func_hybrid_field_type::val_real_from_str_op()
{
- if (cast_to_int_type() != STRING_RESULT)
- return val_int();
+ String *res= str_op_with_null_check(&str_value);
+ return res ? double_from_string_with_check(res) : 0.0;
+}
- int error;
- longlong value= val_int_from_str(&error);
- if (!null_value && value < 0 && error == 0)
- push_note_converted_to_negative_complement(current_thd);
- return value;
+longlong Item_func_hybrid_field_type::val_int_from_str_op()
+{
+ String *res= str_op_with_null_check(&str_value);
+ return res ? longlong_from_string_with_check(res) : 0;
+}
+
+my_decimal *
+Item_func_hybrid_field_type::val_decimal_from_str_op(my_decimal *decimal_value)
+{
+ String *res= str_op_with_null_check(&str_value);
+ return res ? decimal_from_string_with_check(decimal_value, res) : 0;
}
+bool Item_func_hybrid_field_type::get_date_from_str_op(MYSQL_TIME *ltime,
+ ulonglong fuzzydate)
+{
+ StringBuffer<40> tmp;
+ String *res;
+ if (!(res= str_op_with_null_check(&tmp)) ||
+ str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(),
+ ltime, fuzzydate))
+ return make_zero_mysql_time(ltime, fuzzydate);
+ return (null_value= 0);
+}
-void Item_func_unsigned::print(String *str, enum_query_type query_type)
+
+void Item_func_signed::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("cast("));
args[0]->print(str, query_type);
- str->append(STRING_WITH_LEN(" as unsigned)"));
+ str->append(STRING_WITH_LEN(" as signed)"));
}
-longlong Item::val_int_unsigned_typecast()
+void Item_func_unsigned::print(String *str, enum_query_type query_type)
{
- if (cast_to_int_type() == DECIMAL_RESULT)
- {
- longlong value;
- my_decimal tmp, *dec= val_decimal(&tmp);
- if (!null_value)
- my_decimal2int(E_DEC_FATAL_ERROR, dec, 1, &value);
- else
- value= 0;
- return value;
- }
- else if (cast_to_int_type() != STRING_RESULT)
- {
- longlong value= val_int();
- if (!null_value && unsigned_flag == 0 && value < 0)
- push_note_converted_to_positive_complement(current_thd);
- return value;
- }
+ str->append(STRING_WITH_LEN("cast("));
+ args[0]->print(str, query_type);
+ str->append(STRING_WITH_LEN(" as unsigned)"));
- int error;
- longlong value= val_int_from_str(&error);
- if (!null_value && error < 0)
- push_note_converted_to_positive_complement(current_thd);
- return value;
}
@@ -1219,7 +1115,7 @@ err:
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_DATA_OUT_OF_RANGE,
ER_THD(thd, ER_WARN_DATA_OUT_OF_RANGE),
- name, 1L);
+ name.str, 1L);
return dec;
}
@@ -1248,21 +1144,29 @@ void Item_decimal_typecast::print(String *str, enum_query_type query_type)
}
-double Item_double_typecast::val_real()
+double Item_real_typecast::val_real_with_truncate(double max_value)
{
int error;
double tmp= args[0]->val_real();
if ((null_value= args[0]->null_value))
return 0.0;
- if ((error= truncate_double(&tmp, max_length, decimals, 0, DBL_MAX)))
+ if (unlikely((error= truncate_double(&tmp, max_length, decimals,
+ false/*unsigned_flag*/, max_value))))
{
+ /*
+ We don't want automatic escalation from a warning to an error
+ in this scenario:
+ INSERT INTO t1 (float_field) VALUES (CAST(1e100 AS FLOAT));
+ The above statement should work even in the strict mode.
+ So let's use a note rather than a warning.
+ */
THD *thd= current_thd;
push_warning_printf(thd,
- Sql_condition::WARN_LEVEL_WARN,
+ Sql_condition::WARN_LEVEL_NOTE,
ER_WARN_DATA_OUT_OF_RANGE,
ER_THD(thd, ER_WARN_DATA_OUT_OF_RANGE),
- name, 1);
+ name.str, (ulong) 1);
if (error < 0)
{
null_value= 1; // Illegal value
@@ -1273,14 +1177,15 @@ double Item_double_typecast::val_real()
}
-void Item_double_typecast::print(String *str, enum_query_type query_type)
+void Item_real_typecast::print(String *str, enum_query_type query_type)
{
char len_buf[20*3 + 1];
char *end;
str->append(STRING_WITH_LEN("cast("));
args[0]->print(str, query_type);
- str->append(STRING_WITH_LEN(" as double"));
+ str->append(STRING_WITH_LEN(" as "));
+ str->append(type_handler()->name().ptr());
if (decimals != NOT_FIXED_DEC)
{
str->append('(');
@@ -1405,11 +1310,6 @@ void Item_func_additive_op::result_precision()
DBUG_ASSERT(arg1_int >= 0);
DBUG_ASSERT(arg2_int >= 0);
- /* Integer operations keep unsigned_flag if one of arguments is unsigned */
- if (result_type() == INT_RESULT)
- unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag;
- else
- unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag;
max_length= my_decimal_precision_to_length_no_truncation(precision, decimals,
unsigned_flag);
}
@@ -1419,15 +1319,30 @@ void Item_func_additive_op::result_precision()
The following function is here to allow the user to force
subtraction of UNSIGNED BIGINT to return negative values.
*/
+void Item_func_minus::fix_unsigned_flag()
+{
+ if (unsigned_flag &&
+ (current_thd->variables.sql_mode & MODE_NO_UNSIGNED_SUBTRACTION))
+ unsigned_flag=0;
+}
+
bool Item_func_minus::fix_length_and_dec()
{
- if (Item_num_op::fix_length_and_dec())
- return TRUE;
+ DBUG_ENTER("Item_func_minus::fix_length_and_dec");
+ DBUG_PRINT("info", ("name %s", func_name()));
+ const Type_aggregator *aggregator= &type_handler_data->m_type_aggregator_for_minus;
+ DBUG_EXECUTE_IF("num_op", aggregator= &type_handler_data->m_type_aggregator_non_commutative_test;);
+ DBUG_ASSERT(!aggregator->is_commutative());
+ if (fix_type_handler(aggregator))
+ DBUG_RETURN(TRUE);
+ 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;
- return FALSE;
+ DBUG_RETURN(FALSE);
}
@@ -1647,13 +1562,8 @@ my_decimal *Item_func_mul::decimal_op(my_decimal *decimal_value)
void Item_func_mul::result_precision()
{
- /* Integer operations keep unsigned_flag if one of arguments is unsigned */
- if (result_type() == INT_RESULT)
- unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag;
- else
- unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag;
decimals= MY_MIN(args[0]->decimal_scale() + args[1]->decimal_scale(),
- DECIMAL_MAX_SCALE);
+ DECIMAL_MAX_SCALE);
uint est_prec = args[0]->decimal_precision() + args[1]->decimal_precision();
uint precision= MY_MIN(est_prec, DECIMAL_MAX_PRECISION);
max_length= my_decimal_precision_to_length_no_truncation(precision, decimals,
@@ -1661,6 +1571,22 @@ void Item_func_mul::result_precision()
}
+bool Item_func_mul::fix_length_and_dec(void)
+{
+ DBUG_ENTER("Item_func_mul::fix_length_and_dec");
+ DBUG_PRINT("info", ("name %s", func_name()));
+ const Type_aggregator *aggregator= &type_handler_data->m_type_aggregator_for_mul;
+ DBUG_EXECUTE_IF("num_op", aggregator= &type_handler_data->m_type_aggregator_for_result;);
+ DBUG_ASSERT(aggregator->is_commutative());
+ if (fix_type_handler(aggregator))
+ DBUG_RETURN(TRUE);
+ if (Item_func_mul::type_handler()->Item_func_mul_fix_length_and_dec(this))
+ DBUG_RETURN(TRUE);
+ DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr()));
+ DBUG_RETURN(FALSE);
+}
+
+
double Item_func_div::real_op()
{
DBUG_ASSERT(fixed == 1);
@@ -1724,54 +1650,51 @@ void Item_func_div::result_precision()
uint precision=MY_MIN(args[0]->decimal_precision() +
args[1]->divisor_precision_increment() + prec_increment,
DECIMAL_MAX_PRECISION);
-
- /* Integer operations keep unsigned_flag if one of arguments is unsigned */
- if (result_type() == INT_RESULT)
- unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag;
- else
- unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag;
decimals= MY_MIN(args[0]->decimal_scale() + prec_increment, DECIMAL_MAX_SCALE);
max_length= my_decimal_precision_to_length_no_truncation(precision, decimals,
unsigned_flag);
}
+void Item_func_div::fix_length_and_dec_double(void)
+{
+ Item_num_op::fix_length_and_dec_double();
+ decimals= MY_MAX(args[0]->decimals, args[1]->decimals) + prec_increment;
+ set_if_smaller(decimals, NOT_FIXED_DEC);
+ uint tmp= float_length(decimals);
+ if (decimals == NOT_FIXED_DEC)
+ max_length= tmp;
+ else
+ {
+ max_length=args[0]->max_length - args[0]->decimals + decimals;
+ set_if_smaller(max_length, tmp);
+ }
+}
+
+
+void Item_func_div::fix_length_and_dec_int(void)
+{
+ set_handler(&type_handler_newdecimal);
+ DBUG_PRINT("info", ("Type changed: %s", type_handler()->name().ptr()));
+ Item_num_op::fix_length_and_dec_decimal();
+}
+
+
bool Item_func_div::fix_length_and_dec()
{
DBUG_ENTER("Item_func_div::fix_length_and_dec");
+ DBUG_PRINT("info", ("name %s", func_name()));
prec_increment= current_thd->variables.div_precincrement;
- if (Item_num_op::fix_length_and_dec())
- DBUG_RETURN(TRUE);
- switch (Item_func_div::result_type()) {
- case REAL_RESULT:
- {
- decimals=MY_MAX(args[0]->decimals,args[1]->decimals)+prec_increment;
- set_if_smaller(decimals, NOT_FIXED_DEC);
- uint tmp=float_length(decimals);
- if (decimals == NOT_FIXED_DEC)
- max_length= tmp;
- else
- {
- max_length=args[0]->max_length - args[0]->decimals + decimals;
- set_if_smaller(max_length,tmp);
- }
- break;
- }
- case INT_RESULT:
- set_handler_by_result_type(DECIMAL_RESULT);
- DBUG_PRINT("info", ("Type changed: DECIMAL_RESULT"));
- result_precision();
- break;
- case DECIMAL_RESULT:
- result_precision();
- fix_decimals();
- break;
- case STRING_RESULT:
- case ROW_RESULT:
- case TIME_RESULT:
- DBUG_ASSERT(0);
- }
maybe_null= 1; // division by zero
+
+ const Type_aggregator *aggregator= &type_handler_data->m_type_aggregator_for_div;
+ DBUG_EXECUTE_IF("num_op", aggregator= &type_handler_data->m_type_aggregator_non_commutative_test;);
+ DBUG_ASSERT(!aggregator->is_commutative());
+ if (fix_type_handler(aggregator))
+ DBUG_RETURN(TRUE);
+ if (Item_func_div::type_handler()->Item_func_div_fix_length_and_dec(this))
+ DBUG_RETURN(TRUE);
+ DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr()));
DBUG_RETURN(FALSE);
}
@@ -1844,13 +1767,9 @@ longlong Item_func_int_div::val_int()
bool Item_func_int_div::fix_length_and_dec()
{
- Item_result argtype= args[0]->result_type();
- /* use precision only for the data type it is applicable for and valid */
- uint32 char_length= args[0]->max_char_length() -
- (argtype == DECIMAL_RESULT || argtype == INT_RESULT ?
- args[0]->decimals : 0);
- fix_char_length(char_length > MY_INT64_NUM_DECIMAL_DIGITS ?
- MY_INT64_NUM_DECIMAL_DIGITS : char_length);
+ uint32 prec= args[0]->decimal_int_part();
+ set_if_smaller(prec, MY_INT64_NUM_DECIMAL_DIGITS);
+ fix_char_length(prec);
maybe_null=1;
unsigned_flag=args[0]->unsigned_flag | args[1]->unsigned_flag;
return false;
@@ -1935,15 +1854,18 @@ void Item_func_mod::result_precision()
bool Item_func_mod::fix_length_and_dec()
{
- if (Item_num_op::fix_length_and_dec())
- return true;
- maybe_null= 1;
- /*
- result_precision() sets unsigned_flag for INT_RESULT and DECIMAL_RESULT.
- Here we need to set it in case of REAL_RESULT.
- */
- unsigned_flag= args[0]->unsigned_flag;
- return false;
+ DBUG_ENTER("Item_func_mod::fix_length_and_dec");
+ DBUG_PRINT("info", ("name %s", func_name()));
+ maybe_null= true; // division by zero
+ const Type_aggregator *aggregator= &type_handler_data->m_type_aggregator_for_mod;
+ DBUG_EXECUTE_IF("num_op", aggregator= &type_handler_data->m_type_aggregator_non_commutative_test;);
+ DBUG_ASSERT(!aggregator->is_commutative());
+ if (fix_type_handler(aggregator))
+ DBUG_RETURN(TRUE);
+ if (Item_func_mod::type_handler()->Item_func_mod_fix_length_and_dec(this))
+ DBUG_RETURN(TRUE);
+ DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr()));
+ DBUG_RETURN(FALSE);
}
@@ -1990,13 +1912,10 @@ my_decimal *Item_func_neg::decimal_op(my_decimal *decimal_value)
}
-bool Item_func_neg::fix_length_and_dec()
+void Item_func_neg::fix_length_and_dec_int()
{
- DBUG_ENTER("Item_func_neg::fix_length_and_dec");
- if (Item_func_num1::fix_length_and_dec())
- DBUG_RETURN(TRUE);
- /* 1 add because sign can appear */
max_length= args[0]->max_length + 1;
+ set_handler(type_handler_long_or_longlong());
/*
If this is in integer context keep the context as integer if possible
@@ -2004,7 +1923,7 @@ bool Item_func_neg::fix_length_and_dec()
Use val() to get value as arg_type doesn't mean that item is
Item_int or Item_float due to existence of Item_param.
*/
- if (Item_func_neg::result_type() == INT_RESULT && args[0]->const_item())
+ if (args[0]->const_item())
{
longlong val= args[0]->val_int();
if ((ulonglong) val >= (ulonglong) LONGLONG_MIN &&
@@ -2019,7 +1938,39 @@ bool Item_func_neg::fix_length_and_dec()
DBUG_PRINT("info", ("Type changed: DECIMAL_RESULT"));
}
}
- unsigned_flag= 0;
+ unsigned_flag= false;
+}
+
+
+void Item_func_neg::fix_length_and_dec_double()
+{
+ set_handler(&type_handler_double);
+ decimals= args[0]->decimals; // Preserve NOT_FIXED_DEC
+ max_length= args[0]->max_length + 1;
+ // Limit length with something reasonable
+ uint32 mlen= type_handler()->max_display_length(this);
+ set_if_smaller(max_length, mlen);
+ unsigned_flag= false;
+}
+
+
+void Item_func_neg::fix_length_and_dec_decimal()
+{
+ set_handler(&type_handler_newdecimal);
+ decimals= args[0]->decimal_scale(); // Do not preserve NOT_FIXED_DEC
+ max_length= args[0]->max_length + 1;
+ unsigned_flag= false;
+}
+
+
+bool Item_func_neg::fix_length_and_dec()
+{
+ DBUG_ENTER("Item_func_neg::fix_length_and_dec");
+ DBUG_PRINT("info", ("name %s", func_name()));
+ if (args[0]->cast_to_int_type_handler()->
+ Item_func_neg_fix_length_and_dec(this))
+ DBUG_RETURN(TRUE);
+ DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr()));
DBUG_RETURN(FALSE);
}
@@ -2059,13 +2010,41 @@ my_decimal *Item_func_abs::decimal_op(my_decimal *decimal_value)
return 0;
}
+void Item_func_abs::fix_length_and_dec_int()
+{
+ max_length= args[0]->max_length;
+ unsigned_flag= args[0]->unsigned_flag;
+ set_handler(type_handler_long_or_longlong());
+}
-bool Item_func_abs::fix_length_and_dec()
+
+void Item_func_abs::fix_length_and_dec_double()
{
- if (Item_func_num1::fix_length_and_dec())
- return TRUE;
+ set_handler(&type_handler_double);
+ decimals= args[0]->decimals; // Preserve NOT_FIXED_DEC
+ max_length= float_length(decimals);
+ unsigned_flag= args[0]->unsigned_flag;
+}
+
+
+void Item_func_abs::fix_length_and_dec_decimal()
+{
+ set_handler(&type_handler_newdecimal);
+ decimals= args[0]->decimal_scale(); // Do not preserve NOT_FIXED_DEC
+ max_length= args[0]->max_length;
unsigned_flag= args[0]->unsigned_flag;
- return FALSE;
+}
+
+
+bool Item_func_abs::fix_length_and_dec()
+{
+ DBUG_ENTER("Item_func_abs::fix_length_and_dec");
+ DBUG_PRINT("info", ("name %s", func_name()));
+ if (args[0]->cast_to_int_type_handler()->
+ Item_func_abs_fix_length_and_dec(this))
+ DBUG_RETURN(TRUE);
+ DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr()));
+ DBUG_RETURN(FALSE);
}
@@ -2297,57 +2276,57 @@ longlong Item_func_bit_neg::val_int()
// Conversion functions
-bool Item_func_int_val::fix_length_and_dec()
+void Item_func_int_val::fix_length_and_dec_int_or_decimal()
{
- DBUG_ENTER("Item_func_int_val::fix_length_and_dec");
- DBUG_PRINT("info", ("name %s", func_name()));
-
+ /*
+ The INT branch of this code should be revised.
+ It creates too large data types, e.g.
+ CREATE OR REPLACE TABLE t2 AS SELECT FLOOR(9999999.999) AS fa;
+ results in a BININT(10) column, while INT(7) should probably be enough.
+ */
ulonglong tmp_max_length= (ulonglong ) args[0]->max_length -
(args[0]->decimals ? args[0]->decimals + 1 : 0) + 2;
- max_length= tmp_max_length > (ulonglong) 4294967295U ?
- (uint32) 4294967295U : (uint32) tmp_max_length;
+ max_length= tmp_max_length > (ulonglong) UINT_MAX32 ?
+ (uint32) UINT_MAX32 : (uint32) tmp_max_length;
uint tmp= float_length(decimals);
set_if_smaller(max_length,tmp);
decimals= 0;
- // Note, cast_to_int_type() can return TIME_RESULT
- switch (args[0]->cast_to_int_type())
+ /*
+ -2 because in most high position can't be used any digit for longlong
+ and one position for increasing value during operation
+ */
+ if (args[0]->max_length - args[0]->decimals >= DECIMAL_LONGLONG_DIGITS - 2)
{
- case STRING_RESULT:
- case REAL_RESULT:
- set_handler_by_result_type(REAL_RESULT);
- max_length= float_length(decimals);
- break;
- case INT_RESULT:
- case TIME_RESULT:
- case DECIMAL_RESULT:
- /*
- -2 because in most high position can't be used any digit for longlong
- and one position for increasing value during operation
- */
- if ((args[0]->max_length - args[0]->decimals) >=
- (DECIMAL_LONGLONG_DIGITS - 2))
- {
- fix_char_length(
- my_decimal_precision_to_length_no_truncation(
- args[0]->decimal_int_part(), 0, false));
- set_handler_by_result_type(DECIMAL_RESULT);
- }
- else
- {
- unsigned_flag= args[0]->unsigned_flag;
- set_handler_by_result_type(INT_RESULT);
- }
- break;
- case ROW_RESULT:
- DBUG_ASSERT(0);
+ fix_char_length(
+ my_decimal_precision_to_length_no_truncation(
+ args[0]->decimal_int_part(), 0, false));
+ set_handler(&type_handler_newdecimal);
}
- DBUG_PRINT("info", ("Type: %s",
- (result_type() == REAL_RESULT ? "REAL_RESULT" :
- result_type() == DECIMAL_RESULT ? "DECIMAL_RESULT" :
- result_type() == INT_RESULT ? "INT_RESULT" :
- "--ILLEGAL!!!--")));
+ else
+ {
+ unsigned_flag= args[0]->unsigned_flag;
+ set_handler(type_handler_long_or_longlong());
+ }
+}
+
+
+void Item_func_int_val::fix_length_and_dec_double()
+{
+ set_handler(&type_handler_double);
+ max_length= float_length(0);
+ decimals= 0;
+}
+
+bool Item_func_int_val::fix_length_and_dec()
+{
+ DBUG_ENTER("Item_func_int_val::fix_length_and_dec");
+ DBUG_PRINT("info", ("name %s", func_name()));
+ if (args[0]->cast_to_int_type_handler()->
+ Item_func_int_val_fix_length_and_dec(this))
+ DBUG_RETURN(TRUE);
+ DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr()));
DBUG_RETURN(FALSE);
}
@@ -2446,88 +2425,91 @@ my_decimal *Item_func_floor::decimal_op(my_decimal *decimal_value)
}
-bool Item_func_round::fix_length_and_dec()
+void Item_func_round::fix_length_and_dec_decimal(uint decimals_to_set)
{
- int decimals_to_set;
- longlong val1;
- bool val1_unsigned;
-
+ int decimals_delta= args[0]->decimals - decimals_to_set;
+ int length_increase= (decimals_delta <= 0 || truncate) ? 0 : 1;
+ int precision= args[0]->decimal_precision() + length_increase -
+ decimals_delta;
+ DBUG_ASSERT(decimals_to_set <= DECIMAL_MAX_SCALE);
+ set_handler(&type_handler_newdecimal);
+ unsigned_flag= args[0]->unsigned_flag;
+ decimals= decimals_to_set;
+ if (!precision)
+ precision= 1; // DECIMAL(0,0) -> DECIMAL(1,0)
+ max_length= my_decimal_precision_to_length_no_truncation(precision,
+ decimals,
+ unsigned_flag);
+}
+
+void Item_func_round::fix_length_and_dec_double(uint decimals_to_set)
+{
+ set_handler(&type_handler_double);
unsigned_flag= args[0]->unsigned_flag;
- if (!args[1]->const_item())
+ decimals= decimals_to_set;
+ max_length= float_length(decimals_to_set);
+}
+
+
+void Item_func_round::fix_arg_decimal()
+{
+ if (args[1]->const_item())
{
- decimals= args[0]->decimals;
- max_length= float_length(decimals);
- if (args[0]->result_type() == DECIMAL_RESULT)
- {
- max_length++;
- set_handler_by_result_type(DECIMAL_RESULT);
- }
+ Longlong_hybrid dec= args[1]->to_longlong_hybrid();
+ if (args[1]->null_value)
+ fix_length_and_dec_double(NOT_FIXED_DEC);
else
- set_handler_by_result_type(REAL_RESULT);
- return FALSE;
+ fix_length_and_dec_decimal(dec.to_uint(DECIMAL_MAX_SCALE));
+ }
+ else
+ {
+ set_handler(&type_handler_newdecimal);
+ unsigned_flag= args[0]->unsigned_flag;
+ decimals= args[0]->decimals;
+ max_length= float_length(args[0]->decimals) + 1;
}
+}
- val1= args[1]->val_int();
- if ((null_value= args[1]->null_value))
- return FALSE;
- val1_unsigned= args[1]->unsigned_flag;
- if (val1 < 0)
- decimals_to_set= val1_unsigned ? INT_MAX : 0;
+void Item_func_round::fix_arg_double()
+{
+ if (args[1]->const_item())
+ {
+ Longlong_hybrid dec= args[1]->to_longlong_hybrid();
+ fix_length_and_dec_double(args[1]->null_value ? NOT_FIXED_DEC :
+ dec.to_uint(NOT_FIXED_DEC));
+ }
else
- decimals_to_set= (val1 > INT_MAX) ? INT_MAX : (int) val1;
+ fix_length_and_dec_double(args[0]->decimals);
+}
- if (args[0]->decimals == NOT_FIXED_DEC)
+
+void Item_func_round::fix_arg_int()
+{
+ if (args[1]->const_item())
{
- decimals= MY_MIN(decimals_to_set, NOT_FIXED_DEC);
- max_length= float_length(decimals);
- set_handler_by_result_type(REAL_RESULT);
- return FALSE;
- }
-
- switch (args[0]->result_type()) {
- case REAL_RESULT:
- case STRING_RESULT:
- set_handler_by_result_type(REAL_RESULT);
- decimals= MY_MIN(decimals_to_set, NOT_FIXED_DEC);
- max_length= float_length(decimals);
- break;
- case INT_RESULT:
- if ((!decimals_to_set && truncate) || (args[0]->decimal_precision() < DECIMAL_LONGLONG_DIGITS))
+ Longlong_hybrid val1= args[1]->to_longlong_hybrid();
+ if (args[1]->null_value)
+ fix_length_and_dec_double(NOT_FIXED_DEC);
+ else if ((!val1.to_uint(DECIMAL_MAX_SCALE) && truncate) ||
+ args[0]->decimal_precision() < DECIMAL_LONGLONG_DIGITS)
{
- int length_can_increase= MY_TEST(!truncate && (val1 < 0) &&
- !val1_unsigned);
+ // Length can increase in some cases: ROUND(9,-1) -> 10
+ int length_can_increase= MY_TEST(!truncate && val1.neg());
max_length= args[0]->max_length + length_can_increase;
- /* Here we can keep INT_RESULT */
- set_handler_by_result_type(INT_RESULT);
+ // Here we can keep INT_RESULT
+ unsigned_flag= args[0]->unsigned_flag;
decimals= 0;
- break;
+ set_handler(type_handler_long_or_longlong());
}
- /* fall through */
- case DECIMAL_RESULT:
- {
- set_handler_by_result_type(DECIMAL_RESULT);
- decimals_to_set= MY_MIN(DECIMAL_MAX_SCALE, decimals_to_set);
- int decimals_delta= args[0]->decimals - decimals_to_set;
- int precision= args[0]->decimal_precision();
- int length_increase= ((decimals_delta <= 0) || truncate) ? 0:1;
-
- precision-= decimals_delta - length_increase;
- decimals= MY_MIN(decimals_to_set, DECIMAL_MAX_SCALE);
- if (!precision)
- precision= 1; // DECIMAL(0,0) -> DECIMAL(1,0)
- max_length= my_decimal_precision_to_length_no_truncation(precision,
- decimals,
- unsigned_flag);
- break;
- }
- case ROW_RESULT:
- case TIME_RESULT:
- DBUG_ASSERT(0); /* This result type isn't handled */
+ else
+ fix_length_and_dec_decimal(val1.to_uint(DECIMAL_MAX_SCALE));
}
- return FALSE;
+ else
+ fix_length_and_dec_double(args[0]->decimals);
}
+
double my_double_round(double value, longlong dec, bool dec_unsigned,
bool truncate)
{
@@ -2652,7 +2634,7 @@ void Item_func_rand::seed_random(Item *arg)
if (WSREP_ON)
{
THD *thd= current_thd;
- if (thd->variables.wsrep_on)
+ if (WSREP(thd))
{
if (thd->wsrep_exec_mode==REPL_RECV)
tmp= thd->wsrep_rand;
@@ -2751,129 +2733,17 @@ double Item_func_units::val_real()
}
-bool Item_func_min_max::fix_length_and_dec()
+bool Item_func_min_max::fix_attributes(Item **items, uint nitems)
{
- uint unsigned_count= 0;
- int max_int_part=0;
- decimals=0;
- max_length=0;
- maybe_null=0;
- Item_result tmp_cmp_type= args[0]->cmp_type();
- uint string_type_count= 0;
- uint temporal_type_count= 0;
- enum_field_types temporal_field_type= MYSQL_TYPE_DATETIME;
-
- for (uint i=0 ; i < arg_count ; i++)
- {
- set_if_bigger(max_length, args[i]->max_length);
- set_if_bigger(decimals, args[i]->decimals);
- set_if_bigger(max_int_part, args[i]->decimal_int_part());
- unsigned_count+= args[i]->unsigned_flag;
- if (args[i]->maybe_null)
- maybe_null= 1;
- tmp_cmp_type= item_cmp_type(tmp_cmp_type, args[i]->cmp_type());
- string_type_count+= args[i]->cmp_type() == STRING_RESULT;
- if (args[i]->cmp_type() == TIME_RESULT)
- {
- if (!temporal_type_count)
- temporal_field_type= args[i]->field_type();
- else
- temporal_field_type= Field::field_type_merge(temporal_field_type,
- args[i]->field_type());
- temporal_type_count++;
- }
- }
- unsigned_flag= unsigned_count == arg_count; // if all args are unsigned
-
- switch (tmp_cmp_type) {
- case TIME_RESULT:
- {
- // At least one temporal argument was found.
- if (temporal_type_count < arg_count)
- maybe_null= true; // Non-temporal-to-temporal conversion can return NULL
- collation.set_numeric();
- set_handler_by_field_type(temporal_field_type);
- if (is_temporal_type_with_time(temporal_field_type))
- set_if_smaller(decimals, TIME_SECOND_PART_DIGITS);
- else
- decimals= 0;
- uint len= decimals ? (decimals + 1) : 0;
- len+= mysql_temporal_int_part_length(temporal_field_type);
- fix_char_length(len);
- break;
- }
- case STRING_RESULT:
- /*
- All arguments are of string-alike types:
- CHAR, VARCHAR, TEXT, BINARY, VARBINARY, BLOB, SET, ENUM
- No numeric and no temporal types were found.
- */
- agg_arg_charsets_for_string_result_with_comparison(collation,
- args, arg_count);
- set_handler_by_field_type(agg_field_type(args, arg_count, false));
- break;
-
- case INT_RESULT:
- /*
- All arguments have INT-alike types:
- TINY, SHORT, LONG, LONGLONG, INT24, YEAR, BIT.
- */
- collation.set_numeric();
- fix_char_length(my_decimal_precision_to_length_no_truncation(max_int_part +
- decimals,
- decimals,
- unsigned_flag));
- if (unsigned_count != 0 && unsigned_count != arg_count)
- {
- /*
- If all args are of INT-alike type, but have different unsigned_flag,
- then change type to DECIMAL.
- */
- set_handler_by_field_type(MYSQL_TYPE_NEWDECIMAL);
- }
- else
- {
- /*
- There are only INT-alike arguments with equal unsigned_flag.
- Aggregate types to get the best covering type.
- Treat BIT as LONGLONG when aggregating to non-BIT types.
- Possible final type: TINY, SHORT, LONG, LONGLONG, INT24, YEAR, BIT.
- */
- set_handler_by_field_type(agg_field_type(args, arg_count, true));
- }
- break;
-
- case DECIMAL_RESULT:
- // All arguments are of DECIMAL type
- collation.set_numeric();
- fix_char_length(my_decimal_precision_to_length_no_truncation(max_int_part +
- decimals,
- decimals,
- unsigned_flag));
- set_handler_by_field_type(MYSQL_TYPE_NEWDECIMAL);
- break;
-
- case ROW_RESULT:
- DBUG_ASSERT(0);
- // Pass through
- case REAL_RESULT:
- collation.set_numeric();
- fix_char_length(float_length(decimals));
- /*
- Set type to DOUBLE, as Item_func::create_tmp_field() does not
- distinguish between DOUBLE and FLOAT and always creates Field_double.
- Perhaps we should eventually change this to use agg_field_type() here,
- and fix Item_func::create_tmp_field() to create Field_float when possible.
- */
- set_handler_by_field_type(MYSQL_TYPE_DOUBLE);
- break;
- }
- return FALSE;
+ bool rc= Item_func_min_max::type_handler()->
+ Item_func_min_max_fix_attributes(current_thd, this, items, nitems);
+ DBUG_ASSERT(!rc || current_thd->is_error());
+ return rc;
}
/*
- Compare item arguments in the DATETIME context.
+ Compare item arguments using DATETIME/DATE/TIME representation.
DESCRIPTION
Compare item arguments as DATETIME values and return the index of the
@@ -2886,51 +2756,26 @@ bool Item_func_min_max::fix_length_and_dec()
0 Otherwise
*/
-bool Item_func_min_max::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
+bool Item_func_min_max::get_date_native(MYSQL_TIME *ltime, ulonglong fuzzy_date)
{
longlong UNINIT_VAR(min_max);
DBUG_ASSERT(fixed == 1);
- /*
- just like ::val_int() method of a string item can be called,
- for example, SELECT CONCAT("10", "12") + 1,
- ::get_date() can be called for non-temporal values,
- for example, SELECT MONTH(GREATEST("2011-11-21", "2010-10-09"))
-
- */
- if (Item_func_min_max::cmp_type() != TIME_RESULT)
- return Item_func::get_date(ltime, fuzzy_date);
-
for (uint i=0; i < arg_count ; i++)
{
- longlong res= args[i]->val_temporal_packed(Item_func_min_max::field_type());
+ longlong res= args[i]->val_datetime_packed();
/* Check if we need to stop (because of error or KILL) and stop the loop */
- if (args[i]->null_value)
+ if (unlikely(args[i]->null_value))
return (null_value= 1);
if (i == 0 || (res < min_max ? cmp_sign : -cmp_sign) > 0)
min_max= res;
}
- unpack_time(min_max, ltime);
-
- if (Item_func_min_max::field_type() == MYSQL_TYPE_DATE)
- {
- ltime->time_type= MYSQL_TIMESTAMP_DATE;
- ltime->hour= ltime->minute= ltime->second= ltime->second_part= 0;
- }
- else if (Item_func_min_max::field_type() == MYSQL_TYPE_TIME)
- {
- ltime->time_type= MYSQL_TIMESTAMP_TIME;
- ltime->hour+= (ltime->month * 32 + ltime->day) * 24;
- ltime->year= ltime->month= ltime->day= 0;
- if (adjust_time_range_with_warn(ltime,
- MY_MIN(decimals, TIME_SECOND_PART_DIGITS)))
- return (null_value= true);
- }
+ unpack_time(min_max, ltime, mysql_timestamp_type());
if (!(fuzzy_date & TIME_TIME_ONLY) &&
- ((null_value= check_date_with_warn(ltime, fuzzy_date,
+ unlikely((null_value= check_date_with_warn(ltime, fuzzy_date,
MYSQL_TIMESTAMP_ERROR))))
return true;
@@ -2938,63 +2783,58 @@ bool Item_func_min_max::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
}
-String *Item_func_min_max::val_str(String *str)
+bool Item_func_min_max::get_time_native(MYSQL_TIME *ltime)
{
DBUG_ASSERT(fixed == 1);
- if (Item_func_min_max::cmp_type() == TIME_RESULT)
- return val_string_from_date(str);
- switch (Item_func_min_max::result_type()) {
- case INT_RESULT:
- return val_string_from_int(str);
- case DECIMAL_RESULT:
- return val_string_from_decimal(str);
- case REAL_RESULT:
- return val_string_from_real(str);
- case STRING_RESULT:
+
+ Time value(args[0]);
+ if (!value.is_valid_time())
+ return (null_value= true);
+
+ for (uint i= 1; i < arg_count ; i++)
{
- String *UNINIT_VAR(res);
- for (uint i=0; i < arg_count ; i++)
+ Time tmp(args[i]);
+ if (!tmp.is_valid_time())
+ return (null_value= true);
+
+ int cmp= value.cmp(&tmp);
+ if ((cmp_sign < 0 ? cmp : -cmp) < 0)
+ value= tmp;
+ }
+ value.copy_to_mysql_time(ltime);
+ return (null_value= 0);
+}
+
+
+String *Item_func_min_max::val_str_native(String *str)
+{
+ String *UNINIT_VAR(res);
+ for (uint i=0; i < arg_count ; i++)
+ {
+ if (i == 0)
+ res=args[i]->val_str(str);
+ else
{
- if (i == 0)
- res=args[i]->val_str(str);
- else
+ String *res2;
+ res2= args[i]->val_str(res == str ? &tmp_value : str);
+ if (res2)
{
- String *res2;
- res2= args[i]->val_str(res == str ? &tmp_value : str);
- if (res2)
- {
- int cmp= sortcmp(res,res2,collation.collation);
- if ((cmp_sign < 0 ? cmp : -cmp) < 0)
- res=res2;
- }
+ int cmp= sortcmp(res,res2,collation.collation);
+ if ((cmp_sign < 0 ? cmp : -cmp) < 0)
+ res=res2;
}
- if ((null_value= args[i]->null_value))
- return 0;
}
- res->set_charset(collation.collation);
- return res;
- }
- case ROW_RESULT:
- case TIME_RESULT:
- DBUG_ASSERT(0); // This case should never be chosen
- return 0;
+ if ((null_value= args[i]->null_value))
+ return 0;
}
- return 0; // Keep compiler happy
+ res->set_charset(collation.collation);
+ return res;
}
-double Item_func_min_max::val_real()
+double Item_func_min_max::val_real_native()
{
- DBUG_ASSERT(fixed == 1);
double value=0.0;
- if (Item_func_min_max::cmp_type() == TIME_RESULT)
- {
- MYSQL_TIME ltime;
- if (get_date(&ltime, 0))
- return 0;
-
- return TIME_to_double(&ltime);
- }
for (uint i=0; i < arg_count ; i++)
{
if (i == 0)
@@ -3012,18 +2852,10 @@ double Item_func_min_max::val_real()
}
-longlong Item_func_min_max::val_int()
+longlong Item_func_min_max::val_int_native()
{
DBUG_ASSERT(fixed == 1);
longlong value=0;
- if (Item_func_min_max::cmp_type() == TIME_RESULT)
- {
- MYSQL_TIME ltime;
- if (get_date(&ltime, 0))
- return 0;
-
- return TIME_to_ulonglong(&ltime);
- }
for (uint i=0; i < arg_count ; i++)
{
if (i == 0)
@@ -3041,19 +2873,11 @@ longlong Item_func_min_max::val_int()
}
-my_decimal *Item_func_min_max::val_decimal(my_decimal *dec)
+my_decimal *Item_func_min_max::val_decimal_native(my_decimal *dec)
{
DBUG_ASSERT(fixed == 1);
my_decimal tmp_buf, *tmp, *UNINIT_VAR(res);
- if (Item_func_min_max::cmp_type() == TIME_RESULT)
- {
- MYSQL_TIME ltime;
- if (get_date(&ltime, 0))
- return 0;
-
- return date2my_decimal(&ltime, dec);
- }
for (uint i=0; i < arg_count ; i++)
{
if (i == 0)
@@ -3083,7 +2907,15 @@ my_decimal *Item_func_min_max::val_decimal(my_decimal *dec)
}
-longlong Item_func_length::val_int()
+longlong Item_func_bit_length::val_int()
+{
+ DBUG_ASSERT(fixed == 1);
+ String *res= args[0]->val_str(&value);
+ return (null_value= !res) ? 0 : (longlong) res->length() * 8;
+}
+
+
+longlong Item_func_octet_length::val_int()
{
DBUG_ASSERT(fixed == 1);
String *res=args[0]->val_str(&value);
@@ -3119,13 +2951,6 @@ longlong Item_func_coercibility::val_int()
}
-bool Item_func_locate::fix_length_and_dec()
-{
- max_length= MY_INT32_NUM_DECIMAL_DIGITS;
- return agg_arg_charsets_for_comparison(cmp_collation, args, 2);
-}
-
-
longlong Item_func_locate::val_int()
{
DBUG_ASSERT(fixed == 1);
@@ -3448,7 +3273,7 @@ udf_handler::fix_fields(THD *thd, Item_func_or_sum *func,
if (check_stack_overrun(thd, STACK_MIN_SIZE, buff))
DBUG_RETURN(TRUE); // Fatal error flag is set!
- udf_func *tmp_udf=find_udf(u_d->name.str,(uint) u_d->name.length,1);
+ udf_func *tmp_udf=find_udf(u_d->name.str,u_d->name.length,1);
if (!tmp_udf)
{
@@ -3477,13 +3302,10 @@ udf_handler::fix_fields(THD *thd, Item_func_or_sum *func,
arg != arg_end ;
arg++,i++)
{
- if (!(*arg)->fixed &&
- (*arg)->fix_fields(thd, arg))
- DBUG_RETURN(1);
+ if ((*arg)->fix_fields_if_needed_for_scalar(thd, arg))
+ DBUG_RETURN(true);
// we can't assign 'item' before, because fix_fields() can change arg
Item *item= *arg;
- if (item->check_cols(1))
- DBUG_RETURN(TRUE);
/*
TODO: We should think about this. It is not always
right way just to set an UDF result to return my_charset_bin
@@ -3504,21 +3326,19 @@ udf_handler::fix_fields(THD *thd, Item_func_or_sum *func,
item->with_window_func;
func->with_field= func->with_field || item->with_field;
func->with_param= func->with_param || item->with_param;
- func->with_subselect|= item->with_subselect;
+ func->With_subquery_cache::join(item);
func->used_tables_and_const_cache_join(item);
f_args.arg_type[i]=item->result_type();
}
- //TODO: why all following memory is not allocated with 1 thd->alloc() call?
- if (!(buffers=new String[arg_count]) ||
- !(f_args.args= (char**) thd->alloc(arg_count * sizeof(char *))) ||
- !(f_args.lengths= (ulong*) thd->alloc(arg_count * sizeof(long))) ||
- !(f_args.maybe_null= (char*) thd->alloc(arg_count * sizeof(char))) ||
- !(num_buffer= (char*) thd->alloc(arg_count *
- ALIGN_SIZE(sizeof(double)))) ||
- !(f_args.attributes= (char**) thd->alloc(arg_count *
- sizeof(char *))) ||
- !(f_args.attribute_lengths= (ulong*) thd->alloc(arg_count *
- sizeof(long))))
+ if (!(buffers=new (thd->mem_root) String[arg_count]) ||
+ !multi_alloc_root(thd->mem_root,
+ &f_args.args, arg_count * sizeof(char *),
+ &f_args.lengths, arg_count * sizeof(long),
+ &f_args.maybe_null, arg_count * sizeof(char),
+ &num_buffer, arg_count * sizeof(double),
+ &f_args.attributes, arg_count * sizeof(char *),
+ &f_args.attribute_lengths, arg_count * sizeof(long),
+ NullS))
{
free_udf(u_d);
DBUG_RETURN(TRUE);
@@ -3531,6 +3351,8 @@ udf_handler::fix_fields(THD *thd, Item_func_or_sum *func,
initid.const_item=func->const_item_cache;
initid.decimals=func->decimals;
initid.ptr=0;
+ for (uint i1= 0 ; i1 < arg_count ; i1++)
+ buffers[i1].set_thread_specific();
if (u_d->func_init)
{
@@ -3546,8 +3368,8 @@ udf_handler::fix_fields(THD *thd, Item_func_or_sum *func,
f_args.lengths[i]= arguments[i]->max_length;
f_args.maybe_null[i]= (char) arguments[i]->maybe_null;
- f_args.attributes[i]= arguments[i]->name;
- f_args.attribute_lengths[i]= arguments[i]->name_length;
+ f_args.attributes[i]= arguments[i]->name.str;
+ f_args.attribute_lengths[i]= (ulong)arguments[i]->name.length;
if (arguments[i]->const_item())
{
@@ -3584,7 +3406,7 @@ udf_handler::fix_fields(THD *thd, Item_func_or_sum *func,
}
}
Udf_func_init init= u_d->func_init;
- if ((error=(uchar) init(&initid, &f_args, init_msg_buff)))
+ if (unlikely((error=(uchar) init(&initid, &f_args, init_msg_buff))))
{
my_error(ER_CANT_INITIALIZE_UDF, MYF(0),
u_d->name.str, init_msg_buff);
@@ -3602,7 +3424,7 @@ udf_handler::fix_fields(THD *thd, Item_func_or_sum *func,
func->decimals=MY_MIN(initid.decimals,NOT_FIXED_DEC);
}
initialized=1;
- if (error)
+ if (unlikely(error))
{
my_error(ER_CANT_INITIALIZE_UDF, MYF(0),
u_d->name.str, ER_THD(thd, ER_UNKNOWN_ERROR));
@@ -3614,7 +3436,7 @@ udf_handler::fix_fields(THD *thd, Item_func_or_sum *func,
bool udf_handler::get_arguments()
{
- if (error)
+ if (unlikely(error))
return 1; // Got an error earlier
char *to= num_buffer;
uint str_count=0;
@@ -3689,7 +3511,7 @@ String *udf_handler::val_str(String *str,String *save_str)
char *res=func(&initid, &f_args, (char*) str->ptr(), &res_length,
&is_null_tmp, &error);
DBUG_PRINT("info", ("udf func returned, res_length: %lu", res_length));
- if (is_null_tmp || !res || error) // The !res is for safety
+ if (is_null_tmp || !res || unlikely(error)) // The !res is for safety
{
DBUG_PRINT("info", ("Null or error"));
DBUG_RETURN(0);
@@ -3725,7 +3547,7 @@ my_decimal *udf_handler::val_decimal(my_bool *null_value, my_decimal *dec_buf)
u_d->func;
char *res= func(&initid, &f_args, buf, &res_length, &is_null, &error);
- if (is_null || error)
+ if (is_null || unlikely(error))
{
*null_value= 1;
return 0;
@@ -3915,7 +3737,7 @@ longlong Item_master_pos_wait::val_int()
longlong pos = (ulong)args[1]->val_int();
longlong timeout = (arg_count>=3) ? args[2]->val_int() : 0 ;
String connection_name_buff;
- LEX_STRING connection_name;
+ LEX_CSTRING connection_name;
Master_info *mi= NULL;
if (arg_count >= 4)
{
@@ -3923,7 +3745,7 @@ longlong Item_master_pos_wait::val_int()
if (!(con= args[3]->val_str(&connection_name_buff)))
goto err;
- connection_name.str= (char*) con->ptr();
+ connection_name.str= con->ptr();
connection_name.length= con->length();
if (check_master_connection_name(&connection_name))
{
@@ -3980,7 +3802,9 @@ longlong Item_master_gtid_wait::val_int()
timeout_us= (longlong)-1;
result= rpl_global_gtid_waiting.wait_for_pos(thd, gtid_pos, timeout_us);
-#endif
+#else
+ null_value= 0;
+#endif /* REPLICATION */
return result;
}
@@ -4284,7 +4108,7 @@ longlong Item_func_get_lock::val_int()
thd->push_internal_handler(&lock_wait_timeout_handler);
bool error= thd->mdl_context.acquire_lock(&ull_request, timeout);
(void) thd->pop_internal_handler();
- if (error)
+ if (unlikely(error))
{
if (lock_wait_timeout_handler.m_lock_wait_timeout)
null_value= 0;
@@ -4623,16 +4447,16 @@ bool Item_func_user_var::check_vcol_func_processor(void *arg)
#define extra_size sizeof(double)
-user_var_entry *get_variable(HASH *hash, LEX_STRING &name,
- bool create_if_not_exists)
+user_var_entry *get_variable(HASH *hash, LEX_CSTRING *name,
+ bool create_if_not_exists)
{
user_var_entry *entry;
- if (!(entry = (user_var_entry*) my_hash_search(hash, (uchar*) name.str,
- name.length)) &&
+ if (!(entry = (user_var_entry*) my_hash_search(hash, (uchar*) name->str,
+ name->length)) &&
create_if_not_exists)
{
- uint size=ALIGN_SIZE(sizeof(user_var_entry))+name.length+1+extra_size;
+ 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,
@@ -4641,7 +4465,7 @@ user_var_entry *get_variable(HASH *hash, LEX_STRING &name,
return 0;
entry->name.str=(char*) entry+ ALIGN_SIZE(sizeof(user_var_entry))+
extra_size;
- entry->name.length=name.length;
+ entry->name.length=name->length;
entry->value=0;
entry->length=0;
entry->update_query_id=0;
@@ -4659,7 +4483,7 @@ user_var_entry *get_variable(HASH *hash, LEX_STRING &name,
*/
entry->used_query_id=current_thd->query_id;
entry->type=STRING_RESULT;
- memcpy(entry->name.str, name.str, name.length+1);
+ memcpy((char*) entry->name.str, name->str, name->length+1);
if (my_hash_insert(hash,(uchar*) entry))
{
my_free(entry);
@@ -4681,7 +4505,7 @@ bool Item_func_set_user_var::set_entry(THD *thd, bool create_if_not_exists)
{
if (m_var_entry && thd->thread_id == entry_thread_id)
goto end; // update entry->update_query_id for PS
- if (!(m_var_entry= get_variable(&thd->user_vars, name, create_if_not_exists)))
+ if (!(m_var_entry= get_variable(&thd->user_vars, &name, create_if_not_exists)))
{
entry_thread_id= 0;
return TRUE;
@@ -4729,8 +4553,27 @@ bool Item_func_set_user_var::fix_fields(THD *thd, Item **ref)
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);
- set_handler_by_result_type(args[0]->result_type(),
- max_length, collation.collation);
+ switch (args[0]->result_type()) {
+ case STRING_RESULT:
+ case TIME_RESULT:
+ set_handler(type_handler_long_blob.
+ type_handler_adjusted_to_max_octet_length(max_length,
+ collation.collation));
+ break;
+ case REAL_RESULT:
+ set_handler(&type_handler_double);
+ break;
+ case INT_RESULT:
+ set_handler(Type_handler::type_handler_long_or_longlong(max_char_length()));
+ break;
+ case DECIMAL_RESULT:
+ set_handler(&type_handler_newdecimal);
+ break;
+ case ROW_RESULT:
+ DBUG_ASSERT(0);
+ set_handler(&type_handler_row);
+ break;
+ }
if (thd->lex->current_select)
{
/*
@@ -4835,7 +4678,7 @@ bool Item_func_set_user_var::register_field_in_bitmap(void *arg)
*/
bool
-update_hash(user_var_entry *entry, bool set_null, void *ptr, uint length,
+update_hash(user_var_entry *entry, bool set_null, void *ptr, size_t length,
Item_result type, CHARSET_INFO *cs,
bool unsigned_arg)
{
@@ -4896,7 +4739,7 @@ update_hash(user_var_entry *entry, bool set_null, void *ptr, uint length,
bool
-Item_func_set_user_var::update_hash(void *ptr, uint length,
+Item_func_set_user_var::update_hash(void *ptr, size_t length,
Item_result res_type,
CHARSET_INFO *cs,
bool unsigned_arg)
@@ -5067,7 +4910,7 @@ Item_func_set_user_var::check(bool use_result_field)
if (use_result_field && !result_field)
use_result_field= FALSE;
- switch (Item_func_set_user_var::result_type()) {
+ switch (result_type()) {
case REAL_RESULT:
{
save_result.vreal= use_result_field ? result_field->val_real() :
@@ -5160,7 +5003,7 @@ Item_func_set_user_var::update()
bool res= 0;
DBUG_ENTER("Item_func_set_user_var::update");
- switch (Item_func_set_user_var::result_type()) {
+ switch (result_type()) {
case REAL_RESULT:
{
res= update_hash((void*) &save_result.vreal,sizeof(save_result.vreal),
@@ -5290,7 +5133,7 @@ bool Item_func_set_user_var::is_null_result()
void Item_func_set_user_var::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("@"));
- str->append(name.str, name.length);
+ str->append(&name);
str->append(STRING_WITH_LEN(":="));
args[0]->print_parenthesised(str, query_type, precedence());
}
@@ -5300,12 +5143,12 @@ void Item_func_set_user_var::print_as_stmt(String *str,
enum_query_type query_type)
{
str->append(STRING_WITH_LEN("set @"));
- str->append(name.str, name.length);
+ str->append(&name);
str->append(STRING_WITH_LEN(":="));
args[0]->print_parenthesised(str, query_type, precedence());
}
-bool Item_func_set_user_var::send(Protocol *protocol, String *str_arg)
+bool Item_func_set_user_var::send(Protocol *protocol, st_value *buffer)
{
if (result_field)
{
@@ -5313,20 +5156,20 @@ bool Item_func_set_user_var::send(Protocol *protocol, String *str_arg)
update();
return protocol->store(result_field);
}
- return Item::send(protocol, str_arg);
+ return Item::send(protocol, buffer);
}
-void Item_func_set_user_var::make_field(THD *thd, Send_field *tmp_field)
+void Item_func_set_user_var::make_send_field(THD *thd, Send_field *tmp_field)
{
if (result_field)
{
- result_field->make_field(tmp_field);
+ result_field->make_send_field(tmp_field);
DBUG_ASSERT(tmp_field->table_name != 0);
- if (Item::name)
- tmp_field->col_name=Item::name; // Use user supplied name
+ if (Item::name.str)
+ tmp_field->col_name= Item::name; // Use user supplied name
}
else
- Item::make_field(thd, tmp_field);
+ Item::make_send_field(thd, tmp_field);
}
@@ -5491,7 +5334,7 @@ longlong Item_func_get_user_var::val_int()
static int
get_var_with_binlog(THD *thd, enum_sql_command sql_command,
- LEX_STRING &name, user_var_entry **out_entry)
+ LEX_CSTRING *name, user_var_entry **out_entry)
{
BINLOG_USER_VAR_EVENT *user_var_event;
user_var_entry *var_entry;
@@ -5538,14 +5381,15 @@ get_var_with_binlog(THD *thd, enum_sql_command sql_command,
Item_func_set_user_var(thd, name,
new (thd->mem_root) Item_null(thd))),
thd->mem_root);
- /* Create the variable */
- if (sql_set_variables(thd, &tmp_var_list, false))
+ /* Create the variable if the above allocations succeeded */
+ if (unlikely(thd->is_fatal_error) ||
+ unlikely(sql_set_variables(thd, &tmp_var_list, false)))
{
thd->lex= sav_lex;
goto err;
}
thd->lex= sav_lex;
- if (!(var_entry= get_variable(&thd->user_vars, name, 0)))
+ if (unlikely(!(var_entry= get_variable(&thd->user_vars, name, 0))))
goto err;
}
else if (var_entry->used_query_id == thd->query_id ||
@@ -5560,7 +5404,7 @@ get_var_with_binlog(THD *thd, enum_sql_command sql_command,
return 0;
}
- uint size;
+ size_t size;
/*
First we need to store value of var_entry, when the next situation
appears:
@@ -5574,8 +5418,8 @@ get_var_with_binlog(THD *thd, enum_sql_command sql_command,
destroyed.
*/
size= ALIGN_SIZE(sizeof(BINLOG_USER_VAR_EVENT)) + var_entry->length;
- if (!(user_var_event= (BINLOG_USER_VAR_EVENT *)
- alloc_root(thd->user_var_events_alloc, size)))
+ if (unlikely(!(user_var_event= (BINLOG_USER_VAR_EVENT *)
+ alloc_root(thd->user_var_events_alloc, size))))
goto err;
user_var_event->value= (char*) user_var_event +
@@ -5617,20 +5461,20 @@ bool Item_func_get_user_var::fix_length_and_dec()
decimals=NOT_FIXED_DEC;
max_length=MAX_BLOB_WIDTH;
- error= get_var_with_binlog(thd, thd->lex->sql_command, name, &m_var_entry);
+ error= get_var_with_binlog(thd, thd->lex->sql_command, &name, &m_var_entry);
/*
If the variable didn't exist it has been created as a STRING-type.
'm_var_entry' is NULL only if there occurred an error during the call to
get_var_with_binlog.
*/
- if (!error && m_var_entry)
+ if (likely(!error && m_var_entry))
{
unsigned_flag= m_var_entry->unsigned_flag;
- max_length= m_var_entry->length;
+ 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 (Item_func_get_user_var::result_type()) {
+ switch (result_type()) {
case REAL_RESULT:
fix_char_length(DBL_DIG + 8);
break;
@@ -5640,7 +5484,6 @@ bool Item_func_get_user_var::fix_length_and_dec()
break;
case STRING_RESULT:
max_length= MAX_BLOB_WIDTH - 1;
- set_handler_by_field_type(MYSQL_TYPE_MEDIUM_BLOB);
break;
case DECIMAL_RESULT:
fix_char_length(DECIMAL_MAX_STR_LENGTH);
@@ -5656,7 +5499,7 @@ bool Item_func_get_user_var::fix_length_and_dec()
{
collation.set(&my_charset_bin, DERIVATION_IMPLICIT);
null_value= 1;
- set_handler_by_field_type(MYSQL_TYPE_LONG_BLOB);
+ set_handler(&type_handler_long_blob);
max_length= MAX_BLOB_WIDTH;
}
return false;
@@ -5673,7 +5516,7 @@ bool Item_func_get_user_var::const_item() const
void Item_func_get_user_var::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("@"));
- append_identifier(current_thd, str, name.str, name.length);
+ append_identifier(current_thd, str, &name);
}
@@ -5695,7 +5538,8 @@ bool Item_func_get_user_var::eq(const Item *item, bool binary_cmp) const
bool Item_func_get_user_var::set_value(THD *thd,
sp_rcontext * /*ctx*/, Item **it)
{
- Item_func_set_user_var *suv= new (thd->mem_root) Item_func_set_user_var(thd, get_name(), *it);
+ LEX_CSTRING tmp_name= get_name();
+ Item_func_set_user_var *suv= new (thd->mem_root) Item_func_set_user_var(thd, &tmp_name, *it);
/*
Item_func_set_user_var is not fixed after construction, call
fix_fields().
@@ -5709,7 +5553,7 @@ bool Item_user_var_as_out_param::fix_fields(THD *thd, Item **ref)
DBUG_ASSERT(fixed == 0);
DBUG_ASSERT(thd->lex->exchange);
if (Item::fix_fields(thd, ref) ||
- !(entry= get_variable(&thd->user_vars, name, 1)))
+ !(entry= get_variable(&thd->user_vars, &org_name, 1)))
return TRUE;
entry->type= STRING_RESULT;
/*
@@ -5767,18 +5611,25 @@ my_decimal* Item_user_var_as_out_param::val_decimal(my_decimal *decimal_buffer)
}
+bool Item_user_var_as_out_param::get_date(MYSQL_TIME *ltime, ulonglong fuzzy)
+{
+ DBUG_ASSERT(0);
+ return true;
+}
+
+
void Item_user_var_as_out_param::load_data_print_for_log_event(THD *thd,
String *str)
const
{
str->append('@');
- append_identifier(thd, str, name.str, name.length);
+ append_identifier(thd, str, &org_name);
}
Item_func_get_system_var::
Item_func_get_system_var(THD *thd, sys_var *var_arg, enum_var_type var_type_arg,
- LEX_STRING *component_arg, const char *name_arg,
+ LEX_CSTRING *component_arg, const char *name_arg,
size_t name_len_arg):
Item_func(thd), var(var_arg), var_type(var_type_arg),
orig_var_type(var_type_arg), component(*component_arg), cache_present(0)
@@ -5844,7 +5695,7 @@ 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= system_charset_info->cset->numchars(system_charset_info,
+ max_length= (uint32)system_charset_info->cset->numchars(system_charset_info,
cptr,
cptr + strlen(cptr));
mysql_mutex_unlock(&LOCK_global_system_variables);
@@ -5856,7 +5707,7 @@ 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= system_charset_info->cset->numchars(system_charset_info,
+ max_length= (uint32)system_charset_info->cset->numchars(system_charset_info,
ls->str,
ls->str + ls->length);
mysql_mutex_unlock(&LOCK_global_system_variables);
@@ -5886,8 +5737,8 @@ bool Item_func_get_system_var::fix_length_and_dec()
void Item_func_get_system_var::print(String *str, enum_query_type query_type)
{
- if (name_length)
- str->append(name, name_length);
+ if (name.length)
+ str->append(&name);
else
{
str->append(STRING_WITH_LEN("@@"));
@@ -5909,34 +5760,8 @@ bool Item_func_get_system_var::check_vcol_func_processor(void *arg)
return mark_unsupported_function("@@", var->name.str, arg, VCOL_SESSION_FUNC);
}
-enum Item_result Item_func_get_system_var::result_type() const
-{
- switch (var->show_type())
- {
- case SHOW_BOOL:
- case SHOW_MY_BOOL:
- case SHOW_SINT:
- case SHOW_SLONG:
- case SHOW_SLONGLONG:
- case SHOW_UINT:
- case SHOW_ULONG:
- case SHOW_ULONGLONG:
- case SHOW_HA_ROWS:
- return INT_RESULT;
- case SHOW_CHAR:
- case SHOW_CHAR_PTR:
- case SHOW_LEX_STRING:
- return STRING_RESULT;
- case SHOW_DOUBLE:
- return REAL_RESULT;
- default:
- my_error(ER_VAR_CANT_BE_READ, MYF(0), var->name.str);
- return STRING_RESULT; // keep the compiler happy
- }
-}
-
-enum_field_types Item_func_get_system_var::field_type() const
+const Type_handler *Item_func_get_system_var::type_handler() const
{
switch (var->show_type())
{
@@ -5949,16 +5774,16 @@ enum_field_types Item_func_get_system_var::field_type() const
case SHOW_ULONG:
case SHOW_ULONGLONG:
case SHOW_HA_ROWS:
- return MYSQL_TYPE_LONGLONG;
+ return &type_handler_longlong;
case SHOW_CHAR:
case SHOW_CHAR_PTR:
case SHOW_LEX_STRING:
- return MYSQL_TYPE_VARCHAR;
+ return &type_handler_varchar;
case SHOW_DOUBLE:
- return MYSQL_TYPE_DOUBLE;
+ return &type_handler_double;
default:
my_error(ER_VAR_CANT_BE_READ, MYF(0), var->name.str);
- return MYSQL_TYPE_VARCHAR; // keep the compiler happy
+ return &type_handler_varchar; // keep the compiler happy
}
}
@@ -6106,20 +5931,25 @@ void Item_func_get_system_var::cleanup()
cached_strval.free();
}
+/**
+ @retval
+ 0 ok
+ 1 OOM error
+*/
-void Item_func_match::init_search(THD *thd, bool no_order)
+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
- DBUG_VOID_RETURN;
+ DBUG_RETURN(0);
/* Check if init_search() has been called before */
if (ft_handler)
{
if (join_key)
table->file->ft_handler= ft_handler;
- DBUG_VOID_RETURN;
+ DBUG_RETURN(0);
}
if (key == NO_SUCH_KEY)
@@ -6131,6 +5961,8 @@ void Item_func_match::init_search(THD *thd, bool no_order)
for (uint i= 1; i < arg_count; i++)
fields.push_back(args[i]);
concat_ws= new (thd->mem_root) Item_func_concat_ws(thd, fields);
+ if (unlikely(thd->is_fatal_error))
+ DBUG_RETURN(1); // OOM in new or push_back
/*
Above function used only to get value and do not need fix_fields for it:
Item_string - basic constant
@@ -6143,10 +5975,11 @@ void Item_func_match::init_search(THD *thd, bool no_order)
if (master)
{
join_key= master->join_key= join_key | master->join_key;
- master->init_search(thd, no_order);
+ if (master->init_search(thd, no_order))
+ DBUG_RETURN(1);
ft_handler= master->ft_handler;
join_key= master->join_key;
- DBUG_VOID_RETURN;
+ DBUG_RETURN(0);
}
String *ft_tmp= 0;
@@ -6161,8 +5994,9 @@ void Item_func_match::init_search(THD *thd, bool no_order)
if (ft_tmp->charset() != cmp_collation.collation)
{
uint dummy_errors;
- search_value.copy(ft_tmp->ptr(), ft_tmp->length(), ft_tmp->charset(),
- cmp_collation.collation, &dummy_errors);
+ if (search_value.copy(ft_tmp->ptr(), ft_tmp->length(), ft_tmp->charset(),
+ cmp_collation.collation, &dummy_errors))
+ DBUG_RETURN(1);
ft_tmp= &search_value;
}
@@ -6177,7 +6011,7 @@ void Item_func_match::init_search(THD *thd, bool no_order)
if (join_key)
table->file->ft_handler=ft_handler;
- DBUG_VOID_RETURN;
+ DBUG_RETURN(0);
}
@@ -6447,39 +6281,41 @@ longlong Item_func_bit_xor::val_int()
*/
-Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name,
- LEX_STRING component)
+Item *get_system_var(THD *thd, enum_var_type var_type,
+ const LEX_CSTRING *name,
+ const LEX_CSTRING *component)
{
sys_var *var;
- LEX_STRING *base_name, *component_name;
+ LEX_CSTRING base_name, component_name;
- if (component.str)
+ if (component->str)
{
- base_name= &component;
- component_name= &name;
+ base_name= *component;
+ component_name= *name;
}
else
{
- base_name= &name;
- component_name= &component; // Empty string
+ base_name= *name;
+ component_name= *component; // Empty string
}
- if (!(var= find_sys_var(thd, base_name->str, base_name->length)))
+ if (!(var= find_sys_var(thd, base_name.str, base_name.length)))
return 0;
- if (component.str)
+ if (component->str)
{
if (!var->is_struct())
{
- my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), base_name->str);
+ my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), base_name.str);
return 0;
}
}
thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
- set_if_smaller(component_name->length, MAX_SYS_VAR_LENGTH);
+ set_if_smaller(component_name.length, MAX_SYS_VAR_LENGTH);
- return new (thd->mem_root) Item_func_get_system_var(thd, var, var_type, component_name,
- NULL, 0);
+ return new (thd->mem_root) Item_func_get_system_var(thd, var, var_type,
+ &component_name,
+ NULL, 0);
}
@@ -6495,38 +6331,26 @@ longlong Item_func_row_count::val_int()
Item_func_sp::Item_func_sp(THD *thd, Name_resolution_context *context_arg,
- sp_name *name):
- Item_func(thd), context(context_arg), m_name(name), m_sp(NULL), sp_result_field(NULL)
+ sp_name *name, const Sp_handler *sph):
+ Item_func(thd), Item_sp(thd, context_arg, name), m_handler(sph)
{
maybe_null= 1;
- m_name->init_qname(thd);
- dummy_table= (TABLE*) thd->calloc(sizeof(TABLE)+ sizeof(TABLE_SHARE));
- dummy_table->s= (TABLE_SHARE*) (dummy_table+1);
}
Item_func_sp::Item_func_sp(THD *thd, Name_resolution_context *context_arg,
- sp_name *name_arg, List<Item> &list):
- Item_func(thd, list), context(context_arg), m_name(name_arg), m_sp(NULL),
- sp_result_field(NULL)
+ sp_name *name_arg, const Sp_handler *sph,
+ List<Item> &list):
+ Item_func(thd, list), Item_sp(thd, context_arg, name_arg), m_handler(sph)
{
maybe_null= 1;
- m_name->init_qname(thd);
- dummy_table= (TABLE*) thd->calloc(sizeof(TABLE)+ sizeof(TABLE_SHARE));
- dummy_table->s= (TABLE_SHARE*) (dummy_table+1);
}
void
Item_func_sp::cleanup()
{
- if (sp_result_field)
- {
- delete sp_result_field;
- sp_result_field= NULL;
- }
- m_sp= NULL;
- dummy_table->alias.free();
+ Item_sp::cleanup();
Item_func::cleanup();
}
@@ -6534,29 +6358,11 @@ const char *
Item_func_sp::func_name() const
{
THD *thd= current_thd;
- /* Calculate length to avoid reallocation of string for sure */
- uint len= (((m_name->m_explicit_name ? m_name->m_db.length : 0) +
- m_name->m_name.length)*2 + //characters*quoting
- 2 + // ` and `
- (m_name->m_explicit_name ?
- 3 : 0) + // '`', '`' and '.' for the db
- 1 + // end of string
- ALIGN_SIZE(1)); // to avoid String reallocation
- String qname((char *)alloc_root(thd->mem_root, len), len,
- system_charset_info);
-
- qname.length(0);
- if (m_name->m_explicit_name)
- {
- append_identifier(thd, &qname, m_name->m_db.str, m_name->m_db.length);
- qname.append('.');
- }
- append_identifier(thd, &qname, m_name->m_name.str, m_name->m_name.length);
- return qname.c_ptr_safe();
+ return Item_sp::func_name(thd);
}
-void my_missing_function_error(const LEX_STRING &token, const char *func_name)
+void my_missing_function_error(const LEX_CSTRING &token, const char *func_name)
{
if (token.length && is_lex_native_function (&token))
my_error(ER_FUNC_INEXISTENT_NAME_COLLISION, MYF(0), func_name);
@@ -6566,78 +6372,6 @@ void my_missing_function_error(const LEX_STRING &token, const char *func_name)
/**
- @brief Initialize the result field by creating a temporary dummy table
- and assign it to a newly created field object. Meta data used to
- create the field is fetched from the sp_head belonging to the stored
- procedure found in the stored procedure function cache.
-
- @note This function should be called from fix_fields to init the result
- field. It is some what related to Item_field.
-
- @see Item_field
-
- @param thd A pointer to the session and thread context.
-
- @return Function return error status.
- @retval TRUE is returned on an error
- @retval FALSE is returned on success.
-*/
-
-bool
-Item_func_sp::init_result_field(THD *thd)
-{
- LEX_STRING empty_name= { C_STRING_WITH_LEN("") };
- TABLE_SHARE *share;
- DBUG_ENTER("Item_func_sp::init_result_field");
-
- DBUG_ASSERT(m_sp == NULL);
- DBUG_ASSERT(sp_result_field == NULL);
-
- if (!(m_sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, m_name,
- &thd->sp_func_cache, TRUE)))
- {
- my_missing_function_error (m_name->m_name, m_name->m_qname.str);
- context->process_error(thd);
- DBUG_RETURN(TRUE);
- }
-
- /*
- A Field need to be attached to a Table.
- Below we "create" a dummy table by initializing
- the needed pointers.
- */
-
- share= dummy_table->s;
- dummy_table->alias.set("", 0, table_alias_charset);
- dummy_table->maybe_null = maybe_null;
- dummy_table->in_use= thd;
- dummy_table->copy_blobs= TRUE;
- share->table_cache_key = empty_name;
- share->table_name = empty_name;
-
- if (!(sp_result_field= m_sp->create_result_field(max_length, name,
- dummy_table)))
- {
- DBUG_RETURN(TRUE);
- }
-
- if (sp_result_field->pack_length() > sizeof(result_buf))
- {
- void *tmp;
- if (!(tmp= thd->alloc(sp_result_field->pack_length())))
- DBUG_RETURN(TRUE);
- sp_result_field->move_field((uchar*) tmp);
- }
- else
- sp_result_field->move_field(result_buf);
-
- sp_result_field->null_ptr= (uchar *) &null_value;
- sp_result_field->null_bit= 1;
- DBUG_RETURN(FALSE);
-}
-
-
-/**
@note
Deterministic stored procedures are considered inexpensive.
Consequently such procedures may be evaluated during optimization,
@@ -6646,7 +6380,7 @@ Item_func_sp::init_result_field(THD *thd)
bool Item_func_sp::is_expensive()
{
- return !m_sp->m_chistics->detistic ||
+ return !m_sp->detistic() ||
current_thd->locked_tables_mode < LTM_LOCK_TABLES;
}
@@ -6662,134 +6396,49 @@ bool Item_func_sp::fix_length_and_dec()
DBUG_ENTER("Item_func_sp::fix_length_and_dec");
DBUG_ASSERT(sp_result_field);
- Type_std_attributes::set(sp_result_field);
+ Type_std_attributes::set(sp_result_field->type_std_attributes());
+ // There is a bug in the line below. See MDEV-11292 for details.
+ collation.derivation= DERIVATION_COERCIBLE;
maybe_null= 1;
DBUG_RETURN(FALSE);
}
-/**
- @brief Execute function & store value in field.
-
- @return Function returns error status.
- @retval FALSE on success.
- @retval TRUE if an error occurred.
-*/
-
bool
Item_func_sp::execute()
{
- THD *thd= current_thd;
-
/* Execute function and store the return value in the field. */
-
- if (execute_impl(thd))
- {
- null_value= 1;
- context->process_error(thd);
- if (thd->killed)
- thd->send_kill_message();
- return TRUE;
- }
-
- /* Check that the field (the value) is not NULL. */
-
- null_value= sp_result_field->is_null();
-
- return null_value;
-}
-
-
-/**
- @brief Execute function and store the return value in the field.
-
- @note This function was intended to be the concrete implementation of
- the interface function execute. This was never realized.
-
- @return The error state.
- @retval FALSE on success
- @retval TRUE if an error occurred.
-*/
-bool
-Item_func_sp::execute_impl(THD *thd)
-{
- bool err_status= TRUE;
- Sub_statement_state statement_state;
- Security_context *save_security_ctx= thd->security_ctx;
- enum enum_sp_data_access access=
- (m_sp->m_chistics->daccess == SP_DEFAULT_ACCESS) ?
- SP_DEFAULT_ACCESS_MAPPING : m_sp->m_chistics->daccess;
-
- DBUG_ENTER("Item_func_sp::execute_impl");
-
- if (context->security_ctx)
- {
- /* Set view definer security context */
- thd->security_ctx= context->security_ctx;
- }
- if (sp_check_access(thd))
- goto error;
-
- /*
- Throw an error if a non-deterministic function is called while
- statement-based replication (SBR) is active.
- */
-
- if (!m_sp->m_chistics->detistic && !trust_function_creators &&
- (access == SP_CONTAINS_SQL || access == SP_MODIFIES_SQL_DATA) &&
- (mysql_bin_log.is_open() &&
- thd->variables.binlog_format == BINLOG_FORMAT_STMT))
- {
- my_error(ER_BINLOG_UNSAFE_ROUTINE, MYF(0));
- goto error;
- }
-
- /*
- Disable the binlogging if this is not a SELECT statement. If this is a
- SELECT, leave binlogging on, so execute_function() code writes the
- function call into binlog.
- */
- thd->reset_sub_statement_state(&statement_state, SUB_STMT_FUNCTION);
- err_status= m_sp->execute_function(thd, args, arg_count, sp_result_field);
- thd->restore_sub_statement_state(&statement_state);
-
-error:
- thd->security_ctx= save_security_ctx;
-
- DBUG_RETURN(err_status);
+ return Item_sp::execute(current_thd, &null_value, args, arg_count);
}
void
-Item_func_sp::make_field(THD *thd, Send_field *tmp_field)
+Item_func_sp::make_send_field(THD *thd, Send_field *tmp_field)
{
- DBUG_ENTER("Item_func_sp::make_field");
+ DBUG_ENTER("Item_func_sp::make_send_field");
DBUG_ASSERT(sp_result_field);
- sp_result_field->make_field(tmp_field);
- if (name)
+ sp_result_field->make_send_field(tmp_field);
+ if (name.str)
+ {
+ DBUG_ASSERT(name.length == strlen(name.str));
tmp_field->col_name= name;
+ }
DBUG_VOID_RETURN;
}
-enum enum_field_types
-Item_func_sp::field_type() const
+const Type_handler *Item_func_sp::type_handler() const
{
- DBUG_ENTER("Item_func_sp::field_type");
- DBUG_ASSERT(sp_result_field);
- DBUG_RETURN(sp_result_field->type());
-}
-
-Item_result
-Item_func_sp::result_type() const
-{
- DBUG_ENTER("Item_func_sp::result_type");
+ DBUG_ENTER("Item_func_sp::type_handler");
DBUG_PRINT("info", ("m_sp = %p", (void *) m_sp));
DBUG_ASSERT(sp_result_field);
- DBUG_RETURN(sp_result_field->result_type());
+ // This converts ENUM/SET to STRING
+ const Type_handler *handler= sp_result_field->type_handler();
+ DBUG_RETURN(handler->type_handler_for_item_field());
}
+
longlong Item_func_found_rows::val_int()
{
DBUG_ASSERT(fixed == 1);
@@ -6797,30 +6446,31 @@ longlong Item_func_found_rows::val_int()
}
-/**
- @brief Checks if requested access to function can be granted to user.
- If function isn't found yet, it searches function first.
- If function can't be found or user don't have requested access
- error is raised.
-
- @param thd thread handler
+longlong Item_func_oracle_sql_rowcount::val_int()
+{
+ DBUG_ASSERT(fixed == 1);
+ THD *thd= current_thd;
+ /*
+ In case when a query like this:
+ INSERT a INTO @va FROM t1;
+ returns multiple rows, SQL%ROWCOUNT should report 1 rather than -1.
+ */
+ longlong rows= thd->get_row_count_func();
+ return rows != -1 ? rows : // ROW_COUNT()
+ thd->found_rows(); // FOUND_ROWS()
+}
- @return Indication if the access was granted or not.
- @retval FALSE Access is granted.
- @retval TRUE Requested access can't be granted or function doesn't exists.
-
-*/
-bool
-Item_func_sp::sp_check_access(THD *thd)
+longlong Item_func_sqlcode::val_int()
{
- DBUG_ENTER("Item_func_sp::sp_check_access");
- DBUG_ASSERT(m_sp);
- if (check_routine_access(thd, EXECUTE_ACL,
- m_sp->m_db.str, m_sp->m_name.str, 0, FALSE))
- DBUG_RETURN(TRUE);
-
- DBUG_RETURN(FALSE);
+ DBUG_ASSERT(fixed);
+ DBUG_ASSERT(!null_value);
+ Diagnostics_area::Sql_condition_iterator it=
+ current_thd->get_stmt_da()->sql_conditions();
+ const Sql_condition *err;
+ if ((err= it++))
+ return err->get_sql_errno();
+ return 0;
}
@@ -6830,6 +6480,7 @@ Item_func_sp::fix_fields(THD *thd, Item **ref)
bool res;
DBUG_ENTER("Item_func_sp::fix_fields");
DBUG_ASSERT(fixed == 0);
+ sp_head *sp= m_handler->sp_find_routine(thd, m_name, true);
/*
Checking privileges to execute the function while creating view and
@@ -6842,8 +6493,14 @@ Item_func_sp::fix_fields(THD *thd, Item **ref)
if (context->security_ctx)
thd->security_ctx= context->security_ctx;
- res= check_routine_access(thd, EXECUTE_ACL, m_name->m_db.str,
- m_name->m_name.str, 0, FALSE);
+ /*
+ If the routine is not found, let's still check EXECUTE_ACL to decide
+ whether to return "Access denied" or "Routine does not exist".
+ */
+ res= sp ? sp->check_execute_access(thd) :
+ check_routine_access(thd, EXECUTE_ACL, &m_name->m_db,
+ &m_name->m_name,
+ &sp_handler_function, false);
thd->security_ctx= save_security_ctx;
if (res)
@@ -6853,20 +6510,65 @@ Item_func_sp::fix_fields(THD *thd, Item **ref)
}
}
+
+ /* Custom aggregates are transformed into an Item_sum_sp. We can not do this
+ earlier as we have no way of knowing what kind of Item we should create
+ when parsing the query.
+
+ TODO(cvicentiu): See if this limitation can be lifted.
+ */
+
+ DBUG_ASSERT(m_sp == NULL);
+ if (!(m_sp= sp))
+ {
+ my_missing_function_error(m_name->m_name, ErrConvDQName(m_name).ptr());
+ context->process_error(thd);
+ DBUG_RETURN(TRUE);
+ }
+
/*
- We must call init_result_field before Item_func::fix_fields()
+ We must call init_result_field before Item_func::fix_fields()
to make m_sp and result_field members available to fix_length_and_dec(),
which is called from Item_func::fix_fields().
*/
- res= init_result_field(thd);
+ res= init_result_field(thd, max_length, maybe_null, &null_value, &name);
if (res)
- DBUG_RETURN(res);
+ DBUG_RETURN(TRUE);
+
+ if (m_sp->agg_type() == GROUP_AGGREGATE)
+ {
+ Item_sum_sp *item_sp;
+ Query_arena *arena, backup;
+ arena= thd->activate_stmt_arena_if_needed(&backup);
+
+ if (arg_count)
+ {
+ List<Item> list;
+ for (uint i= 0; i < arg_count; i++)
+ list.push_back(args[i]);
+ item_sp= new (thd->mem_root) Item_sum_sp(thd, context, m_name, sp, list);
+ }
+ else
+ item_sp= new (thd->mem_root) Item_sum_sp(thd, context, m_name, sp);
+
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+ if (!item_sp)
+ DBUG_RETURN(TRUE);
+ *ref= item_sp;
+ item_sp->name= name;
+ bool err= item_sp->fix_fields(thd, ref);
+ if (err)
+ DBUG_RETURN(TRUE);
+
+ DBUG_RETURN(FALSE);
+ }
res= Item_func::fix_fields(thd, ref);
if (res)
- DBUG_RETURN(res);
+ DBUG_RETURN(TRUE);
if (thd->lex->is_view_context_analysis())
{
@@ -6886,14 +6588,14 @@ Item_func_sp::fix_fields(THD *thd, Item **ref)
Try to set and restore the security context to see whether it's valid
*/
Security_context *save_secutiry_ctx;
- res= set_routine_security_ctx(thd, m_sp, false, &save_secutiry_ctx);
+ res= set_routine_security_ctx(thd, m_sp, &save_secutiry_ctx);
if (!res)
m_sp->m_security_ctx.restore_security_context(thd, save_secutiry_ctx);
#endif /* ! NO_EMBEDDED_ACCESS_CHECKS */
}
- if (!m_sp->m_chistics->detistic)
+ if (!m_sp->detistic())
{
used_tables_cache |= RAND_TABLE_BIT;
const_item_cache= FALSE;
@@ -6907,7 +6609,7 @@ void Item_func_sp::update_used_tables()
{
Item_func::update_used_tables();
- if (!m_sp->m_chistics->detistic)
+ if (!m_sp->detistic())
{
used_tables_cache |= RAND_TABLE_BIT;
const_item_cache= FALSE;
@@ -7004,6 +6706,15 @@ my_decimal *Item_func_last_value::val_decimal(my_decimal *decimal_value)
}
+bool Item_func_last_value::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+{
+ evaluate_sideeffects();
+ bool tmp= last_value->get_date(ltime, fuzzydate);
+ null_value= last_value->null_value;
+ return tmp;
+}
+
+
bool Item_func_last_value::fix_length_and_dec()
{
last_value= args[arg_count -1];
@@ -7011,3 +6722,293 @@ bool Item_func_last_value::fix_length_and_dec()
maybe_null= last_value->maybe_null;
return FALSE;
}
+
+
+void Cursor_ref::print_func(String *str, const char *func_name)
+{
+ append_identifier(current_thd, str, &m_cursor_name);
+ str->append(func_name);
+}
+
+
+sp_cursor *Cursor_ref::get_open_cursor_or_error()
+{
+ THD *thd= current_thd;
+ sp_cursor *c= thd->spcont->get_cursor(m_cursor_offset);
+ DBUG_ASSERT(c);
+ if (!c/*safety*/ || !c->is_open())
+ {
+ my_message(ER_SP_CURSOR_NOT_OPEN, ER_THD(thd, ER_SP_CURSOR_NOT_OPEN),
+ MYF(0));
+ return NULL;
+ }
+ return c;
+}
+
+
+longlong Item_func_cursor_isopen::val_int()
+{
+ sp_cursor *c= current_thd->spcont->get_cursor(m_cursor_offset);
+ DBUG_ASSERT(c != NULL);
+ return c ? c->is_open() : 0;
+}
+
+
+longlong Item_func_cursor_found::val_int()
+{
+ sp_cursor *c= get_open_cursor_or_error();
+ return !(null_value= (!c || c->fetch_count() == 0)) && c->found();
+}
+
+
+longlong Item_func_cursor_notfound::val_int()
+{
+ sp_cursor *c= get_open_cursor_or_error();
+ return !(null_value= (!c || c->fetch_count() == 0)) && !c->found();
+}
+
+
+longlong Item_func_cursor_rowcount::val_int()
+{
+ sp_cursor *c= get_open_cursor_or_error();
+ return !(null_value= !c) ? c->row_count() : 0;
+}
+
+/*****************************************************************************
+ SEQUENCE functions
+*****************************************************************************/
+
+longlong Item_func_nextval::val_int()
+{
+ longlong value;
+ int error;
+ const char *key;
+ uint length= get_table_def_key(table_list, &key);
+ THD *thd;
+ SEQUENCE_LAST_VALUE *entry;
+ char buff[80];
+ String key_buff(buff,sizeof(buff), &my_charset_bin);
+ DBUG_ENTER("Item_func_nextval::val_int");
+ update_table();
+ DBUG_ASSERT(table && table->s->sequence);
+ thd= table->in_use;
+
+ if (thd->count_cuted_fields == CHECK_FIELD_EXPRESSION)
+ {
+ /* Alter table checking if function works */
+ null_value= 0;
+ DBUG_RETURN(0);
+ }
+
+ if (table->s->tmp_table != NO_TMP_TABLE)
+ {
+ /*
+ Temporary tables has an extra \0 at end to distinguish it from
+ normal tables
+ */
+ key_buff.copy(key, length, &my_charset_bin);
+ key_buff.append((char) 0);
+ key= key_buff.ptr();
+ length++;
+ }
+
+ if (!(entry= ((SEQUENCE_LAST_VALUE*)
+ my_hash_search(&thd->sequences, (uchar*) key, length))))
+ {
+ if (!(key= (char*) my_memdup(key, length, MYF(MY_WME))) ||
+ !(entry= new SEQUENCE_LAST_VALUE((uchar*) key, length)))
+ {
+ /* EOM, error given */
+ my_free((char*) key);
+ delete entry;
+ null_value= 1;
+ DBUG_RETURN(0);
+ }
+ if (my_hash_insert(&thd->sequences, (uchar*) entry))
+ {
+ /* EOM, error given */
+ delete entry;
+ null_value= 1;
+ DBUG_RETURN(0);
+ }
+ }
+ entry->null_value= null_value= 0;
+ value= table->s->sequence->next_value(table, 0, &error);
+ entry->value= value;
+ entry->set_version(table);
+
+ if (unlikely(error)) // Warning already printed
+ entry->null_value= null_value= 1; // For not strict mode
+ DBUG_RETURN(value);
+}
+
+
+/* Print for nextval and lastval */
+
+void Item_func_nextval::print(String *str, enum_query_type query_type)
+{
+ char d_name_buff[MAX_ALIAS_NAME], t_name_buff[MAX_ALIAS_NAME];
+ LEX_CSTRING d_name= table_list->db;
+ LEX_CSTRING t_name= table_list->table_name;
+ bool use_db_name= d_name.str && d_name.str[0];
+ THD *thd= current_thd; // Don't trust 'table'
+
+ str->append(func_name());
+ str->append('(');
+
+ /*
+ for next_val we assume that table_list has been updated to contain
+ the current db.
+ */
+
+ if (lower_case_table_names > 0)
+ {
+ strmake(t_name_buff, t_name.str, MAX_ALIAS_NAME-1);
+ t_name.length= my_casedn_str(files_charset_info, t_name_buff);
+ t_name.str= t_name_buff;
+ if (use_db_name)
+ {
+ strmake(d_name_buff, d_name.str, MAX_ALIAS_NAME-1);
+ d_name.length= my_casedn_str(files_charset_info, d_name_buff);
+ d_name.str= d_name_buff;
+ }
+ }
+
+ if (use_db_name)
+ {
+ append_identifier(thd, str, &d_name);
+ str->append('.');
+ }
+ append_identifier(thd, str, &t_name);
+ str->append(')');
+}
+
+
+/* Return last used value for sequence or NULL if sequence hasn't been used */
+
+longlong Item_func_lastval::val_int()
+{
+ const char *key;
+ SEQUENCE_LAST_VALUE *entry;
+ uint length= get_table_def_key(table_list, &key);
+ THD *thd;
+ char buff[80];
+ String key_buff(buff,sizeof(buff), &my_charset_bin);
+ DBUG_ENTER("Item_func_lastval::val_int");
+ update_table();
+ thd= table->in_use;
+
+ if (table->s->tmp_table != NO_TMP_TABLE)
+ {
+ /*
+ Temporary tables has an extra \0 at end to distinguish it from
+ normal tables
+ */
+ key_buff.copy(key, length, &my_charset_bin);
+ key_buff.append((char) 0);
+ key= key_buff.ptr();
+ length++;
+ }
+
+ if (!(entry= ((SEQUENCE_LAST_VALUE*)
+ my_hash_search(&thd->sequences, (uchar*) key, length))))
+ {
+ /* Sequence not used */
+ null_value= 1;
+ DBUG_RETURN(0);
+ }
+ if (entry->check_version(table))
+ {
+ /* Table droped and re-created, remove current version */
+ my_hash_delete(&thd->sequences, (uchar*) entry);
+ null_value= 1;
+ DBUG_RETURN(0);
+ }
+
+ null_value= entry->null_value;
+ DBUG_RETURN(entry->value);
+}
+
+
+/*
+ Sets next value to be returned from sequences
+
+ SELECT setval(foo, 42, 0); Next nextval will return 43
+ SELECT setval(foo, 42, 0, true); Same as above
+ SELECT setval(foo, 42, 0, false); Next nextval will return 42
+*/
+
+longlong Item_func_setval::val_int()
+{
+ longlong value;
+ int error;
+ THD *thd;
+ DBUG_ENTER("Item_func_setval::val_int");
+
+ update_table();
+ DBUG_ASSERT(table && table->s->sequence);
+ thd= table->in_use;
+
+ if (unlikely(thd->count_cuted_fields == CHECK_FIELD_EXPRESSION))
+ {
+ /* Alter table checking if function works */
+ null_value= 0;
+ DBUG_RETURN(0);
+ }
+
+ value= nextval;
+ error= table->s->sequence->set_value(table, nextval, round, is_used);
+ if (unlikely(error))
+ {
+ null_value= 1;
+ value= 0;
+ }
+ DBUG_RETURN(value);
+}
+
+
+/* Print for setval */
+
+void Item_func_setval::print(String *str, enum_query_type query_type)
+{
+ char d_name_buff[MAX_ALIAS_NAME], t_name_buff[MAX_ALIAS_NAME];
+ LEX_CSTRING d_name= table_list->db;
+ LEX_CSTRING t_name= table_list->table_name;
+ bool use_db_name= d_name.str && d_name.str[0];
+ THD *thd= current_thd; // Don't trust 'table'
+
+ str->append(func_name());
+ str->append('(');
+
+ /*
+ for next_val we assume that table_list has been updated to contain
+ the current db.
+ */
+
+ if (lower_case_table_names > 0)
+ {
+ strmake(t_name_buff, t_name.str, MAX_ALIAS_NAME-1);
+ t_name.length= my_casedn_str(files_charset_info, t_name_buff);
+ t_name.str= t_name_buff;
+ if (use_db_name)
+ {
+ strmake(d_name_buff, d_name.str, MAX_ALIAS_NAME-1);
+ d_name.length= my_casedn_str(files_charset_info, d_name_buff);
+ d_name.str= d_name_buff;
+ }
+ }
+
+ if (use_db_name)
+ {
+ append_identifier(thd, str, &d_name);
+ str->append('.');
+ }
+ append_identifier(thd, str, &t_name);
+ str->append(',');
+ str->append_longlong(nextval);
+ str->append(',');
+ str->append_longlong(is_used);
+ str->append(',');
+ str->append_ulonglong(round);
+ str->append(')');
+}
diff --git a/sql/item_func.h b/sql/item_func.h
index 496109b0e24..754b1cd1eb2 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -30,26 +30,34 @@ extern "C" /* Bug in BSDI include file */
}
#endif
+#include "sql_udf.h" // udf_handler
+#include "my_decimal.h" // string2my_decimal
+#include <cmath>
+
class Item_func :public Item_func_or_sum
{
void sync_with_sum_func_and_with_field(List<Item> &list);
protected:
- /*
- Allowed numbers of columns in result (usually 1, which means scalar value)
- 0 means get this number from first argument
- */
- uint allowed_arg_cols;
String *val_str_from_val_str_ascii(String *str, String *str2);
- void count_only_length(Item **item, uint nitems);
- void count_real_length(Item **item, uint nitems);
- void count_decimal_length(Item **item, uint nitems);
- void count_datetime_length(enum_field_types field_type,
- Item **item, uint nitems);
- bool count_string_result_length(enum_field_types field_type,
- Item **item, uint nitems);
+ virtual bool check_arguments() const
+ {
+ return check_argument_types_scalar(0, arg_count);
+ }
+ bool check_argument_types_like_args0() const;
+ bool check_argument_types_scalar(uint start, uint end) const;
+ bool check_argument_types_traditional_scalar(uint start, uint end) const;
+ bool check_argument_types_or_binary(const Type_handler *handler,
+ uint start, uint end) const;
+ bool check_argument_types_can_return_int(uint start, uint end) const;
+ bool check_argument_types_can_return_real(uint start, uint end) const;
+ bool check_argument_types_can_return_str(uint start, uint end) const;
+ bool check_argument_types_can_return_text(uint start, uint end) const;
+ bool check_argument_types_can_return_date(uint start, uint end) const;
+ bool check_argument_types_can_return_time(uint start, uint end) const;
public:
+
table_map not_null_tables_cache;
enum Functype { UNKNOWN_FUNC,EQ_FUNC,EQUAL_FUNC,NE_FUNC,LT_FUNC,LE_FUNC,
@@ -63,42 +71,45 @@ public:
SP_CONTAINS_FUNC,SP_OVERLAPS_FUNC,
SP_STARTPOINT,SP_ENDPOINT,SP_EXTERIORRING,
SP_POINTN,SP_GEOMETRYN,SP_INTERIORRINGN, SP_RELATE_FUNC,
- NOT_FUNC, NOT_ALL_FUNC,
+ NOT_FUNC, NOT_ALL_FUNC, TEMPTABLE_ROWID,
NOW_FUNC, NOW_UTC_FUNC, SYSDATE_FUNC, TRIG_COND_FUNC,
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,
+ CASE_SEARCHED_FUNC, // Used by ColumnStore/Spider
+ CASE_SIMPLE_FUNC // Used by ColumnStore/spider
+ };
enum Type type() const { return FUNC_ITEM; }
virtual enum Functype functype() const { return UNKNOWN_FUNC; }
- Item_func(THD *thd): Item_func_or_sum(thd), allowed_arg_cols(1)
+ Item_func(THD *thd): Item_func_or_sum(thd)
{
with_sum_func= 0;
with_field= 0;
with_param= 0;
}
- Item_func(THD *thd, Item *a): Item_func_or_sum(thd, a), allowed_arg_cols(1)
+ Item_func(THD *thd, Item *a): Item_func_or_sum(thd, a)
{
with_sum_func= a->with_sum_func;
with_param= a->with_param;
with_field= a->with_field;
}
Item_func(THD *thd, Item *a, Item *b):
- Item_func_or_sum(thd, a, b), allowed_arg_cols(1)
+ Item_func_or_sum(thd, a, b)
{
with_sum_func= a->with_sum_func || b->with_sum_func;
with_param= a->with_param || b->with_param;
with_field= a->with_field || b->with_field;
}
Item_func(THD *thd, Item *a, Item *b, Item *c):
- Item_func_or_sum(thd, a, b, c), allowed_arg_cols(1)
+ Item_func_or_sum(thd, a, b, c)
{
with_sum_func= a->with_sum_func || b->with_sum_func || c->with_sum_func;
with_field= a->with_field || b->with_field || c->with_field;
with_param= a->with_param || b->with_param || c->with_param;
}
Item_func(THD *thd, Item *a, Item *b, Item *c, Item *d):
- Item_func_or_sum(thd, a, b, c, d), allowed_arg_cols(1)
+ Item_func_or_sum(thd, a, b, c, d)
{
with_sum_func= a->with_sum_func || b->with_sum_func ||
c->with_sum_func || d->with_sum_func;
@@ -108,7 +119,7 @@ public:
c->with_param || d->with_param;
}
Item_func(THD *thd, Item *a, Item *b, Item *c, Item *d, Item* e):
- Item_func_or_sum(thd, a, b, c, d, e), allowed_arg_cols(1)
+ Item_func_or_sum(thd, a, b, c, d, e)
{
with_sum_func= a->with_sum_func || b->with_sum_func ||
c->with_sum_func || d->with_sum_func || e->with_sum_func;
@@ -118,14 +129,13 @@ public:
c->with_param || d->with_param || e->with_param;
}
Item_func(THD *thd, List<Item> &list):
- Item_func_or_sum(thd, list), allowed_arg_cols(1)
+ Item_func_or_sum(thd, list)
{
set_arguments(thd, list);
}
// Constructor used for Item_cond_and/or (see Item comment)
Item_func(THD *thd, Item_func *item):
Item_func_or_sum(thd, item),
- allowed_arg_cols(item->allowed_arg_cols),
not_null_tables_cache(item->not_null_tables_cache)
{
}
@@ -155,7 +165,6 @@ public:
virtual Item *key_item() const { return args[0]; }
void set_arguments(THD *thd, List<Item> &list)
{
- allowed_arg_cols= 1;
Item_args::set_arguments(thd, list);
sync_with_sum_func_and_with_field(list);
list.empty(); // Fields are used
@@ -167,14 +176,9 @@ public:
void print_args(String *str, uint from, enum_query_type query_type);
inline bool get_arg0_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
{
- return (null_value=args[0]->get_date_with_conversion(ltime, fuzzy_date));
- }
- inline bool get_arg0_time(MYSQL_TIME *ltime)
- {
- null_value= args[0]->get_time(ltime);
- DBUG_ASSERT(null_value ||
- ltime->time_type != MYSQL_TIMESTAMP_TIME || ltime->day == 0);
- return null_value;
+ DBUG_ASSERT(!(fuzzy_date & TIME_TIME_ONLY));
+ Datetime dt(current_thd, args[0], fuzzy_date);
+ return (null_value= dt.copy_to_mysql_time(ltime));
}
bool is_null() {
update_null_value();
@@ -183,11 +187,7 @@ public:
void signal_divide_by_null();
friend class udf_handler;
Field *create_field_for_create_select(TABLE *table)
- {
- return result_type() != STRING_RESULT ?
- create_tmp_field(false, table, MY_INT32_NUM_DECIMAL_DIGITS) :
- tmp_table_field_from_field_type(table, false, false);
- }
+ { return tmp_table_field_from_field_type(table); }
Item *get_tmp_table_item(THD *thd);
my_decimal *val_decimal(my_decimal *);
@@ -400,10 +400,15 @@ public:
DBUG_ASSERT(fixed == 1);
return Converter_double_to_longlong(val_real(), unsigned_flag).result();
}
- enum Item_result result_type () const { return REAL_RESULT; }
- enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; }
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ { return get_date_from_real(ltime, fuzzydate); }
+ const Type_handler *type_handler() const { return &type_handler_double; }
bool fix_length_and_dec()
- { decimals= NOT_FIXED_DEC; max_length= float_length(decimals); return FALSE; }
+ {
+ decimals= NOT_FIXED_DEC;
+ max_length= float_length(decimals);
+ return FALSE;
+ }
};
@@ -411,10 +416,11 @@ 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_handler_hybrid_field_type,
+ public Type_geometry_attributes
{
protected:
- void fix_attributes(Item **item, uint nitems);
+ bool fix_attributes(Item **item, uint nitems);
public:
Item_hybrid_func(THD *thd): Item_func(thd) { }
Item_hybrid_func(THD *thd, Item *a): Item_func(thd, a) { }
@@ -424,12 +430,14 @@ public:
Item_hybrid_func(THD *thd, List<Item> &list): Item_func(thd, list) { }
Item_hybrid_func(THD *thd, Item_hybrid_func *item)
:Item_func(thd, item), Type_handler_hybrid_field_type(item) { }
- enum_field_types field_type() const
- { return Type_handler_hybrid_field_type::field_type(); }
- enum Item_result result_type () const
- { return Type_handler_hybrid_field_type::result_type(); }
- enum Item_result cmp_type () const
- { return Type_handler_hybrid_field_type::cmp_type(); }
+ 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);
+ }
};
@@ -457,9 +465,15 @@ class Item_func_hybrid_field_type: public Item_hybrid_func
*/
bool date_op_with_null_check(MYSQL_TIME *ltime)
{
- bool rc= date_op(ltime,
- field_type() == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0);
+ bool rc= date_op(ltime, 0);
+ DBUG_ASSERT(!rc ^ null_value);
+ return rc;
+ }
+ bool time_op_with_null_check(MYSQL_TIME *ltime)
+ {
+ bool rc= time_op(ltime);
DBUG_ASSERT(!rc ^ null_value);
+ DBUG_ASSERT(rc || ltime->time_type == MYSQL_TIMESTAMP_TIME);
return rc;
}
String *str_op_with_null_check(String *str)
@@ -474,6 +488,60 @@ class Item_func_hybrid_field_type: public Item_hybrid_func
DBUG_ASSERT((res != NULL) ^ null_value);
return res;
}
+ bool make_zero_mysql_time(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ {
+ bzero(ltime, sizeof(*ltime));
+ return null_value|= !(fuzzydate & TIME_FUZZY_DATES);
+ }
+
+public:
+ // Value methods that involve no conversion
+ String *val_str_from_str_op(String *str)
+ {
+ return str_op_with_null_check(&str_value);
+ }
+ my_decimal *val_decimal_from_decimal_op(my_decimal *dec)
+ {
+ return decimal_op_with_null_check(dec);
+ }
+ longlong val_int_from_int_op()
+ {
+ return int_op();
+ }
+ double val_real_from_real_op()
+ {
+ return real_op();
+ }
+
+ // Value methods that involve conversion
+ String *val_str_from_decimal_op(String *str);
+ String *val_str_from_real_op(String *str);
+ String *val_str_from_int_op(String *str);
+ String *val_str_from_date_op(String *str);
+ String *val_str_from_time_op(String *str);
+
+ my_decimal *val_decimal_from_str_op(my_decimal *dec);
+ my_decimal *val_decimal_from_real_op(my_decimal *dec);
+ my_decimal *val_decimal_from_int_op(my_decimal *dec);
+ my_decimal *val_decimal_from_date_op(my_decimal *dec);
+ my_decimal *val_decimal_from_time_op(my_decimal *dec);
+
+ longlong val_int_from_str_op();
+ longlong val_int_from_real_op();
+ longlong val_int_from_decimal_op();
+ longlong val_int_from_date_op();
+ longlong val_int_from_time_op();
+
+ double val_real_from_str_op();
+ double val_real_from_decimal_op();
+ double val_real_from_date_op();
+ double val_real_from_time_op();
+ double val_real_from_int_op();
+
+ bool get_date_from_str_op(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool get_date_from_real_op(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool get_date_from_decimal_op(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool get_date_from_int_op(MYSQL_TIME *ltime, ulonglong fuzzydate);
public:
Item_func_hybrid_field_type(THD *thd):
@@ -492,11 +560,38 @@ public:
Item_hybrid_func(thd, list)
{ collation.set_numeric(); }
- double val_real();
- longlong val_int();
- my_decimal *val_decimal(my_decimal *);
- String *val_str(String*str);
- bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
+ double val_real()
+ {
+ DBUG_ASSERT(fixed);
+ return Item_func_hybrid_field_type::type_handler()->
+ Item_func_hybrid_field_type_val_real(this);
+ }
+ longlong val_int()
+ {
+ DBUG_ASSERT(fixed);
+ return Item_func_hybrid_field_type::type_handler()->
+ Item_func_hybrid_field_type_val_int(this);
+ }
+ my_decimal *val_decimal(my_decimal *dec)
+ {
+ DBUG_ASSERT(fixed);
+ return Item_func_hybrid_field_type::type_handler()->
+ Item_func_hybrid_field_type_val_decimal(this, dec);
+ }
+ String *val_str(String*str)
+ {
+ DBUG_ASSERT(fixed);
+ String *res= Item_func_hybrid_field_type::type_handler()->
+ Item_func_hybrid_field_type_val_str(this, str);
+ DBUG_ASSERT(null_value == (res == NULL));
+ return res;
+ }
+ bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date)
+ {
+ DBUG_ASSERT(fixed);
+ return Item_func_hybrid_field_type::type_handler()->
+ Item_func_hybrid_field_type_get_date(this, res, fuzzy_date);
+ }
/**
@brief Performs the operation that this functions implements when the
@@ -536,11 +631,46 @@ public:
/**
@brief Performs the operation that this functions implements when
- field type is a temporal type.
+ field type is DATETIME or DATE.
@return The result of the operation.
*/
- virtual bool date_op(MYSQL_TIME *res, uint fuzzy_date)= 0;
+ virtual bool date_op(MYSQL_TIME *res, ulonglong fuzzy_date)= 0;
+ /**
+ @brief Performs the operation that this functions implements when
+ field type is TIME.
+ @return The result of the operation.
+ */
+ virtual bool time_op(MYSQL_TIME *res)= 0;
+
+};
+
+
+/*
+ This class resembles SQL standard CASE-alike expressions:
+ CASE and its abbreviations COALESCE, NULLIF, IFNULL, IF.
+
+ <case expression> ::= <case abbreviation>
+ | <case specification>
+*/
+class Item_func_case_expression: public Item_func_hybrid_field_type
+{
+public:
+ Item_func_case_expression(THD *thd)
+ :Item_func_hybrid_field_type(thd)
+ { }
+ Item_func_case_expression(THD *thd, Item *a)
+ :Item_func_hybrid_field_type(thd, a)
+ { }
+ Item_func_case_expression(THD *thd, Item *a, Item *b)
+ :Item_func_hybrid_field_type(thd, a, b)
+ { }
+ Item_func_case_expression(THD *thd, Item *a, Item *b, Item *c)
+ :Item_func_hybrid_field_type(thd, a, b, c)
+ { }
+ Item_func_case_expression(THD *thd, List<Item> &list):
+ Item_func_hybrid_field_type(thd, list)
+ { }
};
@@ -570,7 +700,16 @@ public:
Item_func_hybrid_field_type(thd, list)
{ }
String *str_op(String *str) { DBUG_ASSERT(0); return 0; }
- bool date_op(MYSQL_TIME *ltime, uint fuzzydate) { DBUG_ASSERT(0); return true; }
+ bool date_op(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ {
+ DBUG_ASSERT(0);
+ return true;
+ }
+ bool time_op(MYSQL_TIME *ltime)
+ {
+ DBUG_ASSERT(0);
+ return true;
+ }
};
@@ -580,7 +719,8 @@ class Item_func_num1: public Item_func_numhybrid
public:
Item_func_num1(THD *thd, Item *a): Item_func_numhybrid(thd, a) {}
Item_func_num1(THD *thd, Item *a, Item *b): Item_func_numhybrid(thd, a, b) {}
- bool fix_length_and_dec();
+ bool check_partition_func_processor(void *int_arg) { return FALSE; }
+ bool check_vcol_func_processor(void *arg) { return FALSE; }
};
@@ -595,7 +735,32 @@ class Item_num_op :public Item_func_numhybrid
{
print_op(str, query_type);
}
- bool fix_length_and_dec();
+ bool fix_type_handler(const Type_aggregator *aggregator);
+ void fix_length_and_dec_double()
+ {
+ count_real_length(args, arg_count);
+ max_length= float_length(decimals);
+ }
+ void fix_length_and_dec_decimal()
+ {
+ unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag;
+ result_precision();
+ fix_decimals();
+ }
+ void fix_length_and_dec_int()
+ {
+ unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag;
+ result_precision();
+ decimals= 0;
+ set_handler(type_handler_long_or_longlong());
+ }
+ void fix_length_and_dec_temporal(bool downcast_decimal_to_int)
+ {
+ set_handler(&type_handler_newdecimal);
+ fix_length_and_dec_decimal();
+ if (decimals == 0 && downcast_decimal_to_int)
+ set_handler(type_handler_long_or_longlong());
+ }
bool need_parentheses_in_default() { return true; }
};
@@ -603,6 +768,12 @@ class Item_num_op :public Item_func_numhybrid
class Item_int_func :public Item_func
{
public:
+ /*
+ QQ: shouldn't 20 characters be enough:
+ Max unsigned = 18,446,744,073,709,551,615 = 20 digits, 20 characters
+ Max signed = 9,223,372,036,854,775,807 = 19 digits, 19 characters
+ 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); }
Item_int_func(THD *thd, Item *a): Item_func(thd, a)
@@ -620,18 +791,84 @@ public:
{ collation.set_numeric(); }
double val_real();
String *val_str(String*str);
- enum Item_result result_type () const { return INT_RESULT; }
- enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; }
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ { return get_date_from_int(ltime, fuzzydate); }
+ const Type_handler *type_handler() const= 0;
bool fix_length_and_dec() { return FALSE; }
};
-class Item_func_connection_id :public Item_int_func
+class Item_long_func: public Item_int_func
+{
+public:
+ Item_long_func(THD *thd): Item_int_func(thd) { }
+ Item_long_func(THD *thd, Item *a): Item_int_func(thd, a) {}
+ Item_long_func(THD *thd, Item *a, Item *b): Item_int_func(thd, a, b) {}
+ 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; }
+ bool fix_length_and_dec() { max_length= 11; return FALSE; }
+};
+
+
+class Item_longlong_func: public Item_int_func
+{
+public:
+ Item_longlong_func(THD *thd): Item_int_func(thd) { }
+ Item_longlong_func(THD *thd, Item *a): Item_int_func(thd, a) {}
+ Item_longlong_func(THD *thd, Item *a, Item *b): Item_int_func(thd, a, b) {}
+ Item_longlong_func(THD *thd, Item *a, Item *b, Item *c): Item_int_func(thd, a, b, c) {}
+ Item_longlong_func(THD *thd, Item *a, Item *b, Item *c, Item *d):
+ 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; }
+};
+
+
+class Cursor_ref
+{
+protected:
+ LEX_CSTRING m_cursor_name;
+ uint m_cursor_offset;
+ class sp_cursor *get_open_cursor_or_error();
+ Cursor_ref(const LEX_CSTRING *name, uint offset)
+ :m_cursor_name(*name), m_cursor_offset(offset)
+ { }
+ void print_func(String *str, const char *func_name);
+};
+
+
+
+class Item_func_cursor_rowcount: public Item_longlong_func,
+ public Cursor_ref
+{
+public:
+ Item_func_cursor_rowcount(THD *thd, const LEX_CSTRING *name, uint offset)
+ :Item_longlong_func(thd), Cursor_ref(name, offset) { maybe_null= true; }
+ const char *func_name() const { return "%ROWCOUNT"; }
+ longlong val_int();
+ bool check_vcol_func_processor(void *arg)
+ {
+ return mark_unsupported_function(func_name(), arg, VCOL_SESSION_FUNC);
+ }
+ void print(String *str, enum_query_type query_type)
+ {
+ return Cursor_ref::print_func(str, func_name());
+ }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_cursor_rowcount>(thd, this); }
+};
+
+
+
+class Item_func_connection_id :public Item_long_func
{
longlong value;
public:
- Item_func_connection_id(THD *thd): Item_int_func(thd) {}
+ Item_func_connection_id(THD *thd): Item_long_func(thd) { unsigned_flag=1; }
const char *func_name() const { return "connection_id"; }
bool fix_length_and_dec();
bool fix_fields(THD *thd, Item **ref);
@@ -640,8 +877,8 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_SESSION_FUNC);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_connection_id>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_connection_id>(thd, this); }
};
@@ -653,13 +890,19 @@ public:
unsigned_flag= 0;
}
const char *func_name() const { return "cast_as_signed"; }
+ const Type_handler *type_handler() const
+ { return type_handler_long_or_longlong(); }
longlong val_int()
{
longlong value= args[0]->val_int_signed_typecast();
null_value= args[0]->null_value;
return value;
}
- bool fix_length_and_dec()
+ void fix_length_and_dec_double()
+ {
+ fix_char_length(MAX_BIGINT_WIDTH);
+ }
+ void fix_length_and_dec_generic()
{
uint32 char_length= MY_MIN(args[0]->max_char_length(),
MY_INT64_NUM_DECIMAL_DIGITS);
@@ -670,13 +913,29 @@ public:
*/
set_if_bigger(char_length, 1U + (unsigned_flag ? 0 : 1));
fix_char_length(char_length);
- return FALSE;
+ }
+ void fix_length_and_dec_string()
+ {
+ /*
+ For strings, use decimal_int_part() instead of max_char_length().
+ This is important for Item_hex_hybrid:
+ SELECT CAST(0x1FFFFFFFF AS SIGNED);
+ Length is 5, decimal_int_part() is 13.
+ */
+ uint32 char_length= MY_MIN(args[0]->decimal_int_part(),
+ MY_INT64_NUM_DECIMAL_DIGITS);
+ set_if_bigger(char_length, 1U + (unsigned_flag ? 0 : 1));
+ fix_char_length(char_length);
+ }
+ bool fix_length_and_dec()
+ {
+ return args[0]->type_handler()->Item_func_signed_fix_length_and_dec(this);
}
virtual void print(String *str, enum_query_type query_type);
uint decimal_precision() const { return args[0]->decimal_precision(); }
bool need_parentheses_in_default() { return true; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_signed>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_signed>(thd, this); }
};
@@ -688,15 +947,26 @@ public:
unsigned_flag= 1;
}
const char *func_name() const { return "cast_as_unsigned"; }
+ const Type_handler *type_handler() const
+ {
+ if (max_char_length() <= MY_INT32_NUM_DECIMAL_DIGITS - 1)
+ return &type_handler_long;
+ return &type_handler_longlong;
+ }
longlong val_int()
{
longlong value= args[0]->val_int_unsigned_typecast();
null_value= args[0]->null_value;
return value;
}
+ bool fix_length_and_dec()
+ {
+ return args[0]->type_handler()->Item_func_unsigned_fix_length_and_dec(this);
+ }
+ uint decimal_precision() const { return max_length; }
virtual void print(String *str, enum_query_type query_type);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_unsigned>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_unsigned>(thd, this); }
};
@@ -704,7 +974,8 @@ class Item_decimal_typecast :public Item_func
{
my_decimal decimal_value;
public:
- Item_decimal_typecast(THD *thd, Item *a, int len, int dec): Item_func(thd, a)
+ Item_decimal_typecast(THD *thd, Item *a, uint len, uint dec)
+ :Item_func(thd, a)
{
decimals= (uint8) dec;
collation.set_numeric();
@@ -715,37 +986,87 @@ public:
double val_real();
longlong val_int();
my_decimal *val_decimal(my_decimal*);
- enum Item_result result_type () const { return DECIMAL_RESULT; }
- enum_field_types field_type() const { return MYSQL_TYPE_NEWDECIMAL; }
- bool fix_length_and_dec() { return FALSE; }
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ { return get_date_from_decimal(ltime, fuzzydate); }
+ const Type_handler *type_handler() const { return &type_handler_newdecimal; }
+ void fix_length_and_dec_generic() {}
+ bool fix_length_and_dec()
+ {
+ return
+ args[0]->type_handler()->Item_decimal_typecast_fix_length_and_dec(this);
+ }
const char *func_name() const { return "decimal_typecast"; }
virtual void print(String *str, enum_query_type query_type);
bool need_parentheses_in_default() { return true; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_decimal_typecast>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_decimal_typecast>(thd, this); }
};
-class Item_double_typecast :public Item_real_func
+class Item_real_typecast: public Item_real_func
{
+protected:
+ double val_real_with_truncate(double max_value);
public:
- Item_double_typecast(THD *thd, Item *a, int len, int dec):
- Item_real_func(thd, a)
+ Item_real_typecast(THD *thd, Item *a, uint len, uint dec)
+ :Item_real_func(thd, a)
{
decimals= (uint8) dec;
max_length= (uint32) len;
}
- double val_real();
- enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; }
- bool fix_length_and_dec() { maybe_null= 1; return FALSE; }
- const char *func_name() const { return "double_typecast"; }
- virtual void print(String *str, enum_query_type query_type);
bool need_parentheses_in_default() { return true; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_double_typecast>(thd, mem_root, this); }
+ void print(String *str, enum_query_type query_type);
+ void fix_length_and_dec_generic() { maybe_null= 1; }
+};
+
+
+class Item_float_typecast :public Item_real_typecast
+{
+public:
+ Item_float_typecast(THD *thd, Item *a)
+ :Item_real_typecast(thd, a, MAX_FLOAT_STR_LENGTH, NOT_FIXED_DEC)
+ { }
+ const Type_handler *type_handler() const { return &type_handler_float; }
+ bool fix_length_and_dec()
+ {
+ return
+ args[0]->type_handler()->Item_float_typecast_fix_length_and_dec(this);
+ }
+ const char *func_name() const { return "float_typecast"; }
+ double val_real()
+ {
+ return (double) (float) val_real_with_truncate(FLT_MAX);
+ }
+ String *val_str(String*str)
+ {
+ Float nr(Item_float_typecast::val_real());
+ if (null_value)
+ return 0;
+ nr.to_string(str, decimals);
+ return str;
+ }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_float_typecast>(thd, this); }
};
+class Item_double_typecast :public Item_real_typecast
+{
+public:
+ Item_double_typecast(THD *thd, Item *a, uint len, uint dec):
+ Item_real_typecast(thd, a, len, dec)
+ { }
+ bool fix_length_and_dec()
+ {
+ return
+ args[0]->type_handler()->Item_double_typecast_fix_length_and_dec(this);
+ }
+ const char *func_name() const { return "double_typecast"; }
+ double val_real() { return val_real_with_truncate(DBL_MAX); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_double_typecast>(thd, this); }
+};
+
class Item_func_additive_op :public Item_num_op
{
@@ -764,11 +1085,12 @@ public:
Item_func_additive_op(thd, a, b) {}
const char *func_name() const { return "+"; }
enum precedence precedence() const { return ADD_PRECEDENCE; }
+ bool fix_length_and_dec();
longlong int_op();
double real_op();
my_decimal *decimal_op(my_decimal *);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_plus>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_plus>(thd, this); }
};
class Item_func_minus :public Item_func_additive_op
@@ -786,8 +1108,24 @@ public:
double real_op();
my_decimal *decimal_op(my_decimal *);
bool fix_length_and_dec();
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_minus>(thd, mem_root, this); }
+ void fix_unsigned_flag();
+ void fix_length_and_dec_double()
+ {
+ Item_func_additive_op::fix_length_and_dec_double();
+ fix_unsigned_flag();
+ }
+ void fix_length_and_dec_decimal()
+ {
+ Item_func_additive_op::fix_length_and_dec_decimal();
+ fix_unsigned_flag();
+ }
+ void fix_length_and_dec_int()
+ {
+ Item_func_additive_op::fix_length_and_dec_int();
+ fix_unsigned_flag();
+ }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_minus>(thd, this); }
};
@@ -802,10 +1140,11 @@ public:
double real_op();
my_decimal *decimal_op(my_decimal *);
void result_precision();
+ bool fix_length_and_dec();
bool check_partition_func_processor(void *int_arg) {return FALSE;}
bool check_vcol_func_processor(void *arg) { return FALSE;}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_mul>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_mul>(thd, this); }
};
@@ -820,9 +1159,11 @@ public:
const char *func_name() const { return "/"; }
enum precedence precedence() const { return MUL_PRECEDENCE; }
bool fix_length_and_dec();
+ void fix_length_and_dec_double();
+ void fix_length_and_dec_int();
void result_precision();
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_div>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_div>(thd, this); }
};
@@ -834,6 +1175,8 @@ public:
longlong val_int();
const char *func_name() const { return "DIV"; }
enum precedence precedence() const { return MUL_PRECEDENCE; }
+ const Type_handler *type_handler() const
+ { return type_handler_long_or_longlong(); }
bool fix_length_and_dec();
void print(String *str, enum_query_type query_type)
{
@@ -843,8 +1186,8 @@ public:
bool check_partition_func_processor(void *int_arg) {return FALSE;}
bool check_vcol_func_processor(void *arg) { return FALSE;}
bool need_parentheses_in_default() { return true; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_int_div>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_int_div>(thd, this); }
};
@@ -855,14 +1198,30 @@ public:
longlong int_op();
double real_op();
my_decimal *decimal_op(my_decimal *);
- const char *func_name() const { return "%"; }
+ const char *func_name() const { return "MOD"; }
enum precedence precedence() const { return MUL_PRECEDENCE; }
void result_precision();
bool fix_length_and_dec();
+ void fix_length_and_dec_double()
+ {
+ Item_num_op::fix_length_and_dec_double();
+ unsigned_flag= args[0]->unsigned_flag;
+ }
+ void fix_length_and_dec_decimal()
+ {
+ result_precision();
+ fix_decimals();
+ }
+ void fix_length_and_dec_int()
+ {
+ result_precision();
+ DBUG_ASSERT(decimals == 0);
+ set_handler(type_handler_long_or_longlong());
+ }
bool check_partition_func_processor(void *int_arg) {return FALSE;}
bool check_vcol_func_processor(void *arg) { return FALSE;}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_mod>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_mod>(thd, this); }
};
@@ -881,13 +1240,14 @@ public:
str->append(func_name());
args[0]->print_parenthesised(str, query_type, precedence());
}
+ void fix_length_and_dec_int();
+ void fix_length_and_dec_double();
+ void fix_length_and_dec_decimal();
bool fix_length_and_dec();
uint decimal_precision() const { return args[0]->decimal_precision(); }
- bool check_partition_func_processor(void *int_arg) {return FALSE;}
- bool check_vcol_func_processor(void *arg) { return FALSE;}
bool need_parentheses_in_default() { return true; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_neg>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_neg>(thd, this); }
};
@@ -899,17 +1259,20 @@ public:
longlong int_op();
my_decimal *decimal_op(my_decimal *);
const char *func_name() const { return "abs"; }
+ void fix_length_and_dec_int();
+ void fix_length_and_dec_double();
+ void fix_length_and_dec_decimal();
bool fix_length_and_dec();
- bool check_partition_func_processor(void *int_arg) {return FALSE;}
- bool check_vcol_func_processor(void *arg) { return FALSE;}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_abs>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_abs>(thd, this); }
};
// A class to handle logarithmic and trigonometric functions
class Item_dec_func :public Item_real_func
{
+ bool check_arguments() const
+ { return check_argument_types_can_return_real(0, arg_count); }
public:
Item_dec_func(THD *thd, Item *a): Item_real_func(thd, a) {}
Item_dec_func(THD *thd, Item *a, Item *b): Item_real_func(thd, a, b) {}
@@ -927,8 +1290,8 @@ public:
Item_func_exp(THD *thd, Item *a): Item_dec_func(thd, a) {}
double val_real();
const char *func_name() const { return "exp"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_exp>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_exp>(thd, this); }
};
@@ -938,8 +1301,8 @@ public:
Item_func_ln(THD *thd, Item *a): Item_dec_func(thd, a) {}
double val_real();
const char *func_name() const { return "ln"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_ln>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_ln>(thd, this); }
};
@@ -950,8 +1313,8 @@ public:
Item_func_log(THD *thd, Item *a, Item *b): Item_dec_func(thd, a, b) {}
double val_real();
const char *func_name() const { return "log"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_log>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_log>(thd, this); }
};
@@ -961,8 +1324,8 @@ public:
Item_func_log2(THD *thd, Item *a): Item_dec_func(thd, a) {}
double val_real();
const char *func_name() const { return "log2"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_log2>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_log2>(thd, this); }
};
@@ -972,8 +1335,8 @@ public:
Item_func_log10(THD *thd, Item *a): Item_dec_func(thd, a) {}
double val_real();
const char *func_name() const { return "log10"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_log10>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_log10>(thd, this); }
};
@@ -983,8 +1346,8 @@ public:
Item_func_sqrt(THD *thd, Item *a): Item_dec_func(thd, a) {}
double val_real();
const char *func_name() const { return "sqrt"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_sqrt>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_sqrt>(thd, this); }
};
@@ -994,8 +1357,8 @@ public:
Item_func_pow(THD *thd, Item *a, Item *b): Item_dec_func(thd, a, b) {}
double val_real();
const char *func_name() const { return "pow"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_pow>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_pow>(thd, this); }
};
@@ -1005,8 +1368,8 @@ public:
Item_func_acos(THD *thd, Item *a): Item_dec_func(thd, a) {}
double val_real();
const char *func_name() const { return "acos"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_acos>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_acos>(thd, this); }
};
class Item_func_asin :public Item_dec_func
@@ -1015,8 +1378,8 @@ public:
Item_func_asin(THD *thd, Item *a): Item_dec_func(thd, a) {}
double val_real();
const char *func_name() const { return "asin"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_asin>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_asin>(thd, this); }
};
class Item_func_atan :public Item_dec_func
@@ -1026,8 +1389,8 @@ public:
Item_func_atan(THD *thd, Item *a, Item *b): Item_dec_func(thd, a, b) {}
double val_real();
const char *func_name() const { return "atan"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_atan>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_atan>(thd, this); }
};
class Item_func_cos :public Item_dec_func
@@ -1036,8 +1399,8 @@ public:
Item_func_cos(THD *thd, Item *a): Item_dec_func(thd, a) {}
double val_real();
const char *func_name() const { return "cos"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_cos>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_cos>(thd, this); }
};
class Item_func_sin :public Item_dec_func
@@ -1046,8 +1409,8 @@ public:
Item_func_sin(THD *thd, Item *a): Item_dec_func(thd, a) {}
double val_real();
const char *func_name() const { return "sin"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_sin>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_sin>(thd, this); }
};
class Item_func_tan :public Item_dec_func
@@ -1056,8 +1419,8 @@ public:
Item_func_tan(THD *thd, Item *a): Item_dec_func(thd, a) {}
double val_real();
const char *func_name() const { return "tan"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_tan>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_tan>(thd, this); }
};
class Item_func_cot :public Item_dec_func
@@ -1066,8 +1429,8 @@ public:
Item_func_cot(THD *thd, Item *a): Item_dec_func(thd, a) {}
double val_real();
const char *func_name() const { return "cot"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_cot>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_cot>(thd, this); }
};
@@ -1075,6 +1438,8 @@ class Item_func_int_val :public Item_func_num1
{
public:
Item_func_int_val(THD *thd, Item *a): Item_func_num1(thd, a) {}
+ void fix_length_and_dec_double();
+ void fix_length_and_dec_int_or_decimal();
bool fix_length_and_dec();
};
@@ -1087,10 +1452,8 @@ public:
longlong int_op();
double real_op();
my_decimal *decimal_op(my_decimal *);
- bool check_partition_func_processor(void *int_arg) {return FALSE;}
- bool check_vcol_func_processor(void *arg) { return FALSE;}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_ceiling>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_ceiling>(thd, this); }
};
@@ -1102,27 +1465,33 @@ public:
longlong int_op();
double real_op();
my_decimal *decimal_op(my_decimal *);
- bool check_partition_func_processor(void *int_arg) {return FALSE;}
- bool check_vcol_func_processor(void *arg) { return FALSE;}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_floor>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_floor>(thd, this); }
};
/* This handles round and truncate */
-class Item_func_round :public Item_func_num1
+class Item_func_round :public Item_func_numhybrid
{
bool truncate;
+ void fix_length_and_dec_decimal(uint decimals_to_set);
+ void fix_length_and_dec_double(uint decimals_to_set);
public:
Item_func_round(THD *thd, Item *a, Item *b, bool trunc_arg)
- :Item_func_num1(thd, a, b), truncate(trunc_arg) {}
+ :Item_func_numhybrid(thd, a, b), truncate(trunc_arg) {}
const char *func_name() const { return truncate ? "truncate" : "round"; }
double real_op();
longlong int_op();
my_decimal *decimal_op(my_decimal *);
- bool fix_length_and_dec();
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_round>(thd, mem_root, this); }
+ void fix_arg_decimal();
+ void fix_arg_int();
+ void fix_arg_double();
+ bool fix_length_and_dec()
+ {
+ return args[0]->type_handler()->Item_func_round_fix_length_and_dec(this);
+ }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_round>(thd, this); }
};
@@ -1130,6 +1499,8 @@ class Item_func_rand :public Item_real_func
{
struct my_rnd_struct *rand;
bool first_eval; // TRUE if val_real() is called 1st time
+ bool check_arguments() const
+ { return check_argument_types_can_return_int(0, arg_count); }
public:
Item_func_rand(THD *thd, Item *a):
Item_real_func(thd, a), rand(0), first_eval(TRUE) {}
@@ -1144,21 +1515,25 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_NON_DETERMINISTIC);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_rand>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_rand>(thd, this); }
private:
void seed_random (Item * val);
};
-class Item_func_sign :public Item_int_func
+class Item_func_sign :public Item_long_func
{
+ bool check_arguments() const
+ { return args[0]->check_type_can_return_real(func_name()); }
public:
- Item_func_sign(THD *thd, Item *a): Item_int_func(thd, a) {}
+ Item_func_sign(THD *thd, Item *a): Item_long_func(thd, a) {}
const char *func_name() const { return "sign"; }
+ uint decimal_precision() const { return 1; }
+ bool fix_length_and_dec() { fix_char_length(2); return FALSE; }
longlong val_int();
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_sign>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_sign>(thd, this); }
};
@@ -1166,6 +1541,8 @@ class Item_func_units :public Item_real_func
{
char *name;
double mul,add;
+ bool check_arguments() const
+ { return check_argument_types_can_return_real(0, arg_count); }
public:
Item_func_units(THD *thd, char *name_arg, Item *a, double mul_arg,
double add_arg):
@@ -1173,9 +1550,13 @@ public:
double val_real();
const char *func_name() const { return name; }
bool fix_length_and_dec()
- { decimals= NOT_FIXED_DEC; max_length= float_length(decimals); return FALSE; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_units>(thd, mem_root, this); }
+ {
+ decimals= NOT_FIXED_DEC;
+ max_length= float_length(decimals);
+ return FALSE;
+ }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_units>(thd, this); }
};
@@ -1194,16 +1575,76 @@ class Item_func_min_max :public Item_hybrid_func
{
String tmp_value;
int cmp_sign;
+protected:
+ bool fix_attributes(Item **item, uint nitems);
public:
Item_func_min_max(THD *thd, List<Item> &list, int cmp_sign_arg):
Item_hybrid_func(thd, list), cmp_sign(cmp_sign_arg)
{}
- double val_real();
- longlong val_int();
- String *val_str(String *);
- my_decimal *val_decimal(my_decimal *);
- bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
- bool fix_length_and_dec();
+ String *val_str_native(String *str);
+ double val_real_native();
+ longlong val_int_native();
+ my_decimal *val_decimal_native(my_decimal *);
+ bool get_date_native(MYSQL_TIME *res, ulonglong fuzzydate);
+ bool get_time_native(MYSQL_TIME *res);
+
+ double val_real()
+ {
+ DBUG_ASSERT(fixed);
+ return Item_func_min_max::type_handler()->
+ Item_func_min_max_val_real(this);
+ }
+ longlong val_int()
+ {
+ DBUG_ASSERT(fixed);
+ return Item_func_min_max::type_handler()->
+ Item_func_min_max_val_int(this);
+ }
+ String *val_str(String *str)
+ {
+ DBUG_ASSERT(fixed);
+ return Item_func_min_max::type_handler()->
+ Item_func_min_max_val_str(this, str);
+ }
+ my_decimal *val_decimal(my_decimal *dec)
+ {
+ DBUG_ASSERT(fixed);
+ return Item_func_min_max::type_handler()->
+ Item_func_min_max_val_decimal(this, dec);
+ }
+ bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date)
+ {
+ DBUG_ASSERT(fixed);
+ return Item_func_min_max::type_handler()->
+ Item_func_min_max_get_date(this, res, fuzzy_date);
+ }
+ void aggregate_attributes_real(Item **items, uint nitems)
+ {
+ /*
+ Aggregating attributes for the double data type for LEAST/GREATEST
+ is almost the same with aggregating for CASE-alike hybrid functions,
+ (CASE..THEN, COALESCE, IF, etc).
+ There is one notable difference though, when a numeric argument is mixed
+ with a string argument:
+ - CASE-alike functions return a string data type in such cases
+ COALESCE(10,'x') -> VARCHAR(2) = '10'
+ - LEAST/GREATEST returns double:
+ GREATEST(10,'10e4') -> DOUBLE = 100000
+ As the string argument can represent a number in the scientific notation,
+ like in the example above, max_length of the result can be longer than
+ max_length of the arguments. To handle this properly, max_length is
+ additionally assigned to the result of float_length(decimals).
+ */
+ Item_func::aggregate_attributes_real(items, nitems);
+ max_length= float_length(decimals);
+ }
+ bool fix_length_and_dec()
+ {
+ if (aggregate_for_min_max(func_name(), args, arg_count))
+ return true;
+ fix_attributes(args, arg_count);
+ return false;
+ }
};
class Item_func_min :public Item_func_min_max
@@ -1211,8 +1652,8 @@ class Item_func_min :public Item_func_min_max
public:
Item_func_min(THD *thd, List<Item> &list): Item_func_min_max(thd, list, 1) {}
const char *func_name() const { return "least"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_min>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_min>(thd, this); }
};
class Item_func_max :public Item_func_min_max
@@ -1220,8 +1661,8 @@ class Item_func_max :public Item_func_min_max
public:
Item_func_max(THD *thd, List<Item> &list): Item_func_min_max(thd, list, -1) {}
const char *func_name() const { return "greatest"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_max>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_max>(thd, this); }
};
@@ -1236,7 +1677,6 @@ public:
Item_func_rollup_const(THD *thd, Item *a): Item_func(thd, a)
{
name= a->name;
- name_length= a->name_length;
}
double val_real() { return val_real_from_item(args[0]); }
longlong val_int() { return val_int_from_item(args[0]); }
@@ -1247,61 +1687,73 @@ public:
{ return get_date_from_item(args[0], ltime, fuzzydate); }
const char *func_name() const { return "rollup_const"; }
bool const_item() const { return 0; }
- Item_result result_type() const { return args[0]->result_type(); }
- enum_field_types field_type() const { return args[0]->field_type(); }
+ 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;
- /* The item could be a NULL constant. */
- null_value= args[0]->is_null();
+ decimals=args[0]->decimals;
return FALSE;
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_rollup_const>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_rollup_const>(thd, this); }
+};
+
+
+class Item_long_func_length: public Item_long_func
+{
+ bool check_arguments() const
+ { return args[0]->check_type_can_return_str(func_name()); }
+public:
+ Item_long_func_length(THD *thd, Item *a): Item_long_func(thd, a) {}
+ bool fix_length_and_dec() { max_length=10; return FALSE; }
};
-class Item_func_length :public Item_int_func
+class Item_func_octet_length :public Item_long_func_length
{
String value;
public:
- Item_func_length(THD *thd, Item *a): Item_int_func(thd, a) {}
+ Item_func_octet_length(THD *thd, Item *a): Item_long_func_length(thd, a) {}
longlong val_int();
- const char *func_name() const { return "length"; }
- bool fix_length_and_dec() { max_length=10; return FALSE; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_length>(thd, mem_root, this); }
+ const char *func_name() const { return "octet_length"; }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_octet_length>(thd, this); }
};
-class Item_func_bit_length :public Item_func_length
+class Item_func_bit_length :public Item_longlong_func
{
+ String value;
public:
- Item_func_bit_length(THD *thd, Item *a): Item_func_length(thd, a) {}
- longlong val_int()
- { DBUG_ASSERT(fixed == 1); return Item_func_length::val_int()*8; }
+ Item_func_bit_length(THD *thd, Item *a): Item_longlong_func(thd, a) {}
+ bool fix_length_and_dec()
+ {
+ max_length= 11; // 0x100000000*8 = 34,359,738,368
+ return FALSE;
+ }
+ longlong val_int();
const char *func_name() const { return "bit_length"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_bit_length>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_bit_length>(thd, this); }
};
-class Item_func_char_length :public Item_int_func
+class Item_func_char_length :public Item_long_func_length
{
String value;
public:
- Item_func_char_length(THD *thd, Item *a): Item_int_func(thd, a) {}
+ Item_func_char_length(THD *thd, Item *a): Item_long_func_length(thd, a) {}
longlong val_int();
const char *func_name() const { return "char_length"; }
- bool fix_length_and_dec() { max_length=10; return FALSE; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_char_length>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_char_length>(thd, this); }
};
-class Item_func_coercibility :public Item_int_func
+class Item_func_coercibility :public Item_long_func
{
+ bool check_arguments() const
+ { return args[0]->check_type_can_return_str(func_name()); }
public:
- Item_func_coercibility(THD *thd, Item *a): Item_int_func(thd, a) {}
+ Item_func_coercibility(THD *thd, Item *a): Item_long_func(thd, a) {}
longlong val_int();
const char *func_name() const { return "coercibility"; }
bool fix_length_and_dec() { max_length=10; maybe_null= 0; return FALSE; }
@@ -1313,87 +1765,114 @@ public:
Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond)
{ return this; }
bool const_item() const { return true; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_coercibility>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_coercibility>(thd, this); }
};
-class Item_func_locate :public Item_int_func
+
+/*
+ In the corner case LOCATE could return (4,294,967,296 + 1),
+ which would not fit into Item_long_func range.
+ But string lengths are limited with max_allowed_packet,
+ which cannot be bigger than 1024*1024*1024.
+*/
+class Item_func_locate :public Item_long_func
{
+ bool check_arguments() const
+ {
+ return check_argument_types_can_return_str(0, 2) ||
+ (arg_count > 2 && args[2]->check_type_can_return_int(func_name()));
+ }
String value1,value2;
DTCollation cmp_collation;
public:
- Item_func_locate(THD *thd, Item *a, Item *b): Item_int_func(thd, a, b) {}
- Item_func_locate(THD *thd, Item *a, Item *b, Item *c): Item_int_func(thd, a, b, c) {}
+ Item_func_locate(THD *thd, Item *a, Item *b)
+ :Item_long_func(thd, a, b) {}
+ Item_func_locate(THD *thd, Item *a, Item *b, Item *c)
+ :Item_long_func(thd, a, b, c) {}
const char *func_name() const { return "locate"; }
longlong val_int();
- bool fix_length_and_dec();
+ bool fix_length_and_dec()
+ {
+ max_length= MY_INT32_NUM_DECIMAL_DIGITS;
+ return agg_arg_charsets_for_comparison(cmp_collation, args, 2);
+ }
virtual void print(String *str, enum_query_type query_type);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_locate>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_locate>(thd, this); }
};
-class Item_func_field :public Item_int_func
+class Item_func_field :public Item_long_func
{
String value,tmp;
Item_result cmp_type;
DTCollation cmp_collation;
public:
- Item_func_field(THD *thd, List<Item> &list): Item_int_func(thd, list) {}
+ Item_func_field(THD *thd, List<Item> &list): Item_long_func(thd, list) {}
longlong val_int();
const char *func_name() const { return "field"; }
bool fix_length_and_dec();
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_field>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_field>(thd, this); }
};
-class Item_func_ascii :public Item_int_func
+class Item_func_ascii :public Item_long_func
{
+ bool check_arguments() const
+ { return check_argument_types_can_return_str(0, arg_count); }
String value;
public:
- Item_func_ascii(THD *thd, Item *a): Item_int_func(thd, a) {}
+ Item_func_ascii(THD *thd, Item *a): Item_long_func(thd, a) {}
longlong val_int();
const char *func_name() const { return "ascii"; }
bool fix_length_and_dec() { max_length=3; return FALSE; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_ascii>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_ascii>(thd, this); }
};
-class Item_func_ord :public Item_int_func
+class Item_func_ord :public Item_long_func
{
+ bool check_arguments() const
+ { return args[0]->check_type_can_return_str(func_name()); }
String value;
public:
- Item_func_ord(THD *thd, Item *a): Item_int_func(thd, a) {}
+ Item_func_ord(THD *thd, Item *a): Item_long_func(thd, a) {}
+ bool fix_length_and_dec() { fix_char_length(7); return FALSE; }
longlong val_int();
const char *func_name() const { return "ord"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_ord>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_ord>(thd, this); }
};
-class Item_func_find_in_set :public Item_int_func
+class Item_func_find_in_set :public Item_long_func
{
+ bool check_arguments() const
+ { return check_argument_types_can_return_str(0, 2); }
String value,value2;
uint enum_value;
ulonglong enum_bit;
DTCollation cmp_collation;
public:
Item_func_find_in_set(THD *thd, Item *a, Item *b):
- Item_int_func(thd, a, b), enum_value(0) {}
+ Item_long_func(thd, a, b), enum_value(0) {}
longlong val_int();
const char *func_name() const { return "find_in_set"; }
bool fix_length_and_dec();
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_find_in_set>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_find_in_set>(thd, this); }
};
/* Base class for all bit functions: '~', '|', '^', '&', '>>', '<<' */
-class Item_func_bit: public Item_int_func
+class Item_func_bit: public Item_longlong_func
{
+ bool check_arguments() const
+ { return check_argument_types_can_return_int(0, arg_count); }
public:
- Item_func_bit(THD *thd, Item *a, Item *b): Item_int_func(thd, a, b) {}
- Item_func_bit(THD *thd, Item *a): Item_int_func(thd, a) {}
+ 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)
@@ -1410,8 +1889,8 @@ public:
longlong val_int();
const char *func_name() const { return "|"; }
enum precedence precedence() const { return BITOR_PRECEDENCE; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_bit_or>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_bit_or>(thd, this); }
};
class Item_func_bit_and :public Item_func_bit
@@ -1421,19 +1900,21 @@ public:
longlong val_int();
const char *func_name() const { return "&"; }
enum precedence precedence() const { return BITAND_PRECEDENCE; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_bit_and>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_bit_and>(thd, this); }
};
-class Item_func_bit_count :public Item_int_func
+class Item_func_bit_count :public Item_long_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_int_func(thd, a) {}
+ Item_func_bit_count(THD *thd, Item *a): Item_long_func(thd, a) {}
longlong val_int();
const char *func_name() const { return "bit_count"; }
bool fix_length_and_dec() { max_length=2; return FALSE; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_bit_count>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_bit_count>(thd, this); }
};
class Item_func_shift_left :public Item_func_bit
@@ -1443,8 +1924,8 @@ public:
longlong val_int();
const char *func_name() const { return "<<"; }
enum precedence precedence() const { return SHIFT_PRECEDENCE; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_shift_left>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_shift_left>(thd, this); }
};
class Item_func_shift_right :public Item_func_bit
@@ -1454,8 +1935,8 @@ public:
longlong val_int();
const char *func_name() const { return ">>"; }
enum precedence precedence() const { return SHIFT_PRECEDENCE; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_shift_right>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_shift_right>(thd, this); }
};
class Item_func_bit_neg :public Item_func_bit
@@ -1470,24 +1951,25 @@ public:
str->append(func_name());
args[0]->print_parenthesised(str, query_type, precedence());
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_bit_neg>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_bit_neg>(thd, this); }
};
-class Item_func_last_insert_id :public Item_int_func
+class Item_func_last_insert_id :public Item_longlong_func
{
+ bool check_arguments() const
+ { return check_argument_types_can_return_int(0, arg_count); }
public:
- Item_func_last_insert_id(THD *thd): Item_int_func(thd) {}
- Item_func_last_insert_id(THD *thd, Item *a): Item_int_func(thd, a) {}
+ Item_func_last_insert_id(THD *thd): Item_longlong_func(thd) {}
+ Item_func_last_insert_id(THD *thd, Item *a): Item_longlong_func(thd, a) {}
longlong val_int();
const char *func_name() const { return "last_insert_id"; }
bool fix_length_and_dec()
{
- unsigned_flag= TRUE;
+ unsigned_flag= true;
if (arg_count)
max_length= args[0]->max_length;
- unsigned_flag=1;
return FALSE;
}
bool fix_fields(THD *thd, Item **ref);
@@ -1495,16 +1977,21 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_last_insert_id>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_last_insert_id>(thd, this); }
};
-class Item_func_benchmark :public Item_int_func
+class Item_func_benchmark :public Item_long_func
{
+ bool check_arguments() const
+ {
+ return args[0]->check_type_can_return_int(func_name()) ||
+ args[1]->check_type_scalar(func_name());
+ }
public:
Item_func_benchmark(THD *thd, Item *count_expr, Item *expr):
- Item_int_func(thd, count_expr, expr)
+ Item_long_func(thd, count_expr, expr)
{}
longlong val_int();
const char *func_name() const { return "benchmark"; }
@@ -1514,18 +2001,21 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_benchmark>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_benchmark>(thd, this); }
};
void item_func_sleep_init(void);
void item_func_sleep_free(void);
-class Item_func_sleep :public Item_int_func
+class Item_func_sleep :public Item_long_func
{
+ bool check_arguments() const
+ { return args[0]->check_type_can_return_real(func_name()); }
public:
- Item_func_sleep(THD *thd, Item *a): Item_int_func(thd, a) {}
+ Item_func_sleep(THD *thd, Item *a): Item_long_func(thd, a) {}
+ bool fix_length_and_dec() { fix_char_length(1); return FALSE; }
bool const_item() const { return 0; }
const char *func_name() const { return "sleep"; }
table_map used_tables() const
@@ -1538,8 +2028,8 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_sleep>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_sleep>(thd, this); }
};
@@ -1627,7 +2117,6 @@ public:
}
}
void cleanup();
- Item_result result_type () const { return udf.result_type(); }
bool eval_not_null_tables(void *opt_arg)
{
not_null_tables_cache= 0;
@@ -1639,6 +2128,10 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_NON_DETERMINISTIC);
}
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ {
+ return type_handler()->Item_get_date(this, ltime, fuzzydate);
+ }
};
@@ -1666,10 +2159,10 @@ class Item_func_udf_float :public Item_udf_func
}
double val_real();
String *val_str(String *str);
- enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; }
+ const Type_handler *type_handler() const { return &type_handler_double; }
bool fix_length_and_dec() { fix_num_length_and_dec(); return FALSE; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_udf_float>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_udf_float>(thd, this); }
};
@@ -1684,11 +2177,10 @@ public:
longlong val_int();
double val_real() { return (double) Item_func_udf_int::val_int(); }
String *val_str(String *str);
- enum Item_result result_type () const { return INT_RESULT; }
- enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; }
+ const Type_handler *type_handler() const { return &type_handler_longlong; }
bool fix_length_and_dec() { decimals= 0; max_length= 21; return FALSE; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_udf_int>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_udf_int>(thd, this); }
};
@@ -1703,11 +2195,10 @@ public:
double val_real();
my_decimal *val_decimal(my_decimal *);
String *val_str(String *str);
- enum Item_result result_type () const { return DECIMAL_RESULT; }
- enum_field_types field_type() const { return MYSQL_TYPE_NEWDECIMAL; }
+ const Type_handler *type_handler() const { return &type_handler_newdecimal; }
bool fix_length_and_dec() { fix_num_length_and_dec(); return FALSE; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_udf_decimal>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_udf_decimal>(thd, this); }
};
@@ -1743,11 +2234,10 @@ public:
string2my_decimal(E_DEC_FATAL_ERROR, res, dec_buf);
return dec_buf;
}
- enum Item_result result_type () const { return STRING_RESULT; }
- enum_field_types field_type() const { return string_field_type(); }
+ const Type_handler *type_handler() const { return string_type_handler(); }
bool fix_length_and_dec();
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_udf_str>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_udf_str>(thd, this); }
};
#else /* Dummy functions to get sql_yacc.cc compiled */
@@ -1770,6 +2260,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; }
longlong val_int() { DBUG_ASSERT(fixed == 1); return 0; }
};
@@ -1781,6 +2272,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; }
my_decimal *val_decimal(my_decimal *) { DBUG_ASSERT(fixed == 1); return 0; }
};
@@ -1796,7 +2288,6 @@ public:
{ DBUG_ASSERT(fixed == 1); null_value=1; return 0; }
double val_real() { DBUG_ASSERT(fixed == 1); null_value= 1; return 0.0; }
longlong val_int() { DBUG_ASSERT(fixed == 1); null_value=1; return 0; }
- enum Item_result result_type () const { return STRING_RESULT; }
bool fix_length_and_dec() { maybe_null=1; max_length=0; return FALSE; }
};
@@ -1805,11 +2296,16 @@ public:
void mysql_ull_cleanup(THD *thd);
void mysql_ull_set_explicit_lock_duration(THD *thd);
-class Item_func_get_lock :public Item_int_func
+class Item_func_get_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_int_func(thd, a, b) {}
+ 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; }
@@ -1823,15 +2319,17 @@ class Item_func_get_lock :public Item_int_func
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_get_lock>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_get_lock>(thd, this); }
};
-class Item_func_release_lock :public Item_int_func
+class Item_func_release_lock :public Item_long_func
{
+ 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_int_func(thd, a) {}
+ Item_func_release_lock(THD *thd, Item *a): Item_long_func(thd, a) {}
longlong val_int();
const char *func_name() const { return "release_lock"; }
bool fix_length_and_dec() { max_length= 1; maybe_null= 1; return FALSE; }
@@ -1845,21 +2343,30 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_release_lock>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_release_lock>(thd, this); }
};
/* replication functions */
-class Item_master_pos_wait :public Item_int_func
+class Item_master_pos_wait :public Item_longlong_func
{
+ bool check_arguments() const
+ {
+ return
+ args[0]->check_type_general_purpose_string(func_name()) ||
+ args[1]->check_type_can_return_int(func_name()) ||
+ (arg_count > 2 && args[2]->check_type_can_return_int(func_name())) ||
+ (arg_count > 3 && args[3]->check_type_general_purpose_string(func_name()));
+ }
String value;
public:
- Item_master_pos_wait(THD *thd, Item *a, Item *b): Item_int_func(thd, a, b) {}
+ Item_master_pos_wait(THD *thd, Item *a, Item *b)
+ :Item_longlong_func(thd, a, b) {}
Item_master_pos_wait(THD *thd, Item *a, Item *b, Item *c):
- Item_int_func(thd, a, b, c) {}
+ Item_longlong_func(thd, a, b, c) {}
Item_master_pos_wait(THD *thd, Item *a, Item *b, Item *c, Item *d):
- Item_int_func(thd, a, b, c, d) {}
+ Item_longlong_func(thd, a, b, c, d) {}
longlong val_int();
const char *func_name() const { return "master_pos_wait"; }
bool fix_length_and_dec() { max_length=21; maybe_null=1; return FALSE; }
@@ -1867,17 +2374,24 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_master_pos_wait>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_master_pos_wait>(thd, this); }
};
-class Item_master_gtid_wait :public Item_int_func
+class Item_master_gtid_wait :public Item_long_func
{
+ bool check_arguments() const
+ {
+ return args[0]->check_type_general_purpose_string(func_name()) ||
+ (arg_count > 1 && args[1]->check_type_can_return_real(func_name()));
+ }
String value;
public:
- Item_master_gtid_wait(THD *thd, Item *a): Item_int_func(thd, a) {}
- Item_master_gtid_wait(THD *thd, Item *a, Item *b): Item_int_func(thd, a, b) {}
+ Item_master_gtid_wait(THD *thd, Item *a)
+ :Item_long_func(thd, a) {}
+ Item_master_gtid_wait(THD *thd, Item *a, Item *b)
+ :Item_long_func(thd, a, b) {}
longlong val_int();
const char *func_name() const { return "master_gtid_wait"; }
bool fix_length_and_dec() { max_length=2; return FALSE; }
@@ -1885,8 +2399,8 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_master_gtid_wait>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_master_gtid_wait>(thd, this); }
};
@@ -1903,15 +2417,21 @@ class Item_func_user_var :public Item_hybrid_func
protected:
user_var_entry *m_var_entry;
public:
- LEX_STRING name; // keep it public
- Item_func_user_var(THD *thd, LEX_STRING a)
- :Item_hybrid_func(thd), m_var_entry(NULL), name(a) { }
- Item_func_user_var(THD *thd, LEX_STRING a, Item *b)
- :Item_hybrid_func(thd, b), m_var_entry(NULL), name(a) { }
+ LEX_CSTRING name; // keep it public
+ Item_func_user_var(THD *thd, const LEX_CSTRING *a)
+ :Item_hybrid_func(thd), m_var_entry(NULL), name(*a) { }
+ Item_func_user_var(THD *thd, const LEX_CSTRING *a, Item *b)
+ :Item_hybrid_func(thd, b), m_var_entry(NULL), name(*a) { }
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(bool group, TABLE *table)
+ { return create_table_field_from_handler(table); }
+ Field *create_field_for_create_select(TABLE *table)
+ { return create_table_field_from_handler(table); }
bool check_vcol_func_processor(void *arg);
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ { return type_handler()->Item_get_date(this, ltime, fuzzydate); }
};
@@ -1940,7 +2460,7 @@ class Item_func_set_user_var :public Item_func_user_var
} save_result;
public:
- Item_func_set_user_var(THD *thd, LEX_STRING a, Item *b):
+ Item_func_set_user_var(THD *thd, const LEX_CSTRING *a, Item *b):
Item_func_user_var(thd, a, b),
entry_thread_id(0)
{}
@@ -1962,21 +2482,15 @@ public:
String *str_result(String *str);
my_decimal *val_decimal_result(my_decimal *);
bool is_null_result();
- bool update_hash(void *ptr, uint length, enum Item_result type,
+ bool update_hash(void *ptr, size_t length, enum Item_result type,
CHARSET_INFO *cs, bool unsigned_arg);
- bool send(Protocol *protocol, String *str_arg);
- void make_field(THD *thd, Send_field *tmp_field);
+ bool send(Protocol *protocol, st_value *buffer);
+ void make_send_field(THD *thd, Send_field *tmp_field);
bool check(bool use_result_field);
void save_item_result(Item *item);
bool update();
bool fix_fields(THD *thd, Item **ref);
bool fix_length_and_dec();
- Field *create_field_for_create_select(TABLE *table)
- {
- return result_type() != STRING_RESULT ?
- create_tmp_field(false, table, MY_INT32_NUM_DECIMAL_DIGITS) :
- tmp_table_field_from_field_type(table, false, true);
- }
void print(String *str, enum_query_type query_type);
enum precedence precedence() const { return ASSIGN_PRECEDENCE; }
void print_as_stmt(String *str, enum_query_type query_type);
@@ -1994,8 +2508,8 @@ public:
bool register_field_in_bitmap(void *arg);
bool set_entry(THD *thd, bool create_if_not_exists);
void cleanup();
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_set_user_var>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_set_user_var>(thd, this); }
bool excl_dep_on_table(table_map tab_map) { return false; }
};
@@ -2004,10 +2518,10 @@ class Item_func_get_user_var :public Item_func_user_var,
private Settable_routine_parameter
{
public:
- Item_func_get_user_var(THD *thd, LEX_STRING a):
+ Item_func_get_user_var(THD *thd, const LEX_CSTRING *a):
Item_func_user_var(thd, a) {}
enum Functype functype() const { return GUSERVAR_FUNC; }
- LEX_STRING get_name() { return name; }
+ LEX_CSTRING get_name() { return name; }
double val_real();
longlong val_int();
my_decimal *val_decimal(my_decimal*);
@@ -2023,8 +2537,8 @@ public:
table_map used_tables() const
{ return const_item() ? 0 : RAND_TABLE_BIT; }
bool eq(const Item *item, bool binary_cmp) const;
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_get_user_var>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_get_user_var>(thd, this); }
private:
bool set_value(THD *thd, sp_rcontext *ctx, Item **it);
@@ -2048,11 +2562,16 @@ public:
class Item_user_var_as_out_param :public Item,
public Load_data_outvar
{
- LEX_STRING name;
+ LEX_CSTRING org_name;
user_var_entry *entry;
public:
- Item_user_var_as_out_param(THD *thd, LEX_STRING a): Item(thd), name(a)
- { set_name(thd, a.str, 0, system_charset_info); }
+ Item_user_var_as_out_param(THD *thd, const LEX_CSTRING *a)
+ :Item(thd)
+ {
+ DBUG_ASSERT(a->length < UINT_MAX32);
+ org_name= *a;
+ set_name(thd, a->str, a->length, system_charset_info);
+ }
Load_data_outvar *get_load_data_outvar()
{
return this;
@@ -2087,14 +2606,15 @@ public:
double val_real();
longlong val_int();
String *val_str(String *str);
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
my_decimal *val_decimal(my_decimal *decimal_buffer);
/* fix_fields() binds variable name with its entry structure */
bool fix_fields(THD *thd, Item **ref);
void set_null_value(CHARSET_INFO* cs);
void set_value(const char *str, uint length, CHARSET_INFO* cs);
- enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_user_var_as_out_param>(thd, mem_root, this); }
+ const Type_handler *type_handler() const { return &type_handler_double; }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_user_var_as_out_param>(thd, this); }
};
@@ -2108,7 +2628,7 @@ class Item_func_get_system_var :public Item_func
{
sys_var *var;
enum_var_type var_type, orig_var_type;
- LEX_STRING component;
+ LEX_CSTRING component;
longlong cached_llval;
double cached_dval;
String cached_strval;
@@ -2119,7 +2639,7 @@ class Item_func_get_system_var :public Item_func
public:
Item_func_get_system_var(THD *thd, sys_var *var_arg,
enum_var_type var_type_arg,
- LEX_STRING *component_arg, const char *name_arg,
+ LEX_CSTRING *component_arg, const char *name_arg,
size_t name_len_arg);
enum Functype functype() const { return GSYSVAR_FUNC; }
void update_null_value();
@@ -2127,13 +2647,16 @@ public:
void print(String *str, enum_query_type query_type);
bool const_item() const { return true; }
table_map used_tables() const { return 0; }
- enum Item_result result_type() const;
- enum_field_types field_type() const;
+ const Type_handler *type_handler() const;
double val_real();
longlong val_int();
String* val_str(String*);
my_decimal *val_decimal(my_decimal *dec_buf)
{ return val_decimal_from_real(dec_buf); }
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ {
+ return type_handler()->Item_get_date(this, ltime, fuzzydate);
+ }
/* TODO: fix to support views */
const char *func_name() const { return "get_system_var"; }
/**
@@ -2149,8 +2672,8 @@ public:
void cleanup();
bool check_vcol_func_processor(void *arg);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_get_system_var>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_get_system_var>(thd, this); }
};
@@ -2199,14 +2722,14 @@ public:
virtual void print(String *str, enum_query_type query_type);
bool fix_index();
- void init_search(THD *thd, bool no_order);
+ bool init_search(THD *thd, bool no_order);
bool check_vcol_func_processor(void *arg)
{
return mark_unsupported_function("match ... against()", arg, VCOL_IMPOSSIBLE);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_match>(thd, mem_root, this); }
- Item *build_clone(THD *thd, MEM_ROOT *mem_root) { return 0; }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_match>(thd, this); }
+ Item *build_clone(THD *thd) { return 0; }
private:
/**
Check whether storage engine for given table,
@@ -2251,15 +2774,17 @@ public:
longlong val_int();
const char *func_name() const { return "^"; }
enum precedence precedence() const { return BITXOR_PRECEDENCE; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_bit_xor>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_bit_xor>(thd, this); }
};
-class Item_func_is_free_lock :public Item_int_func
+class Item_func_is_free_lock :public Item_long_func
{
+ bool check_arguments() const
+ { return args[0]->check_type_general_purpose_string(func_name()); }
String value;
public:
- Item_func_is_free_lock(THD *thd, Item *a): Item_int_func(thd, a) {}
+ Item_func_is_free_lock(THD *thd, Item *a): Item_long_func(thd, a) {}
longlong val_int();
const char *func_name() const { return "is_free_lock"; }
bool fix_length_and_dec()
@@ -2271,15 +2796,17 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_is_free_lock>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_is_free_lock>(thd, this); }
};
-class Item_func_is_used_lock :public Item_int_func
+class Item_func_is_used_lock :public Item_long_func
{
+ bool check_arguments() const
+ { return args[0]->check_type_general_purpose_string(func_name()); }
String value;
public:
- Item_func_is_used_lock(THD *thd, Item *a): Item_int_func(thd, a) {}
+ Item_func_is_used_lock(THD *thd, Item *a): Item_long_func(thd, a) {}
longlong val_int();
const char *func_name() const { return "is_used_lock"; }
bool fix_length_and_dec()
@@ -2291,51 +2818,48 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_is_used_lock>(thd, mem_root, this); }
-};
-
-/* For type casts */
-
-enum Cast_target
-{
- ITEM_CAST_BINARY, ITEM_CAST_SIGNED_INT, ITEM_CAST_UNSIGNED_INT,
- ITEM_CAST_DATE, ITEM_CAST_TIME, ITEM_CAST_DATETIME, ITEM_CAST_CHAR,
- ITEM_CAST_DECIMAL, ITEM_CAST_DOUBLE
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_is_used_lock>(thd, this); }
};
struct Lex_cast_type_st: public Lex_length_and_dec_st
{
private:
- Cast_target m_type;
+ const Type_handler *m_type_handler;
public:
- void set(Cast_target type, const char *length, const char *dec)
+ void set(const Type_handler *handler, const char *length, const char *dec)
{
- m_type= type;
+ m_type_handler= handler;
Lex_length_and_dec_st::set(length, dec);
}
- void set(Cast_target type, Lex_length_and_dec_st length_and_dec)
+ void set(const Type_handler *handler, Lex_length_and_dec_st length_and_dec)
{
- m_type= type;
+ m_type_handler= handler;
Lex_length_and_dec_st::operator=(length_and_dec);
}
- void set(Cast_target type, const char *length)
+ void set(const Type_handler *handler, const char *length)
+ {
+ set(handler, length, 0);
+ }
+ void set(const Type_handler *handler)
{
- set(type, length, 0);
+ set(handler, 0, 0);
}
- void set(Cast_target type)
+ const Type_handler *type_handler() const { return m_type_handler; }
+ Item *create_typecast_item(THD *thd, Item *item, CHARSET_INFO *cs= NULL)
{
- set(type, 0, 0);
+ return m_type_handler->
+ create_typecast_item(thd, item,
+ Type_cast_attributes(length(), dec(), cs));
}
- Cast_target type() const { return m_type; }
};
-class Item_func_row_count :public Item_int_func
+class Item_func_row_count :public Item_longlong_func
{
public:
- Item_func_row_count(THD *thd): Item_int_func(thd) {}
+ Item_func_row_count(THD *thd): Item_longlong_func(thd) {}
longlong val_int();
const char *func_name() const { return "row_count"; }
bool fix_length_and_dec() { decimals= 0; maybe_null=0; return FALSE; }
@@ -2343,8 +2867,8 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_row_count>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_row_count>(thd, this); }
};
@@ -2354,37 +2878,30 @@ public:
*
*/
-class sp_head;
-class sp_name;
-struct st_sp_security_context;
-
-class Item_func_sp :public Item_func
+class Item_func_sp :public Item_func,
+ public Item_sp
{
private:
- Name_resolution_context *context;
- sp_name *m_name;
- mutable sp_head *m_sp;
- TABLE *dummy_table;
- uchar result_buf[64];
- /*
- The result field of the concrete stored function.
- */
- Field *sp_result_field;
+ const Sp_handler *m_handler;
bool execute();
- bool execute_impl(THD *thd);
- bool init_result_field(THD *thd);
protected:
bool is_expensive_processor(void *arg)
{ return is_expensive(); }
-
+
+ bool check_arguments() const
+ {
+ // sp_prepare_func_item() checks that the number of columns is correct
+ return false;
+ }
public:
- Item_func_sp(THD *thd, Name_resolution_context *context_arg, sp_name *name);
+ Item_func_sp(THD *thd, Name_resolution_context *context_arg,
+ sp_name *name, const Sp_handler *sph);
Item_func_sp(THD *thd, Name_resolution_context *context_arg,
- sp_name *name, List<Item> &list);
+ sp_name *name, const Sp_handler *sph, List<Item> &list);
virtual ~Item_func_sp()
{}
@@ -2395,17 +2912,15 @@ public:
const char *func_name() const;
- enum enum_field_types field_type() const;
+ const Type_handler *type_handler() const;
Field *create_field_for_create_select(TABLE *table)
{
return result_type() != STRING_RESULT ?
sp_result_field :
- tmp_table_field_from_field_type(table, false, false);
+ create_table_field_from_handler(table);
}
- void make_field(THD *thd, Send_field *tmp_field);
-
- Item_result result_type() const;
+ void make_send_field(THD *thd, Send_field *tmp_field);
longlong val_int()
{
@@ -2462,7 +2977,6 @@ public:
virtual bool change_context_processor(void *cntx)
{ context= (Name_resolution_context *)cntx; return FALSE; }
- bool sp_check_access(THD * thd);
virtual enum Functype functype() const { return FUNC_SP; }
bool fix_fields(THD *thd, Item **ref);
@@ -2473,13 +2987,17 @@ public:
{
return sp_result_field;
}
+ const sp_name *get_sp_name() const
+ {
+ return m_name;
+ }
bool check_vcol_func_processor(void *arg);
bool limit_index_condition_pushdown_processor(void *opt_arg)
{
return TRUE;
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; }
+ Item *get_copy(THD *) { return 0; }
bool eval_not_null_tables(void *opt_arg)
{
not_null_tables_cache= 0;
@@ -2488,10 +3006,10 @@ public:
};
-class Item_func_found_rows :public Item_int_func
+class Item_func_found_rows :public Item_longlong_func
{
public:
- Item_func_found_rows(THD *thd): Item_int_func(thd) {}
+ Item_func_found_rows(THD *thd): Item_longlong_func(thd) {}
longlong val_int();
const char *func_name() const { return "found_rows"; }
bool fix_length_and_dec() { decimals= 0; maybe_null=0; return FALSE; }
@@ -2499,17 +3017,61 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_found_rows>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_found_rows>(thd, this); }
+};
+
+
+class Item_func_oracle_sql_rowcount :public Item_longlong_func
+{
+public:
+ Item_func_oracle_sql_rowcount(THD *thd): Item_longlong_func(thd) {}
+ longlong val_int();
+ const char *func_name() const { return "SQL%ROWCOUNT"; }
+ void print(String *str, enum_query_type query_type)
+ {
+ str->append(func_name());
+ }
+ bool check_vcol_func_processor(void *arg)
+ {
+ return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
+ }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_oracle_sql_rowcount>(thd, this); }
+};
+
+
+class Item_func_sqlcode: public Item_long_func
+{
+public:
+ Item_func_sqlcode(THD *thd): Item_long_func(thd) { }
+ longlong val_int();
+ const char *func_name() const { return "SQLCODE"; }
+ void print(String *str, enum_query_type query_type)
+ {
+ str->append(func_name());
+ }
+ bool check_vcol_func_processor(void *arg)
+ {
+ return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
+ }
+ bool fix_length_and_dec()
+ {
+ maybe_null= null_value= false;
+ max_length= 11;
+ return FALSE;
+ }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_sqlcode>(thd, this); }
};
void uuid_short_init();
-class Item_func_uuid_short :public Item_int_func
+class Item_func_uuid_short :public Item_longlong_func
{
public:
- Item_func_uuid_short(THD *thd): Item_int_func(thd) {}
+ Item_func_uuid_short(THD *thd): Item_longlong_func(thd) {}
const char *func_name() const { return "uuid_short"; }
longlong val_int();
bool const_item() const { return false; }
@@ -2520,8 +3082,8 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_NON_DETERMINISTIC);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_uuid_short>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_uuid_short>(thd, this); }
};
@@ -2535,15 +3097,15 @@ public:
longlong val_int();
String *val_str(String *);
my_decimal *val_decimal(my_decimal *);
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
bool fix_length_and_dec();
- enum Item_result result_type () const { return last_value->result_type(); }
const char *func_name() const { return "last_value"; }
+ const Type_handler *type_handler() const { return last_value->type_handler(); }
bool eval_not_null_tables(void *)
{
not_null_tables_cache= 0;
return 0;
}
- enum_field_types field_type() const { return last_value->field_type(); }
bool const_item() const { return 0; }
void evaluate_sideeffects();
void update_used_tables()
@@ -2551,22 +3113,103 @@ public:
Item_func::update_used_tables();
maybe_null= last_value->maybe_null;
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_last_value>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_last_value>(thd, this); }
+};
+
+
+/* Implementation for sequences: NEXT VALUE FOR sequence and NEXTVAL() */
+
+class Item_func_nextval :public Item_longlong_func
+{
+protected:
+ TABLE_LIST *table_list;
+ TABLE *table;
+public:
+ Item_func_nextval(THD *thd, TABLE_LIST *table_list_arg):
+ Item_longlong_func(thd), table_list(table_list_arg) {}
+ longlong val_int();
+ const char *func_name() const { return "nextval"; }
+ bool fix_length_and_dec()
+ {
+ unsigned_flag= 0;
+ max_length= MAX_BIGINT_WIDTH;
+ maybe_null= 1; /* In case of errors */
+ return FALSE;
+ }
+ /*
+ update_table() function must be called during the value function
+ as in case of DEFAULT the sequence table may not yet be open
+ while fix_fields() are called
+ */
+ void update_table()
+ {
+ if (!(table= table_list->table))
+ {
+ /*
+ If nextval was used in DEFAULT then next_local points to
+ the table_list used by to open the sequence table
+ */
+ table= table_list->next_local->table;
+ }
+ }
+ bool const_item() const { return 0; }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_nextval>(thd, this); }
+ void print(String *str, enum_query_type query_type);
+ bool check_vcol_func_processor(void *arg)
+ {
+ return mark_unsupported_function(func_name(), "()", arg,
+ (VCOL_NON_DETERMINISTIC |
+ VCOL_NOT_VIRTUAL));
+ }
+};
+
+
+/* Implementation for sequences: LASTVAL(sequence), PostgreSQL style */
+
+class Item_func_lastval :public Item_func_nextval
+{
+public:
+ Item_func_lastval(THD *thd, TABLE_LIST *table_list_arg):
+ Item_func_nextval(thd, table_list_arg) {}
+ longlong val_int();
+ const char *func_name() const { return "lastval"; }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_lastval>(thd, this); }
+};
+
+
+/* Implementation for sequences: SETVAL(sequence), PostgreSQL style */
+
+class Item_func_setval :public Item_func_nextval
+{
+ longlong nextval;
+ ulonglong round;
+ bool is_used;
+public:
+ Item_func_setval(THD *thd, TABLE_LIST *table_list_arg, longlong nextval_arg,
+ ulonglong round_arg, bool is_used_arg)
+ : Item_func_nextval(thd, table_list_arg),
+ nextval(nextval_arg), round(round_arg), is_used(is_used_arg)
+ {}
+ longlong val_int();
+ const char *func_name() const { return "setval"; }
+ void print(String *str, enum_query_type query_type);
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_setval>(thd, this); }
};
-Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name,
- LEX_STRING component);
-extern bool check_reserved_words(LEX_STRING *name);
-extern enum_field_types agg_field_type(Item **items, uint nitems,
- bool treat_bit_as_number);
+Item *get_system_var(THD *thd, enum_var_type var_type,
+ const LEX_CSTRING *name, const LEX_CSTRING *component);
+extern bool check_reserved_words(const LEX_CSTRING *name);
double my_double_round(double value, longlong dec, bool dec_unsigned,
bool truncate);
extern bool volatile mqh_used;
-bool update_hash(user_var_entry *entry, bool set_null, void *ptr, uint length,
+bool update_hash(user_var_entry *entry, bool set_null, void *ptr, size_t length,
Item_result type, CHARSET_INFO *cs,
bool unsigned_arg);
diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc
index 645a3668ed8..b58a82733a4 100644
--- a/sql/item_geofunc.cc
+++ b/sql/item_geofunc.cc
@@ -26,7 +26,7 @@
#pragma implementation // gcc: Class implementation
#endif
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
/*
It is necessary to include set_var.h instead of item.h because there
@@ -40,20 +40,11 @@
#include "opt_range.h"
-Field *Item_geometry_func::create_field_for_create_select(TABLE *t_arg)
-{
- Field *result;
- if ((result= new Field_geom(max_length, maybe_null, name, t_arg->s,
- get_geometry_type())))
- result->init(t_arg);
- return result;
-}
-
bool Item_geometry_func::fix_length_and_dec()
{
collation.set(&my_charset_bin);
decimals=0;
- max_length= (uint32) 4294967295U;
+ max_length= (uint32) UINT_MAX32;
maybe_null= 1;
return FALSE;
}
@@ -227,7 +218,7 @@ String *Item_func_as_wkt::val_str_ascii(String *str)
bool Item_func_as_wkt::fix_length_and_dec()
{
collation.set(default_charset(), DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
- max_length=MAX_BLOB_WIDTH;
+ max_length= (uint32) UINT_MAX32;
maybe_null= 1;
return FALSE;
}
diff --git a/sql/item_geofunc.h b/sql/item_geofunc.h
index a858a9a2d57..24bed6aa3ca 100644
--- a/sql/item_geofunc.h
+++ b/sql/item_geofunc.h
@@ -39,38 +39,200 @@ public:
Item_str_func(thd, a, b, c) {}
Item_geometry_func(THD *thd, List<Item> &list): Item_str_func(thd, list) {}
bool fix_length_and_dec();
- enum_field_types field_type() const { return MYSQL_TYPE_GEOMETRY; }
- Field *create_field_for_create_select(TABLE *table);
+ const Type_handler *type_handler() const { return &type_handler_geometry; }
};
+
+/*
+ Functions returning REAL measurements of a single GEOMETRY argument
+*/
+class Item_real_func_args_geometry: public Item_real_func
+{
+protected:
+ String value;
+ bool check_arguments() const
+ {
+ DBUG_ASSERT(arg_count == 1);
+ return args[0]->check_type_or_binary(func_name(), &type_handler_geometry);
+ }
+public:
+ Item_real_func_args_geometry(THD *thd, Item *a)
+ :Item_real_func(thd, a) {}
+};
+
+
+/*
+ Functions returning INT measurements of a single GEOMETRY argument
+*/
+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);
+ }
+protected:
+ String value;
+public:
+ Item_long_func_args_geometry(THD *thd, Item *a)
+ :Item_long_func(thd, a) {}
+};
+
+
+/*
+ Functions returning BOOL measurements of a single GEOMETRY argument
+*/
+class Item_bool_func_args_geometry: public Item_bool_func
+{
+protected:
+ String value;
+ bool check_arguments() const
+ {
+ DBUG_ASSERT(arg_count == 1);
+ return args[0]->check_type_or_binary(func_name(), &type_handler_geometry);
+ }
+public:
+ Item_bool_func_args_geometry(THD *thd, Item *a)
+ :Item_bool_func(thd, a) {}
+};
+
+
+/*
+ Functions returning ASCII string measurements of a single GEOMETRY argument
+*/
+class Item_str_ascii_func_args_geometry: public Item_str_ascii_func
+{
+protected:
+ bool check_arguments() const
+ {
+ DBUG_ASSERT(arg_count >= 1);
+ return args[0]->check_type_or_binary(func_name(), &type_handler_geometry);
+ }
+public:
+ Item_str_ascii_func_args_geometry(THD *thd, Item *a)
+ :Item_str_ascii_func(thd, a) {}
+ Item_str_ascii_func_args_geometry(THD *thd, Item *a, Item *b)
+ :Item_str_ascii_func(thd, a, b) {}
+ Item_str_ascii_func_args_geometry(THD *thd, Item *a, Item *b, Item *c)
+ :Item_str_ascii_func(thd, a, b, c) {}
+};
+
+
+/*
+ Functions returning binary string measurements of a single GEOMETRY argument
+*/
+class Item_binary_func_args_geometry: public Item_str_func
+{
+protected:
+ bool check_arguments() const
+ {
+ DBUG_ASSERT(arg_count >= 1);
+ return args[0]->check_type_or_binary(func_name(), &type_handler_geometry);
+ }
+public:
+ Item_binary_func_args_geometry(THD *thd, Item *a)
+ :Item_str_func(thd, a) {}
+};
+
+
+/*
+ Functions returning GEOMETRY measurements of a single GEOEMETRY argument
+*/
+class Item_geometry_func_args_geometry: public Item_geometry_func
+{
+protected:
+ bool check_arguments() const
+ {
+ DBUG_ASSERT(arg_count >= 1);
+ return args[0]->check_type_or_binary(func_name(), &type_handler_geometry);
+ }
+public:
+ Item_geometry_func_args_geometry(THD *thd, Item *a)
+ :Item_geometry_func(thd, a) {}
+ Item_geometry_func_args_geometry(THD *thd, Item *a, Item *b)
+ :Item_geometry_func(thd, a, b) {}
+};
+
+
+/*
+ Functions returning REAL result relationships between two GEOMETRY arguments
+*/
+class Item_real_func_args_geometry_geometry: public Item_real_func
+{
+protected:
+ bool check_arguments() const
+ {
+ DBUG_ASSERT(arg_count >= 2);
+ return check_argument_types_or_binary(&type_handler_geometry, 0, 2);
+ }
+public:
+ Item_real_func_args_geometry_geometry(THD *thd, Item *a, Item *b)
+ :Item_real_func(thd, a, b) {}
+};
+
+
+/*
+ Functions returning BOOL result relationships between two GEOMETRY arguments
+*/
+class Item_bool_func_args_geometry_geometry: public Item_bool_func
+{
+protected:
+ String value;
+ bool check_arguments() const
+ {
+ DBUG_ASSERT(arg_count >= 2);
+ return check_argument_types_or_binary(&type_handler_geometry, 0, 2);
+ }
+public:
+ Item_bool_func_args_geometry_geometry(THD *thd, Item *a, Item *b, Item *c)
+ :Item_bool_func(thd, a, b, c) {}
+};
+
+
class Item_func_geometry_from_text: public Item_geometry_func
{
+ bool check_arguments() const
+ {
+ return args[0]->check_type_general_purpose_string(func_name()) ||
+ check_argument_types_can_return_int(1, MY_MIN(2, arg_count));
+ }
public:
Item_func_geometry_from_text(THD *thd, Item *a): Item_geometry_func(thd, a) {}
Item_func_geometry_from_text(THD *thd, Item *a, Item *srid):
Item_geometry_func(thd, a, srid) {}
const char *func_name() const { return "st_geometryfromtext"; }
String *val_str(String *);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_geometry_from_text>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_geometry_from_text>(thd, this); }
};
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));
+ }
public:
Item_func_geometry_from_wkb(THD *thd, Item *a): Item_geometry_func(thd, a) {}
Item_func_geometry_from_wkb(THD *thd, Item *a, Item *srid):
Item_geometry_func(thd, a, srid) {}
const char *func_name() const { return "st_geometryfromwkb"; }
String *val_str(String *);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_geometry_from_wkb>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_geometry_from_wkb>(thd, this); }
};
class Item_func_geometry_from_json: public Item_geometry_func
{
String tmp_js;
+ bool check_arguments() const
+ {
+ // TODO: check with Alexey, for better args[1] and args[2] type control
+ return args[0]->check_type_general_purpose_string(func_name()) ||
+ check_argument_types_traditional_scalar(1, MY_MIN(3, arg_count));
+ }
public:
Item_func_geometry_from_json(THD *thd, Item *js): Item_geometry_func(thd, js) {}
Item_func_geometry_from_json(THD *thd, Item *js, Item *opt):
@@ -79,54 +241,72 @@ public:
Item_geometry_func(thd, js, opt, srid) {}
const char *func_name() const { return "st_geomfromgeojson"; }
String *val_str(String *);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_geometry_from_json>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_geometry_from_json>(thd, this); }
};
-class Item_func_as_wkt: public Item_str_ascii_func
+class Item_func_as_wkt: public Item_str_ascii_func_args_geometry
{
public:
- Item_func_as_wkt(THD *thd, Item *a): Item_str_ascii_func(thd, a) {}
+ Item_func_as_wkt(THD *thd, Item *a)
+ :Item_str_ascii_func_args_geometry(thd, a) {}
const char *func_name() const { return "st_astext"; }
String *val_str_ascii(String *);
bool fix_length_and_dec();
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_as_wkt>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_as_wkt>(thd, this); }
};
-class Item_func_as_wkb: public Item_geometry_func
+class Item_func_as_wkb: public Item_binary_func_args_geometry
{
public:
- Item_func_as_wkb(THD *thd, Item *a): Item_geometry_func(thd, a) {}
+ Item_func_as_wkb(THD *thd, Item *a)
+ :Item_binary_func_args_geometry(thd, a) {}
const char *func_name() const { return "st_aswkb"; }
String *val_str(String *);
- enum_field_types field_type() const { return MYSQL_TYPE_BLOB; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_as_wkb>(thd, mem_root, this); }
+ const Type_handler *type_handler() const { return &type_handler_long_blob; }
+ bool fix_length_and_dec()
+ {
+ collation.set(&my_charset_bin);
+ decimals=0;
+ max_length= (uint32) UINT_MAX32;
+ maybe_null= 1;
+ return FALSE;
+ }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_as_wkb>(thd, this); }
};
-class Item_func_as_geojson: public Item_str_ascii_func
+class Item_func_as_geojson: public Item_str_ascii_func_args_geometry
{
+ bool check_arguments() const
+ {
+ // TODO: check with Alexey, for better args[1] and args[2] type control
+ return Item_str_ascii_func_args_geometry::check_arguments() ||
+ check_argument_types_traditional_scalar(1, MY_MIN(3, arg_count));
+ }
public:
- Item_func_as_geojson(THD *thd, Item *js): Item_str_ascii_func(thd, js) {}
- Item_func_as_geojson(THD *thd, Item *js, Item *max_dec_digits):
- Item_str_ascii_func(thd, js, max_dec_digits) {}
- Item_func_as_geojson(THD *thd, Item *js, Item *max_dec_digits, Item *opt):
- Item_str_ascii_func(thd, js, max_dec_digits, opt) {}
+ Item_func_as_geojson(THD *thd, Item *js)
+ :Item_str_ascii_func_args_geometry(thd, js) {}
+ Item_func_as_geojson(THD *thd, Item *js, Item *max_dec_digits)
+ :Item_str_ascii_func_args_geometry(thd, js, max_dec_digits) {}
+ Item_func_as_geojson(THD *thd, Item *js, Item *max_dec_digits, Item *opt)
+ :Item_str_ascii_func_args_geometry(thd, js, max_dec_digits, opt) {}
const char *func_name() const { return "st_asgeojson"; }
bool fix_length_and_dec();
String *val_str_ascii(String *);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_as_geojson>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_as_geojson>(thd, this); }
};
-class Item_func_geometry_type: public Item_str_ascii_func
+class Item_func_geometry_type: public Item_str_ascii_func_args_geometry
{
public:
- Item_func_geometry_type(THD *thd, Item *a): Item_str_ascii_func(thd, a) {}
+ Item_func_geometry_type(THD *thd, Item *a)
+ :Item_str_ascii_func_args_geometry(thd, a) {}
String *val_str_ascii(String *);
const char *func_name() const { return "st_geometrytype"; }
bool fix_length_and_dec()
@@ -136,13 +316,13 @@ public:
maybe_null= 1;
return FALSE;
};
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_geometry_type>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_geometry_type>(thd, this); }
};
// #define HEAVY_CONVEX_HULL
-class Item_func_convexhull: public Item_geometry_func
+class Item_func_convexhull: public Item_geometry_func_args_geometry
{
class ch_node: public Gcalc_dyn_list::Item
{
@@ -165,40 +345,43 @@ class Item_func_convexhull: public Item_geometry_func
ch_node *new_ch_node() { return (ch_node *) res_heap.new_item(); }
int add_node_to_line(ch_node **p_cur, int dir, const Gcalc_heap::Info *pi);
public:
- Item_func_convexhull(THD *thd, Item *a): Item_geometry_func(thd, a),
+ Item_func_convexhull(THD *thd, Item *a)
+ :Item_geometry_func_args_geometry(thd, a),
res_heap(8192, sizeof(ch_node))
{}
const char *func_name() const { return "st_convexhull"; }
String *val_str(String *);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_convexhull>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_convexhull>(thd, this); }
};
-class Item_func_centroid: public Item_geometry_func
+class Item_func_centroid: public Item_geometry_func_args_geometry
{
public:
- Item_func_centroid(THD *thd, Item *a): Item_geometry_func(thd, a) {}
+ Item_func_centroid(THD *thd, Item *a)
+ :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;
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_centroid>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_centroid>(thd, this); }
};
-class Item_func_envelope: public Item_geometry_func
+class Item_func_envelope: public Item_geometry_func_args_geometry
{
public:
- Item_func_envelope(THD *thd, Item *a): Item_geometry_func(thd, a) {}
+ Item_func_envelope(THD *thd, Item *a)
+ :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;
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_envelope>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_envelope>(thd, this); }
};
-class Item_func_boundary: public Item_geometry_func
+class Item_func_boundary: public Item_geometry_func_args_geometry
{
class Transporter : public Gcalc_shape_transporter
{
@@ -223,16 +406,19 @@ class Item_func_boundary: public Item_geometry_func
};
Gcalc_result_receiver res_receiver;
public:
- Item_func_boundary(THD *thd, Item *a): Item_geometry_func(thd, a) {}
+ Item_func_boundary(THD *thd, Item *a)
+ :Item_geometry_func_args_geometry(thd, a) {}
const char *func_name() const { return "st_boundary"; }
String *val_str(String *);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_boundary>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_boundary>(thd, this); }
};
class Item_func_point: public Item_geometry_func
{
+ bool check_arguments() const
+ { return check_argument_types_can_return_real(0, 2); }
public:
Item_func_point(THD *thd, Item *a, Item *b): Item_geometry_func(thd, a, b) {}
Item_func_point(THD *thd, Item *a, Item *b, Item *srid):
@@ -240,16 +426,16 @@ public:
const char *func_name() const { return "point"; }
String *val_str(String *);
Field::geometry_type get_geometry_type() const;
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_point>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_point>(thd, this); }
};
-class Item_func_spatial_decomp: public Item_geometry_func
+class Item_func_spatial_decomp: public Item_geometry_func_args_geometry
{
enum Functype decomp_func;
public:
Item_func_spatial_decomp(THD *thd, Item *a, Item_func::Functype ft):
- Item_geometry_func(thd, a) { decomp_func = ft; }
+ Item_geometry_func_args_geometry(thd, a) { decomp_func = ft; }
const char *func_name() const
{
switch (decomp_func)
@@ -266,16 +452,23 @@ public:
}
}
String *val_str(String *);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_spatial_decomp>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_spatial_decomp>(thd, this); }
};
-class Item_func_spatial_decomp_n: public Item_geometry_func
+class Item_func_spatial_decomp_n: public Item_geometry_func_args_geometry
{
enum Functype decomp_func_n;
+ bool check_arguments() const
+ {
+ return Item_geometry_func_args_geometry::check_arguments() ||
+ args[1]->check_type_can_return_int(func_name());
+ }
public:
- Item_func_spatial_decomp_n(THD *thd, Item *a, Item *b, Item_func::Functype ft):
- Item_geometry_func(thd, a, b) { decomp_func_n = ft; }
+ Item_func_spatial_decomp_n(THD *thd, Item *a, Item *b, Item_func::Functype ft)
+ :Item_geometry_func_args_geometry(thd, a, b),
+ decomp_func_n(ft)
+ { }
const char *func_name() const
{
switch (decomp_func_n)
@@ -292,12 +485,16 @@ public:
}
}
String *val_str(String *);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_spatial_decomp_n>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_spatial_decomp_n>(thd, this); }
};
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);
+ }
enum Geometry::wkbType coll_type;
enum Geometry::wkbType item_type;
public:
@@ -329,8 +526,8 @@ public:
}
const char *func_name() const { return "geometrycollection"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_spatial_collection>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_spatial_collection>(thd, this); }
};
@@ -346,6 +543,11 @@ protected:
SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, Field *field,
KEY_PART *key_part,
Item_func::Functype type, Item *value);
+ bool check_arguments() const
+ {
+ DBUG_ASSERT(arg_count >= 2);
+ return check_argument_types_or_binary(&type_handler_geometry, 0, 2);
+ }
public:
Item_func_spatial_rel(THD *thd, Item *a, Item *b, enum Functype sp_rel):
Item_bool_func2_with_rev(thd, a, b), spatial_rel(sp_rel)
@@ -374,7 +576,7 @@ public:
usable_tables, sargables, false);
}
bool need_parentheses_in_default() { return false; }
- Item *build_clone(THD *thd, MEM_ROOT *mem_root) { return 0; }
+ Item *build_clone(THD *thd) { return 0; }
};
@@ -386,8 +588,8 @@ public:
{ }
longlong val_int();
const char *func_name() const;
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_spatial_mbr_rel>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_spatial_mbr_rel>(thd, this); }
};
@@ -402,26 +604,31 @@ public:
{ }
longlong val_int();
const char *func_name() const;
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_spatial_precise_rel>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_spatial_precise_rel>(thd, this); }
};
-class Item_func_spatial_relate: public Item_bool_func
+class Item_func_spatial_relate: public Item_bool_func_args_geometry_geometry
{
Gcalc_heap collector;
Gcalc_scan_iterator scan_it;
Gcalc_function func;
String tmp_value1, tmp_value2, tmp_matrix;
+ bool check_arguments() const
+ {
+ return Item_bool_func_args_geometry_geometry::check_arguments() ||
+ args[2]->check_type_general_purpose_string(func_name());
+ }
public:
Item_func_spatial_relate(THD *thd, Item *a, Item *b, Item *matrix):
- Item_bool_func(thd, a, b, matrix)
+ Item_bool_func_args_geometry_geometry(thd, a, b, matrix)
{ }
longlong val_int();
const char *func_name() const { return "st_relate"; }
bool need_parentheses_in_default() { return false; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_spatial_relate>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_spatial_relate>(thd, this); }
};
@@ -431,6 +638,11 @@ public:
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);
+ }
public:
Gcalc_function::op_type spatial_op;
Gcalc_heap collector;
@@ -451,13 +663,18 @@ public:
{
Item_func::print(str, query_type);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_spatial_operation>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_spatial_operation>(thd, this); }
};
-class Item_func_buffer: public Item_geometry_func
+class Item_func_buffer: public Item_geometry_func_args_geometry
{
+ bool check_arguments() const
+ {
+ return Item_geometry_func_args_geometry::check_arguments() ||
+ args[1]->check_type_can_return_real(func_name());
+ }
protected:
class Transporter : public Gcalc_operation_transporter
{
@@ -499,53 +716,56 @@ protected:
Gcalc_operation_reducer operation;
public:
- Item_func_buffer(THD *thd, Item *obj, Item *distance):
- Item_geometry_func(thd, obj, distance) {}
+ Item_func_buffer(THD *thd, Item *obj, Item *distance)
+ :Item_geometry_func_args_geometry(thd, obj, distance) {}
const char *func_name() const { return "st_buffer"; }
String *val_str(String *);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_buffer>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_buffer>(thd, this); }
};
-class Item_func_isempty: public Item_bool_func
+class Item_func_isempty: public Item_bool_func_args_geometry
{
public:
- Item_func_isempty(THD *thd, Item *a): Item_bool_func(thd, a) {}
+ Item_func_isempty(THD *thd, Item *a)
+ :Item_bool_func_args_geometry(thd, a) {}
longlong val_int();
const char *func_name() const { return "st_isempty"; }
bool fix_length_and_dec() { maybe_null= 1; return FALSE; }
bool need_parentheses_in_default() { return false; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_isempty>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_isempty>(thd, this); }
};
-class Item_func_issimple: public Item_int_func
+class Item_func_issimple: public Item_long_func_args_geometry
{
Gcalc_heap collector;
Gcalc_function func;
Gcalc_scan_iterator scan_it;
String tmp;
public:
- Item_func_issimple(THD *thd, Item *a): Item_int_func(thd, a) {}
+ Item_func_issimple(THD *thd, Item *a)
+ :Item_long_func_args_geometry(thd, a) {}
longlong val_int();
const char *func_name() const { return "st_issimple"; }
bool fix_length_and_dec() { decimals=0; max_length=2; return FALSE; }
uint decimal_precision() const { return 1; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_issimple>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_issimple>(thd, this); }
};
-class Item_func_isclosed: public Item_int_func
+class Item_func_isclosed: public Item_long_func_args_geometry
{
public:
- Item_func_isclosed(THD *thd, Item *a): Item_int_func(thd, a) {}
+ Item_func_isclosed(THD *thd, Item *a)
+ :Item_long_func_args_geometry(thd, a) {}
longlong val_int();
const char *func_name() const { return "st_isclosed"; }
bool fix_length_and_dec() { decimals=0; max_length=2; return FALSE; }
uint decimal_precision() const { return 1; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_isclosed>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_isclosed>(thd, this); }
};
class Item_func_isring: public Item_func_issimple
@@ -554,27 +774,27 @@ public:
Item_func_isring(THD *thd, Item *a): Item_func_issimple(thd, a) {}
longlong val_int();
const char *func_name() const { return "st_isring"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_isring>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_isring>(thd, this); }
};
-class Item_func_dimension: public Item_int_func
+class Item_func_dimension: public Item_long_func_args_geometry
{
- String value;
public:
- Item_func_dimension(THD *thd, Item *a): Item_int_func(thd, a) {}
+ Item_func_dimension(THD *thd, Item *a)
+ :Item_long_func_args_geometry(thd, a) {}
longlong val_int();
const char *func_name() const { return "st_dimension"; }
bool fix_length_and_dec() { max_length= 10; maybe_null= 1; return FALSE; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_dimension>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_dimension>(thd, this); }
};
-class Item_func_x: public Item_real_func
+
+class Item_func_x: public Item_real_func_args_geometry
{
- String value;
public:
- Item_func_x(THD *thd, Item *a): Item_real_func(thd, a) {}
+ Item_func_x(THD *thd, Item *a): Item_real_func_args_geometry(thd, a) {}
double val_real();
const char *func_name() const { return "st_x"; }
bool fix_length_and_dec()
@@ -584,16 +804,15 @@ public:
maybe_null= 1;
return FALSE;
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_x>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_x>(thd, this); }
};
-class Item_func_y: public Item_real_func
+class Item_func_y: public Item_real_func_args_geometry
{
- String value;
public:
- Item_func_y(THD *thd, Item *a): Item_real_func(thd, a) {}
+ Item_func_y(THD *thd, Item *a): Item_real_func_args_geometry(thd, a) {}
double val_real();
const char *func_name() const { return "st_y"; }
bool fix_length_and_dec()
@@ -603,55 +822,54 @@ public:
maybe_null= 1;
return FALSE;
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_y>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_y>(thd, this); }
};
-class Item_func_numgeometries: public Item_int_func
+class Item_func_numgeometries: public Item_long_func_args_geometry
{
- String value;
public:
- Item_func_numgeometries(THD *thd, Item *a): Item_int_func(thd, a) {}
+ Item_func_numgeometries(THD *thd, Item *a)
+ :Item_long_func_args_geometry(thd, a) {}
longlong val_int();
const char *func_name() const { return "st_numgeometries"; }
bool fix_length_and_dec() { max_length= 10; maybe_null= 1; return FALSE; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_numgeometries>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_numgeometries>(thd, this); }
};
-class Item_func_numinteriorring: public Item_int_func
+class Item_func_numinteriorring: public Item_long_func_args_geometry
{
- String value;
public:
- Item_func_numinteriorring(THD *thd, Item *a): Item_int_func(thd, a) {}
+ Item_func_numinteriorring(THD *thd, Item *a)
+ :Item_long_func_args_geometry(thd, a) {}
longlong val_int();
const char *func_name() const { return "st_numinteriorrings"; }
bool fix_length_and_dec() { max_length= 10; maybe_null= 1; return FALSE; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_numinteriorring>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_numinteriorring>(thd, this); }
};
-class Item_func_numpoints: public Item_int_func
+class Item_func_numpoints: public Item_long_func_args_geometry
{
- String value;
public:
- Item_func_numpoints(THD *thd, Item *a): Item_int_func(thd, a) {}
+ Item_func_numpoints(THD *thd, Item *a)
+ :Item_long_func_args_geometry(thd, a) {}
longlong val_int();
const char *func_name() const { return "st_numpoints"; }
bool fix_length_and_dec() { max_length= 10; maybe_null= 1; return FALSE; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_numpoints>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_numpoints>(thd, this); }
};
-class Item_func_area: public Item_real_func
+class Item_func_area: public Item_real_func_args_geometry
{
- String value;
public:
- Item_func_area(THD *thd, Item *a): Item_real_func(thd, a) {}
+ Item_func_area(THD *thd, Item *a): Item_real_func_args_geometry(thd, a) {}
double val_real();
const char *func_name() const { return "st_area"; }
bool fix_length_and_dec()
@@ -661,16 +879,17 @@ public:
maybe_null= 1;
return FALSE;
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_area>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_area>(thd, this); }
};
-class Item_func_glength: public Item_real_func
+class Item_func_glength: public Item_real_func_args_geometry
{
String value;
public:
- Item_func_glength(THD *thd, Item *a): Item_real_func(thd, a) {}
+ Item_func_glength(THD *thd, Item *a)
+ :Item_real_func_args_geometry(thd, a) {}
double val_real();
const char *func_name() const { return "st_length"; }
bool fix_length_and_dec()
@@ -680,25 +899,25 @@ public:
maybe_null= 1;
return FALSE;
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_glength>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_glength>(thd, this); }
};
-class Item_func_srid: public Item_int_func
+class Item_func_srid: public Item_long_func_args_geometry
{
- String value;
public:
- Item_func_srid(THD *thd, Item *a): Item_int_func(thd, a) {}
+ Item_func_srid(THD *thd, Item *a)
+ :Item_long_func_args_geometry(thd, a) {}
longlong val_int();
const char *func_name() const { return "srid"; }
bool fix_length_and_dec() { max_length= 10; maybe_null= 1; return FALSE; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_srid>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_srid>(thd, this); }
};
-class Item_func_distance: public Item_real_func
+class Item_func_distance: public Item_real_func_args_geometry_geometry
{
String tmp_value1;
String tmp_value2;
@@ -706,44 +925,47 @@ class Item_func_distance: public Item_real_func
Gcalc_function func;
Gcalc_scan_iterator scan_it;
public:
- Item_func_distance(THD *thd, Item *a, Item *b): Item_real_func(thd, a, b) {}
+ Item_func_distance(THD *thd, Item *a, Item *b)
+ :Item_real_func_args_geometry_geometry(thd, a, b) {}
double val_real();
const char *func_name() const { return "st_distance"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_distance>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_distance>(thd, this); }
};
-class Item_func_pointonsurface: public Item_geometry_func
+class Item_func_pointonsurface: public Item_geometry_func_args_geometry
{
String tmp_value;
Gcalc_heap collector;
Gcalc_function func;
Gcalc_scan_iterator scan_it;
public:
- Item_func_pointonsurface(THD *thd, Item *a): Item_geometry_func(thd, a) {}
+ Item_func_pointonsurface(THD *thd, Item *a)
+ :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;
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_pointonsurface>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_pointonsurface>(thd, this); }
};
#ifndef DBUG_OFF
-class Item_func_gis_debug: public Item_int_func
+class Item_func_gis_debug: public Item_long_func
{
public:
- Item_func_gis_debug(THD *thd, Item *a): Item_int_func(thd, a)
+ Item_func_gis_debug(THD *thd, Item *a): Item_long_func(thd, a)
{ null_value= false; }
+ bool fix_length_and_dec() { fix_char_length(10); return FALSE; }
const char *func_name() const { return "st_gis_debug"; }
longlong val_int();
bool check_vcol_func_processor(void *arg)
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_gis_debug>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_gis_debug>(thd, this); }
};
#endif
diff --git a/sql/item_inetfunc.cc b/sql/item_inetfunc.cc
index bb00ccf2771..52c3e0635af 100644
--- a/sql/item_inetfunc.cc
+++ b/sql/item_inetfunc.cc
@@ -14,7 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include "item_inetfunc.h"
#include "my_net.h"
@@ -149,13 +149,14 @@ longlong Item_func_inet_bool_base::val_int()
{
DBUG_ASSERT(fixed);
- if (args[0]->result_type() != STRING_RESULT) // String argument expected
+ // String argument expected
+ if (unlikely(args[0]->result_type() != STRING_RESULT))
return 0;
String buffer;
String *arg_str= args[0]->val_str(&buffer);
- if (!arg_str) // Out-of memory happened. The error has been reported.
+ if (unlikely(!arg_str)) // Out-of memory happened. error has been reported.
return 0; // Or: the underlying field is NULL
return calc_value(arg_str) ? 1 : 0;
@@ -175,7 +176,8 @@ String *Item_func_inet_str_base::val_str_ascii(String *buffer)
{
DBUG_ASSERT(fixed);
- if (args[0]->result_type() != STRING_RESULT) // String argument expected
+ // String argument expected
+ if (unlikely(args[0]->result_type() != STRING_RESULT))
{
null_value= true;
return NULL;
@@ -183,15 +185,17 @@ String *Item_func_inet_str_base::val_str_ascii(String *buffer)
StringBuffer<STRING_BUFFER_USUAL_SIZE> tmp;
String *arg_str= args[0]->val_str(&tmp);
- if (!arg_str) // Out-of memory happened. The error has been reported.
- { // Or: the underlying field is NULL
+ if (unlikely(!arg_str))
+ {
+ // Out-of memory happened. error has been reported.
+ // Or: the underlying field is NULL
null_value= true;
return NULL;
}
null_value= !calc_value(arg_str, buffer);
- return null_value ? NULL : buffer;
+ return unlikely(null_value) ? NULL : buffer;
}
///////////////////////////////////////////////////////////////////////////
@@ -218,7 +222,7 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres
{
DBUG_PRINT("error", ("str_to_ipv4(%.*s): "
"invalid IPv4 address: too short.",
- (int)str_length, str));
+ (int) str_length, str));
return false;
}
@@ -226,7 +230,7 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres
{
DBUG_PRINT("error", ("str_to_ipv4(%.*s): "
"invalid IPv4 address: too long.",
- (int)str_length, str));
+ (int) str_length, str));
return false;
}
@@ -249,7 +253,7 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres
{
DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: "
"too many characters in a group.",
- (int)str_length, str));
+ (int) str_length, str));
return false;
}
@@ -259,7 +263,7 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres
{
DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: "
"invalid byte value.",
- (int)str_length, str));
+ (int) str_length, str));
return false;
}
}
@@ -269,7 +273,7 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres
{
DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: "
"too few characters in a group.",
- (int)str_length, str));
+ (int) str_length, str));
return false;
}
@@ -282,7 +286,7 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres
if (dot_count > 3)
{
DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: "
- "too many dots.", (int)str_length, str));
+ "too many dots.", (int) str_length, str));
return false;
}
}
@@ -290,7 +294,7 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres
{
DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: "
"invalid character at pos %d.",
- (int)str_length, str, (int) (p - str)));
+ (int) str_length, str, (int) (p - str)));
return false;
}
}
@@ -298,7 +302,7 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres
if (c == '.')
{
DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: "
- "ending at '.'.",(int)str_length, str));
+ "ending at '.'.", (int) str_length, str));
return false;
}
@@ -306,14 +310,14 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres
{
DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: "
"too few groups.",
- (int)str_length, str));
+ (int) str_length, str));
return false;
}
ipv4_bytes[3]= (unsigned char) byte_value;
DBUG_PRINT("info", ("str_to_ipv4(%.*s): valid IPv4 address: %d.%d.%d.%d",
- (int)str_length, str,
+ (int) str_length, str,
ipv4_bytes[0], ipv4_bytes[1],
ipv4_bytes[2], ipv4_bytes[3]));
return true;
diff --git a/sql/item_inetfunc.h b/sql/item_inetfunc.h
index 3704f3c952e..9ab46e88053 100644
--- a/sql/item_inetfunc.h
+++ b/sql/item_inetfunc.h
@@ -24,10 +24,12 @@
Item_func_inet_aton implements INET_ATON() SQL-function.
*************************************************************************/
-class Item_func_inet_aton : public Item_int_func
+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_int_func(thd, a) {}
+ 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()
@@ -38,8 +40,8 @@ public:
unsigned_flag= 1;
return FALSE;
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_inet_aton>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_inet_aton>(thd, this); }
};
@@ -61,8 +63,8 @@ public:
maybe_null= 1;
return FALSE;
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_inet_ntoa>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_inet_ntoa>(thd, this); }
};
@@ -131,8 +133,8 @@ public:
maybe_null= 1;
return FALSE;
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_inet6_aton>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_inet6_aton>(thd, this); }
protected:
virtual bool calc_value(const String *arg, String *buffer);
@@ -166,8 +168,8 @@ public:
maybe_null= 1;
return FALSE;
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_inet6_ntoa>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_inet6_ntoa>(thd, this); }
protected:
virtual bool calc_value(const String *arg, String *buffer);
@@ -188,8 +190,8 @@ public:
public:
virtual const char *func_name() const
{ return "is_ipv4"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_is_ipv4>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_is_ipv4>(thd, this); }
protected:
virtual bool calc_value(const String *arg);
@@ -210,8 +212,8 @@ public:
public:
virtual const char *func_name() const
{ return "is_ipv6"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_is_ipv6>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_is_ipv6>(thd, this); }
protected:
virtual bool calc_value(const String *arg);
@@ -232,8 +234,8 @@ public:
public:
virtual const char *func_name() const
{ return "is_ipv4_compat"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_is_ipv4_compat>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_is_ipv4_compat>(thd, this); }
protected:
virtual bool calc_value(const String *arg);
@@ -254,8 +256,8 @@ public:
public:
virtual const char *func_name() const
{ return "is_ipv4_mapped"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_is_ipv4_mapped>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_is_ipv4_mapped>(thd, this); }
protected:
virtual bool calc_value(const String *arg);
diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc
index b3c8366907a..7e99a845194 100644
--- a/sql/item_jsonfunc.cc
+++ b/sql/item_jsonfunc.cc
@@ -14,7 +14,7 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "sql_class.h"
#include "item.h"
@@ -173,7 +173,7 @@ static int json_nice(json_engine_t *je, String *nice_js,
key_end= je->s.c_str;
} while (json_read_keyname_chr(je) == 0);
- if (je->s.error)
+ if (unlikely(je->s.error))
goto error;
if (!first_value)
@@ -390,7 +390,7 @@ longlong Item_func_json_valid::val_int()
bool Item_func_json_exists::fix_length_and_dec()
{
- if (Item_int_func::fix_length_and_dec())
+ if (Item_bool_func::fix_length_and_dec())
return TRUE;
maybe_null= 1;
path.set_constant_flag(args[1]->const_item());
@@ -495,7 +495,7 @@ continue_search:
if (json_read_value(&je))
goto err_return;
- if (check_and_get_value(&je, str, &error))
+ if (unlikely(check_and_get_value(&je, str, &error)))
{
if (error)
goto err_return;
@@ -647,7 +647,7 @@ String *Item_func_json_unquote::val_str(String *str)
if (!(js= read_json(&je)))
return NULL;
- if (je.s.error || je.value_type != JSON_VALUE_STRING)
+ if (unlikely(je.s.error) || je.value_type != JSON_VALUE_STRING)
return js;
str->length(0);
@@ -860,7 +860,7 @@ String *Item_func_json_extract::read_json(String *str,
}
}
- if (je.s.error)
+ if (unlikely(je.s.error))
goto error;
if (!not_first_value)
@@ -1004,7 +1004,7 @@ bool Item_func_json_contains::fix_length_and_dec()
maybe_null= 1;
if (arg_count > 2)
path.set_constant_flag(args[2]->const_item());
- return Item_int_func::fix_length_and_dec();
+ return Item_bool_func::fix_length_and_dec();
}
@@ -1054,7 +1054,7 @@ static int check_contains(json_engine_t *js, json_engine_t *value)
k_end= value->s.c_str;
} while (json_read_keyname_chr(value) == 0);
- if (value->s.error || json_read_value(value))
+ if (unlikely(value->s.error) || json_read_value(value))
return FALSE;
if (set_js)
@@ -1097,7 +1097,7 @@ static int check_contains(json_engine_t *js, json_engine_t *value)
return FALSE;
return TRUE;
}
- if (value->s.error || js->s.error ||
+ if (unlikely(value->s.error) || unlikely(js->s.error) ||
(!v_scalar && json_skip_to_level(js, c_level)))
return FALSE;
}
@@ -1225,7 +1225,7 @@ longlong Item_func_json_contains::val_int()
goto error;
result= check_contains(&je, &ve);
- if (je.s.error || ve.s.error)
+ if (unlikely(je.s.error || ve.s.error))
goto error;
return result;
@@ -1256,7 +1256,7 @@ bool Item_func_json_contains_path::fix_length_and_dec()
ooa_parsed= FALSE;
maybe_null= 1;
mark_constant_paths(paths, args+2, arg_count-2);
- return Item_int_func::fix_length_and_dec();
+ return Item_bool_func::fix_length_and_dec();
}
@@ -1379,6 +1379,7 @@ longlong Item_func_json_contains_path::val_int()
longlong result;
json_path_t p;
int n_found;
+ LINT_INIT(n_found);
if ((null_value= args[0]->null_value))
return 0;
@@ -1444,7 +1445,7 @@ longlong Item_func_json_contains_path::val_int()
}
}
- if (je.s.error == 0)
+ if (likely(je.s.error == 0))
return result;
report_json_error(js, &je, 0);
@@ -1814,7 +1815,7 @@ String *Item_func_json_array_insert::val_str(String *str)
goto js_error;
}
- if (je.s.error)
+ if (unlikely(je.s.error))
goto js_error;
str->length(0);
@@ -1946,7 +1947,7 @@ static int do_merge(String *str, json_engine_t *je1, json_engine_t *je2)
key_end= je1->s.c_str;
} while (json_read_keyname_chr(je1) == 0);
- if (je1->s.error)
+ if (unlikely(je1->s.error))
return 1;
if (first_key)
@@ -1981,7 +1982,7 @@ static int do_merge(String *str, json_engine_t *je1, json_engine_t *je2)
return ires;
goto merged_j1;
}
- if (je2->s.error)
+ if (unlikely(je2->s.error))
return 2;
key_start= je1->s.c_str;
@@ -2011,7 +2012,7 @@ merged_j1:
key_end= je2->s.c_str;
} while (json_read_keyname_chr(je2) == 0);
- if (je2->s.error)
+ if (unlikely(je2->s.error))
return 1;
*je1= sav_je1;
@@ -2022,7 +2023,7 @@ merged_j1:
json_string_set_str(&key_name, key_start, key_end);
if (!json_key_matches(je1, &key_name))
{
- if (je1->s.error || json_skip_key(je1))
+ if (unlikely(je1->s.error || json_skip_key(je1)))
return 2;
continue;
}
@@ -2032,7 +2033,7 @@ merged_j1:
goto continue_j2;
}
- if (je1->s.error)
+ if (unlikely(je1->s.error))
return 2;
if (first_key)
@@ -2127,6 +2128,7 @@ String *Item_func_json_merge::val_str(String *str)
json_engine_t je1, je2;
String *js1= args[0]->val_json(&tmp_js1), *js2=NULL;
uint n_arg;
+ LINT_INIT(js2);
if (args[0]->null_value)
goto null_return;
@@ -2515,6 +2517,7 @@ bool Item_func_json_length::fix_length_and_dec()
if (arg_count > 1)
path.set_constant_flag(args[1]->const_item());
maybe_null= 1;
+ max_length= 10;
return FALSE;
}
@@ -2592,7 +2595,7 @@ longlong Item_func_json_length::val_int()
while (json_scan_next(&je) == 0) {}
}
- if (!je.s.error)
+ if (likely(!je.s.error))
return length;
err_return:
@@ -2646,7 +2649,7 @@ longlong Item_func_json_depth::val_int()
}
} while (json_scan_next(&je) == 0);
- if (!je.s.error)
+ if (likely(!je.s.error))
return depth;
report_json_error(js, &je, 0);
@@ -2864,7 +2867,7 @@ String *Item_func_json_insert::val_str(String *str)
}
}
- if (je.s.error)
+ if (unlikely(je.s.error))
goto js_error;
if (!mode_insert)
@@ -2902,7 +2905,7 @@ String *Item_func_json_insert::val_str(String *str)
}
}
- if (je.s.error)
+ if (unlikely(je.s.error))
goto js_error;
if (!mode_insert)
@@ -3004,7 +3007,7 @@ String *Item_func_json_remove::val_str(String *str)
{
uint array_counters[JSON_DEPTH_LIMIT];
json_path_with_flags *c_path= paths + n_path;
- const char *rem_start, *rem_end;
+ const char *rem_start= 0, *rem_end;
const json_path_step_t *lp;
uint n_item= 0;
@@ -3075,7 +3078,7 @@ String *Item_func_json_remove::val_str(String *str)
}
}
- if (je.s.error)
+ if (unlikely(je.s.error))
goto js_error;
continue;
@@ -3107,7 +3110,7 @@ String *Item_func_json_remove::val_str(String *str)
}
}
- if (je.s.error)
+ if (unlikely(je.s.error))
goto js_error;
continue;
@@ -3273,7 +3276,7 @@ skip_search:
{
key_end= je.s.c_str;
} while (json_read_keyname_chr(&je) == 0);
- if (je.s.error)
+ if (unlikely(je.s.error))
goto err_return;
key_len= (int)(key_end - key_start);
@@ -3297,7 +3300,7 @@ skip_search:
}
}
- if (je.s.error || str->append("]", 1))
+ if (unlikely(je.s.error || str->append("]", 1)))
goto err_return;
null_value= 0;
@@ -3482,7 +3485,7 @@ String *Item_func_json_search::val_str(String *str)
}
}
- if (je.s.error)
+ if (unlikely(je.s.error))
goto js_error;
end:
@@ -3664,5 +3667,3 @@ int Arg_comparator::compare_e_json_str_basic(Item *j, Item *s)
return MY_TEST(sortcmp(res1, res2, compare_collation()) == 0);
}
-
-
diff --git a/sql/item_jsonfunc.h b/sql/item_jsonfunc.h
index 0277cb15154..c703533f799 100644
--- a/sql/item_jsonfunc.h
+++ b/sql/item_jsonfunc.h
@@ -40,29 +40,28 @@ public:
};
-class Item_func_json_valid: public Item_int_func
+class Item_func_json_valid: public Item_bool_func
{
protected:
String tmp_value;
public:
- Item_func_json_valid(THD *thd, Item *json) : Item_int_func(thd, json) {}
+ Item_func_json_valid(THD *thd, Item *json) : Item_bool_func(thd, json) {}
longlong val_int();
const char *func_name() const { return "json_valid"; }
bool fix_length_and_dec()
{
- if (Item_int_func::fix_length_and_dec())
+ if (Item_bool_func::fix_length_and_dec())
return TRUE;
maybe_null= 1;
return FALSE;
}
- bool is_bool_type() { return true; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_json_valid>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_json_valid>(thd, this); }
};
-class Item_func_json_exists: public Item_int_func
+class Item_func_json_exists: public Item_bool_func
{
protected:
json_path_with_flags path;
@@ -70,12 +69,11 @@ protected:
public:
Item_func_json_exists(THD *thd, Item *js, Item *i_path):
- Item_int_func(thd, js, i_path) {}
+ Item_bool_func(thd, js, i_path) {}
const char *func_name() const { return "json_exists"; }
- bool is_bool_type() { return true; }
bool fix_length_and_dec();
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_json_exists>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_json_exists>(thd, this); }
longlong val_int();
};
@@ -93,8 +91,8 @@ public:
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, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_json_value>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_json_value>(thd, this); }
};
@@ -106,8 +104,8 @@ public:
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, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_json_query>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_json_query>(thd, this); }
};
@@ -121,8 +119,8 @@ public:
const char *func_name() const { return "json_quote"; }
bool fix_length_and_dec();
String *val_str(String *);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_json_quote>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_json_quote>(thd, this); }
};
@@ -136,8 +134,8 @@ public:
const char *func_name() const { return "json_unquote"; }
bool fix_length_and_dec();
String *val_str(String *);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_json_unquote>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_json_unquote>(thd, this); }
};
@@ -173,12 +171,12 @@ public:
double val_real();
my_decimal *val_decimal(my_decimal *);
uint get_n_paths() const { return arg_count - 1; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_json_extract>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_json_extract>(thd, this); }
};
-class Item_func_json_contains: public Item_int_func
+class Item_func_json_contains: public Item_bool_func
{
protected:
String tmp_js;
@@ -188,16 +186,16 @@ protected:
String tmp_val, *val;
public:
Item_func_json_contains(THD *thd, List<Item> &list):
- Item_int_func(thd, list) {}
+ Item_bool_func(thd, list) {}
const char *func_name() const { return "json_contains"; }
bool fix_length_and_dec();
longlong val_int();
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_json_contains>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_json_contains>(thd, this); }
};
-class Item_func_json_contains_path: public Item_int_func
+class Item_func_json_contains_path: public Item_bool_func
{
protected:
String tmp_js;
@@ -209,14 +207,14 @@ protected:
public:
Item_func_json_contains_path(THD *thd, List<Item> &list):
- Item_int_func(thd, list), tmp_paths(0) {}
+ Item_bool_func(thd, list), tmp_paths(0) {}
const char *func_name() const { return "json_contains_path"; }
bool fix_fields(THD *thd, Item **ref);
bool fix_length_and_dec();
void cleanup();
longlong val_int();
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_json_contains_path>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_json_contains_path>(thd, this); }
};
@@ -234,8 +232,8 @@ public:
bool is_json_type() { return true; }
bool fix_length_and_dec();
const char *func_name() const { return "json_array"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_json_array>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_json_array>(thd, this); }
};
@@ -251,8 +249,8 @@ public:
String *val_str(String *);
uint get_n_paths() const { return arg_count/2; }
const char *func_name() const { return "json_array_append"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_json_array_append>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_json_array_append>(thd, this); }
};
@@ -263,8 +261,8 @@ public:
Item_func_json_array_append(thd, list) {}
String *val_str(String *);
const char *func_name() const { return "json_array_insert"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_json_array_insert>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_json_array_insert>(thd, this); }
};
@@ -278,8 +276,8 @@ public:
String *val_str(String *);
bool is_json_type() { return true; }
const char *func_name() const { return "json_object"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_json_object>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_json_object>(thd, this); }
};
@@ -293,11 +291,10 @@ public:
String *val_str(String *);
bool is_json_type() { return true; }
const char *func_name() const { return "json_merge_preserve"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_json_merge>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_json_merge>(thd, this); }
};
-
class Item_func_json_merge_patch: public Item_func_json_merge
{
public:
@@ -305,38 +302,46 @@ public:
Item_func_json_merge(thd, list) {}
const char *func_name() const { return "json_merge_patch"; }
String *val_str(String *);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_json_merge_patch>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_json_merge_patch>(thd, this); }
};
-
-class Item_func_json_length: public Item_int_func
+class Item_func_json_length: public Item_long_func
{
+ bool check_arguments() const
+ {
+ return args[0]->check_type_can_return_text(func_name()) ||
+ (arg_count > 1 &&
+ args[1]->check_type_general_purpose_string(func_name()));
+ }
protected:
json_path_with_flags path;
String tmp_js;
String tmp_path;
public:
Item_func_json_length(THD *thd, List<Item> &list):
- Item_int_func(thd, list) {}
+ Item_long_func(thd, list) {}
const char *func_name() const { return "json_length"; }
bool fix_length_and_dec();
longlong val_int();
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_json_length>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_json_length>(thd, this); }
};
-class Item_func_json_depth: public Item_int_func
+class Item_func_json_depth: public Item_long_func
{
+ bool check_arguments() const
+ { return args[0]->check_type_can_return_text(func_name()); }
protected:
String tmp_js;
public:
- Item_func_json_depth(THD *thd, Item *js): Item_int_func(thd, js) {}
+ Item_func_json_depth(THD *thd, Item *js): Item_long_func(thd, js) {}
const char *func_name() const { return "json_depth"; }
+ bool fix_length_and_dec() { max_length= 10; return FALSE; }
longlong val_int();
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_json_depth>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_json_depth>(thd, this); }
};
@@ -349,8 +354,8 @@ public:
const char *func_name() const { return "json_type"; }
bool fix_length_and_dec();
String *val_str(String *);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_json_type>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_json_type>(thd, this); }
};
@@ -372,8 +377,8 @@ public:
return mode_insert ?
(mode_replace ? "json_set" : "json_insert") : "json_update";
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_json_insert>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_json_insert>(thd, this); }
};
@@ -388,8 +393,8 @@ public:
String *val_str(String *);
uint get_n_paths() const { return arg_count - 1; }
const char *func_name() const { return "json_remove"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_json_remove>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_json_remove>(thd, this); }
};
@@ -405,8 +410,8 @@ public:
const char *func_name() const { return "json_keys"; }
bool fix_length_and_dec();
String *val_str(String *);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_json_keys>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_json_keys>(thd, this); }
};
@@ -430,8 +435,8 @@ public:
bool fix_length_and_dec();
String *val_str(String *);
uint get_n_paths() const { return arg_count > 4 ? arg_count - 4 : 0; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_json_search>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_json_search>(thd, this); }
};
@@ -459,8 +464,8 @@ public:
String *val_str(String *str);
String *val_json(String *str);
bool is_json_type() { return true; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_json_format>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_json_format>(thd, this); }
};
diff --git a/sql/item_row.cc b/sql/item_row.cc
index 6ae049dbfe5..04868842f11 100644
--- a/sql/item_row.cc
+++ b/sql/item_row.cc
@@ -1,5 +1,6 @@
/*
Copyright (c) 2002, 2011, Oracle and/or its affiliates.
+ 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
@@ -14,7 +15,7 @@
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>
+#include "mariadb.h"
#include "sql_priv.h"
/*
It is necessary to include set_var.h instead of item.h because there
@@ -41,8 +42,7 @@ bool Item_row::fix_fields(THD *thd, Item **ref)
Item **arg, **arg_end;
for (arg= args, arg_end= args + arg_count; arg != arg_end ; arg++)
{
- if (!(*arg)->fixed &&
- (*arg)->fix_fields(thd, arg))
+ if ((*arg)->fix_fields_if_needed(thd, arg))
return TRUE;
// we can't assign 'item' before, because fix_fields() can change arg
Item *item= *arg;
@@ -64,7 +64,7 @@ bool Item_row::fix_fields(THD *thd, Item **ref)
with_sum_func= with_sum_func || item->with_sum_func;
with_window_func = with_window_func || item->with_window_func;
with_field= with_field || item->with_field;
- with_subselect|= item->with_subselect;
+ m_with_subquery|= item->with_subquery();
with_param|= item->with_param;
}
fixed= 1;
@@ -164,22 +164,22 @@ void Item_row::bring_value()
}
-Item* Item_row::build_clone(THD *thd, MEM_ROOT *mem_root)
+Item* Item_row::build_clone(THD *thd)
{
- Item **copy_args= 0;
- if (!(copy_args= (Item**) alloc_root(mem_root, sizeof(Item*) * arg_count)))
+ Item **copy_args= static_cast<Item**>
+ (alloc_root(thd->mem_root, sizeof(Item*) * arg_count));
+ if (unlikely(!copy_args))
return 0;
for (uint i= 0; i < arg_count; i++)
{
- Item *arg_clone= args[i]->build_clone(thd, mem_root);
+ Item *arg_clone= args[i]->build_clone(thd);
if (!arg_clone)
return 0;
copy_args[i]= arg_clone;
}
- Item_row *copy= (Item_row *) get_copy(thd, mem_root);
- if (!copy)
+ Item_row *copy= (Item_row *) get_copy(thd);
+ if (unlikely(!copy))
return 0;
copy->args= copy_args;
return copy;
}
-
diff --git a/sql/item_row.h b/sql/item_row.h
index cb92e741af9..0d6a6db2e0e 100644
--- a/sql/item_row.h
+++ b/sql/item_row.h
@@ -35,7 +35,8 @@
*/
class Item_row: public Item,
private Item_args,
- private Used_tables_and_const_cache
+ private Used_tables_and_const_cache,
+ private With_subquery_cache
{
table_map not_null_tables_cache;
/**
@@ -52,12 +53,14 @@ public:
not_null_tables_cache(0), with_null(0)
{ }
+ 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; }
void illegal_method_call(const char *);
bool is_null() { return null_value; }
- void make_field(THD *thd, Send_field *)
+ void make_send_field(THD *thd, Send_field *)
{
- illegal_method_call((const char*)"make_field");
+ illegal_method_call((const char*)"make_send_field");
};
double val_real()
{
@@ -79,6 +82,11 @@ public:
illegal_method_call((const char*)"val_decimal");
return 0;
};
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ {
+ illegal_method_call((const char*)"get_date");
+ return true;
+ }
bool fix_fields(THD *thd, Item **ref);
void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge);
void cleanup();
@@ -86,12 +94,6 @@ public:
List<Item> &fields, uint flags);
table_map used_tables() const { return used_tables_cache; };
bool const_item() const { return const_item_cache; };
- enum Item_result result_type() const { return ROW_RESULT; }
- Item_result cmp_type() const { return ROW_RESULT; }
- enum_field_types field_type() const
- {
- return MYSQL_TYPE_NULL;
- }
void update_used_tables()
{
used_tables_and_const_cache_init();
@@ -109,7 +111,7 @@ public:
Item *transform(THD *thd, Item_transformer transformer, uchar *arg);
bool eval_not_null_tables(void *opt_arg);
- uint cols() { return arg_count; }
+ uint cols() const { return arg_count; }
Item* element_index(uint i) { return args[i]; }
Item** addr(uint i) { return args + i; }
bool check_cols(uint c);
@@ -133,9 +135,9 @@ public:
}
bool check_vcol_func_processor(void *arg) {return FALSE; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_row>(thd, mem_root, this); }
- Item *build_clone(THD *thd, MEM_ROOT *mem_root);
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_row>(thd, this); }
+ Item *build_clone(THD *thd);
};
#endif /* ITEM_ROW_INCLUDED */
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index f5a8a649ac2..0d519edc5b3 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -31,7 +31,7 @@
#pragma implementation // gcc: Class implementation
#endif
-#include <my_global.h> // HAVE_*
+#include "mariadb.h" // HAVE_*
#include "sql_priv.h"
/*
@@ -58,6 +58,27 @@ C_MODE_END
size_t username_char_length= 80;
+
+class Repeat_count
+{
+ ulonglong m_count;
+public:
+ Repeat_count(Item *item)
+ :m_count(0)
+ {
+ 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;
+ }
+ }
+ ulonglong count() const { return m_count; }
+};
+
+
/*
For the Items which have only val_str_ascii() method
and don't have their own "native" val_str(),
@@ -463,7 +484,7 @@ String *Item_func_from_base64::val_str(String *str)
THD *thd= current_thd;
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_BAD_BASE64_DATA, ER_THD(thd, ER_BAD_BASE64_DATA),
- end_ptr - res->ptr());
+ (int) (end_ptr - res->ptr()));
goto err;
}
@@ -533,14 +554,14 @@ String *Item_func_decode_histogram::val_str(String *str)
DBUG_ASSERT(0);
}
/* show delta with previous value */
- int size= my_snprintf(numbuf, sizeof(numbuf),
+ size_t size= my_snprintf(numbuf, sizeof(numbuf),
representation_by_type[type], val - prev);
str->append(numbuf, size);
str->append(",");
prev= val;
}
/* show delta with max */
- int size= my_snprintf(numbuf, sizeof(numbuf),
+ size_t size= my_snprintf(numbuf, sizeof(numbuf),
representation_by_type[type], 1.0 - prev);
str->append(numbuf, size);
@@ -607,23 +628,45 @@ String *Item_func_concat::val_str(String *str)
for (uint i= 1 ; i < arg_count ; i++)
{
- uint concat_len;
- if (!(res= args[i]->val_str(&tmp_value)))
+ if (!(res= args[i]->val_str(&tmp_value)) ||
+ append_value(thd, str, res))
goto null;
- if (res->length() == 0)
+ }
+
+ str->set_charset(collation.collation);
+ return str;
+
+null:
+ null_value= true;
+ return 0;
+}
+
+
+String *Item_func_concat_operator_oracle::val_str(String *str)
+{
+ DBUG_ASSERT(fixed == 1);
+ THD *thd= current_thd;
+ String *res;
+ uint i;
+
+ null_value=0;
+ // Search first non null argument
+ for (i= 0; i < arg_count; i++)
+ {
+ if ((res= args[i]->val_str(str)))
+ break;
+ }
+ if (i == arg_count)
+ goto null;
+
+ if (res != str)
+ str->copy(res->ptr(), res->length(), res->charset());
+
+ for (i++ ; i < arg_count ; i++)
+ {
+ if (!(res= args[i]->val_str(&tmp_value)) || res->length() == 0)
continue;
- if ((concat_len= str->length() + res->length()) >
- thd->variables.max_allowed_packet)
- {
- push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
- ER_WARN_ALLOWED_PACKET_OVERFLOWED,
- ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), func_name(),
- thd->variables.max_allowed_packet);
- goto null;
- }
- DBUG_ASSERT(!res->uses_buffer_owned_by(str));
- DBUG_ASSERT(!str->uses_buffer_owned_by(res));
- if (realloc_result(str, concat_len) || str->append(*res))
+ if (append_value(thd, str, res))
goto null;
}
@@ -631,11 +674,29 @@ String *Item_func_concat::val_str(String *str)
return str;
null:
- null_value=1;
+ null_value= true;
return 0;
}
+bool Item_func_concat::append_value(THD *thd, String *res, const String *app)
+{
+ uint concat_len;
+ if ((concat_len= res->length() + app->length()) >
+ thd->variables.max_allowed_packet)
+ {
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_WARN_ALLOWED_PACKET_OVERFLOWED,
+ ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), func_name(),
+ thd->variables.max_allowed_packet);
+ return true;
+ }
+ DBUG_ASSERT(!res->uses_buffer_owned_by(app));
+ DBUG_ASSERT(!app->uses_buffer_owned_by(res));
+ return realloc_result(res, concat_len) || res->append(*app);
+}
+
+
bool Item_func_concat::fix_length_and_dec()
{
ulonglong char_length= 0;
@@ -1072,7 +1133,8 @@ String *Item_func_reverse::val_str(String *str)
bool Item_func_reverse::fix_length_and_dec()
{
- agg_arg_charsets_for_string_result(collation, args, 1);
+ if (agg_arg_charsets_for_string_result(collation, args, 1))
+ return TRUE;
DBUG_ASSERT(collation.collation != NULL);
fix_char_length(args[0]->max_char_length());
return FALSE;
@@ -1087,7 +1149,8 @@ bool Item_func_reverse::fix_length_and_dec()
Fix that this works with binary strings when using USE_MB
*/
-String *Item_func_replace::val_str(String *str)
+String *Item_func_replace::val_str_internal(String *str,
+ String *empty_string_for_null)
{
DBUG_ASSERT(fixed == 1);
String *res,*res2,*res3;
@@ -1107,8 +1170,11 @@ String *Item_func_replace::val_str(String *str)
goto null;
res2=args[1]->val_str(&tmp_value);
if (args[1]->null_value)
- goto null;
-
+ {
+ if (!empty_string_for_null)
+ goto null;
+ res2= empty_string_for_null;
+ }
res->set_charset(collation.collation);
#ifdef USE_MB
@@ -1126,7 +1192,11 @@ String *Item_func_replace::val_str(String *str)
return res;
#endif
if (!(res3=args[2]->val_str(&tmp_value2)))
- goto null;
+ {
+ if (!empty_string_for_null)
+ goto null;
+ res3= empty_string_for_null;
+ }
from_length= res2->length();
to_length= res3->length();
@@ -1209,6 +1279,9 @@ redo:
}
while ((offset=res->strstr(*res2,(uint) offset)) >= 0);
}
+ if (empty_string_for_null && !res->length())
+ goto null;
+
return res;
null:
@@ -1518,7 +1591,7 @@ String *Item_str_conv::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
String *res;
- uint alloced_length, len;
+ size_t alloced_length, len;
if ((null_value= (!(res= args[0]->val_str(&tmp_value)) ||
str->alloc((alloced_length= res->length() * multiply)))))
@@ -1583,13 +1656,10 @@ String *Item_func_left::val_str(String *str)
void Item_str_func::left_right_max_length()
{
uint32 char_length= args[0]->max_char_length();
- if (args[1]->const_item())
+ if (args[1]->const_item() && !args[1]->is_expensive())
{
- int length= (int) args[1]->val_int();
- if (args[1]->null_value || length <= 0)
- char_length=0;
- else
- set_if_smaller(char_length, (uint) length);
+ Repeat_count tmp(args[1]);
+ set_if_smaller(char_length, (uint) tmp.count());
}
fix_char_length(char_length);
}
@@ -1646,7 +1716,7 @@ String *Item_func_substr::val_str(String *str)
DBUG_ASSERT(fixed == 1);
String *res = args[0]->val_str(str);
/* must be longlong to avoid truncation */
- longlong start= args[1]->val_int();
+ longlong start= get_position();
/* Assumes that the maximum length of a String is < INT_MAX32. */
/* Limit so that code sees out-of-bound value properly. */
longlong length= arg_count == 3 ? args[2]->val_int() : INT_MAX32;
@@ -1697,7 +1767,7 @@ bool Item_func_substr::fix_length_and_dec()
DBUG_ASSERT(collation.collation != NULL);
if (args[1]->const_item())
{
- int32 start= (int32) args[1]->val_int();
+ int32 start= (int32) get_position();
if (args[1]->null_value)
max_length= 0;
else if (start < 0)
@@ -2093,6 +2163,7 @@ void Item_func_trim::print(String *str, enum_query_type query_type)
return;
}
str->append(Item_func_trim::func_name());
+ str->append(func_name_ext());
str->append('(');
str->append(mode_name());
str->append(' ');
@@ -2201,9 +2272,8 @@ char *Item_func_password::alloc(THD *thd, const char *password,
String *Item_func_encrypt::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
- String *res =args[0]->val_str(str);
-
#ifdef HAVE_CRYPT
+ String *res =args[0]->val_str(str);
char salt[3],*salt_ptr;
if ((null_value=args[0]->null_value))
return 0;
@@ -2309,18 +2379,37 @@ String *Item_func_database::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
THD *thd= current_thd;
- if (thd->db == NULL)
+ if (thd->db.str == NULL)
{
null_value= 1;
return 0;
}
else
- str->copy(thd->db, thd->db_length, system_charset_info);
+ str->copy(thd->db.str, thd->db.length, system_charset_info);
null_value= 0;
return str;
}
+String *Item_func_sqlerrm::val_str(String *str)
+{
+ DBUG_ASSERT(fixed);
+ DBUG_ASSERT(!null_value);
+ Diagnostics_area::Sql_condition_iterator it=
+ current_thd->get_stmt_da()->sql_conditions();
+ const Sql_condition *err;
+ if ((err= it++))
+ {
+ str->copy(err->get_message_text(), err->get_message_octet_length(),
+ system_charset_info);
+ return str;
+ }
+ str->copy(STRING_WITH_LEN("normal, successful completion"),
+ system_charset_info);
+ return str;
+}
+
+
/**
@note USER() is replicated correctly if binlog_format=ROW or (as of
BUG#28086) binlog_format=MIXED, but is incorrectly replicated to ''
@@ -2414,7 +2503,8 @@ bool Item_func_current_role::fix_fields(THD *thd, Item **ref)
bool Item_func_soundex::fix_length_and_dec()
{
uint32 char_length= args[0]->max_char_length();
- agg_arg_charsets_for_string_result(collation, args, 1);
+ if (agg_arg_charsets_for_string_result(collation, args, 1))
+ return TRUE;
DBUG_ASSERT(collation.collation != NULL);
set_if_bigger(char_length, 4);
fix_char_length(char_length);
@@ -2576,24 +2666,6 @@ String *Item_func_soundex::val_str(String *str)
const int FORMAT_MAX_DECIMALS= 30;
-MY_LOCALE *Item_func_format::get_locale(Item *item)
-{
- DBUG_ASSERT(arg_count == 3);
- String tmp, *locale_name= args[2]->val_str_ascii(&tmp);
- MY_LOCALE *lc;
- if (!locale_name ||
- !(lc= my_locale_by_name(locale_name->c_ptr_safe())))
- {
- THD *thd= current_thd;
- push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
- ER_UNKNOWN_LOCALE,
- ER_THD(thd, ER_UNKNOWN_LOCALE),
- locale_name ? locale_name->c_ptr_safe() : "NULL");
- lc= &my_locale_en_US;
- }
- return lc;
-}
-
bool Item_func_format::fix_length_and_dec()
{
uint32 char_length= args[0]->max_char_length();
@@ -2601,7 +2673,7 @@ bool Item_func_format::fix_length_and_dec()
collation.set(default_charset());
fix_char_length(char_length + max_sep_count + decimals);
if (arg_count == 3)
- locale= args[2]->basic_const_item() ? get_locale(args[2]) : NULL;
+ locale= args[2]->basic_const_item() ? args[2]->locale_from_val_str() : NULL;
else
locale= &my_locale_en_US; /* Two arguments */
return FALSE;
@@ -2621,7 +2693,7 @@ String *Item_func_format::val_str_ascii(String *str)
int dec;
/* Number of characters used to represent the decimals, including '.' */
uint32 dec_length;
- MY_LOCALE *lc;
+ const MY_LOCALE *lc;
DBUG_ASSERT(fixed == 1);
dec= (int) args[1]->val_int();
@@ -2631,7 +2703,7 @@ String *Item_func_format::val_str_ascii(String *str)
return NULL;
}
- lc= locale ? locale : get_locale(args[2]);
+ lc= locale ? locale : args[2]->locale_from_val_str();
dec= set_zone(dec, 0, FORMAT_MAX_DECIMALS);
dec_length= dec ? dec+1 : 0;
@@ -2854,7 +2926,7 @@ void Item_func_char::print(String *str, enum_query_type query_type)
print_args(str, 0, query_type);
if (collation.collation != &my_charset_bin)
{
- str->append(C_STRING_WITH_LEN(" using "));
+ str->append(STRING_WITH_LEN(" using "));
str->append(collation.collation->csname);
}
str->append(')');
@@ -2870,29 +2942,51 @@ String *Item_func_char::val_str(String *str)
{
int32 num=(int32) args[i]->val_int();
if (!args[i]->null_value)
- {
- char tmp[4];
- if (num & 0xFF000000L)
- {
- mi_int4store(tmp, num);
- str->append(tmp, 4, &my_charset_bin);
- }
- else if (num & 0xFF0000L)
- {
- mi_int3store(tmp, num);
- str->append(tmp, 3, &my_charset_bin);
- }
- else if (num & 0xFF00L)
- {
- mi_int2store(tmp, num);
- str->append(tmp, 2, &my_charset_bin);
- }
- else
- {
- tmp[0]= (char) num;
- str->append(tmp, 1, &my_charset_bin);
- }
- }
+ append_char(str, num);
+ }
+ str->realloc(str->length()); // Add end 0 (for Purify)
+ return check_well_formed_result(str);
+}
+
+
+void Item_func_char::append_char(String *str, int32 num)
+{
+ char tmp[4];
+ if (num & 0xFF000000L)
+ {
+ mi_int4store(tmp, num);
+ str->append(tmp, 4, &my_charset_bin);
+ }
+ else if (num & 0xFF0000L)
+ {
+ mi_int3store(tmp, num);
+ str->append(tmp, 3, &my_charset_bin);
+ }
+ else if (num & 0xFF00L)
+ {
+ mi_int2store(tmp, num);
+ str->append(tmp, 2, &my_charset_bin);
+ }
+ else
+ {
+ tmp[0]= (char) num;
+ str->append(tmp, 1, &my_charset_bin);
+ }
+}
+
+
+String *Item_func_chr::val_str(String *str)
+{
+ DBUG_ASSERT(fixed == 1);
+ str->length(0);
+ str->set_charset(collation.collation);
+ int32 num=(int32) args[0]->val_int();
+ if (!args[0]->null_value)
+ append_char(str, num);
+ else
+ {
+ null_value= 1;
+ return 0;
}
str->realloc(str->length()); // Add end 0 (for Purify)
return check_well_formed_result(str);
@@ -2926,27 +3020,16 @@ bool Item_func_repeat::fix_length_and_dec()
if (agg_arg_charsets_for_string_result(collation, args, 1))
return TRUE;
DBUG_ASSERT(collation.collation != NULL);
- if (args[1]->const_item())
+ if (args[1]->const_item() && !args[1]->is_expensive())
{
- /* must be longlong to avoid truncation */
- longlong count= args[1]->val_int();
-
- /* Assumes that the maximum length of a String is < INT_MAX32. */
- /* Set here so that rest of code sees out-of-bound value as such. */
- if (args[1]->null_value)
- count= 0;
- else if (count > INT_MAX32)
- count= INT_MAX32;
-
- ulonglong char_length= (ulonglong) args[0]->max_char_length() * count;
+ Repeat_count tmp(args[1]);
+ ulonglong char_length= (ulonglong) args[0]->max_char_length() * tmp.count();
fix_char_length_ulonglong(char_length);
+ return false;
}
- else
- {
- max_length= MAX_BLOB_WIDTH;
- maybe_null= 1;
- }
- return FALSE;
+ max_length= MAX_BLOB_WIDTH;
+ maybe_null= true;
+ return false;
}
/**
@@ -3011,26 +3094,14 @@ err:
bool Item_func_space::fix_length_and_dec()
{
collation.set(default_charset(), DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
- if (args[0]->const_item())
+ if (args[0]->const_item() && !args[0]->is_expensive())
{
- /* must be longlong to avoid truncation */
- longlong count= args[0]->val_int();
- if (args[0]->null_value)
- goto end;
- /*
- Assumes that the maximum length of a String is < INT_MAX32.
- Set here so that rest of code sees out-of-bound value as such.
- */
- if (count > INT_MAX32)
- count= INT_MAX32;
- fix_char_length_ulonglong(count);
- return FALSE;
+ fix_char_length_ulonglong(Repeat_count(args[0]).count());
+ return false;
}
-
-end:
max_length= MAX_BLOB_WIDTH;
- maybe_null= 1;
- return FALSE;
+ maybe_null= true;
+ return false;
}
@@ -3123,31 +3194,33 @@ err:
bool Item_func_pad::fix_length_and_dec()
{
- String *str;
- if (!args[2]->basic_const_item() || !(str= args[2]->val_str(&pad_str)) || !str->length())
- maybe_null= true;
-
- // Handle character set for args[0] and args[2].
- if (agg_arg_charsets_for_string_result(collation, &args[0], 2, 2))
- return TRUE;
- if (args[1]->const_item())
+ if (arg_count == 3)
{
- ulonglong char_length= (ulonglong) args[1]->val_int();
- DBUG_ASSERT(collation.collation->mbmaxlen > 0);
- /* Assumes that the maximum length of a String is < INT_MAX32. */
- /* Set here so that rest of code sees out-of-bound value as such. */
- if (args[1]->null_value)
- char_length= 0;
- else if (char_length > INT_MAX32)
- char_length= INT_MAX32;
- fix_char_length_ulonglong(char_length);
+ String *str;
+ if (!args[2]->basic_const_item() || !(str= args[2]->val_str(&pad_str)) || !str->length())
+ maybe_null= true;
+ // Handle character set for args[0] and args[2].
+ if (agg_arg_charsets_for_string_result(collation, &args[0], 2, 2))
+ return TRUE;
}
else
{
- max_length= MAX_BLOB_WIDTH;
- maybe_null= 1;
+ if (agg_arg_charsets_for_string_result(collation, &args[0], 1, 1))
+ return TRUE;
+ pad_str.set_charset(collation.collation);
+ pad_str.length(0);
+ pad_str.append(" ", 1);
}
- return FALSE;
+
+ DBUG_ASSERT(collation.collation->mbmaxlen > 0);
+ if (args[1]->const_item() && !args[1]->is_expensive())
+ {
+ fix_char_length_ulonglong(Repeat_count(args[1]).count());
+ return false;
+ }
+ max_length= MAX_BLOB_WIDTH;
+ maybe_null= true;
+ return false;
}
@@ -3193,12 +3266,17 @@ String *Item_func_rpad::val_str(String *str)
longlong count= args[1]->val_int();
longlong byte_count;
String *res= args[0]->val_str(str);
- String *rpad= args[2]->val_str(&pad_str);
+ String *rpad= arg_count == 2 ? &pad_str : args[2]->val_str(&pad_str);
if (!res || args[1]->null_value || !rpad ||
((count < 0) && !args[1]->unsigned_flag))
goto err;
+
null_value=0;
+
+ if (count == 0)
+ return make_empty_result();
+
/* Assumes that the maximum length of a String is < INT_MAX32. */
/* Set here so that rest of code sees out-of-bound value as such. */
if ((ulonglong) count > INT_MAX32)
@@ -3222,7 +3300,6 @@ String *Item_func_rpad::val_str(String *str)
res->length(res->charpos((int) count)); // Shorten result if longer
return (res);
}
- pad_char_length= rpad->numchars();
byte_count= count * collation.collation->mbmaxlen;
{
@@ -3236,8 +3313,15 @@ String *Item_func_rpad::val_str(String *str)
goto err;
}
}
- if (args[2]->null_value || !pad_char_length)
- goto err;
+
+ if (arg_count == 3)
+ {
+ if (args[2]->null_value || !(pad_char_length= rpad->numchars()))
+ goto err;
+ }
+ else
+ pad_char_length= 1; // Implicit space
+
res_byte_length= res->length(); /* Must be done before alloc_buffer */
if (!(res= alloc_buffer(res,str,&tmp_value, (ulong) byte_count)))
goto err;
@@ -3274,12 +3358,17 @@ String *Item_func_lpad::val_str(String *str)
longlong count= args[1]->val_int();
longlong byte_count;
String *res= args[0]->val_str(&tmp_value);
- String *pad= args[2]->val_str(&pad_str);
+ String *pad= arg_count == 2 ? &pad_str : args[2]->val_str(&pad_str);
if (!res || args[1]->null_value || !pad ||
((count < 0) && !args[1]->unsigned_flag))
goto err;
+
null_value=0;
+
+ if (count == 0)
+ return make_empty_result();
+
/* Assumes that the maximum length of a String is < INT_MAX32. */
/* Set here so that rest of code sees out-of-bound value as such. */
if ((ulonglong) count > INT_MAX32)
@@ -3307,7 +3396,6 @@ String *Item_func_lpad::val_str(String *str)
return res;
}
- pad_char_length= pad->numchars();
byte_count= count * collation.collation->mbmaxlen;
{
@@ -3322,9 +3410,16 @@ String *Item_func_lpad::val_str(String *str)
}
}
- if (args[2]->null_value || !pad_char_length ||
- str->alloc((uint32) byte_count))
+ if (str->alloc((uint32) byte_count))
goto err;
+
+ if (arg_count == 3)
+ {
+ if (args[2]->null_value || !(pad_char_length= pad->numchars()))
+ goto err;
+ }
+ else
+ pad_char_length= 1; // Implicit space
str->length(0);
str->set_charset(collation.collation);
@@ -3440,27 +3535,13 @@ String *Item_func_set_collation::val_str(String *str)
bool Item_func_set_collation::fix_length_and_dec()
{
- CHARSET_INFO *set_collation;
- const char *colname;
- String tmp, *str= args[1]->val_str(&tmp);
- colname= str->c_ptr();
- if (colname == binary_keyword)
- set_collation= get_charset_by_csname(args[0]->collation.collation->csname,
- MY_CS_BINSORT,MYF(0));
- else
- {
- if (!(set_collation= mysqld_collation_get_by_name(colname)))
- return TRUE;
- }
-
- if (!set_collation ||
- !my_charset_same(args[0]->collation.collation,set_collation))
+ if (!my_charset_same(args[0]->collation.collation, m_set_collation))
{
my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0),
- colname, args[0]->collation.collation->csname);
+ m_set_collation->name, args[0]->collation.collation->csname);
return TRUE;
}
- collation.set(set_collation, DERIVATION_EXPLICIT,
+ collation.set(m_set_collation, DERIVATION_EXPLICIT,
args[0]->collation.repertoire);
max_length= args[0]->max_length;
return FALSE;
@@ -3469,22 +3550,8 @@ bool Item_func_set_collation::fix_length_and_dec()
bool Item_func_set_collation::eq(const Item *item, bool binary_cmp) const
{
- /* Assume we don't have rtti */
- if (this == item)
- return 1;
- if (item->type() != FUNC_ITEM)
- return 0;
- Item_func *item_func=(Item_func*) item;
- if (arg_count != item_func->argument_count() ||
- functype() != item_func->functype())
- return 0;
- Item_func_set_collation *item_func_sc=(Item_func_set_collation*) item;
- if (collation.collation != item_func_sc->collation.collation)
- return 0;
- for (uint i=0; i < arg_count ; i++)
- if (!args[i]->eq(item_func_sc->args[i], binary_cmp))
- return 0;
- return 1;
+ return Item_func::eq(item, binary_cmp) &&
+ collation.collation == item->collation.collation;
}
@@ -3492,9 +3559,7 @@ void Item_func_set_collation::print(String *str, enum_query_type query_type)
{
args[0]->print_parenthesised(str, query_type, precedence());
str->append(STRING_WITH_LEN(" collate "));
- DBUG_ASSERT(args[1]->basic_const_item() &&
- args[1]->type() == Item::STRING_ITEM);
- ((Item_string *)args[1])->print_value(str);
+ str->append(m_set_collation->name);
}
String *Item_func_charset::val_str(String *str)
@@ -3534,10 +3599,10 @@ bool Item_func_weight_string::fix_length_and_dec()
*/
if (!(max_length= result_length))
{
- uint char_length;
+ 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= cs->coll->strnxfrmlen(cs, char_length * cs->mbmaxlen);
+ max_length= (uint32)cs->coll->strnxfrmlen(cs, char_length * cs->mbmaxlen);
}
maybe_null= 1;
return FALSE;
@@ -3549,7 +3614,7 @@ String *Item_func_weight_string::val_str(String *str)
{
String *res;
CHARSET_INFO *cs= args[0]->collation.collation;
- uint tmp_length, frm_length;
+ size_t tmp_length, frm_length;
DBUG_ASSERT(fixed == 1);
if (args[0]->result_type() != STRING_RESULT ||
@@ -3563,7 +3628,7 @@ String *Item_func_weight_string::val_str(String *str)
*/
if (!(tmp_length= result_length))
{
- uint char_length;
+ size_t char_length;
if (cs->state & MY_CS_STRNXFRM_BAD_NWEIGHTS)
{
/*
@@ -3609,7 +3674,7 @@ String *Item_func_weight_string::val_str(String *str)
frm_length= cs->coll->strnxfrm(cs,
(uchar *) str->ptr(), tmp_length,
- nweights ? nweights : tmp_length,
+ nweights ? nweights : (uint)tmp_length,
(const uchar *) res->ptr(), res->length(),
flags);
DBUG_ASSERT(frm_length <= tmp_length);
@@ -3639,53 +3704,41 @@ void Item_func_weight_string::print(String *str, enum_query_type query_type)
}
-String *Item_func_hex::val_str_ascii(String *str)
+String *Item_func_hex::val_str_ascii_from_val_real(String *str)
{
- String *res;
- DBUG_ASSERT(fixed == 1);
- if (args[0]->result_type() != STRING_RESULT)
- {
- ulonglong dec;
- char ans[65],*ptr;
- /* Return hex of unsigned longlong value */
- if (args[0]->result_type() == REAL_RESULT ||
- args[0]->result_type() == DECIMAL_RESULT)
- {
- double val= args[0]->val_real();
- if ((val <= (double) LONGLONG_MIN) ||
- (val >= (double) (ulonglong) ULONGLONG_MAX))
- dec= ~(longlong) 0;
- else
- dec= (ulonglong) (val + (val > 0 ? 0.5 : -0.5));
- }
- else
- dec= (ulonglong) args[0]->val_int();
+ ulonglong dec;
+ double val= args[0]->val_real();
+ if ((null_value= args[0]->null_value))
+ return 0;
+ if ((val <= (double) LONGLONG_MIN) ||
+ (val >= (double) (ulonglong) ULONGLONG_MAX))
+ dec= ~(longlong) 0;
+ else
+ dec= (ulonglong) (val + (val > 0 ? 0.5 : -0.5));
+ return str->set_hex(dec) ? make_empty_result() : str;
+}
- if ((null_value= args[0]->null_value))
- return 0;
-
- if (!(ptr= longlong2str(dec, ans, 16)) ||
- str->copy(ans,(uint32) (ptr - ans),
- &my_charset_numeric))
- return make_empty_result(); // End of memory
- return str;
- }
- /* Convert given string to a hex string, character by character */
- res= args[0]->val_str(&tmp_value);
- if (!res || str->alloc(res->length()*2+1))
- {
- null_value=1;
- return 0;
- }
- null_value=0;
- str->length(res->length()*2);
- str->set_charset(&my_charset_latin1);
+String *Item_func_hex::val_str_ascii_from_val_str(String *str)
+{
+ DBUG_ASSERT(&tmp_value != str);
+ String *res= args[0]->val_str(&tmp_value);
+ DBUG_ASSERT(res != str);
+ if ((null_value= (res == NULL)))
+ return NULL;
+ return str->set_hex(res->ptr(), res->length()) ? make_empty_result() : str;
+}
- octet2hex((char*) str->ptr(), res->ptr(), res->length());
- return str;
+
+String *Item_func_hex::val_str_ascii_from_val_int(String *str)
+{
+ ulonglong dec= (ulonglong) args[0]->val_int();
+ if ((null_value= args[0]->null_value))
+ return 0;
+ return str->set_hex(dec) ? make_empty_result() : str;
}
+
/** Convert given hex string to a binary string. */
String *Item_func_unhex::val_str(String *str)
@@ -4444,6 +4497,9 @@ bool Item_func_dyncol_create::prepare_arguments(THD *thd, bool force_names_arg)
case MYSQL_TYPE_GEOMETRY:
type= DYN_COL_STRING;
break;
+ case MYSQL_TYPE_VARCHAR_COMPRESSED:
+ case MYSQL_TYPE_BLOB_COMPRESSED:
+ DBUG_ASSERT(0);
}
}
if (type == DYN_COL_STRING &&
@@ -4963,7 +5019,7 @@ longlong Item_dyncol_get::val_int()
char *end= val.x.string.value.str + val.x.string.value.length, *org_end= end;
num= my_strtoll10(val.x.string.value.str, &end, &error);
- if (end != org_end || error > 0)
+ if (unlikely(end != org_end || error > 0))
{
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_BAD_DATA,
@@ -5249,3 +5305,24 @@ null:
my_free(names);
return NULL;
}
+
+Item_temptable_rowid::Item_temptable_rowid(TABLE *table_arg)
+ : Item_str_func(table_arg->in_use), table(table_arg)
+{
+ max_length= table->file->ref_length;
+}
+
+bool Item_temptable_rowid::fix_length_and_dec()
+{
+ used_tables_cache= table->map;
+ const_item_cache= false;
+ return FALSE;
+}
+
+String *Item_temptable_rowid::val_str(String *str)
+{
+ if (!((null_value= table->null_row)))
+ table->file->position(table->record[0]);
+ str_value.set((char*)(table->file->ref), max_length, &my_charset_bin);
+ return &str_value;
+}
diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h
index df8761534a1..a279dd8598f 100644
--- a/sql/item_strfunc.h
+++ b/sql/item_strfunc.h
@@ -27,8 +27,6 @@
extern size_t username_char_length;
-class MY_LOCALE;
-
class Item_str_func :public Item_func
{
protected:
@@ -37,7 +35,7 @@ protected:
character set. No memory is allocated.
@retval A pointer to the str_value member.
*/
- String *make_empty_result()
+ virtual String *make_empty_result()
{
/*
Reset string length to an empty string. We don't use str_value.set() as
@@ -64,8 +62,9 @@ public:
longlong val_int();
double val_real();
my_decimal *val_decimal(my_decimal *);
- enum Item_result result_type () const { return STRING_RESULT; }
- enum_field_types field_type() const { return string_field_type(); }
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ { return get_date_from_string(ltime, fuzzydate); }
+ const Type_handler *type_handler() const { return string_type_handler(); }
void left_right_max_length();
bool fix_fields(THD *thd, Item **ref);
void update_null_value()
@@ -151,8 +150,8 @@ public:
return FALSE;
}
const char *func_name() const { return "md5"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_md5>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_md5>(thd, this); }
};
@@ -163,8 +162,8 @@ public:
String *val_str_ascii(String *);
bool fix_length_and_dec();
const char *func_name() const { return "sha"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_sha>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_sha>(thd, this); }
};
class Item_func_sha2 :public Item_str_ascii_checksum_func
@@ -175,8 +174,8 @@ public:
String *val_str_ascii(String *);
bool fix_length_and_dec();
const char *func_name() const { return "sha2"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_sha2>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_sha2>(thd, this); }
};
class Item_func_to_base64 :public Item_str_ascii_checksum_func
@@ -188,8 +187,8 @@ public:
String *val_str_ascii(String *);
bool fix_length_and_dec();
const char *func_name() const { return "to_base64"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_to_base64>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_to_base64>(thd, this); }
};
class Item_func_from_base64 :public Item_str_binary_checksum_func
@@ -201,8 +200,8 @@ public:
String *val_str(String *);
bool fix_length_and_dec();
const char *func_name() const { return "from_base64"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_from_base64>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_from_base64>(thd, this); }
};
#include <my_crypt.h>
@@ -228,8 +227,8 @@ public:
:Item_aes_crypt(thd, a, b) {}
bool fix_length_and_dec();
const char *func_name() const { return "aes_encrypt"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_aes_encrypt>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_aes_encrypt>(thd, this); }
};
class Item_func_aes_decrypt :public Item_aes_crypt
@@ -239,14 +238,23 @@ public:
Item_aes_crypt(thd, a, b) {}
bool fix_length_and_dec();
const char *func_name() const { return "aes_decrypt"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_aes_decrypt>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_aes_decrypt>(thd, this); }
};
class Item_func_concat :public Item_str_func
{
+protected:
String tmp_value;
+ /*
+ Append a non-NULL value to the result.
+ @param [IN] thd - The current thread.
+ @param [IN/OUT] res - The current val_str() return value.
+ @param [IN] app - The value to be appended.
+ @retval - false on success, true on error
+ */
+ bool append_value(THD *thd, String *res, const String *app);
bool realloc_result(String *str, uint length) const;
public:
Item_func_concat(THD *thd, List<Item> &list): Item_str_func(thd, list) {}
@@ -254,10 +262,33 @@ public:
String *val_str(String *);
bool fix_length_and_dec();
const char *func_name() const { return "concat"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_concat>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_concat>(thd, this); }
+};
+
+
+/*
+ This class handles the || operator in sql_mode=ORACLE.
+ Unlike the traditional MariaDB concat(), it treats NULL arguments as ''.
+*/
+class Item_func_concat_operator_oracle :public Item_func_concat
+{
+public:
+ Item_func_concat_operator_oracle(THD *thd, List<Item> &list)
+ :Item_func_concat(thd, list)
+ { }
+ Item_func_concat_operator_oracle(THD *thd, Item *a, Item *b)
+ :Item_func_concat(thd, a, b)
+ { }
+ String *val_str(String *);
+ const char *func_name() const { return "concat_operator_oracle"; }
+ Item *get_copy(THD *thd)
+ {
+ return get_item_copy<Item_func_concat_operator_oracle>(thd, this);
+ }
};
+
class Item_func_decode_histogram :public Item_str_func
{
public:
@@ -272,8 +303,8 @@ public:
return FALSE;
}
const char *func_name() const { return "decode_histogram"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_decode_histogram>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_decode_histogram>(thd, this); }
};
class Item_func_concat_ws :public Item_str_func
@@ -285,8 +316,8 @@ public:
bool fix_length_and_dec();
const char *func_name() const { return "concat_ws"; }
table_map not_null_tables() const { return 0; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_concat_ws>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_concat_ws>(thd, this); }
};
class Item_func_reverse :public Item_str_func
@@ -297,8 +328,8 @@ public:
String *val_str(String *);
bool fix_length_and_dec();
const char *func_name() const { return "reverse"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_reverse>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_reverse>(thd, this); }
};
@@ -308,11 +339,25 @@ class Item_func_replace :public Item_str_func
public:
Item_func_replace(THD *thd, Item *org, Item *find, Item *replace):
Item_str_func(thd, org, find, replace) {}
- String *val_str(String *);
+ String *val_str(String *to) { return val_str_internal(to, NULL); };
bool fix_length_and_dec();
+ String *val_str_internal(String *str, String *empty_string_for_null);
const char *func_name() const { return "replace"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_replace>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_replace>(thd, this); }
+};
+
+
+class Item_func_replace_oracle :public Item_func_replace
+{
+ String tmp_emtpystr;
+public:
+ Item_func_replace_oracle(THD *thd, Item *org, Item *find, Item *replace):
+ Item_func_replace(thd, org, find, replace) {}
+ String *val_str(String *to) { return val_str_internal(to, &tmp_emtpystr); };
+ const char *func_name() const { return "replace_oracle"; }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_replace_oracle>(thd, this); }
};
@@ -337,7 +382,7 @@ public:
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, MEM_ROOT *mem_root) { return 0;}
+ Item *get_copy(THD *thd) { return 0;}
};
@@ -359,7 +404,7 @@ public:
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, MEM_ROOT *mem_root) { return 0; }
+ Item *get_copy(THD *thd) { return 0; }
};
@@ -373,8 +418,8 @@ public:
String *val_str(String *);
bool fix_length_and_dec();
const char *func_name() const { return "insert"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_insert>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_insert>(thd, this); }
};
@@ -396,8 +441,8 @@ public:
Item_func_lcase(THD *thd, Item *item): Item_str_conv(thd, item) {}
const char *func_name() const { return "lcase"; }
bool fix_length_and_dec();
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_lcase>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_lcase>(thd, this); }
};
class Item_func_ucase :public Item_str_conv
@@ -406,8 +451,8 @@ public:
Item_func_ucase(THD *thd, Item *item): Item_str_conv(thd, item) {}
const char *func_name() const { return "ucase"; }
bool fix_length_and_dec();
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_ucase>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_ucase>(thd, this); }
};
@@ -419,8 +464,8 @@ public:
String *val_str(String *);
bool fix_length_and_dec();
const char *func_name() const { return "left"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_left>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_left>(thd, this); }
};
@@ -432,24 +477,49 @@ public:
String *val_str(String *);
bool fix_length_and_dec();
const char *func_name() const { return "right"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_right>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_right>(thd, this); }
};
class Item_func_substr :public Item_str_func
{
String tmp_value;
+protected:
+ virtual longlong get_position() { return args[1]->val_int(); }
public:
Item_func_substr(THD *thd, Item *a, Item *b): Item_str_func(thd, a, b) {}
- Item_func_substr(THD *thd, Item *a, Item *b, Item *c): Item_str_func(thd, a, b, c) {}
+ Item_func_substr(THD *thd, Item *a, Item *b, Item *c):
+ Item_str_func(thd, a, b, c) {}
String *val_str(String *);
bool fix_length_and_dec();
const char *func_name() const { return "substr"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_substr>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_substr>(thd, this); }
};
+class Item_func_substr_oracle :public Item_func_substr
+{
+protected:
+ longlong get_position()
+ { longlong pos= args[1]->val_int(); return pos == 0 ? 1 : pos; }
+ String *make_empty_result()
+ { null_value= 1; return NULL; }
+public:
+ Item_func_substr_oracle(THD *thd, Item *a, Item *b):
+ Item_func_substr(thd, a, b) {}
+ Item_func_substr_oracle(THD *thd, Item *a, Item *b, Item *c):
+ Item_func_substr(thd, a, b, c) {}
+ bool fix_length_and_dec()
+ {
+ bool res= Item_func_substr::fix_length_and_dec();
+ maybe_null= true;
+ return res;
+ }
+ const char *func_name() const { return "substr_oracle"; }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_substr_oracle>(thd, this); }
+};
class Item_func_substr_index :public Item_str_func
{
@@ -460,8 +530,8 @@ public:
String *val_str(String *);
bool fix_length_and_dec();
const char *func_name() const { return "substring_index"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_substr_index>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_substr_index>(thd, this); }
};
@@ -473,6 +543,9 @@ protected:
String remove;
String *trimmed_value(String *res, uint32 offset, uint32 length)
{
+ if (length == 0)
+ return make_empty_result();
+
tmp_value.set(*res, offset, length);
/*
Make sure to return correct charset and collation:
@@ -486,6 +559,7 @@ protected:
{
return trimmed_value(res, 0, res->length());
}
+ virtual const char *func_name_ext() const { return ""; }
public:
Item_func_trim(THD *thd, Item *a, Item *b): Item_str_func(thd, a, b) {}
Item_func_trim(THD *thd, Item *a): Item_str_func(thd, a) {}
@@ -495,8 +569,30 @@ public:
const char *func_name() const { return "trim"; }
void print(String *str, enum_query_type query_type);
virtual const char *mode_name() const { return "both"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_trim>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_trim>(thd, this); }
+};
+
+
+class Item_func_trim_oracle :public Item_func_trim
+{
+protected:
+ String *make_empty_result()
+ { null_value= 1; return NULL; }
+ const char *func_name_ext() const { return "_oracle"; }
+public:
+ Item_func_trim_oracle(THD *thd, Item *a, Item *b):
+ Item_func_trim(thd, a, b) {}
+ Item_func_trim_oracle(THD *thd, Item *a): Item_func_trim(thd, a) {}
+ const char *func_name() const { return "trim_oracle"; }
+ bool fix_length_and_dec()
+ {
+ bool res= Item_func_trim::fix_length_and_dec();
+ maybe_null= true;
+ return res;
+ }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_trim_oracle>(thd, this); }
};
@@ -512,8 +608,30 @@ public:
String *val_str(String *);
const char *func_name() const { return "ltrim"; }
const char *mode_name() const { return "leading"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_ltrim>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_ltrim>(thd, this); }
+};
+
+
+class Item_func_ltrim_oracle :public Item_func_ltrim
+{
+protected:
+ String *make_empty_result()
+ { null_value= 1; return NULL; }
+ const char *func_name_ext() const { return "_oracle"; }
+public:
+ Item_func_ltrim_oracle(THD *thd, Item *a, Item *b):
+ Item_func_ltrim(thd, a, b) {}
+ Item_func_ltrim_oracle(THD *thd, Item *a): Item_func_ltrim(thd, a) {}
+ const char *func_name() const { return "ltrim_oracle"; }
+ bool fix_length_and_dec()
+ {
+ bool res= Item_func_ltrim::fix_length_and_dec();
+ maybe_null= true;
+ return res;
+ }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_ltrim_oracle>(thd, this); }
};
@@ -525,11 +643,32 @@ public:
String *val_str(String *);
const char *func_name() const { return "rtrim"; }
const char *mode_name() const { return "trailing"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_rtrim>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_rtrim>(thd, this); }
};
+class Item_func_rtrim_oracle :public Item_func_rtrim
+{
+protected:
+ String *make_empty_result()
+ { null_value= 1; return NULL; }
+ const char *func_name_ext() const { return "_oracle"; }
+public:
+ Item_func_rtrim_oracle(THD *thd, Item *a, Item *b):
+ Item_func_rtrim(thd, a, b) {}
+ Item_func_rtrim_oracle(THD *thd, Item *a): Item_func_rtrim(thd, a) {}
+ const char *func_name() const { return "rtrim_oracle"; }
+ bool fix_length_and_dec()
+ {
+ bool res= Item_func_rtrim::fix_length_and_dec();
+ maybe_null= true;
+ return res;
+ }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_rtrim_oracle>(thd, this); }
+};
+
/*
Item_func_password -- new (4.1.1) PASSWORD() function implementation.
Returns strcat('*', octet2hex(sha1(sha1(password)))). '*' stands for new
@@ -565,8 +704,8 @@ public:
"password" : "old_password"); }
static char *alloc(THD *thd, const char *password, size_t pass_len,
enum PW_Alg al);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_password>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_password>(thd, this); }
};
@@ -588,8 +727,8 @@ public:
return FALSE;
}
const char *func_name() const { return "des_encrypt"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_des_encrypt>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_des_encrypt>(thd, this); }
};
class Item_func_des_decrypt :public Item_str_binary_checksum_func
@@ -611,8 +750,8 @@ public:
return FALSE;
}
const char *func_name() const { return "des_decrypt"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_des_decrypt>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_des_decrypt>(thd, this); }
};
@@ -647,8 +786,8 @@ public:
{
return FALSE;
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_encrypt>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_encrypt>(thd, this); }
};
#include "sql_crypt.h"
@@ -667,8 +806,8 @@ public:
String *val_str(String *);
bool fix_length_and_dec();
const char *func_name() const { return "encode"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_encode>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_encode>(thd, this); }
protected:
virtual void crypto_transform(String *);
private:
@@ -682,8 +821,8 @@ class Item_func_decode :public Item_func_encode
public:
Item_func_decode(THD *thd, Item *a, Item *seed_arg): Item_func_encode(thd, a, seed_arg) {}
const char *func_name() const { return "decode"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_decode>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_decode>(thd, this); }
protected:
void crypto_transform(String *);
};
@@ -723,8 +862,30 @@ public:
}
const char *func_name() const { return "database"; }
const char *fully_qualified_func_name() const { return "database()"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_database>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_database>(thd, this); }
+};
+
+
+class Item_func_sqlerrm :public Item_func_sysconst
+{
+public:
+ Item_func_sqlerrm(THD *thd): Item_func_sysconst(thd) {}
+ String *val_str(String *);
+ const char *func_name() const { return "SQLERRM"; }
+ const char *fully_qualified_func_name() const { return "SQLERRM"; }
+ void print(String *str, enum_query_type query_type)
+ {
+ str->append(func_name());
+ }
+ bool fix_length_and_dec()
+ {
+ max_length= 512 * system_charset_info->mbmaxlen;
+ null_value= maybe_null= false;
+ return FALSE;
+ }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_sqlerrm>(thd, this); }
};
@@ -756,8 +917,8 @@ public:
{
return save_str_value_in_field(field, &str_value);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_user>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_user>(thd, this); }
};
@@ -809,8 +970,8 @@ public:
return mark_unsupported_function(fully_qualified_func_name(), arg,
VCOL_SESSION_FUNC);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_current_role>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_current_role>(thd, this); }
};
@@ -822,8 +983,8 @@ public:
String *val_str(String *);
bool fix_length_and_dec();
const char *func_name() const { return "soundex"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_soundex>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_soundex>(thd, this); }
};
@@ -836,8 +997,8 @@ public:
String *val_str(String *str);
bool fix_length_and_dec();
const char *func_name() const { return "elt"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_elt>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_elt>(thd, this); }
};
@@ -850,26 +1011,25 @@ public:
String *val_str(String *str);
bool fix_length_and_dec();
const char *func_name() const { return "make_set"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_make_set>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_make_set>(thd, this); }
};
class Item_func_format :public Item_str_ascii_func
{
- MY_LOCALE *locale;
+ const MY_LOCALE *locale;
public:
Item_func_format(THD *thd, Item *org, Item *dec):
Item_str_ascii_func(thd, org, dec) {}
Item_func_format(THD *thd, Item *org, Item *dec, Item *lang):
Item_str_ascii_func(thd, org, dec, lang) {}
- MY_LOCALE *get_locale(Item *item);
String *val_str_ascii(String *);
bool fix_length_and_dec();
const char *func_name() const { return "format"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_format>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_format>(thd, this); }
};
@@ -881,7 +1041,11 @@ public:
Item_func_char(THD *thd, List<Item> &list, CHARSET_INFO *cs):
Item_str_func(thd, list)
{ collation.set(cs); }
+ Item_func_char(THD *thd, Item *arg1, CHARSET_INFO *cs):
+ Item_str_func(thd, arg1)
+ { collation.set(cs); }
String *val_str(String *);
+ void append_char(String * str, int32 num);
bool fix_length_and_dec()
{
max_length= arg_count * 4;
@@ -889,10 +1053,25 @@ public:
}
const char *func_name() const { return "char"; }
void print(String *str, enum_query_type query_type);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_char>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_char>(thd, this); }
};
+class Item_func_chr :public Item_func_char
+{
+public:
+ Item_func_chr(THD *thd, Item *arg1, CHARSET_INFO *cs):
+ Item_func_char(thd, arg1, cs) {}
+ String *val_str(String *);
+ bool fix_length_and_dec()
+ {
+ max_length= 4;
+ return FALSE;
+ }
+ const char *func_name() const { return "chr"; }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_chr>(thd, this); }
+};
class Item_func_repeat :public Item_str_func
{
@@ -903,8 +1082,8 @@ public:
String *val_str(String *);
bool fix_length_and_dec();
const char *func_name() const { return "repeat"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_repeat>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_repeat>(thd, this); }
};
@@ -915,8 +1094,8 @@ public:
String *val_str(String *);
bool fix_length_and_dec();
const char *func_name() const { return "space"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_space>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_space>(thd, this); }
};
@@ -932,8 +1111,8 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_binlog_gtid_pos>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_binlog_gtid_pos>(thd, this); }
};
@@ -944,6 +1123,8 @@ protected:
public:
Item_func_pad(THD *thd, Item *arg1, Item *arg2, Item *arg3):
Item_str_func(thd, arg1, arg2, arg3) {}
+ Item_func_pad(THD *thd, Item *arg1, Item *arg2):
+ Item_str_func(thd, arg1, arg2) {}
bool fix_length_and_dec();
};
@@ -953,11 +1134,34 @@ class Item_func_rpad :public Item_func_pad
public:
Item_func_rpad(THD *thd, Item *arg1, Item *arg2, Item *arg3):
Item_func_pad(thd, arg1, arg2, arg3) {}
+ Item_func_rpad(THD *thd, Item *arg1, Item *arg2):
+ Item_func_pad(thd, arg1, arg2) {}
String *val_str(String *);
const char *func_name() const { return "rpad"; }
Sql_mode_dependency value_depends_on_sql_mode() const;
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_rpad>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_rpad>(thd, this); }
+};
+
+
+class Item_func_rpad_oracle :public Item_func_rpad
+{
+ String *make_empty_result()
+ { null_value= 1; return NULL; }
+public:
+ Item_func_rpad_oracle(THD *thd, Item *arg1, Item *arg2, Item *arg3):
+ Item_func_rpad(thd, arg1, arg2, arg3) {}
+ Item_func_rpad_oracle(THD *thd, Item *arg1, Item *arg2):
+ Item_func_rpad(thd, arg1, arg2) {}
+ bool fix_length_and_dec()
+ {
+ bool res= Item_func_rpad::fix_length_and_dec();
+ maybe_null= true;
+ return res;
+ }
+ const char *func_name() const { return "rpad_oracle"; }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_rpad_oracle>(thd, this); }
};
@@ -966,10 +1170,33 @@ class Item_func_lpad :public Item_func_pad
public:
Item_func_lpad(THD *thd, Item *arg1, Item *arg2, Item *arg3):
Item_func_pad(thd, arg1, arg2, arg3) {}
+ Item_func_lpad(THD *thd, Item *arg1, Item *arg2):
+ Item_func_pad(thd, arg1, arg2) {}
String *val_str(String *);
const char *func_name() const { return "lpad"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_lpad>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_lpad>(thd, this); }
+};
+
+
+class Item_func_lpad_oracle :public Item_func_lpad
+{
+ String *make_empty_result()
+ { null_value= 1; return NULL; }
+public:
+ Item_func_lpad_oracle(THD *thd, Item *arg1, Item *arg2, Item *arg3):
+ Item_func_lpad(thd, arg1, arg2, arg3) {}
+ Item_func_lpad_oracle(THD *thd, Item *arg1, Item *arg2):
+ Item_func_lpad(thd, arg1, arg2) {}
+ bool fix_length_and_dec()
+ {
+ bool res= Item_func_lpad::fix_length_and_dec();
+ maybe_null= true;
+ return res;
+ }
+ const char *func_name() const { return "lpad_oracle"; }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_lpad_oracle>(thd, this); }
};
@@ -987,28 +1214,44 @@ public:
maybe_null= 1;
return FALSE;
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_conv>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_conv>(thd, this); }
};
class Item_func_hex :public Item_str_ascii_checksum_func
{
+protected:
String tmp_value;
+ /*
+ Calling arg[0]->type_handler() can be expensive on every row.
+ It's a virtual method, and in case if args[0] is a complex Item,
+ its type_handler() can call more virtual methods.
+ So let's cache it during fix_length_and_dec().
+ */
+ const Type_handler *m_arg0_type_handler;
public:
Item_func_hex(THD *thd, Item *a):
- Item_str_ascii_checksum_func(thd, a) {}
+ Item_str_ascii_checksum_func(thd, a), m_arg0_type_handler(NULL) {}
const char *func_name() const { return "hex"; }
- String *val_str_ascii(String *);
+ String *val_str_ascii_from_val_int(String *str);
+ String *val_str_ascii_from_val_real(String *str);
+ String *val_str_ascii_from_val_str(String *str);
+ String *val_str_ascii(String *str)
+ {
+ DBUG_ASSERT(fixed);
+ return m_arg0_type_handler->Item_func_hex_val_str_ascii(this, str);
+ }
bool fix_length_and_dec()
{
- collation.set(default_charset());
+ collation.set(default_charset(), DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
decimals=0;
fix_char_length(args[0]->max_length * 2);
+ m_arg0_type_handler= args[0]->type_handler();
return FALSE;
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_hex>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_hex>(thd, this); }
};
class Item_func_unhex :public Item_str_func
@@ -1029,8 +1272,8 @@ public:
max_length=(1+args[0]->max_length)/2;
return FALSE;
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_unhex>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_unhex>(thd, this); }
};
@@ -1062,8 +1305,8 @@ public:
Item_func_like_range_min(THD *thd, Item *a, Item *b):
Item_func_like_range(thd, a, b, true) { }
const char *func_name() const { return "like_range_min"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_like_range_min>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_like_range_min>(thd, this); }
};
@@ -1073,8 +1316,8 @@ public:
Item_func_like_range_max(THD *thd, Item *a, Item *b):
Item_func_like_range(thd, a, b, false) { }
const char *func_name() const { return "like_range_max"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_like_range_max>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_like_range_max>(thd, this); }
};
#endif
@@ -1101,8 +1344,8 @@ public:
void print(String *str, enum_query_type query_type);
const char *func_name() const { return "cast_as_binary"; }
bool need_parentheses_in_default() { return true; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_binary>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_binary>(thd, this); }
};
@@ -1124,8 +1367,8 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_load_file>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_load_file>(thd, this); }
};
@@ -1141,8 +1384,8 @@ class Item_func_export_set: public Item_str_func
String *val_str(String *str);
bool fix_length_and_dec();
const char *func_name() const { return "export_set"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_export_set>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_export_set>(thd, this); }
};
@@ -1161,8 +1404,8 @@ public:
max_length= (uint32) MY_MIN(max_result_length, MAX_BLOB_WIDTH);
return FALSE;
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_quote>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_quote>(thd, this); }
};
class Item_func_conv_charset :public Item_str_func
@@ -1246,15 +1489,16 @@ public:
bool fix_length_and_dec();
const char *func_name() const { return "convert"; }
void print(String *str, enum_query_type query_type);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_conv_charset>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_conv_charset>(thd, this); }
};
class Item_func_set_collation :public Item_str_func
{
+ CHARSET_INFO *m_set_collation;
public:
- Item_func_set_collation(THD *thd, Item *a, Item *b):
- Item_str_func(thd, a, b) {}
+ Item_func_set_collation(THD *thd, Item *a, CHARSET_INFO *set_collation):
+ Item_str_func(thd, a), m_set_collation(set_collation) {}
String *val_str(String *);
bool fix_length_and_dec();
bool eq(const Item *item, bool binary_cmp) const;
@@ -1268,8 +1512,8 @@ public:
return args[0]->field_for_view_update();
}
bool need_parentheses_in_default() { return true; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_set_collation>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_set_collation>(thd, this); }
};
@@ -1298,8 +1542,8 @@ public:
:Item_func_expr_str_metadata(thd, a) { }
String *val_str(String *);
const char *func_name() const { return "charset"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_charset>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_charset>(thd, this); }
};
@@ -1310,8 +1554,8 @@ public:
:Item_func_expr_str_metadata(thd, a) {}
String *val_str(String *);
const char *func_name() const { return "collation"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_collation>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_collation>(thd, this); }
};
@@ -1345,33 +1589,36 @@ public:
Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond)
{ return this; }
void print(String *str, enum_query_type query_type);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_weight_string>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_weight_string>(thd, this); }
};
-class Item_func_crc32 :public Item_int_func
+class Item_func_crc32 :public Item_long_func
{
+ bool check_arguments() const
+ { return args[0]->check_type_can_return_str(func_name()); }
String value;
public:
- Item_func_crc32(THD *thd, Item *a): Item_int_func(thd, a)
+ Item_func_crc32(THD *thd, Item *a): Item_long_func(thd, a)
{ unsigned_flag= 1; }
const char *func_name() const { return "crc32"; }
bool fix_length_and_dec() { max_length=10; return FALSE; }
longlong val_int();
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_crc32>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_crc32>(thd, this); }
};
-class Item_func_uncompressed_length : public Item_int_func
+class Item_func_uncompressed_length : public Item_long_func_length
{
String value;
public:
- Item_func_uncompressed_length(THD *thd, Item *a): Item_int_func(thd, a) {}
+ Item_func_uncompressed_length(THD *thd, Item *a)
+ :Item_long_func_length(thd, a) {}
const char *func_name() const{return "uncompressed_length";}
bool fix_length_and_dec() { max_length=10; maybe_null= true; return FALSE; }
longlong val_int();
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_uncompressed_length>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_uncompressed_length>(thd, this); }
};
#ifdef HAVE_COMPRESS
@@ -1393,8 +1640,8 @@ public:
}
const char *func_name() const{return "compress";}
String *val_str(String *) ZLIB_DEPENDED_FUNCTION
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_compress>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_compress>(thd, this); }
};
class Item_func_uncompress: public Item_str_binary_checksum_func
@@ -1410,8 +1657,8 @@ public:
}
const char *func_name() const{return "uncompress";}
String *val_str(String *) ZLIB_DEPENDED_FUNCTION
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_uncompress>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_uncompress>(thd, this); }
};
@@ -1434,8 +1681,8 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_NON_DETERMINISTIC);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_uuid>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_uuid>(thd, this); }
};
@@ -1457,8 +1704,8 @@ public:
String *val_str(String *);
void print(String *str, enum_query_type query_type);
enum Functype functype() const { return DYNCOL_FUNC; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_dyncol_create>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_dyncol_create>(thd, this); }
};
@@ -1471,8 +1718,8 @@ public:
const char *func_name() const{ return "column_add"; }
String *val_str(String *);
void print(String *str, enum_query_type query_type);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_dyncol_add>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_dyncol_add>(thd, this); }
};
class Item_func_dyncol_json: public Item_str_func
@@ -1489,8 +1736,8 @@ public:
decimals= 0;
return FALSE;
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_dyncol_json>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_dyncol_json>(thd, this); }
};
/*
@@ -1531,8 +1778,8 @@ public:
bool get_dyn_value(THD *thd, DYNAMIC_COLUMN_VALUE *val, String *tmp);
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
void print(String *str, enum_query_type query_type);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_dyncol_get>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_dyncol_get>(thd, this); }
};
@@ -1545,9 +1792,29 @@ public:
{ maybe_null= 1; max_length= MAX_BLOB_WIDTH; return FALSE; };
const char *func_name() const{ return "column_list"; }
String *val_str(String *);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_dyncol_list>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_dyncol_list>(thd, this); }
};
-#endif /* ITEM_STRFUNC_INCLUDED */
+/*
+ this is used by JOIN_TAB::keep_current_rowid
+ and stores handler::position().
+ It has nothing to do with _rowid pseudo-column, that the parser supports.
+*/
+class Item_temptable_rowid :public Item_str_func
+{
+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); }
+ String *val_str(String *str);
+ enum Functype functype() const { return TEMPTABLE_ROWID; }
+ const char *func_name() const { return "<rowid>"; }
+ bool fix_length_and_dec();
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_temptable_rowid>(thd, this); }
+};
+#endif /* ITEM_STRFUNC_INCLUDED */
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 0499a677be9..c32cd5ef84a 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -29,7 +29,7 @@
#pragma implementation // gcc: Class implementation
#endif
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
/*
It is necessary to include set_var.h instead of item.h because there
@@ -45,7 +45,7 @@
double get_post_group_estimate(JOIN* join, double join_op_rows);
-const char *exists_outer_expr_name= "<exists outer expr>";
+LEX_CSTRING exists_outer_expr_name= { STRING_WITH_LEN("<exists outer expr>") };
int check_and_do_in_subquery_rewrites(JOIN *join);
@@ -64,7 +64,6 @@ Item_subselect::Item_subselect(THD *thd_arg):
#ifndef DBUG_OFF
exec_counter= 0;
#endif
- with_subselect= 1;
reset();
/*
Item value is NULL if select_result_interceptor didn't change this value
@@ -116,9 +115,10 @@ void Item_subselect::init(st_select_lex *select_lex,
do not take into account expression inside aggregate functions because
they can access original table fields
*/
- parsing_place= (outer_select->in_sum_expr ? NO_MATTER
- : outer_select->parsing_place);
- if (unit->is_union())
+ parsing_place= (outer_select->in_sum_expr ?
+ NO_MATTER :
+ outer_select->parsing_place);
+ if (unit->is_unit_op() && unit->first_select()->next_select())
engine= new subselect_union_engine(unit, result, this);
else
engine= new subselect_single_select_engine(select_lex, result, this);
@@ -156,7 +156,7 @@ void Item_subselect::cleanup()
reset();
filesort_buffer.free_sort_buffer();
my_free(sortbuffer.str);
- sortbuffer= null_lex_str;
+ sortbuffer.str= 0;
value_assigned= 0;
expr_cache= 0;
@@ -265,6 +265,13 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref)
if (check_stack_overrun(thd, STACK_MIN_SIZE, (uchar*)&res))
return TRUE;
+ for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select())
+ {
+ if (sl->tvc)
+ {
+ wrap_tvc_into_select(thd, sl);
+ }
+ }
if (!(res= engine->prepare(thd)))
{
@@ -291,13 +298,11 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref)
(*ref)= substitution;
substitution->name= name;
- substitution->name_length= name_length;
if (have_to_be_excluded)
engine->exclude();
substitution= 0;
thd->where= "checking transformed subquery";
- if (!(*ref)->fixed)
- res= (*ref)->fix_fields(thd, ref);
+ res= (*ref)->fix_fields_if_needed(thd, ref);
goto end;
}
@@ -705,11 +710,20 @@ bool Item_subselect::exec()
DBUG_ENTER("Item_subselect::exec");
DBUG_ASSERT(fixed);
+ DBUG_EXECUTE_IF("Item_subselect",
+ Item::Print print(this,
+ enum_query_type(QT_TO_SYSTEM_CHARSET |
+ QT_WITHOUT_INTRODUCERS));
+
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_UNKNOWN_ERROR, "DBUG: Item_subselect::exec %.*s",
+ print.length(),print.ptr());
+ );
/*
Do not execute subselect in case of a fatal error
or if the query has been killed.
*/
- if (thd->is_error() || thd->killed)
+ if (unlikely(thd->is_error() || thd->killed))
DBUG_RETURN(true);
DBUG_ASSERT(!thd->lex->context_analysis_only);
@@ -959,7 +973,7 @@ void Item_subselect::print(String *str, enum_query_type query_type)
{
if (query_type & QT_ITEM_SUBSELECT_ID_ONLY)
{
- str->append("(subquery#");
+ str->append(STRING_WITH_LEN("(subquery#"));
if (unit && unit->first_select())
{
char buf[64];
@@ -1074,7 +1088,7 @@ void Item_maxmin_subselect::no_rows_in_result()
*/
if (parsing_place != SELECT_LIST || const_item())
return;
- value= Item_cache::get_cache(thd, new (thd->mem_root) Item_null(thd));
+ value= (new (thd->mem_root) Item_null(thd))->get_cache(thd);
null_value= 0;
was_values= 0;
make_const();
@@ -1092,7 +1106,7 @@ void Item_singlerow_subselect::no_rows_in_result()
*/
if (parsing_place != SELECT_LIST || const_item())
return;
- value= Item_cache::get_cache(thd, new (thd->mem_root) Item_null(thd));
+ value= (new (thd->mem_root) Item_null(thd))->get_cache(thd);
reset();
make_const();
}
@@ -1134,7 +1148,7 @@ Item_singlerow_subselect::select_transformer(JOIN *join)
SELECT_LEX *select_lex= join->select_lex;
Query_arena *arena= thd->stmt_arena;
- if (!select_lex->master_unit()->is_union() &&
+ if (!select_lex->master_unit()->is_unit_op() &&
!select_lex->table_list.elements &&
select_lex->item_list.elements == 1 &&
!select_lex->item_list.head()->with_sum_func &&
@@ -1182,23 +1196,9 @@ void Item_singlerow_subselect::store(uint i, Item *item)
row[i]->cache_value();
}
-enum Item_result Item_singlerow_subselect::result_type() const
-{
- return engine->type();
-}
-
-enum Item_result Item_singlerow_subselect::cmp_type() const
-{
- return engine->cmptype();
-}
-
-/*
- Don't rely on the result type to calculate field type.
- Ask the engine instead.
-*/
-enum_field_types Item_singlerow_subselect::field_type() const
+const Type_handler *Item_singlerow_subselect::type_handler() const
{
- return engine->field_type();
+ return engine->type_handler();
}
bool Item_singlerow_subselect::fix_length_and_dec()
@@ -1271,7 +1271,7 @@ Item* Item_singlerow_subselect::expr_cache_insert_transformer(THD *tmp_thd,
}
-uint Item_singlerow_subselect::cols()
+uint Item_singlerow_subselect::cols() const
{
return engine->cols();
}
@@ -1434,14 +1434,14 @@ void Item_exists_subselect::print(String *str, enum_query_type query_type)
bool Item_in_subselect::test_limit(st_select_lex_unit *unit_arg)
{
- if (unit_arg->fake_select_lex &&
- unit_arg->fake_select_lex->test_limit())
+ if (unlikely(unit_arg->fake_select_lex &&
+ unit_arg->fake_select_lex->test_limit()))
return(1);
SELECT_LEX *sl= unit_arg->first_select();
for (; sl; sl= sl->next_select())
{
- if (sl->test_limit())
+ if (unlikely(sl->test_limit()))
return(1);
}
return(0);
@@ -1847,7 +1847,7 @@ Item_in_subselect::single_value_transformer(JOIN *join)
if (!(join_having || select_lex->with_sum_func ||
select_lex->group_list.elements) &&
select_lex->table_list.elements == 0 && !join->conds &&
- !select_lex->master_unit()->is_union())
+ !select_lex->master_unit()->is_unit_op())
{
Item *where_item= (Item*) select_lex->item_list.head();
/*
@@ -1907,8 +1907,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(),
- (char *)"<no matter>",
- (char *)in_left_expr_name);
+ "<no matter>",
+ &in_left_expr_name);
}
DBUG_RETURN(false);
@@ -1966,7 +1966,7 @@ bool Item_allany_subselect::transform_into_max_min(JOIN *join)
(!select_lex->ref_pointer_array[0]->maybe_null || /*4*/
substype() != Item_subselect::ALL_SUBS)) /*4*/
{
- Item_sum_hybrid *item;
+ Item_sum_min_max *item;
nesting_map save_allow_sum_func;
if (func->l_op())
{
@@ -1999,8 +1999,7 @@ bool Item_allany_subselect::transform_into_max_min(JOIN *join)
print_where(item, "rewrite with MIN/MAX", QT_ORDINARY););
save_allow_sum_func= thd->lex->allow_sum_func;
- thd->lex->allow_sum_func|=
- (nesting_map)1 << thd->lex->current_select->nest_level;
+ thd->lex->allow_sum_func.set_bit(thd->lex->current_select->nest_level);
/*
Item_sum_(max|min) can't substitute other item => we can use 0 as
reference, also Item_sum_(max|min) can't be fixed after creation, so
@@ -2131,6 +2130,8 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN *join,
if (join_having || select_lex->with_sum_func ||
select_lex->group_list.elements)
{
+ const char *tmp= this->full_name();
+ LEX_CSTRING field_name= {tmp, safe_strlen(tmp)};
Item *item= func->create(thd, expr,
new (thd->mem_root) Item_ref_null_helper(
thd,
@@ -2139,7 +2140,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN *join,
&select_lex->
ref_pointer_array[0],
(char *)"<ref>",
- this->full_name()));
+ &field_name));
if (!abort_on_null && left_expr->maybe_null)
{
/*
@@ -2151,7 +2152,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN *join,
}
if (!join_having)
- item->name= (char*) in_having_cond;
+ item->name= in_having_cond;
if (fix_having(item, select_lex))
DBUG_RETURN(true);
*having_item= item;
@@ -2182,7 +2183,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN *join,
get_cond_guard(0))))
DBUG_RETURN(true);
}
- having->name= (char*) in_having_cond;
+ having->name= in_having_cond;
if (fix_having(having, select_lex))
DBUG_RETURN(true);
*having_item= having;
@@ -2207,15 +2208,16 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN *join,
single_value_transformer but there is no corresponding action in
row_value_transformer?
*/
- item->name= (char *) in_additional_cond;
- if (!item->fixed && item->fix_fields(thd, 0))
+ item->name= in_additional_cond;
+ if (item->fix_fields_if_needed(thd, 0))
DBUG_RETURN(true);
*where_item= item;
}
else
{
- if (select_lex->master_unit()->is_union())
+ if (select_lex->master_unit()->is_unit_op())
{
+ LEX_CSTRING field_name= {STRING_WITH_LEN("<result>") };
Item *new_having=
func->create(thd, expr,
new (thd->mem_root) Item_ref_null_helper(thd,
@@ -2223,7 +2225,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN *join,
this,
&select_lex->ref_pointer_array[0],
(char *)"<no matter>",
- (char *)"<result>"));
+ &field_name));
if (!abort_on_null && left_expr->maybe_null)
{
disable_cond_guard_for_const_null_left_expr(0);
@@ -2232,7 +2234,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN *join,
DBUG_RETURN(true);
}
- new_having->name= (char*) in_having_cond;
+ new_having->name= in_having_cond;
if (fix_having(new_having, select_lex))
DBUG_RETURN(true);
*having_item= new_having;
@@ -2379,7 +2381,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
bool is_having_used= (join_having || select_lex->with_sum_func ||
select_lex->group_list.first ||
!select_lex->table_list.elements);
-
+ LEX_CSTRING list_ref= { STRING_WITH_LEN("<list ref>")};
DBUG_ENTER("Item_in_subselect::create_row_in_to_exists_cond");
DBUG_ASSERT(thd == join->thd);
@@ -2401,6 +2403,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
if (select_lex->ref_pointer_array[i]->
check_cols(left_expr->element_index(i)->cols()))
DBUG_RETURN(true);
+
Item *item_eq=
new (thd->mem_root)
Item_func_eq(thd, new (thd->mem_root)
@@ -2408,12 +2411,12 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
(*optimizer->get_cache())->
addr(i),
(char *)"<no matter>",
- (char *)in_left_expr_name),
+ &in_left_expr_name),
new (thd->mem_root)
Item_ref(thd, &select_lex->context,
&select_lex->ref_pointer_array[i],
(char *)"<no matter>",
- (char *)"<list ref>"));
+ &list_ref));
Item *item_isnull=
new (thd->mem_root)
Item_func_isnull(thd,
@@ -2421,7 +2424,7 @@ 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>",
- (char *)"<list ref>"));
+ &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 &&
@@ -2442,7 +2445,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
&select_lex->
ref_pointer_array[i],
(char *)"<no matter>",
- (char *)"<list ref>"));
+ &list_ref));
if (!abort_on_null && left_expr->element_index(i)->maybe_null &&
get_cond_guard(i) )
{
@@ -2477,13 +2480,13 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
(*optimizer->get_cache())->
addr(i),
(char *)"<no matter>",
- (char *)in_left_expr_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>",
- (char *)"<list ref>"));
+ &list_ref));
if (!abort_on_null && select_lex->ref_pointer_array[i]->maybe_null)
{
Item *having_col_item=
@@ -2493,8 +2496,7 @@ 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>",
- (char *)"<list ref>"));
-
+ &list_ref));
item_isnull= new (thd->mem_root)
Item_func_isnull(thd,
@@ -2503,7 +2505,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
&select_lex->
ref_pointer_array[i],
(char *)"<no matter>",
- (char *)"<list ref>"));
+ &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))
{
@@ -2530,7 +2532,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
if (*where_item)
{
- if (!(*where_item)->fixed && (*where_item)->fix_fields(thd, 0))
+ if ((*where_item)->fix_fields_if_needed(thd, 0))
DBUG_RETURN(true);
(*where_item)->top_level_item();
}
@@ -2538,7 +2540,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
if (*having_item)
{
if (!join_having)
- (*having_item)->name= (char*) in_having_cond;
+ (*having_item)->name= in_having_cond;
if (fix_having(*having_item, select_lex))
DBUG_RETURN(true);
(*having_item)->top_level_item();
@@ -2677,7 +2679,7 @@ bool Item_in_subselect::inject_in_to_exists_cond(JOIN *join_arg)
}
where_item= and_items(thd, join_arg->conds, where_item);
- if (!where_item->fixed && where_item->fix_fields(thd, 0))
+ if (where_item->fix_fields_if_needed(thd, 0))
DBUG_RETURN(true);
// TIMOUR TODO: call optimize_cond() for the new where clause
thd->change_item_tree(&select_lex->where, where_item);
@@ -2774,7 +2776,7 @@ static bool check_equality_for_exist2in(Item_func *func,
if (args[0]->real_type() == Item::FIELD_ITEM &&
args[0]->all_used_tables() != OUTER_REF_TABLE_BIT &&
args[1]->all_used_tables() == OUTER_REF_TABLE_BIT &&
- (allow_subselect || !args[1]->has_subquery()))
+ (allow_subselect || !args[1]->with_subquery()))
{
/* It is Item_field or Item_direct_view_ref) */
DBUG_ASSERT(args[0]->type() == Item::FIELD_ITEM ||
@@ -2786,7 +2788,7 @@ static bool check_equality_for_exist2in(Item_func *func,
else if (args[1]->real_type() == Item::FIELD_ITEM &&
args[1]->all_used_tables() != OUTER_REF_TABLE_BIT &&
args[0]->all_used_tables() == OUTER_REF_TABLE_BIT &&
- (allow_subselect || !args[0]->has_subquery()))
+ (allow_subselect || !args[0]->with_subquery()))
{
/* It is Item_field or Item_direct_view_ref) */
DBUG_ASSERT(args[1]->type() == Item::FIELD_ITEM ||
@@ -3051,7 +3053,7 @@ bool Item_exists_subselect::exists2in_processor(void *opt_arg)
Item_direct_ref(thd, &first_select->context,
(Item**)optimizer->get_cache(),
(char *)"<no matter>",
- (char *)in_left_expr_name);
+ &in_left_expr_name);
if (in_subs->fix_fields(thd, optimizer->arguments() + 1))
{
res= TRUE;
@@ -3122,7 +3124,7 @@ bool Item_exists_subselect::exists2in_processor(void *opt_arg)
&unit->outer_select()->context,
optimizer->arguments(),
(char *)"<no matter>",
- (char *)exists_outer_expr_name)),
+ &exists_outer_expr_name)),
optimizer) :
(Item *)optimizer);
}
@@ -3144,9 +3146,9 @@ bool Item_exists_subselect::exists2in_processor(void *opt_arg)
new (thd->mem_root)
Item_direct_ref(thd,
&unit->outer_select()->context,
- optimizer->arguments()[0]->addr(i),
+ optimizer->arguments()[0]->addr((int)i),
(char *)"<no matter>",
- (char *)exists_outer_expr_name)),
+ &exists_outer_expr_name)),
thd->mem_root);
}
}
@@ -3159,7 +3161,7 @@ bool Item_exists_subselect::exists2in_processor(void *opt_arg)
exp= optimizer;
}
upper_not->arguments()[0]= exp;
- if (!exp->fixed && exp->fix_fields(thd, upper_not->arguments()))
+ if (exp->fix_fields_if_needed(thd, upper_not->arguments()))
{
res= TRUE;
goto out;
@@ -3335,7 +3337,7 @@ bool Item_in_subselect::fix_fields(THD *thd_arg, Item **ref)
{
outer_cols_num= left_expr->cols();
- if (unit->is_union())
+ if (unit->is_unit_op())
inner_cols= &(unit->types);
else
inner_cols= &(unit->first_select()->item_list);
@@ -3357,8 +3359,7 @@ bool Item_in_subselect::fix_fields(THD *thd_arg, Item **ref)
}
}
- if (left_expr && !left_expr->fixed &&
- left_expr->fix_fields(thd_arg, &left_expr))
+ if (left_expr && left_expr->fix_fields_if_needed(thd_arg, &left_expr))
goto err;
else
if (Item_subselect::fix_fields(thd_arg, ref))
@@ -3717,7 +3718,7 @@ int subselect_single_select_engine::prepare(THD *thd)
int subselect_union_engine::prepare(THD *thd_arg)
{
set_thd(thd_arg);
- return unit->prepare(thd, result, SELECT_NO_UNLOCK);
+ return unit->prepare(unit->derived, result, SELECT_NO_UNLOCK);
}
int subselect_uniquesubquery_engine::prepare(THD *)
@@ -3757,24 +3758,21 @@ bool subselect_engine::set_row(List<Item> &item_list, Item_cache **row)
{
Item *sel_item;
List_iterator_fast<Item> li(item_list);
- cmp_type= res_type= STRING_RESULT;
- res_field_type= MYSQL_TYPE_VAR_STRING;
+ set_handler(&type_handler_varchar);
for (uint i= 0; (sel_item= li++); i++)
{
item->max_length= sel_item->max_length;
- res_type= sel_item->result_type();
- cmp_type= sel_item->cmp_type();
- res_field_type= sel_item->field_type();
+ set_handler(sel_item->type_handler());
item->decimals= sel_item->decimals;
item->unsigned_flag= sel_item->unsigned_flag;
maybe_null= sel_item->maybe_null;
- if (!(row[i]= Item_cache::get_cache(thd, sel_item, sel_item->cmp_type())))
+ if (!(row[i]= sel_item->get_cache(thd)))
return TRUE;
row[i]->setup(thd, sel_item);
//psergey-backport-timours: row[i]->store(sel_item);
}
if (item_list.elements > 1)
- cmp_type= res_type= ROW_RESULT;
+ set_handler(&type_handler_row);
return FALSE;
}
@@ -3902,10 +3900,9 @@ int subselect_single_select_engine::exec()
{
/* Change the access method to full table scan */
tab->save_read_first_record= tab->read_first_record;
- tab->save_read_record= tab->read_record.read_record;
- tab->read_record.read_record= rr_sequential;
+ tab->save_read_record= tab->read_record.read_record_func;
+ tab->read_record.read_record_func= rr_sequential;
tab->read_first_record= read_first_record_seq;
- tab->read_record.record= tab->table->record[0];
tab->read_record.thd= join->thd;
tab->read_record.ref_length= tab->table->file->ref_length;
tab->read_record.unlock_row= rr_unlock_row;
@@ -3923,10 +3920,9 @@ int subselect_single_select_engine::exec()
for (JOIN_TAB **ptab= changed_tabs; ptab != last_changed_tab; ptab++)
{
JOIN_TAB *tab= *ptab;
- tab->read_record.record= 0;
tab->read_record.ref_length= 0;
- tab->read_first_record= tab->save_read_first_record;
- tab->read_record.read_record= tab->save_read_record;
+ tab->read_first_record= tab->save_read_first_record;
+ tab->read_record.read_record_func= tab->save_read_record;
}
executed= 1;
if (!(uncacheable() & ~UNCACHEABLE_EXPLAIN) &&
@@ -3988,12 +3984,8 @@ int subselect_uniquesubquery_engine::scan_table()
for (;;)
{
error=table->file->ha_rnd_next(table->record[0]);
- if (error) {
- if (error == HA_ERR_RECORD_DELETED)
- {
- error= 0;
- continue;
- }
+ if (unlikely(error))
+ {
if (error == HA_ERR_END_OF_FILE)
{
error= 0;
@@ -4129,8 +4121,8 @@ int subselect_uniquesubquery_engine::exec()
make_prev_keypart_map(tab->
ref.key_parts),
HA_READ_KEY_EXACT);
- if (error &&
- error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
+ if (unlikely(error &&
+ error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE))
error= report_error(table, error);
else
{
@@ -4168,7 +4160,8 @@ int subselect_uniquesubquery_engine::index_lookup()
HA_READ_KEY_EXACT);
DBUG_PRINT("info", ("lookup result: %i", error));
- if (error && error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
+ if (unlikely(error && error != HA_ERR_KEY_NOT_FOUND &&
+ error != HA_ERR_END_OF_FILE))
{
/*
TIMOUR: I don't understand at all when do we need to call report_error.
@@ -4299,8 +4292,8 @@ int subselect_indexsubquery_engine::exec()
make_prev_keypart_map(tab->
ref.key_parts),
HA_READ_KEY_EXACT);
- if (error &&
- error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
+ if (unlikely(error &&
+ error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE))
error= report_error(table, error);
else
{
@@ -4322,7 +4315,7 @@ int subselect_indexsubquery_engine::exec()
error= table->file->ha_index_next_same(table->record[0],
tab->ref.key_buff,
tab->ref.key_length);
- if (error && error != HA_ERR_END_OF_FILE)
+ if (unlikely(error && error != HA_ERR_END_OF_FILE))
{
error= report_error(table, error);
break;
@@ -4335,7 +4328,7 @@ int subselect_indexsubquery_engine::exec()
*tab->ref.null_ref_key= 1;
null_finding= 1;
/* Check if there exists a row with a null value in the index */
- if ((error= (safe_index_read(tab) == 1)))
+ if (unlikely((error= (safe_index_read(tab) == 1))))
break;
}
}
@@ -4344,7 +4337,7 @@ int subselect_indexsubquery_engine::exec()
}
-uint subselect_single_select_engine::cols()
+uint subselect_single_select_engine::cols() const
{
//psergey-sj-backport: the following assert was gone in 6.0:
//DBUG_ASSERT(select_lex->join != 0); // should be called after fix_fields()
@@ -4353,7 +4346,7 @@ uint subselect_single_select_engine::cols()
}
-uint subselect_union_engine::cols()
+uint subselect_union_engine::cols() const
{
DBUG_ASSERT(unit->is_prepared()); // should be called after fix_fields()
return unit->types.elements;
@@ -4437,7 +4430,6 @@ void subselect_union_engine::print(String *str, enum_query_type query_type)
void subselect_uniquesubquery_engine::print(String *str,
enum_query_type query_type)
{
- char *table_name= tab->table->s->table_name.str;
str->append(STRING_WITH_LEN("<primary_index_lookup>("));
tab->ref.items[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" in "));
@@ -4450,10 +4442,10 @@ void subselect_uniquesubquery_engine::print(String *str,
str->append(STRING_WITH_LEN("<temporary table>"));
}
else
- str->append(table_name, tab->table->s->table_name.length);
+ str->append(&tab->table->s->table_name);
KEY *key_info= tab->table->key_info+ tab->ref.key;
str->append(STRING_WITH_LEN(" on "));
- str->append(key_info->name);
+ str->append(&key_info->name);
if (cond)
{
str->append(STRING_WITH_LEN(" where "));
@@ -4474,9 +4466,9 @@ void subselect_uniquesubquery_engine::print(String *str)
for (uint i= 0; i < key_info->user_defined_key_parts; i++)
tab->ref.items[i]->print(str);
str->append(STRING_WITH_LEN(" in "));
- str->append(tab->table->s->table_name.str, tab->table->s->table_name.length);
+ str->append(&tab->table->s->table_name);
str->append(STRING_WITH_LEN(" on "));
- str->append(key_info->name);
+ str->append(&key_info->name);
if (cond)
{
str->append(STRING_WITH_LEN(" where "));
@@ -4495,7 +4487,7 @@ void subselect_indexsubquery_engine::print(String *str,
str->append(tab->table->s->table_name.str, tab->table->s->table_name.length);
KEY *key_info= tab->table->key_info+ tab->ref.key;
str->append(STRING_WITH_LEN(" on "));
- str->append(key_info->name);
+ str->append(&key_info->name);
if (check_null)
str->append(STRING_WITH_LEN(" checking NULL"));
if (cond)
@@ -4956,7 +4948,7 @@ my_bitmap_init_memroot(MY_BITMAP *map, uint n_bits, MEM_ROOT *mem_root)
bool subselect_hash_sj_engine::init(List<Item> *tmp_columns, uint subquery_id)
{
THD *thd= get_thd();
- select_union *result_sink;
+ select_unit *result_sink;
/* Options to create_tmp_table. */
ulonglong tmp_create_options= thd->variables.option_bits | TMP_TABLE_ALL_COLUMNS;
/* | TMP_TABLE_FORCE_MYISAM; TIMOUR: force MYISAM */
@@ -4994,11 +4986,10 @@ bool subselect_hash_sj_engine::init(List<Item> *tmp_columns, uint subquery_id)
DBUG_RETURN(TRUE);
char buf[32];
- uint len= my_snprintf(buf, sizeof(buf), "<subquery%d>", subquery_id);
- char *name;
- if (!(name= (char*)thd->alloc(len + 1)))
+ LEX_CSTRING name;
+ name.length= my_snprintf(buf, sizeof(buf), "<subquery%u>", subquery_id);
+ if (!(name.str= (char*) thd->memdup(buf, name.length + 1)))
DBUG_RETURN(TRUE);
- memcpy(name, buf, len+1);
result_sink->get_tmp_table_param()->materialized_subquery= true;
if (item->substype() == Item_subselect::IN_SUBS &&
@@ -5008,7 +4999,7 @@ bool subselect_hash_sj_engine::init(List<Item> *tmp_columns, uint subquery_id)
}
if (result_sink->create_result_table(thd, tmp_columns, TRUE,
tmp_create_options,
- name, TRUE, TRUE))
+ &name, TRUE, TRUE, FALSE, 0))
DBUG_RETURN(TRUE);
tmp_table= result_sink->table;
@@ -5056,8 +5047,8 @@ bool subselect_hash_sj_engine::init(List<Item> *tmp_columns, uint subquery_id)
Repeat name resolution for 'cond' since cond is not part of any
clause of the query, and it is not 'fixed' during JOIN::prepare.
*/
- if (semi_join_conds && !semi_join_conds->fixed &&
- semi_join_conds->fix_fields(thd, (Item**)&semi_join_conds))
+ if (semi_join_conds &&
+ semi_join_conds->fix_fields_if_needed(thd, (Item**)&semi_join_conds))
DBUG_RETURN(TRUE);
/* Let our engine reuse this query plan for materialization. */
materialize_join= materialize_engine->join;
@@ -5094,7 +5085,7 @@ bool subselect_hash_sj_engine::make_semi_join_conds()
/* Name resolution context for all tmp_table columns created below. */
Name_resolution_context *context;
Item_in_subselect *item_in= (Item_in_subselect *) item;
-
+ LEX_CSTRING table_name;
DBUG_ENTER("subselect_hash_sj_engine::make_semi_join_conds");
DBUG_ASSERT(semi_join_conds == NULL);
@@ -5104,10 +5095,9 @@ bool subselect_hash_sj_engine::make_semi_join_conds()
if (!(tmp_table_ref= (TABLE_LIST*) thd->alloc(sizeof(TABLE_LIST))))
DBUG_RETURN(TRUE);
- tmp_table_ref->init_one_table(STRING_WITH_LEN(""),
- tmp_table->alias.c_ptr(),
- tmp_table->alias.length(),
- NULL, TL_READ);
+ table_name.str= tmp_table->alias.c_ptr();
+ table_name.length= tmp_table->alias.length(),
+ tmp_table_ref->init_one_table(&empty_clex_str, &table_name, NULL, TL_READ);
tmp_table_ref->table= tmp_table;
context= new Name_resolution_context;
@@ -5481,8 +5471,8 @@ int subselect_hash_sj_engine::exec()
DBUG_ASSERT(materialize_join->optimization_state == JOIN::OPTIMIZATION_DONE &&
!is_materialized);
materialize_join->exec();
- if ((res= MY_TEST(materialize_join->error || thd->is_fatal_error ||
- thd->is_error())))
+ if (unlikely((res= MY_TEST(materialize_join->error || thd->is_fatal_error ||
+ thd->is_error()))))
goto err;
/*
@@ -5841,14 +5831,14 @@ Ordered_key::cmp_keys_by_row_data(ha_rows a, ha_rows b)
rowid_a= row_num_to_rowid + a * rowid_length;
rowid_b= row_num_to_rowid + b * rowid_length;
/* Fetch the rows for comparison. */
- if ((error= tbl->file->ha_rnd_pos(tbl->record[0], rowid_a)))
+ if (unlikely((error= tbl->file->ha_rnd_pos(tbl->record[0], rowid_a))))
{
/* purecov: begin inspected */
tbl->file->print_error(error, MYF(ME_FATALERROR)); // Sets fatal_error
return 0;
/* purecov: end */
}
- if ((error= tbl->file->ha_rnd_pos(tbl->record[1], rowid_b)))
+ if (unlikely((error= tbl->file->ha_rnd_pos(tbl->record[1], rowid_b))))
{
/* purecov: begin inspected */
tbl->file->print_error(error, MYF(ME_FATALERROR)); // Sets fatal_error
@@ -5934,7 +5924,7 @@ int Ordered_key::cmp_key_with_search_key(rownum_t row_num)
int __attribute__((unused)) error;
int cmp_res;
- if ((error= tbl->file->ha_rnd_pos(tbl->record[0], cur_rowid)))
+ if (unlikely((error= tbl->file->ha_rnd_pos(tbl->record[0], cur_rowid))))
{
/* purecov: begin inspected */
tbl->file->print_error(error, MYF(ME_FATALERROR)); // Sets fatal_error
@@ -6042,10 +6032,10 @@ void Ordered_key::print(String *str)
str->append(", (");
for (i= 0; i < key_column_count - 1; i++)
{
- str->append(key_columns[i]->field->field_name);
+ str->append(&key_columns[i]->field->field_name);
str->append(", ");
}
- str->append(key_columns[i]->field->field_name);
+ str->append(&key_columns[i]->field->field_name);
str->append("), ");
str->append("null_bitmap: (bits=");
@@ -6283,7 +6273,7 @@ subselect_rowid_merge_engine::init(MY_BITMAP *non_null_key_parts,
DBUG_ASSERT(cur_keyid == merge_keys_count);
/* Populate the indexes with data from the temporary table. */
- if (tmp_table->file->ha_rnd_init_with_error(1))
+ if (unlikely(tmp_table->file->ha_rnd_init_with_error(1)))
return TRUE;
tmp_table->file->extra_opt(HA_EXTRA_CACHE,
current_thd->variables.read_buff_size);
@@ -6291,17 +6281,12 @@ subselect_rowid_merge_engine::init(MY_BITMAP *non_null_key_parts,
while (TRUE)
{
error= tmp_table->file->ha_rnd_next(tmp_table->record[0]);
- if (error == HA_ERR_RECORD_DELETED)
- {
- /* We get this for duplicate records that should not be in tmp_table. */
- continue;
- }
/*
This is a temp table that we fully own, there should be no other
cause to stop the iteration than EOF.
*/
DBUG_ASSERT(!error || error == HA_ERR_END_OF_FILE);
- if (error == HA_ERR_END_OF_FILE)
+ if (unlikely(error == HA_ERR_END_OF_FILE))
{
DBUG_ASSERT(cur_rownum == tmp_table->file->stats.records);
break;
@@ -6522,7 +6507,7 @@ bool subselect_rowid_merge_engine::partial_match()
DBUG_ASSERT(!pq.elements);
/* All data accesses during execution are via handler::ha_rnd_pos() */
- if (tmp_table->file->ha_rnd_init_with_error(0))
+ if (unlikely(tmp_table->file->ha_rnd_init_with_error(0)))
{
res= FALSE;
goto end;
@@ -6728,7 +6713,7 @@ bool subselect_table_scan_engine::partial_match()
int error;
bool res;
- if (tmp_table->file->ha_rnd_init_with_error(1))
+ if (unlikely(tmp_table->file->ha_rnd_init_with_error(1)))
{
res= FALSE;
goto end;
@@ -6739,12 +6724,8 @@ bool subselect_table_scan_engine::partial_match()
for (;;)
{
error= tmp_table->file->ha_rnd_next(tmp_table->record[0]);
- if (error) {
- if (error == HA_ERR_RECORD_DELETED)
- {
- error= 0;
- continue;
- }
+ if (unlikely(error))
+ {
if (error == HA_ERR_END_OF_FILE)
{
error= 0;
diff --git a/sql/item_subselect.h b/sql/item_subselect.h
index 816073ed5d3..52480743855 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -186,6 +186,7 @@ public:
return null_value;
}
bool fix_fields(THD *thd, Item **ref);
+ bool with_subquery() const { DBUG_ASSERT(fixed); return true; }
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 recalc_used_tables(st_select_lex *new_parent, bool after_pullout);
@@ -267,9 +268,10 @@ public:
void register_as_with_rec_ref(With_element *with_elem);
void init_expr_cache_tracker(THD *thd);
- Item* build_clone(THD *thd, MEM_ROOT *mem_root) { return 0; }
- Item* get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; }
+ Item* build_clone(THD *thd) { return 0; }
+ Item* get_copy(THD *thd) { return 0; }
+ bool wrap_tvc_into_select(THD *thd, st_select_lex *tvc_sl);
friend class select_result_interceptor;
friend class Item_in_optimizer;
@@ -307,12 +309,10 @@ public:
my_decimal *val_decimal(my_decimal *);
bool val_bool();
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
- enum Item_result result_type() const;
- enum Item_result cmp_type() const;
- enum_field_types field_type() const;
+ const Type_handler *type_handler() const;
bool fix_length_and_dec();
- uint cols();
+ uint cols() const;
Item* element_index(uint i) { return reinterpret_cast<Item*>(row[i]); }
Item** addr(uint i) { return (Item**)row + i; }
bool check_cols(uint c);
@@ -399,13 +399,14 @@ public:
}
void no_rows_in_result();
- enum Item_result result_type() const { return INT_RESULT;}
- enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; }
+ const Type_handler *type_handler() const { return &type_handler_longlong; }
longlong val_int();
double val_real();
String *val_str(String*);
my_decimal *val_decimal(my_decimal *);
bool val_bool();
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ { return get_date_from_int(ltime, fuzzydate); }
bool fix_fields(THD *thd, Item **ref);
bool fix_length_and_dec();
void print(String *str, enum_query_type query_type);
@@ -777,15 +778,13 @@ public:
};
-class subselect_engine: public Sql_alloc
+class subselect_engine: public Sql_alloc,
+ public Type_handler_hybrid_field_type
{
protected:
select_result_interceptor *result; /* results storage class */
THD *thd; /* pointer to current THD */
Item_subselect *item; /* item, that use this engine */
- enum Item_result res_type; /* type of results */
- enum Item_result cmp_type; /* how to compare the results */
- enum_field_types res_field_type; /* column type of the results */
bool maybe_null; /* may be null (first item in select) */
public:
@@ -796,12 +795,11 @@ public:
subselect_engine(Item_subselect *si,
select_result_interceptor *res):
+ Type_handler_hybrid_field_type(&type_handler_varchar),
thd(NULL)
{
result= res;
item= si;
- cmp_type= res_type= STRING_RESULT;
- res_field_type= MYSQL_TYPE_VAR_STRING;
maybe_null= 0;
}
virtual ~subselect_engine() {}; // to satisfy compiler
@@ -836,11 +834,8 @@ public:
caller should call exec() again for the new engine.
*/
virtual int exec()= 0;
- virtual uint cols()= 0; /* return number of columns in select */
+ virtual uint cols() const= 0; /* return number of columns in select */
virtual uint8 uncacheable()= 0; /* query is uncacheable */
- enum Item_result type() { return res_type; }
- enum Item_result cmptype() { return cmp_type; }
- enum_field_types field_type() { return res_field_type; }
virtual void exclude()= 0;
virtual bool may_be_null() { return maybe_null; };
virtual table_map upper_select_const_tables()= 0;
@@ -876,7 +871,7 @@ public:
int prepare(THD *thd);
bool fix_length_and_dec(Item_cache** row);
int exec();
- uint cols();
+ uint cols() const;
uint8 uncacheable();
void exclude();
table_map upper_select_const_tables();
@@ -891,6 +886,7 @@ public:
virtual enum_engine_type engine_type() { return SINGLE_SELECT_ENGINE; }
int get_identifier();
void force_reexecution();
+ void change_select(st_select_lex *new_select) { select_lex= new_select; }
friend class subselect_hash_sj_engine;
friend class Item_in_subselect;
@@ -911,7 +907,7 @@ public:
int prepare(THD *);
bool fix_length_and_dec(Item_cache** row);
int exec();
- uint cols();
+ uint cols() const;
uint8 uncacheable();
void exclude();
table_map upper_select_const_tables();
@@ -969,7 +965,7 @@ public:
int prepare(THD *);
bool fix_length_and_dec(Item_cache** row);
int exec();
- uint cols() { return 1; }
+ uint cols() const { return 1; }
uint8 uncacheable() { return UNCACHEABLE_DEPENDENT_INJECTED; }
void exclude();
table_map upper_select_const_tables() { return 0; }
@@ -1107,7 +1103,7 @@ public:
int prepare(THD *);
int exec();
void print(String *str, enum_query_type query_type);
- uint cols() { return materialize_engine->cols(); }
+ uint cols() const { return materialize_engine->cols(); }
uint8 uncacheable() { return materialize_engine->uncacheable(); }
table_map upper_select_const_tables() { return 0; }
bool no_rows() { return !tmp_table->file->stats.records; }
@@ -1390,7 +1386,7 @@ public:
int prepare(THD *thd_arg) { set_thd(thd_arg); return 0; }
int exec();
bool fix_length_and_dec(Item_cache**) { return FALSE; }
- uint cols() { /* TODO: what is the correct value? */ return 1; }
+ uint cols() const { /* TODO: what is the correct value? */ return 1; }
uint8 uncacheable() { return UNCACHEABLE_DEPENDENT; }
void exclude() {}
table_map upper_select_const_tables() { return 0; }
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index e50822e71f2..b276387e745 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -26,10 +26,14 @@
#pragma implementation // gcc: Class implementation
#endif
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "sql_select.h"
#include "uniques.h"
+#include "sp_rcontext.h"
+#include "sp.h"
+#include "sql_parse.h"
+#include "sp_head.h"
/**
Calculate the affordable RAM limit for structures like TREE or Unique
@@ -68,14 +72,15 @@ size_t Item_sum::ram_limitation(THD *thd)
bool Item_sum::init_sum_func_check(THD *thd)
{
SELECT_LEX *curr_sel= thd->lex->current_select;
- if (curr_sel && !curr_sel->name_visibility_map)
+ if (curr_sel && curr_sel->name_visibility_map.is_clear_all())
{
for (SELECT_LEX *sl= curr_sel; sl; sl= sl->context.outer_select())
{
- curr_sel->name_visibility_map|= (1 << sl-> nest_level);
+ curr_sel->name_visibility_map.set_bit(sl->nest_level);
}
}
- if (!curr_sel || !(thd->lex->allow_sum_func & curr_sel->name_visibility_map))
+ if (!curr_sel ||
+ !(thd->lex->allow_sum_func.is_overlapping(curr_sel->name_visibility_map)))
{
my_message(ER_INVALID_GROUP_FUNC_USE, ER_THD(thd, ER_INVALID_GROUP_FUNC_USE),
MYF(0));
@@ -151,10 +156,11 @@ bool Item_sum::init_sum_func_check(THD *thd)
bool Item_sum::check_sum_func(THD *thd, Item **ref)
{
SELECT_LEX *curr_sel= thd->lex->current_select;
- nesting_map allow_sum_func= (thd->lex->allow_sum_func &
- curr_sel->name_visibility_map);
+ nesting_map allow_sum_func(thd->lex->allow_sum_func);
+ allow_sum_func.intersect(curr_sel->name_visibility_map);
bool invalid= FALSE;
- DBUG_ASSERT(curr_sel->name_visibility_map); // should be set already
+ // should be set already
+ DBUG_ASSERT(!curr_sel->name_visibility_map.is_clear_all());
/*
Window functions can not be used as arguments to sum functions.
@@ -185,10 +191,10 @@ bool Item_sum::check_sum_func(THD *thd, Item **ref)
If it is there under a construct where it is not allowed
we report an error.
*/
- invalid= !(allow_sum_func & ((nesting_map)1 << max_arg_level));
+ invalid= !(allow_sum_func.is_set(max_arg_level));
}
else if (max_arg_level >= 0 ||
- !(allow_sum_func & ((nesting_map)1 << nest_level)))
+ !(allow_sum_func.is_set(nest_level)))
{
/*
The set function can be aggregated only in outer subqueries.
@@ -198,7 +204,7 @@ bool Item_sum::check_sum_func(THD *thd, Item **ref)
if (register_sum_func(thd, ref))
return TRUE;
invalid= aggr_level < 0 &&
- !(allow_sum_func & ((nesting_map)1 << nest_level));
+ !(allow_sum_func.is_set(nest_level));
if (!invalid && thd->variables.sql_mode & MODE_ANSI)
invalid= aggr_level < 0 && max_arg_level < nest_level;
}
@@ -309,6 +315,8 @@ bool Item_sum::check_sum_func(THD *thd, Item **ref)
}
}
aggr_sel->set_agg_func_used(true);
+ if (sum_func() == SP_AGGREGATE_FUNC)
+ aggr_sel->set_custom_agg_func_used(true);
update_used_tables();
thd->lex->in_sum_func= in_sum_func;
return FALSE;
@@ -348,14 +356,14 @@ bool Item_sum::register_sum_func(THD *thd, Item **ref)
sl= sl->context.outer_select())
{
if (aggr_level < 0 &&
- (allow_sum_func & ((nesting_map)1 << sl->nest_level)))
+ (allow_sum_func.is_set(sl->nest_level)))
{
/* Found the most nested subquery where the function can be aggregated */
aggr_level= sl->nest_level;
aggr_sel= sl;
}
}
- if (sl && (allow_sum_func & ((nesting_map)1 << sl->nest_level)))
+ if (sl && (allow_sum_func.is_set(sl->nest_level)))
{
/*
We reached the subquery of level max_arg_level and checked
@@ -790,7 +798,7 @@ bool Aggregator_distinct::setup(THD *thd)
if (!(table= create_tmp_table(thd, tmp_table_param, list, (ORDER*) 0, 1,
0,
(select_lex->options | thd->variables.option_bits),
- HA_POS_ERROR, const_cast<char*>(""))))
+ HA_POS_ERROR, &empty_clex_str)))
return TRUE;
table->file->extra(HA_EXTRA_NO_ROWS); // Don't update rows
table->no_rows=1;
@@ -897,7 +905,8 @@ bool Aggregator_distinct::setup(THD *thd)
if (always_null)
DBUG_RETURN(FALSE);
- Field *field= arg->make_num_distinct_aggregator_field(thd->mem_root, arg);
+ Field *field= arg->type_handler()->
+ make_num_distinct_aggregator_field(thd->mem_root, arg);
if (!field || !(table= create_virtual_tmp_table(thd, field)))
DBUG_RETURN(TRUE);
@@ -992,7 +1001,7 @@ bool Aggregator_distinct::add()
*/
return tree->unique_add(table->record[0] + table->s->null_bytes);
}
- if ((error= table->file->ha_write_tmp_row(table->record[0])) &&
+ if (unlikely((error= table->file->ha_write_tmp_row(table->record[0]))) &&
table->file->is_fatal_error(error, HA_CHECK_DUP))
return TRUE;
return FALSE;
@@ -1085,19 +1094,6 @@ void Aggregator_distinct::endup()
String *
-Item_sum_num::val_str(String *str)
-{
- return val_string_from_real(str);
-}
-
-
-my_decimal *Item_sum_num::val_decimal(my_decimal *decimal_value)
-{
- return val_decimal_from_real(decimal_value);
-}
-
-
-String *
Item_sum_int::val_str(String *str)
{
return val_string_from_int(str);
@@ -1122,11 +1118,11 @@ Item_sum_num::fix_fields(THD *thd, Item **ref)
maybe_null= sum_func() != COUNT_FUNC;
for (uint i=0 ; i < arg_count ; i++)
{
- if (args[i]->fix_fields(thd, args + i) || args[i]->check_cols(1))
+ if (args[i]->fix_fields_if_needed_for_scalar(thd, &args[i]))
return TRUE;
set_if_bigger(decimals, args[i]->decimals);
- with_subselect|= args[i]->with_subselect;
- with_param|= args[i]->with_param;
+ m_with_subquery|= args[i]->with_subquery();
+ with_param|= args[i]->with_param;
with_window_func|= args[i]->with_window_func;
}
result_field=0;
@@ -1143,58 +1139,105 @@ Item_sum_num::fix_fields(THD *thd, Item **ref)
bool
-Item_sum_hybrid::fix_fields(THD *thd, Item **ref)
+Item_sum_min_max::fix_fields(THD *thd, Item **ref)
{
+ DBUG_ENTER("Item_sum_min_max::fix_fields");
DBUG_ASSERT(fixed == 0);
- Item *item= args[0];
-
if (init_sum_func_check(thd))
- return TRUE;
+ DBUG_RETURN(TRUE);
// 'item' can be changed during fix_fields
- if ((!item->fixed && item->fix_fields(thd, args)) ||
- (item= args[0])->check_cols(1))
- return TRUE;
- Type_std_attributes::set(args[0]);
- with_subselect= args[0]->with_subselect;
+ if (args[0]->fix_fields_if_needed_for_scalar(thd, &args[0]))
+ DBUG_RETURN(TRUE);
+
+ m_with_subquery= args[0]->with_subquery();
with_param= args[0]->with_param;
with_window_func|= args[0]->with_window_func;
- Item *item2= item->real_item();
- if (item2->type() == Item::FIELD_ITEM)
- set_handler_by_field_type(((Item_field*) item2)->field->type());
- else if (item->cmp_type() == TIME_RESULT)
- set_handler_by_field_type(item2->field_type());
- else
- set_handler_by_result_type(item2->result_type(),
- max_length, collation.collation);
+ if (fix_length_and_dec())
+ DBUG_RETURN(TRUE);
- switch (Item_sum_hybrid::result_type()) {
- case INT_RESULT:
- case DECIMAL_RESULT:
- case STRING_RESULT:
- break;
- case REAL_RESULT:
- max_length= float_length(decimals);
- break;
- case ROW_RESULT:
- case TIME_RESULT:
- DBUG_ASSERT(0);
- };
if (!is_window_func_sum_expr())
setup_hybrid(thd, args[0], NULL);
- /* MIN/MAX can return NULL for empty set independent of the used column */
- maybe_null= 1;
result_field=0;
- null_value=1;
- if (fix_length_and_dec() ||
- check_sum_func(thd, ref))
- return TRUE;
+
+ if (check_sum_func(thd, ref))
+ DBUG_RETURN(TRUE);
orig_args[0]= args[0];
fixed= 1;
- return FALSE;
+ DBUG_RETURN(FALSE);
+}
+
+
+bool Item_sum_hybrid::fix_length_and_dec_generic()
+{
+ Item *item= arguments()[0];
+ Type_std_attributes::set(item);
+ set_handler(item->type_handler());
+ return false;
+}
+
+
+/**
+ MAX/MIN for the traditional numeric types preserve the exact data type
+ from Fields, but do not preserve the exact type from Items:
+ MAX(float_field) -> FLOAT
+ MAX(smallint_field) -> LONGLONG
+ MAX(COALESCE(float_field)) -> DOUBLE
+ MAX(COALESCE(smallint_field)) -> LONGLONG
+ QQ: Items should probably be fixed to preserve the exact type.
+*/
+bool Item_sum_hybrid::fix_length_and_dec_numeric(const Type_handler *handler)
+{
+ Item *item= arguments()[0];
+ Item *item2= item->real_item();
+ Type_std_attributes::set(item);
+ if (item2->type() == Item::FIELD_ITEM)
+ set_handler(item2->type_handler());
+ else
+ set_handler(handler);
+ return false;
+}
+
+
+/**
+ MAX(str_field) converts ENUM/SET to CHAR, and preserve all other types
+ for Fields.
+ QQ: This works differently from UNION, which preserve the exact data
+ type for ENUM/SET if the joined ENUM/SET fields are equally defined.
+ Perhaps should be fixed.
+ MAX(str_item) chooses the best suitable string type.
+*/
+bool Item_sum_hybrid::fix_length_and_dec_string()
+{
+ Item *item= arguments()[0];
+ Item *item2= item->real_item();
+ Type_std_attributes::set(item);
+ if (item2->type() == Item::FIELD_ITEM)
+ {
+ // Fields: convert ENUM/SET to CHAR, preserve the type otherwise.
+ set_handler(item->type_handler());
+ }
+ else
+ {
+ // Items: choose VARCHAR/BLOB/MEDIUMBLOB/LONGBLOB, depending on length.
+ set_handler(type_handler_varchar.
+ type_handler_adjusted_to_max_octet_length(max_length,
+ collation.collation));
+ }
+ return false;
+}
+
+
+bool Item_sum_min_max::fix_length_and_dec()
+{
+ DBUG_ASSERT(args[0]->field_type() == args[0]->real_item()->field_type());
+ DBUG_ASSERT(args[0]->result_type() == args[0]->real_item()->result_type());
+ /* MIN/MAX can return NULL for empty set indepedent of the used column */
+ maybe_null= null_value= true;
+ return args[0]->type_handler()->Item_sum_hybrid_fix_length_and_dec(this);
}
@@ -1215,17 +1258,18 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref)
and Item_sum_min::add() to use different values!
*/
-void Item_sum_hybrid::setup_hybrid(THD *thd, Item *item, Item *value_arg)
+void Item_sum_min_max::setup_hybrid(THD *thd, Item *item, Item *value_arg)
{
- if (!(value= Item_cache::get_cache(thd, item, item->cmp_type())))
- return;
+ DBUG_ENTER("Item_sum_min_max::setup_hybrid");
+ if (!(value= item->get_cache(thd)))
+ DBUG_VOID_RETURN;
value->setup(thd, item);
value->store(value_arg);
/* Don't cache value, as it will change */
if (!item->const_item())
value->set_used_tables(RAND_TABLE_BIT);
- if (!(arg_cache= Item_cache::get_cache(thd, item, item->cmp_type())))
- return;
+ if (!(arg_cache= item->get_cache(thd)))
+ DBUG_VOID_RETURN;
arg_cache->setup(thd, item);
/* Don't cache value, as it will change */
if (!item->const_item())
@@ -1233,54 +1277,195 @@ void Item_sum_hybrid::setup_hybrid(THD *thd, Item *item, Item *value_arg)
cmp= new Arg_comparator();
if (cmp)
cmp->set_cmp_func(this, (Item**)&arg_cache, (Item**)&value, FALSE);
- collation.set(item->collation);
+ DBUG_VOID_RETURN;
}
-Field *Item_sum_hybrid::create_tmp_field(bool group, TABLE *table)
+Field *Item_sum_min_max::create_tmp_field(bool group, TABLE *table)
{
- Field *field;
- MEM_ROOT *mem_root;
+ DBUG_ENTER("Item_sum_min_max::create_tmp_field");
if (args[0]->type() == Item::FIELD_ITEM)
{
- field= ((Item_field*) args[0])->field;
-
- if ((field= create_tmp_field_from_field(table->in_use, field, name, table,
- NULL)))
+ Field *field= ((Item_field*) args[0])->field;
+ if ((field= create_tmp_field_from_field(table->in_use, field, &name,
+ table, NULL)))
field->flags&= ~NOT_NULL_FLAG;
- return field;
+ DBUG_RETURN(field);
+ }
+ DBUG_RETURN(tmp_table_field_from_field_type(table));
+}
+
+/***********************************************************************
+** Item_sum_sp class
+***********************************************************************/
+
+Item_sum_sp::Item_sum_sp(THD *thd, Name_resolution_context *context_arg,
+ sp_name *name_arg, sp_head *sp, List<Item> &list)
+ :Item_sum(thd, list), Item_sp(thd, context_arg, name_arg)
+{
+ maybe_null= 1;
+ quick_group= 0;
+ m_sp= sp;
+}
+
+Item_sum_sp::Item_sum_sp(THD *thd, Name_resolution_context *context_arg,
+ sp_name *name_arg, sp_head *sp)
+ :Item_sum(thd), Item_sp(thd, context_arg, name_arg)
+{
+ maybe_null= 1;
+ quick_group= 0;
+ m_sp= sp;
+}
+
+Item_sum_sp::Item_sum_sp(THD *thd, Item_sum_sp *item):
+ Item_sum(thd, item), Item_sp(thd, item)
+{
+ maybe_null= item->maybe_null;
+ quick_group= item->quick_group;
+}
+
+bool
+Item_sum_sp::fix_fields(THD *thd, Item **ref)
+{
+ DBUG_ASSERT(fixed == 0);
+ if (init_sum_func_check(thd))
+ return TRUE;
+ decimals= 0;
+
+ m_sp= m_sp ? m_sp : sp_handler_function.sp_find_routine(thd, m_name, true);
+
+ if (!m_sp)
+ {
+ my_missing_function_error(m_name->m_name, ErrConvDQName(m_name).ptr());
+ context->process_error(thd);
+ return TRUE;
}
+ if (init_result_field(thd, max_length, maybe_null, &null_value, &name))
+ return TRUE;
+
+ for (uint i= 0 ; i < arg_count ; i++)
+ {
+ if (args[i]->fix_fields_if_needed_for_scalar(thd, &args[i]))
+ return TRUE;
+ set_if_bigger(decimals, args[i]->decimals);
+ m_with_subquery|= args[i]->with_subquery();
+ with_window_func|= args[i]->with_window_func;
+ }
+ result_field= NULL;
+ max_length= float_length(decimals);
+ null_value= 1;
+ if (fix_length_and_dec())
+ return TRUE;
+
+ if (check_sum_func(thd, ref))
+ return TRUE;
+
+ memcpy(orig_args, args, sizeof(Item *) * arg_count);
+ fixed= 1;
+ return FALSE;
+}
+
+/**
+ Execute function to store value in result field.
+ This is called when we need the value to be returned for the function.
+ Here we send a signal in form of the server status that all rows have been
+ fetched and now we have to exit from the function with the return value.
+ @return Function returns error status.
+ @retval FALSE on success.
+ @retval TRUE if an error occurred.
+*/
+
+bool
+Item_sum_sp::execute()
+{
+ THD *thd= current_thd;
+ bool res;
+ uint old_server_status= thd->server_status;
+
/*
- DATE/TIME fields have STRING_RESULT result types.
- In order to preserve field type, it's needed to handle DATE/TIME
- fields creations separately.
+ We set server status so we can send a signal to exit from the
+ function with the return value.
*/
- mem_root= table->in_use->mem_root;
- switch (args[0]->field_type()) {
- case MYSQL_TYPE_DATE:
- field= new (mem_root)
- Field_newdate(0, maybe_null ? (uchar*)"" : 0, 0, Field::NONE, name);
- break;
- case MYSQL_TYPE_TIME:
- field= new_Field_time(mem_root, 0, maybe_null ? (uchar*)"" : 0, 0,
- Field::NONE, name, decimals);
- break;
- case MYSQL_TYPE_TIMESTAMP:
- case MYSQL_TYPE_DATETIME:
- field= new_Field_datetime(mem_root, 0, maybe_null ? (uchar*)"" : 0, 0,
- Field::NONE, name, decimals);
- break;
- default:
- return Item_sum::create_tmp_field(group, table);
- }
- if (field)
- field->init(table);
- return field;
+
+ thd->server_status|= SERVER_STATUS_LAST_ROW_SENT;
+ res= Item_sp::execute(thd, &null_value, args, arg_count);
+ thd->server_status= old_server_status;
+ return res;
+}
+
+/**
+ Handles the aggregation of the values.
+ @note: See class description for more details on how and why this is done.
+ @return The error state.
+ @retval FALSE on success.
+ @retval TRUE if an error occurred.
+*/
+
+bool
+Item_sum_sp::add()
+{
+ return execute_impl(current_thd, args, arg_count);
}
+void
+Item_sum_sp::clear()
+{
+ delete func_ctx;
+ func_ctx= NULL;
+ sp_query_arena->free_items();
+ free_root(&sp_mem_root, MYF(0));
+}
+
+const Type_handler *Item_sum_sp::type_handler() const
+{
+ DBUG_ENTER("Item_sum_sp::type_handler");
+ DBUG_PRINT("info", ("m_sp = %p", (void *) m_sp));
+ DBUG_ASSERT(sp_result_field);
+ // This converts ENUM/SET to STRING
+ const Type_handler *handler= sp_result_field->type_handler();
+ DBUG_RETURN(handler->type_handler_for_item_field());
+}
+
+void
+Item_sum_sp::cleanup()
+{
+ Item_sp::cleanup();
+ Item_sum::cleanup();
+}
+
+/**
+ Initialize local members with values from the Field interface.
+ @note called from Item::fix_fields.
+*/
+
+bool
+Item_sum_sp::fix_length_and_dec()
+{
+ DBUG_ENTER("Item_sum_sp::fix_length_and_dec");
+ DBUG_ASSERT(sp_result_field);
+ Type_std_attributes::set(sp_result_field->type_std_attributes());
+ bool res= Item_sum::fix_length_and_dec();
+ DBUG_RETURN(res);
+}
+
+const char *
+Item_sum_sp::func_name() const
+{
+ THD *thd= current_thd;
+ return Item_sp::func_name(thd);
+}
+
+Item* Item_sum_sp::copy_or_same(THD *thd)
+{
+ Item_sum_sp *copy_item= new (thd->mem_root) Item_sum_sp(thd, this);
+ copy_item->init_result_field(thd, max_length, maybe_null,
+ &copy_item->null_value, &copy_item->name);
+ return copy_item;
+}
+
/***********************************************************************
** reset and add of sum_func
***********************************************************************/
@@ -1292,11 +1477,12 @@ Field *Item_sum_hybrid::create_tmp_field(bool group, TABLE *table)
Item_sum_sum::Item_sum_sum(THD *thd, Item_sum_sum *item)
:Item_sum_num(thd, item),
Type_handler_hybrid_field_type(item),
+ direct_added(FALSE), direct_reseted_field(FALSE),
curr_dec_buff(item->curr_dec_buff),
count(item->count)
{
/* TODO: check if the following assignments are really needed */
- if (Item_sum_sum::result_type() == DECIMAL_RESULT)
+ if (result_type() == DECIMAL_RESULT)
{
my_decimal2decimal(item->dec_buffs, dec_buffs);
my_decimal2decimal(item->dec_buffs + 1, dec_buffs + 1);
@@ -1311,12 +1497,21 @@ Item *Item_sum_sum::copy_or_same(THD* thd)
}
+void Item_sum_sum::cleanup()
+{
+ DBUG_ENTER("Item_sum_sum::cleanup");
+ direct_added= direct_reseted_field= FALSE;
+ Item_sum_num::cleanup();
+ DBUG_VOID_RETURN;
+}
+
+
void Item_sum_sum::clear()
{
DBUG_ENTER("Item_sum_sum::clear");
null_value=1;
count= 0;
- if (Item_sum_sum::result_type() == DECIMAL_RESULT)
+ if (result_type() == DECIMAL_RESULT)
{
curr_dec_buff= 0;
my_decimal_set_zero(dec_buffs);
@@ -1327,42 +1522,70 @@ void Item_sum_sum::clear()
}
+void Item_sum_sum::fix_length_and_dec_double()
+{
+ set_handler(&type_handler_double); // Change FLOAT to DOUBLE
+ decimals= args[0]->decimals;
+ sum= 0.0;
+}
+
+
+void Item_sum_sum::fix_length_and_dec_decimal()
+{
+ set_handler(&type_handler_newdecimal); // Change temporal to new DECIMAL
+ decimals= args[0]->decimals;
+ /* SUM result can't be longer than length(arg) + length(MAX_ROWS) */
+ int precision= args[0]->decimal_precision() + DECIMAL_LONGLONG_DIGITS;
+ max_length= my_decimal_precision_to_length_no_truncation(precision,
+ decimals,
+ unsigned_flag);
+ curr_dec_buff= 0;
+ my_decimal_set_zero(dec_buffs);
+}
+
+
bool Item_sum_sum::fix_length_and_dec()
{
DBUG_ENTER("Item_sum_sum::fix_length_and_dec");
maybe_null=null_value=1;
- decimals= args[0]->decimals;
- switch (args[0]->cast_to_int_type()) {
- case REAL_RESULT:
- case STRING_RESULT:
- set_handler_by_field_type(MYSQL_TYPE_DOUBLE);
- sum= 0.0;
- break;
- case INT_RESULT:
- case TIME_RESULT:
- case DECIMAL_RESULT:
+ if (args[0]->cast_to_int_type_handler()->
+ Item_sum_sum_fix_length_and_dec(this))
+ DBUG_RETURN(TRUE);
+ DBUG_PRINT("info", ("Type: %s (%d, %d)", type_handler()->name().ptr(),
+ max_length, (int) decimals));
+ DBUG_RETURN(FALSE);
+}
+
+
+void Item_sum_sum::direct_add(my_decimal *add_sum_decimal)
+{
+ DBUG_ENTER("Item_sum_sum::direct_add");
+ DBUG_PRINT("info", ("add_sum_decimal: %p", add_sum_decimal));
+ direct_added= TRUE;
+ direct_reseted_field= FALSE;
+ if (add_sum_decimal)
{
- /* SUM result can't be longer than length(arg) + length(MAX_ROWS) */
- int precision= args[0]->decimal_precision() + DECIMAL_LONGLONG_DIGITS;
- max_length= my_decimal_precision_to_length_no_truncation(precision,
- decimals,
- unsigned_flag);
- curr_dec_buff= 0;
- set_handler_by_field_type(MYSQL_TYPE_NEWDECIMAL);
- my_decimal_set_zero(dec_buffs);
- break;
+ direct_sum_is_null= FALSE;
+ direct_sum_decimal= *add_sum_decimal;
}
- case ROW_RESULT:
- DBUG_ASSERT(0);
+ else
+ {
+ direct_sum_is_null= TRUE;
+ direct_sum_decimal= decimal_zero;
}
- DBUG_PRINT("info", ("Type: %s (%d, %d)",
- (result_type() == REAL_RESULT ? "REAL_RESULT" :
- result_type() == DECIMAL_RESULT ? "DECIMAL_RESULT" :
- result_type() == INT_RESULT ? "INT_RESULT" :
- "--ILLEGAL!!!--"),
- max_length,
- (int)decimals));
- DBUG_RETURN(FALSE);
+ DBUG_VOID_RETURN;
+}
+
+
+void Item_sum_sum::direct_add(double add_sum_real, bool add_sum_is_null)
+{
+ DBUG_ENTER("Item_sum_sum::direct_add");
+ DBUG_PRINT("info", ("add_sum_real: %f", add_sum_real));
+ direct_added= TRUE;
+ direct_reseted_field= FALSE;
+ direct_sum_is_null= add_sum_is_null;
+ direct_sum_real= add_sum_real;
+ DBUG_VOID_RETURN;
}
@@ -1377,52 +1600,86 @@ void Item_sum_sum::add_helper(bool perform_removal)
{
DBUG_ENTER("Item_sum_sum::add_helper");
- if (Item_sum_sum::result_type() == DECIMAL_RESULT)
+ if (result_type() == DECIMAL_RESULT)
{
- my_decimal value;
- const my_decimal *val= aggr->arg_val_decimal(&value);
- if (!aggr->arg_is_null(true))
+ if (unlikely(direct_added))
{
- if (perform_removal)
+ /* Add value stored by Item_sum_sum::direct_add */
+ DBUG_ASSERT(!perform_removal);
+
+ direct_added= FALSE;
+ if (likely(!direct_sum_is_null))
{
- if (count > 0)
+ my_decimal_add(E_DEC_FATAL_ERROR, dec_buffs + (curr_dec_buff^1),
+ &direct_sum_decimal, dec_buffs + curr_dec_buff);
+ curr_dec_buff^= 1;
+ null_value= 0;
+ }
+ }
+ else
+ {
+ direct_reseted_field= FALSE;
+ my_decimal value;
+ const my_decimal *val= aggr->arg_val_decimal(&value);
+ if (!aggr->arg_is_null(true))
+ {
+ if (perform_removal)
{
- my_decimal_sub(E_DEC_FATAL_ERROR, dec_buffs + (curr_dec_buff ^ 1),
- dec_buffs + curr_dec_buff, val);
- count--;
+ if (count > 0)
+ {
+ my_decimal_sub(E_DEC_FATAL_ERROR, dec_buffs + (curr_dec_buff ^ 1),
+ dec_buffs + curr_dec_buff, val);
+ count--;
+ }
+ else
+ DBUG_VOID_RETURN;
}
else
- DBUG_VOID_RETURN;
- }
- else
- {
- count++;
- my_decimal_add(E_DEC_FATAL_ERROR, dec_buffs + (curr_dec_buff ^ 1),
- val, dec_buffs + curr_dec_buff);
+ {
+ count++;
+ my_decimal_add(E_DEC_FATAL_ERROR, dec_buffs + (curr_dec_buff ^ 1),
+ val, dec_buffs + curr_dec_buff);
+ }
+ curr_dec_buff^= 1;
+ null_value= (count > 0) ? 0 : 1;
}
- curr_dec_buff^= 1;
- null_value= (count > 0) ? 0 : 1;
}
}
else
{
- if (perform_removal && count > 0)
- sum-= aggr->arg_val_real();
+ if (unlikely(direct_added))
+ {
+ /* Add value stored by Item_sum_sum::direct_add */
+ DBUG_ASSERT(!perform_removal);
+
+ direct_added= FALSE;
+ if (!direct_sum_is_null)
+ {
+ sum+= direct_sum_real;
+ null_value= 0;
+ }
+ }
else
- sum+= aggr->arg_val_real();
- if (!aggr->arg_is_null(true))
{
- if (perform_removal)
+ direct_reseted_field= FALSE;
+ if (perform_removal && count > 0)
+ sum-= aggr->arg_val_real();
+ else
+ sum+= aggr->arg_val_real();
+ if (!aggr->arg_is_null(true))
{
- if (count > 0)
+ if (perform_removal)
{
- count--;
+ if (count > 0)
+ {
+ count--;
+ }
}
- }
- else
- count++;
+ else
+ count++;
- null_value= (count > 0) ? 0 : 1;
+ null_value= (count > 0) ? 0 : 1;
+ }
}
}
DBUG_VOID_RETURN;
@@ -1434,7 +1691,7 @@ longlong Item_sum_sum::val_int()
DBUG_ASSERT(fixed == 1);
if (aggr)
aggr->endup();
- if (Item_sum_sum::result_type() == DECIMAL_RESULT)
+ if (result_type() == DECIMAL_RESULT)
{
longlong result;
my_decimal2int(E_DEC_FATAL_ERROR, dec_buffs + curr_dec_buff, unsigned_flag,
@@ -1450,7 +1707,7 @@ double Item_sum_sum::val_real()
DBUG_ASSERT(fixed == 1);
if (aggr)
aggr->endup();
- if (Item_sum_sum::result_type() == DECIMAL_RESULT)
+ if (result_type() == DECIMAL_RESULT)
my_decimal2double(E_DEC_FATAL_ERROR, dec_buffs + curr_dec_buff, &sum);
return sum;
}
@@ -1460,7 +1717,7 @@ String *Item_sum_sum::val_str(String *str)
{
if (aggr)
aggr->endup();
- if (Item_sum_sum::result_type() == DECIMAL_RESULT)
+ if (result_type() == DECIMAL_RESULT)
return val_string_from_decimal(str);
return val_string_from_real(str);
}
@@ -1470,7 +1727,7 @@ my_decimal *Item_sum_sum::val_decimal(my_decimal *val)
{
if (aggr)
aggr->endup();
- if (Item_sum_sum::result_type() == DECIMAL_RESULT)
+ if (result_type() == DECIMAL_RESULT)
return null_value ? NULL : (dec_buffs + curr_dec_buff);
return val_decimal_from_real(val);
}
@@ -1609,22 +1866,46 @@ bool Aggregator_distinct::arg_is_null(bool use_null_value)
Item *Item_sum_count::copy_or_same(THD* thd)
{
- return new (thd->mem_root) Item_sum_count(thd, this);
+ DBUG_ENTER("Item_sum_count::copy_or_same");
+ DBUG_RETURN(new (thd->mem_root) Item_sum_count(thd, this));
+}
+
+
+void Item_sum_count::direct_add(longlong add_count)
+{
+ DBUG_ENTER("Item_sum_count::direct_add");
+ DBUG_PRINT("info", ("add_count: %lld", add_count));
+ direct_counted= TRUE;
+ direct_reseted_field= FALSE;
+ direct_count= add_count;
+ DBUG_VOID_RETURN;
}
void Item_sum_count::clear()
{
+ DBUG_ENTER("Item_sum_count::clear");
count= 0;
+ DBUG_VOID_RETURN;
}
bool Item_sum_count::add()
{
- if (aggr->arg_is_null(false))
- return 0;
- count++;
- return 0;
+ DBUG_ENTER("Item_sum_count::add");
+ if (direct_counted)
+ {
+ direct_counted= FALSE;
+ count+= direct_count;
+ }
+ else
+ {
+ direct_reseted_field= FALSE;
+ if (aggr->arg_is_null(false))
+ DBUG_RETURN(0);
+ count++;
+ }
+ DBUG_RETURN(0);
}
@@ -1643,10 +1924,11 @@ void Item_sum_count::remove()
longlong Item_sum_count::val_int()
{
+ DBUG_ENTER("Item_sum_count::val_int");
DBUG_ASSERT(fixed == 1);
if (aggr)
aggr->endup();
- return (longlong) count;
+ DBUG_RETURN((longlong)count);
}
@@ -1654,6 +1936,8 @@ void Item_sum_count::cleanup()
{
DBUG_ENTER("Item_sum_count::cleanup");
count= 0;
+ direct_counted= FALSE;
+ direct_reseted_field= FALSE;
Item_sum_int::cleanup();
DBUG_VOID_RETURN;
}
@@ -1662,30 +1946,41 @@ void Item_sum_count::cleanup()
/*
Average
*/
+
+void Item_sum_avg::fix_length_and_dec_decimal()
+{
+ Item_sum_sum::fix_length_and_dec_decimal();
+ int precision= args[0]->decimal_precision() + prec_increment;
+ decimals= MY_MIN(args[0]->decimals + prec_increment, DECIMAL_MAX_SCALE);
+ max_length= my_decimal_precision_to_length_no_truncation(precision,
+ decimals,
+ unsigned_flag);
+ f_precision= MY_MIN(precision+DECIMAL_LONGLONG_DIGITS, DECIMAL_MAX_PRECISION);
+ f_scale= args[0]->decimals;
+ dec_bin_size= my_decimal_get_binary_size(f_precision, f_scale);
+}
+
+
+void Item_sum_avg::fix_length_and_dec_double()
+{
+ Item_sum_sum::fix_length_and_dec_double();
+ decimals= MY_MIN(args[0]->decimals + prec_increment,
+ FLOATING_POINT_DECIMALS);
+ max_length= MY_MIN(args[0]->max_length + prec_increment, float_length(decimals));
+}
+
+
bool Item_sum_avg::fix_length_and_dec()
{
- if (Item_sum_sum::fix_length_and_dec())
- return TRUE;
- maybe_null=null_value=1;
+ DBUG_ENTER("Item_sum_avg::fix_length_and_dec");
prec_increment= current_thd->variables.div_precincrement;
- if (Item_sum_avg::result_type() == DECIMAL_RESULT)
- {
- int precision= args[0]->decimal_precision() + prec_increment;
- decimals= MY_MIN(args[0]->decimals + prec_increment, DECIMAL_MAX_SCALE);
- max_length= my_decimal_precision_to_length_no_truncation(precision,
- decimals,
- unsigned_flag);
- f_precision= MY_MIN(precision+DECIMAL_LONGLONG_DIGITS, DECIMAL_MAX_PRECISION);
- f_scale= args[0]->decimals;
- dec_bin_size= my_decimal_get_binary_size(f_precision, f_scale);
- }
- else
- {
- decimals= MY_MIN(args[0]->decimals + prec_increment,
- FLOATING_POINT_DECIMALS);
- max_length= MY_MIN(args[0]->max_length + prec_increment, float_length(decimals));
- }
- return FALSE;
+ maybe_null=null_value=1;
+ if (args[0]->cast_to_int_type_handler()->
+ Item_sum_avg_fix_length_and_dec(this))
+ DBUG_RETURN(TRUE);
+ DBUG_PRINT("info", ("Type: %s (%d, %d)", type_handler()->name().ptr(),
+ max_length, (int) decimals));
+ DBUG_RETURN(FALSE);
}
@@ -1697,8 +1992,6 @@ Item *Item_sum_avg::copy_or_same(THD* thd)
Field *Item_sum_avg::create_tmp_field(bool group, TABLE *table)
{
- Field *field;
- MEM_ROOT *mem_root= table->in_use->mem_root;
if (group)
{
@@ -1707,19 +2000,15 @@ 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= new (mem_root)
- Field_string(((Item_sum_avg::result_type() == DECIMAL_RESULT) ?
- dec_bin_size : sizeof(double)) + sizeof(longlong),
- 0, name, &my_charset_bin);
+ Field *field= new (table->in_use->mem_root)
+ Field_string(((result_type() == DECIMAL_RESULT) ?
+ dec_bin_size : sizeof(double)) + sizeof(longlong),
+ 0, &name, &my_charset_bin);
+ if (field)
+ field->init(table);
+ return field;
}
- else if (Item_sum_avg::result_type() == DECIMAL_RESULT)
- field= Field_new_decimal::create_from_item(mem_root, this);
- else
- field= new (mem_root) Field_double(max_length, maybe_null, name, decimals,
- TRUE);
- if (field)
- field->init(table);
- return field;
+ return tmp_table_field_from_field_type(table);
}
@@ -1780,7 +2069,7 @@ my_decimal *Item_sum_avg::val_decimal(my_decimal *val)
For non-DECIMAL result_type() the division will be done in
Item_sum_avg::val_real().
*/
- if (Item_sum_avg::result_type() != DECIMAL_RESULT)
+ if (result_type() != DECIMAL_RESULT)
return val_decimal_from_real(val);
sum_dec= dec_buffs + curr_dec_buff;
@@ -1794,7 +2083,7 @@ String *Item_sum_avg::val_str(String *str)
{
if (aggr)
aggr->endup();
- if (Item_sum_avg::result_type() == DECIMAL_RESULT)
+ if (result_type() == DECIMAL_RESULT)
return val_string_from_decimal(str);
return val_string_from_real(str);
}
@@ -1888,7 +2177,7 @@ static double variance_fp_recurrence_result(double s, ulonglong count, bool is_s
Item_sum_variance::Item_sum_variance(THD *thd, Item_sum_variance *item):
- Item_sum_num(thd, item),
+ Item_sum_double(thd, item),
count(item->count), sample(item->sample),
prec_increment(item->prec_increment)
{
@@ -1897,6 +2186,25 @@ Item_sum_variance::Item_sum_variance(THD *thd, Item_sum_variance *item):
}
+void Item_sum_variance::fix_length_and_dec_double()
+{
+ DBUG_ASSERT(Item_sum_variance::type_handler() == &type_handler_double);
+ decimals= MY_MIN(args[0]->decimals + 4, FLOATING_POINT_DECIMALS);
+}
+
+
+void Item_sum_variance::fix_length_and_dec_decimal()
+{
+ DBUG_ASSERT(Item_sum_variance::type_handler() == &type_handler_double);
+ int precision= args[0]->decimal_precision() * 2 + prec_increment;
+ decimals= MY_MIN(args[0]->decimals + prec_increment,
+ FLOATING_POINT_DECIMALS - 1);
+ max_length= my_decimal_precision_to_length_no_truncation(precision,
+ decimals,
+ unsigned_flag);
+}
+
+
bool Item_sum_variance::fix_length_and_dec()
{
DBUG_ENTER("Item_sum_variance::fix_length_and_dec");
@@ -1909,29 +2217,10 @@ bool Item_sum_variance::fix_length_and_dec()
type of the result is an implementation-defined approximate numeric
type.
*/
-
- switch (args[0]->result_type()) {
- case REAL_RESULT:
- case STRING_RESULT:
- decimals= MY_MIN(args[0]->decimals + 4, FLOATING_POINT_DECIMALS);
- break;
- case INT_RESULT:
- case DECIMAL_RESULT:
- {
- int precision= args[0]->decimal_precision()*2 + prec_increment;
- decimals= MY_MIN(args[0]->decimals + prec_increment,
- FLOATING_POINT_DECIMALS-1);
- max_length= my_decimal_precision_to_length_no_truncation(precision,
- decimals,
- unsigned_flag);
-
- break;
- }
- case ROW_RESULT:
- case TIME_RESULT:
- DBUG_ASSERT(0);
- }
- DBUG_PRINT("info", ("Type: REAL_RESULT (%d, %d)", max_length, (int)decimals));
+ if (args[0]->type_handler()->Item_sum_variance_fix_length_and_dec(this))
+ DBUG_RETURN(TRUE);
+ DBUG_PRINT("info", ("Type: %s (%d, %d)", type_handler()->name().ptr(),
+ max_length, (int)decimals));
DBUG_RETURN(FALSE);
}
@@ -1957,10 +2246,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(sizeof(double)*2 + sizeof(longlong), 0, name, &my_charset_bin);
+ field= new Field_string(sizeof(double)*2 + sizeof(longlong), 0,
+ &name, &my_charset_bin);
}
else
- field= new Field_double(max_length, maybe_null, name, decimals, TRUE);
+ field= new Field_double(max_length, maybe_null, &name, decimals,
+ TRUE);
if (field != NULL)
field->init(table);
@@ -2012,13 +2303,6 @@ double Item_sum_variance::val_real()
}
-my_decimal *Item_sum_variance::val_decimal(my_decimal *dec_buf)
-{
- DBUG_ASSERT(fixed == 1);
- return val_decimal_from_real(dec_buf);
-}
-
-
void Item_sum_variance::reset_field()
{
double nr;
@@ -2074,14 +2358,17 @@ Item *Item_sum_variance::result_item(THD *thd, Field *field)
/* min & max */
-void Item_sum_hybrid::clear()
+void Item_sum_min_max::clear()
{
+ DBUG_ENTER("Item_sum_min_max::clear");
value->clear();
null_value= 1;
+ DBUG_VOID_RETURN;
}
+
bool
-Item_sum_hybrid::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+Item_sum_min_max::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
{
DBUG_ASSERT(fixed == 1);
if (null_value)
@@ -2092,57 +2379,72 @@ Item_sum_hybrid::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
return retval;
}
-double Item_sum_hybrid::val_real()
+
+void Item_sum_min_max::direct_add(Item *item)
{
+ DBUG_ENTER("Item_sum_min_max::direct_add");
+ DBUG_PRINT("info", ("item: %p", item));
+ direct_added= TRUE;
+ direct_item= item;
+ DBUG_VOID_RETURN;
+}
+
+
+double Item_sum_min_max::val_real()
+{
+ DBUG_ENTER("Item_sum_min_max::val_real");
DBUG_ASSERT(fixed == 1);
if (null_value)
- return 0.0;
+ DBUG_RETURN(0.0);
double retval= value->val_real();
if ((null_value= value->null_value))
DBUG_ASSERT(retval == 0.0);
- return retval;
+ DBUG_RETURN(retval);
}
-longlong Item_sum_hybrid::val_int()
+longlong Item_sum_min_max::val_int()
{
+ DBUG_ENTER("Item_sum_min_max::val_int");
DBUG_ASSERT(fixed == 1);
if (null_value)
- return 0;
+ DBUG_RETURN(0);
longlong retval= value->val_int();
if ((null_value= value->null_value))
DBUG_ASSERT(retval == 0);
- return retval;
+ DBUG_RETURN(retval);
}
-my_decimal *Item_sum_hybrid::val_decimal(my_decimal *val)
+my_decimal *Item_sum_min_max::val_decimal(my_decimal *val)
{
+ DBUG_ENTER("Item_sum_min_max::val_decimal");
DBUG_ASSERT(fixed == 1);
if (null_value)
- return 0;
+ DBUG_RETURN(0);
my_decimal *retval= value->val_decimal(val);
if ((null_value= value->null_value))
DBUG_ASSERT(retval == NULL);
- return retval;
+ DBUG_RETURN(retval);
}
String *
-Item_sum_hybrid::val_str(String *str)
+Item_sum_min_max::val_str(String *str)
{
+ DBUG_ENTER("Item_sum_min_max::val_str");
DBUG_ASSERT(fixed == 1);
if (null_value)
- return 0;
+ DBUG_RETURN(0);
String *retval= value->val_str(str);
if ((null_value= value->null_value))
DBUG_ASSERT(retval == NULL);
- return retval;
+ DBUG_RETURN(retval);
}
-void Item_sum_hybrid::cleanup()
+void Item_sum_min_max::cleanup()
{
- DBUG_ENTER("Item_sum_hybrid::cleanup");
+ DBUG_ENTER("Item_sum_min_max::cleanup");
Item_sum::cleanup();
if (cmp)
delete cmp;
@@ -2158,8 +2460,9 @@ void Item_sum_hybrid::cleanup()
DBUG_VOID_RETURN;
}
-void Item_sum_hybrid::no_rows_in_result()
+void Item_sum_min_max::no_rows_in_result()
{
+ DBUG_ENTER("Item_sum_min_max::no_rows_in_result");
/* We may be called here twice in case of ref field in function */
if (was_values)
{
@@ -2167,9 +2470,10 @@ void Item_sum_hybrid::no_rows_in_result()
was_null_value= value->null_value;
clear();
}
+ DBUG_VOID_RETURN;
}
-void Item_sum_hybrid::restore_to_before_no_rows_in_result()
+void Item_sum_min_max::restore_to_before_no_rows_in_result()
{
if (!was_values)
{
@@ -2181,14 +2485,26 @@ void Item_sum_hybrid::restore_to_before_no_rows_in_result()
Item *Item_sum_min::copy_or_same(THD* thd)
{
+ DBUG_ENTER("Item_sum_min::copy_or_same");
Item_sum_min *item= new (thd->mem_root) Item_sum_min(thd, this);
item->setup_hybrid(thd, args[0], value);
- return item;
+ DBUG_RETURN(item);
}
bool Item_sum_min::add()
{
+ Item *UNINIT_VAR(tmp_item);
+ DBUG_ENTER("Item_sum_min::add");
+ DBUG_PRINT("enter", ("this: %p", this));
+
+ if (unlikely(direct_added))
+ {
+ /* Change to use direct_item */
+ tmp_item= arg_cache->get_item();
+ arg_cache->store(direct_item);
+ }
+ DBUG_PRINT("info", ("null_value: %s", null_value ? "TRUE" : "FALSE"));
/* args[0] < value */
arg_cache->cache_value();
if (!arg_cache->null_value &&
@@ -2198,7 +2514,13 @@ bool Item_sum_min::add()
value->cache_value();
null_value= 0;
}
- return 0;
+ if (unlikely(direct_added))
+ {
+ /* Restore original item */
+ direct_added= FALSE;
+ arg_cache->store(tmp_item);
+ }
+ DBUG_RETURN(0);
}
@@ -2212,8 +2534,19 @@ Item *Item_sum_max::copy_or_same(THD* thd)
bool Item_sum_max::add()
{
+ Item * UNINIT_VAR(tmp_item);
+ DBUG_ENTER("Item_sum_max::add");
+ DBUG_PRINT("enter", ("this: %p", this));
+
+ if (unlikely(direct_added))
+ {
+ /* Change to use direct_item */
+ tmp_item= arg_cache->get_item();
+ arg_cache->store(direct_item);
+ }
/* args[0] > value */
arg_cache->cache_value();
+ DBUG_PRINT("info", ("null_value: %s", null_value ? "TRUE" : "FALSE"));
if (!arg_cache->null_value &&
(null_value || cmp->compare() > 0))
{
@@ -2221,7 +2554,13 @@ bool Item_sum_max::add()
value->cache_value();
null_value= 0;
}
- return 0;
+ if (unlikely(direct_added))
+ {
+ /* Restore original item */
+ direct_added= FALSE;
+ arg_cache->store(tmp_item);
+ }
+ DBUG_RETURN(0);
}
@@ -2398,16 +2737,28 @@ void Item_sum_num::reset_field()
}
-void Item_sum_hybrid::reset_field()
+void Item_sum_min_max::reset_field()
{
- switch(Item_sum_hybrid::result_type()) {
+ Item *UNINIT_VAR(tmp_item), *arg0;
+ DBUG_ENTER("Item_sum_min_max::reset_field");
+
+ arg0= args[0];
+ if (unlikely(direct_added))
+ {
+ /* Switch to use direct item */
+ tmp_item= value->get_item();
+ value->store(direct_item);
+ arg0= direct_item;
+ }
+
+ switch(result_type()) {
case STRING_RESULT:
{
char buff[MAX_FIELD_WIDTH];
String tmp(buff,sizeof(buff),result_field->charset()),*res;
- res=args[0]->val_str(&tmp);
- if (args[0]->null_value)
+ res= arg0->val_str(&tmp);
+ if (arg0->null_value)
{
result_field->set_null();
result_field->reset();
@@ -2421,11 +2772,11 @@ void Item_sum_hybrid::reset_field()
}
case INT_RESULT:
{
- longlong nr=args[0]->val_int();
+ longlong nr= arg0->val_int();
if (maybe_null)
{
- if (args[0]->null_value)
+ if (arg0->null_value)
{
nr=0;
result_field->set_null();
@@ -2433,16 +2784,17 @@ void Item_sum_hybrid::reset_field()
else
result_field->set_notnull();
}
+ DBUG_PRINT("info", ("nr: %lld", nr));
result_field->store(nr, unsigned_flag);
break;
}
case REAL_RESULT:
{
- double nr= args[0]->val_real();
+ double nr= arg0->val_real();
if (maybe_null)
{
- if (args[0]->null_value)
+ if (arg0->null_value)
{
nr=0.0;
result_field->set_null();
@@ -2455,11 +2807,11 @@ void Item_sum_hybrid::reset_field()
}
case DECIMAL_RESULT:
{
- my_decimal value_buff, *arg_dec= args[0]->val_decimal(&value_buff);
+ my_decimal value_buff, *arg_dec= arg0->val_decimal(&value_buff);
if (maybe_null)
{
- if (args[0]->null_value)
+ if (arg0->null_value)
result_field->set_null();
else
result_field->set_notnull();
@@ -2477,26 +2829,49 @@ void Item_sum_hybrid::reset_field()
case TIME_RESULT:
DBUG_ASSERT(0);
}
+
+ if (unlikely(direct_added))
+ {
+ direct_added= FALSE;
+ value->store(tmp_item);
+ }
+ DBUG_VOID_RETURN;
}
void Item_sum_sum::reset_field()
{
+ my_bool null_flag;
DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR);
- if (Item_sum_sum::result_type() == DECIMAL_RESULT)
+ if (result_type() == DECIMAL_RESULT)
{
- my_decimal value, *arg_val= args[0]->val_decimal(&value);
- if (!arg_val) // Null
- arg_val= &decimal_zero;
+ my_decimal value, *arg_val;
+ if (unlikely(direct_added))
+ arg_val= &direct_sum_decimal;
+ else
+ {
+ if (!(arg_val= args[0]->val_decimal(&value)))
+ arg_val= &decimal_zero; // Null
+ }
result_field->store_decimal(arg_val);
}
else
{
DBUG_ASSERT(result_type() == REAL_RESULT);
- double nr= args[0]->val_real(); // Nulls also return 0
+ double nr= likely(!direct_added) ? args[0]->val_real() : direct_sum_real;
float8store(result_field->ptr, nr);
}
- if (args[0]->null_value)
+
+ if (unlikely(direct_added))
+ {
+ direct_added= FALSE;
+ direct_reseted_field= TRUE;
+ null_flag= direct_sum_is_null;
+ }
+ else
+ null_flag= args[0]->null_value;
+
+ if (null_flag)
result_field->set_null();
else
result_field->set_notnull();
@@ -2505,13 +2880,22 @@ void Item_sum_sum::reset_field()
void Item_sum_count::reset_field()
{
+ DBUG_ENTER("Item_sum_count::reset_field");
uchar *res=result_field->ptr;
longlong nr=0;
DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR);
- if (!args[0]->maybe_null || !args[0]->is_null())
- nr=1;
+ if (unlikely(direct_counted))
+ {
+ nr= direct_count;
+ direct_counted= FALSE;
+ direct_reseted_field= TRUE;
+ }
+ else if (!args[0]->maybe_null || !args[0]->is_null())
+ nr= 1;
+ DBUG_PRINT("info", ("nr: %lld", nr));
int8store(res,nr);
+ DBUG_VOID_RETURN;
}
@@ -2519,7 +2903,7 @@ void Item_sum_avg::reset_field()
{
uchar *res=result_field->ptr;
DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR);
- if (Item_sum_avg::result_type() == DECIMAL_RESULT)
+ if (result_type() == DECIMAL_RESULT)
{
longlong tmp;
my_decimal value, *arg_dec= args[0]->val_decimal(&value);
@@ -2577,15 +2961,28 @@ void Item_sum_bit::update_field()
void Item_sum_sum::update_field()
{
DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR);
- if (Item_sum_sum::result_type() == DECIMAL_RESULT)
+ if (result_type() == DECIMAL_RESULT)
{
- my_decimal value, *arg_val= args[0]->val_decimal(&value);
- if (!args[0]->null_value)
+ my_decimal value, *arg_val;
+ my_bool null_flag;
+ if (unlikely(direct_added || direct_reseted_field))
+ {
+ direct_added= direct_reseted_field= FALSE;
+ arg_val= &direct_sum_decimal;
+ null_flag= direct_sum_is_null;
+ }
+ else
+ {
+ arg_val= args[0]->val_decimal(&value);
+ null_flag= args[0]->null_value;
+ }
+
+ if (!null_flag)
{
if (!result_field->is_null())
{
- my_decimal field_value,
- *field_val= result_field->val_decimal(&field_value);
+ my_decimal field_value;
+ my_decimal *field_val= result_field->val_decimal(&field_value);
my_decimal_add(E_DEC_FATAL_ERROR, dec_buffs, arg_val, field_val);
result_field->store_decimal(dec_buffs);
}
@@ -2599,11 +2996,22 @@ void Item_sum_sum::update_field()
else
{
double old_nr,nr;
- uchar *res=result_field->ptr;
+ uchar *res= result_field->ptr;
+ my_bool null_flag;
float8get(old_nr,res);
- nr= args[0]->val_real();
- if (!args[0]->null_value)
+ if (unlikely(direct_added || direct_reseted_field))
+ {
+ direct_added= direct_reseted_field= FALSE;
+ null_flag= direct_sum_is_null;
+ nr= direct_sum_real;
+ }
+ else
+ {
+ nr= args[0]->val_real();
+ null_flag= args[0]->null_value;
+ }
+ if (!null_flag)
{
old_nr+=nr;
result_field->set_notnull();
@@ -2615,13 +3023,21 @@ void Item_sum_sum::update_field()
void Item_sum_count::update_field()
{
+ DBUG_ENTER("Item_sum_count::update_field");
longlong nr;
uchar *res=result_field->ptr;
nr=sint8korr(res);
- if (!args[0]->maybe_null || !args[0]->is_null())
+ if (unlikely(direct_counted || direct_reseted_field))
+ {
+ direct_counted= direct_reseted_field= FALSE;
+ nr+= direct_count;
+ }
+ else if (!args[0]->maybe_null || !args[0]->is_null())
nr++;
+ DBUG_PRINT("info", ("nr: %lld", nr));
int8store(res,nr);
+ DBUG_VOID_RETURN;
}
@@ -2632,7 +3048,7 @@ void Item_sum_avg::update_field()
DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR);
- if (Item_sum_avg::result_type() == DECIMAL_RESULT)
+ if (result_type() == DECIMAL_RESULT)
{
my_decimal value, *arg_val= args[0]->val_decimal(&value);
if (!args[0]->null_value)
@@ -2671,15 +3087,22 @@ void Item_sum_avg::update_field()
Item *Item_sum_avg::result_item(THD *thd, Field *field)
{
return
- Item_sum_avg::result_type() == DECIMAL_RESULT ?
+ result_type() == DECIMAL_RESULT ?
(Item_avg_field*) new (thd->mem_root) Item_avg_field_decimal(thd, this) :
(Item_avg_field*) new (thd->mem_root) Item_avg_field_double(thd, this);
}
-void Item_sum_hybrid::update_field()
+void Item_sum_min_max::update_field()
{
- switch (Item_sum_hybrid::result_type()) {
+ DBUG_ENTER("Item_sum_min_max::update_field");
+ Item *UNINIT_VAR(tmp_item);
+ if (unlikely(direct_added))
+ {
+ tmp_item= args[0];
+ args[0]= direct_item;
+ }
+ switch (result_type()) {
case STRING_RESULT:
min_max_update_str_field();
break;
@@ -2692,12 +3115,19 @@ void Item_sum_hybrid::update_field()
default:
min_max_update_real_field();
}
+ if (unlikely(direct_added))
+ {
+ direct_added= FALSE;
+ args[0]= tmp_item;
+ }
+ DBUG_VOID_RETURN;
}
void
-Item_sum_hybrid::min_max_update_str_field()
+Item_sum_min_max::min_max_update_str_field()
{
+ DBUG_ENTER("Item_sum_min_max::min_max_update_str_field");
DBUG_ASSERT(cmp);
String *res_str=args[0]->val_str(&cmp->value1);
@@ -2713,14 +3143,16 @@ Item_sum_hybrid::min_max_update_str_field()
}
result_field->set_notnull();
}
+ DBUG_VOID_RETURN;
}
void
-Item_sum_hybrid::min_max_update_real_field()
+Item_sum_min_max::min_max_update_real_field()
{
double nr,old_nr;
+ DBUG_ENTER("Item_sum_min_max::min_max_update_real_field");
old_nr=result_field->val_real();
nr= args[0]->val_real();
if (!args[0]->null_value)
@@ -2733,14 +3165,16 @@ Item_sum_hybrid::min_max_update_real_field()
else if (result_field->is_null(0))
result_field->set_null();
result_field->store(old_nr);
+ DBUG_VOID_RETURN;
}
void
-Item_sum_hybrid::min_max_update_int_field()
+Item_sum_min_max::min_max_update_int_field()
{
longlong nr,old_nr;
+ DBUG_ENTER("Item_sum_min_max::min_max_update_int_field");
old_nr=result_field->val_int();
nr=args[0]->val_int();
if (!args[0]->null_value)
@@ -2760,7 +3194,9 @@ Item_sum_hybrid::min_max_update_int_field()
}
else if (result_field->is_null(0))
result_field->set_null();
+ DBUG_PRINT("info", ("nr: %lld", old_nr));
result_field->store(old_nr, unsigned_flag);
+ DBUG_VOID_RETURN;
}
@@ -2769,8 +3205,9 @@ Item_sum_hybrid::min_max_update_int_field()
optimize: do not get result_field in case of args[0] is NULL
*/
void
-Item_sum_hybrid::min_max_update_decimal_field()
+Item_sum_min_max::min_max_update_decimal_field()
{
+ DBUG_ENTER("Item_sum_min_max::min_max_update_decimal_field");
my_decimal old_val, nr_val;
const my_decimal *old_nr;
const my_decimal *nr= args[0]->val_decimal(&nr_val);
@@ -2791,6 +3228,7 @@ Item_sum_hybrid::min_max_update_decimal_field()
}
else if (result_field->is_null(0))
result_field->set_null();
+ DBUG_VOID_RETURN;
}
@@ -2914,7 +3352,7 @@ double Item_sum_udf_float::val_real()
double res;
DBUG_ASSERT(fixed == 1);
DBUG_ENTER("Item_sum_udf_float::val");
- DBUG_PRINT("info",("result_type: %d arg_count: %d",
+ DBUG_PRINT("enter",("result_type: %d arg_count: %d",
args[0]->result_type(), arg_count));
res= udf.val(&tmp_null_value);
null_value= tmp_null_value;
@@ -2958,7 +3396,7 @@ my_decimal *Item_sum_udf_decimal::val_decimal(my_decimal *dec_buf)
my_bool tmp_null_value;
DBUG_ASSERT(fixed == 1);
DBUG_ENTER("Item_func_udf_decimal::val_decimal");
- DBUG_PRINT("info",("result_type: %d arg_count: %d",
+ DBUG_PRINT("enter",("result_type: %d arg_count: %d",
args[0]->result_type(), arg_count));
res= udf.val_decimal(&tmp_null_value, dec_buf);
@@ -2984,7 +3422,7 @@ longlong Item_sum_udf_int::val_int()
longlong res;
DBUG_ASSERT(fixed == 1);
DBUG_ENTER("Item_sum_udf_int::val_int");
- DBUG_PRINT("info",("result_type: %d arg_count: %d",
+ DBUG_PRINT("enter",("result_type: %d arg_count: %d",
args[0]->result_type(), arg_count));
res= udf.val_int(&tmp_null_value);
null_value= tmp_null_value;
@@ -3178,13 +3616,28 @@ int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)),
Item **arg= item->args, **arg_end= item->args + item->arg_count_field;
uint old_length= result->length();
- if (item->no_appended)
- item->no_appended= FALSE;
- else
- result->append(*item->separator);
+ ulonglong *offset_limit= &item->copy_offset_limit;
+ ulonglong *row_limit = &item->copy_row_limit;
+ if (item->limit_clause && !(*row_limit))
+ {
+ item->result_finalized= true;
+ return 1;
+ }
tmp.length(0);
+ if (item->limit_clause && (*offset_limit))
+ {
+ item->row_count++;
+ (*offset_limit)--;
+ return 0;
+ }
+
+ if (!item->result_finalized)
+ item->result_finalized= true;
+ else
+ result->append(*item->separator);
+
for (; arg < arg_end; arg++)
{
String *res;
@@ -3214,6 +3667,8 @@ int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)),
result->append(*res);
}
+ if (item->limit_clause)
+ (*row_limit)--;
item->row_count++;
/* stop if length of result more than max_length */
@@ -3227,7 +3682,7 @@ int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)),
as this is never used to limit the length of the data.
Cut is done with the third argument.
*/
- uint add_length= Well_formed_prefix(cs,
+ size_t add_length= Well_formed_prefix(cs,
ptr + old_length,
ptr + max_length,
result->length()).length();
@@ -3262,7 +3717,8 @@ Item_func_group_concat::
Item_func_group_concat(THD *thd, Name_resolution_context *context_arg,
bool distinct_arg, List<Item> *select_list,
const SQL_I_List<ORDER> &order_list,
- String *separator_arg)
+ String *separator_arg, bool limit_clause,
+ Item *row_limit_arg, Item *offset_limit_arg)
:Item_sum(thd), tmp_table_param(0), separator(separator_arg), tree(0),
unique_filter(NULL), table(0),
order(0), context(context_arg),
@@ -3271,7 +3727,9 @@ Item_func_group_concat(THD *thd, Name_resolution_context *context_arg,
row_count(0),
distinct(distinct_arg),
warning_for_row(FALSE),
- force_copy_fields(0), original(0)
+ force_copy_fields(0), row_limit(NULL),
+ offset_limit(NULL), limit_clause(limit_clause),
+ copy_offset_limit(0), copy_row_limit(0), original(0)
{
Item *item_select;
Item **arg_ptr;
@@ -3313,6 +3771,11 @@ Item_func_group_concat(THD *thd, Name_resolution_context *context_arg,
/* orig_args is only used for print() */
orig_args= (Item**) (order + arg_count_order);
memcpy(orig_args, args, sizeof(Item*) * arg_count);
+ if (limit_clause)
+ {
+ row_limit= row_limit_arg;
+ offset_limit= offset_limit_arg;
+ }
}
@@ -3333,7 +3796,9 @@ Item_func_group_concat::Item_func_group_concat(THD *thd,
warning_for_row(item->warning_for_row),
always_null(item->always_null),
force_copy_fields(item->force_copy_fields),
- original(item)
+ row_limit(item->row_limit), offset_limit(item->offset_limit),
+ limit_clause(item->limit_clause),copy_offset_limit(item->copy_offset_limit),
+ copy_row_limit(item->copy_row_limit), original(item)
{
quick_group= item->quick_group;
result.set_charset(collation.collation);
@@ -3387,7 +3852,7 @@ void Item_func_group_concat::cleanup()
table= 0;
if (tree)
{
- delete_tree(tree);
+ delete_tree(tree, 0);
tree= 0;
}
if (unique_filter)
@@ -3415,24 +3880,6 @@ void Item_func_group_concat::cleanup()
}
-Field *Item_func_group_concat::make_string_field(TABLE *table_arg)
-{
- Field *field;
- DBUG_ASSERT(collation.collation);
- if (too_big_for_varchar())
- field= new Field_blob(max_length,
- maybe_null, name, collation.collation, TRUE);
- else
- field= new Field_varstring(max_length,
- maybe_null, name, table_arg->s,
- collation.collation);
-
- if (field)
- field->init(table_arg);
- return field;
-}
-
-
Item *Item_func_group_concat::copy_or_same(THD* thd)
{
return new (thd->mem_root) Item_func_group_concat(thd, this);
@@ -3445,7 +3892,11 @@ void Item_func_group_concat::clear()
result.copy();
null_value= TRUE;
warning_for_row= FALSE;
- no_appended= TRUE;
+ result_finalized= FALSE;
+ if (offset_limit)
+ copy_offset_limit= offset_limit->val_int();
+ if (row_limit)
+ copy_row_limit= row_limit->val_int();
if (tree)
{
reset_tree(tree);
@@ -3500,10 +3951,10 @@ bool Item_func_group_concat::repack_tree(THD *thd)
tree_walk(tree, &copy_to_tree, &st, left_root_right);
if (st.len <= st.maxlen) // Copying aborted. Must be OOM
{
- delete_tree(&st.tree);
+ delete_tree(&st.tree, 0);
return 1;
}
- delete_tree(tree);
+ delete_tree(tree, 0);
*tree= st.tree;
tree_len= st.len;
return 0;
@@ -3574,13 +4025,12 @@ bool Item_func_group_concat::add()
return 1;
tree_len+= row_str_len;
}
+
/*
- If the row is not a duplicate (el->count == 1)
- we can dump the row here in case of GROUP_CONCAT(DISTINCT...)
- instead of doing tree traverse later.
+ In case of GROUP_CONCAT with DISTINCT or ORDER BY (or both) don't dump the
+ row to the output buffer here. That will be done in val_str.
*/
- if (row_eligible && !warning_for_row &&
- (!tree || (el->count == 1 && distinct && !arg_count_order)))
+ if (row_eligible && !warning_for_row && (!tree && !distinct))
dump_leaf_key(table->record[0] + table->s->null_bytes, 1, this);
return 0;
@@ -3604,11 +4054,9 @@ Item_func_group_concat::fix_fields(THD *thd, Item **ref)
for (i=0 ; i < arg_count ; i++)
{
- if ((!args[i]->fixed &&
- args[i]->fix_fields(thd, args + i)) ||
- args[i]->check_cols(1))
+ if (args[i]->fix_fields_if_needed_for_scalar(thd, &args[i]))
return TRUE;
- with_subselect|= args[i]->with_subselect;
+ m_with_subquery|= args[i]->with_subquery();
with_param|= args[i]->with_param;
with_window_func|= args[i]->with_window_func;
}
@@ -3749,7 +4197,7 @@ bool Item_func_group_concat::setup(THD *thd)
(ORDER*) 0, 0, TRUE,
(select_lex->options |
thd->variables.option_bits),
- HA_POS_ERROR, (char*) "")))
+ HA_POS_ERROR, &empty_clex_str)))
DBUG_RETURN(TRUE);
table->file->extra(HA_EXTRA_NO_ROWS);
table->no_rows= 1;
@@ -3789,6 +4237,12 @@ bool Item_func_group_concat::setup(THD *thd)
(void*)this,
tree_key_length,
ram_limitation(thd));
+ if ((row_limit && row_limit->cmp_type() != INT_RESULT) ||
+ (offset_limit && offset_limit->cmp_type() != INT_RESULT))
+ {
+ my_error(ER_INVALID_VALUE_TO_LIMIT, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
DBUG_RETURN(FALSE);
}
@@ -3811,9 +4265,18 @@ String* Item_func_group_concat::val_str(String* str)
DBUG_ASSERT(fixed == 1);
if (null_value)
return 0;
- if (no_appended && tree)
- /* Tree is used for sorting as in ORDER BY */
- tree_walk(tree, &dump_leaf_key, this, left_root_right);
+
+ if (!result_finalized) // Result yet to be written.
+ {
+ if (tree != NULL) // order by
+ tree_walk(tree, &dump_leaf_key, this, left_root_right);
+ else if (distinct) // distinct (and no order by).
+ unique_filter->walk(table, &dump_leaf_key, this);
+ else if (row_limit && copy_row_limit == (ulonglong)row_limit->val_int())
+ return &result;
+ else
+ DBUG_ASSERT(false); // Can't happen
+ }
if (table && table->blob_storage &&
table->blob_storage->is_truncated_value())
@@ -3855,7 +4318,19 @@ void Item_func_group_concat::print(String *str, enum_query_type query_type)
}
str->append(STRING_WITH_LEN(" separator \'"));
str->append_for_single_quote(separator->ptr(), separator->length());
- str->append(STRING_WITH_LEN("\')"));
+ str->append(STRING_WITH_LEN("\'"));
+
+ if (limit_clause)
+ {
+ str->append(STRING_WITH_LEN(" limit "));
+ if (offset_limit)
+ {
+ offset_limit->print(str, query_type);
+ str->append(',');
+ }
+ row_limit->print(str, query_type);
+ }
+ str->append(STRING_WITH_LEN(")"));
}
diff --git a/sql/item_sum.h b/sql/item_sum.h
index 4be8ea58742..48565ece5a8 100644
--- a/sql/item_sum.h
+++ b/sql/item_sum.h
@@ -354,7 +354,8 @@ public:
VARIANCE_FUNC, SUM_BIT_FUNC, UDF_SUM_FUNC, GROUP_CONCAT_FUNC,
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
+ NTH_VALUE_FUNC, LEAD_FUNC, LAG_FUNC, PERCENTILE_CONT_FUNC,
+ PERCENTILE_DISC_FUNC, SP_AGGREGATE_FUNC
};
Item **ref_by; /* pointer to a ref to the object used to register it */
@@ -456,7 +457,6 @@ public:
Updated value is then saved in the field.
*/
virtual void update_field()=0;
- virtual bool keep_field_type(void) const { return 0; }
virtual bool fix_length_and_dec()
{ maybe_null=1; null_value=1; return FALSE; }
virtual Item *result_item(THD *thd, Field *field);
@@ -511,10 +511,7 @@ public:
}
virtual void make_unique() { force_copy_fields= TRUE; }
Item *get_tmp_table_item(THD *thd);
- Field *create_tmp_field(bool group, TABLE *table)
- {
- return Item::create_tmp_field(group, table, MY_INT32_NUM_DECIMAL_DIGITS);
- }
+ Field *create_tmp_field(bool group, TABLE *table);
virtual bool collect_outer_ref_processor(void *param);
bool init_sum_func_check(THD *thd);
bool check_sum_func(THD *thd, Item **ref);
@@ -522,9 +519,10 @@ public:
st_select_lex *depended_from()
{ return (nest_level == aggr_level ? 0 : aggr_sel); }
- Item *get_arg(uint i) { return args[i]; }
+ Item *get_arg(uint i) const { return args[i]; }
Item *set_arg(uint i, THD *thd, Item *new_val);
uint get_arg_count() const { return arg_count; }
+ virtual Item **get_args() { return fixed ? orig_args : args; }
/* Initialization of distinct related members */
void init_aggregator()
@@ -580,6 +578,7 @@ public:
void mark_as_window_func_sum_expr() { window_func_sum_expr_flag= true; }
bool is_window_func_sum_expr() { return window_func_sum_expr_flag; }
virtual void setup_caches(THD *thd) {};
+ virtual void set_partition_row_count(ulonglong count) { DBUG_ASSERT(0); }
};
@@ -715,32 +714,48 @@ public:
class Item_sum_num :public Item_sum
{
-protected:
- /*
- val_xxx() functions may be called several times during the execution of a
- query. Derived classes that require extensive calculation in val_xxx()
- maintain cache of aggregate value. This variable governs the validity of
- that cache.
- */
- bool is_evaluated;
public:
- Item_sum_num(THD *thd): Item_sum(thd), is_evaluated(FALSE) {}
+ Item_sum_num(THD *thd): Item_sum(thd) {}
Item_sum_num(THD *thd, Item *item_par):
- Item_sum(thd, item_par), is_evaluated(FALSE) {}
+ Item_sum(thd, item_par) {}
Item_sum_num(THD *thd, Item *a, Item* b):
- Item_sum(thd, a, b), is_evaluated(FALSE) {}
+ Item_sum(thd, a, b) {}
Item_sum_num(THD *thd, List<Item> &list):
- Item_sum(thd, list), is_evaluated(FALSE) {}
+ Item_sum(thd, list) {}
Item_sum_num(THD *thd, Item_sum_num *item):
- Item_sum(thd, item),is_evaluated(item->is_evaluated) {}
+ Item_sum(thd, item) {}
bool fix_fields(THD *, Item **);
- longlong val_int() { return val_int_from_real(); /* Real as default */ }
- String *val_str(String*str);
- my_decimal *val_decimal(my_decimal *);
void reset_field();
};
+class Item_sum_double :public Item_sum_num
+{
+public:
+ Item_sum_double(THD *thd): Item_sum_num(thd) {}
+ Item_sum_double(THD *thd, Item *item_par): Item_sum_num(thd, item_par) {}
+ Item_sum_double(THD *thd, List<Item> &list): Item_sum_num(thd, list) {}
+ Item_sum_double(THD *thd, Item_sum_double *item) :Item_sum_num(thd, item) {}
+ longlong val_int()
+ {
+ return val_int_from_real();
+ }
+ String *val_str(String*str)
+ {
+ return val_string_from_real(str);
+ }
+ my_decimal *val_decimal(my_decimal *to)
+ {
+ return val_decimal_from_real(to);
+ }
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ {
+ return get_date_from_real(ltime, fuzzydate);
+ }
+ const Type_handler *type_handler() const { return &type_handler_double; }
+};
+
+
class Item_sum_int :public Item_sum_num
{
public:
@@ -751,8 +766,11 @@ public:
double val_real() { DBUG_ASSERT(fixed == 1); return (double) val_int(); }
String *val_str(String*str);
my_decimal *val_decimal(my_decimal *);
- enum Item_result result_type () const { return INT_RESULT; }
- enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; }
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ {
+ return get_date_from_int(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; }
};
@@ -762,14 +780,20 @@ class Item_sum_sum :public Item_sum_num,
public Type_handler_hybrid_field_type
{
protected:
+ bool direct_added;
+ bool direct_reseted_field;
+ bool direct_sum_is_null;
+ double direct_sum_real;
double sum;
+ my_decimal direct_sum_decimal;
my_decimal dec_buffs[2];
uint curr_dec_buff;
bool fix_length_and_dec();
public:
Item_sum_sum(THD *thd, Item *item_par, bool distinct):
- Item_sum_num(thd, item_par)
+ Item_sum_num(thd, item_par), direct_added(FALSE),
+ direct_reseted_field(FALSE)
{
set_distinct(distinct);
}
@@ -778,18 +802,23 @@ public:
{
return has_with_distinct() ? SUM_DISTINCT_FUNC : SUM_FUNC;
}
+ void cleanup();
+ void direct_add(my_decimal *add_sum_decimal);
+ void direct_add(double add_sum_real, bool add_sum_is_null);
void clear();
bool add();
double val_real();
longlong val_int();
String *val_str(String*str);
my_decimal *val_decimal(my_decimal *);
- enum_field_types field_type() const
- { return Type_handler_hybrid_field_type::field_type(); }
- enum Item_result result_type () const
- { return Type_handler_hybrid_field_type::result_type(); }
- enum Item_result cmp_type () const
- { return Type_handler_hybrid_field_type::cmp_type(); }
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ {
+ return type_handler()->Item_get_date(this, ltime, fuzzydate);
+ }
+ const Type_handler *type_handler() const
+ { return Type_handler_hybrid_field_type::type_handler(); }
+ void fix_length_and_dec_double();
+ void fix_length_and_dec_decimal();
void reset_field();
void update_field();
void no_rows_in_result() {}
@@ -799,8 +828,8 @@ public:
}
Item *copy_or_same(THD* thd);
void remove();
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_sum_sum>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_sum_sum>(thd, this); }
bool supports_removal() const
{
@@ -815,6 +844,9 @@ private:
class Item_sum_count :public Item_sum_int
{
+ bool direct_counted;
+ bool direct_reseted_field;
+ longlong direct_count;
longlong count;
friend class Aggregator_distinct;
@@ -824,9 +856,10 @@ class Item_sum_count :public Item_sum_int
void cleanup();
void remove();
- public:
+public:
Item_sum_count(THD *thd, Item *item_par):
- Item_sum_int(thd, item_par), count(0)
+ Item_sum_int(thd, item_par), direct_counted(FALSE),
+ direct_reseted_field(FALSE), count(0)
{}
/**
@@ -838,12 +871,14 @@ class Item_sum_count :public Item_sum_int
*/
Item_sum_count(THD *thd, List<Item> &list):
- Item_sum_int(thd, list), count(0)
+ Item_sum_int(thd, list), direct_counted(FALSE),
+ direct_reseted_field(FALSE), count(0)
{
set_distinct(TRUE);
}
Item_sum_count(THD *thd, Item_sum_count *item):
- Item_sum_int(thd, item), count(item->count)
+ Item_sum_int(thd, item), direct_counted(FALSE),
+ direct_reseted_field(FALSE), count(item->count)
{}
enum Sumfunctype sum_func () const
{
@@ -858,13 +893,14 @@ class Item_sum_count :public Item_sum_int
longlong val_int();
void reset_field();
void update_field();
+ void direct_add(longlong add_count);
const char *func_name() const
{
return has_with_distinct() ? "count(distinct " : "count(";
}
Item *copy_or_same(THD* thd);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_sum_count>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_sum_count>(thd, this); }
bool supports_removal() const
{
@@ -889,6 +925,8 @@ public:
:Item_sum_sum(thd, item), count(item->count),
prec_increment(item->prec_increment) {}
+ void fix_length_and_dec_double();
+ void fix_length_and_dec_decimal();
bool fix_length_and_dec();
enum Sumfunctype sum_func () const
{
@@ -917,8 +955,8 @@ public:
count= 0;
Item_sum_sum::cleanup();
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_sum_avg>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_sum_avg>(thd, this); }
bool supports_removal() const
{
@@ -947,7 +985,7 @@ But, this falls prey to catastrophic cancellation. Instead, use the recurrence
*/
-class Item_sum_variance : public Item_sum_num
+class Item_sum_variance : public Item_sum_double
{
bool fix_length_and_dec();
@@ -958,15 +996,16 @@ public:
uint prec_increment;
Item_sum_variance(THD *thd, Item *item_par, uint sample_arg):
- Item_sum_num(thd, item_par), count(0),
+ Item_sum_double(thd, item_par), count(0),
sample(sample_arg)
{}
Item_sum_variance(THD *thd, Item_sum_variance *item);
enum Sumfunctype sum_func () const { return VARIANCE_FUNC; }
+ void fix_length_and_dec_double();
+ void fix_length_and_dec_decimal();
void clear();
bool add();
double val_real();
- my_decimal *val_decimal(my_decimal *);
void reset_field();
void update_field();
Item *result_item(THD *thd, Field *field);
@@ -975,15 +1014,13 @@ public:
{ return sample ? "var_samp(" : "variance("; }
Item *copy_or_same(THD* thd);
Field *create_tmp_field(bool group, TABLE *table);
- enum Item_result result_type () const { return REAL_RESULT; }
- enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE;}
void cleanup()
{
count= 0;
- Item_sum_num::cleanup();
+ Item_sum_double::cleanup();
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_sum_variance>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_sum_variance>(thd, this); }
};
/*
@@ -1003,51 +1040,76 @@ class Item_sum_std :public Item_sum_variance
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, MEM_ROOT *mem_root)
- { return get_item_copy<Item_sum_std>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_sum_std>(thd, this); }
+};
+
+
+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)
+ { 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)
+ { collation.set(&my_charset_bin); }
+ Item_sum_hybrid(THD *thd, Item_sum_hybrid *item)
+ :Item_sum(thd, item),
+ Type_handler_hybrid_field_type(item)
+ { }
+ const Type_handler *type_handler() const
+ { return Type_handler_hybrid_field_type::type_handler(); }
+ bool fix_length_and_dec_generic();
+ bool fix_length_and_dec_numeric(const Type_handler *h);
+ bool fix_length_and_dec_string();
};
+
// This class is a string or number function depending on num_func
class Arg_comparator;
class Item_cache;
-class Item_sum_hybrid :public Item_sum, public Type_handler_hybrid_field_type
+class Item_sum_min_max :public Item_sum_hybrid
{
protected:
+ bool direct_added;
+ Item *direct_item;
Item_cache *value, *arg_cache;
Arg_comparator *cmp;
int cmp_sign;
bool was_values; // Set if we have found at least one row (for max/min only)
bool was_null_value;
- public:
- Item_sum_hybrid(THD *thd, Item *item_par,int sign):
- Item_sum(thd, item_par),
- Type_handler_hybrid_field_type(MYSQL_TYPE_LONGLONG),
- value(0), arg_cache(0), cmp(0),
+public:
+ Item_sum_min_max(THD *thd, Item *item_par,int sign):
+ Item_sum_hybrid(thd, item_par),
+ direct_added(FALSE), value(0), arg_cache(0), cmp(0),
cmp_sign(sign), was_values(TRUE)
{ collation.set(&my_charset_bin); }
- Item_sum_hybrid(THD *thd, Item_sum_hybrid *item)
- :Item_sum(thd, item),
- Type_handler_hybrid_field_type(item),
- value(item->value), arg_cache(0),
+ Item_sum_min_max(THD *thd, Item_sum_min_max *item)
+ :Item_sum_hybrid(thd, item),
+ direct_added(FALSE), value(item->value), arg_cache(0),
cmp_sign(item->cmp_sign), was_values(item->was_values)
{ }
bool fix_fields(THD *, Item **);
+ bool fix_length_and_dec();
void setup_hybrid(THD *thd, Item *item, Item *value_arg);
void clear();
+ void direct_add(Item *item);
double val_real();
longlong val_int();
my_decimal *val_decimal(my_decimal *);
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
void reset_field();
String *val_str(String *);
- bool keep_field_type(void) const { return 1; }
- enum Item_result result_type () const
- { return Type_handler_hybrid_field_type::result_type(); }
- enum Item_result cmp_type () const
- { return Type_handler_hybrid_field_type::cmp_type(); }
- enum enum_field_types field_type() const
- { return Type_handler_hybrid_field_type::field_type(); }
+ const Type_handler *real_type_handler() const
+ {
+ return get_arg(0)->real_type_handler();
+ }
+ TYPELIB *get_typelib() const { return args[0]->get_typelib(); }
void update_field();
void min_max_update_str_field();
void min_max_update_real_field();
@@ -1062,33 +1124,33 @@ protected:
};
-class Item_sum_min :public Item_sum_hybrid
+class Item_sum_min :public Item_sum_min_max
{
public:
- Item_sum_min(THD *thd, Item *item_par): Item_sum_hybrid(thd, item_par, 1) {}
- Item_sum_min(THD *thd, Item_sum_min *item) :Item_sum_hybrid(thd, item) {}
+ Item_sum_min(THD *thd, Item *item_par): Item_sum_min_max(thd, item_par, 1) {}
+ Item_sum_min(THD *thd, Item_sum_min *item) :Item_sum_min_max(thd, item) {}
enum Sumfunctype sum_func () const {return MIN_FUNC;}
bool add();
const char *func_name() const { return "min("; }
Item *copy_or_same(THD* thd);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_sum_min>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_sum_min>(thd, this); }
};
-class Item_sum_max :public Item_sum_hybrid
+class Item_sum_max :public Item_sum_min_max
{
public:
- Item_sum_max(THD *thd, Item *item_par): Item_sum_hybrid(thd, item_par, -1) {}
- Item_sum_max(THD *thd, Item_sum_max *item) :Item_sum_hybrid(thd, item) {}
+ Item_sum_max(THD *thd, Item *item_par): Item_sum_min_max(thd, item_par, -1) {}
+ Item_sum_max(THD *thd, Item_sum_max *item) :Item_sum_min_max(thd, item) {}
enum Sumfunctype sum_func () const {return MAX_FUNC;}
bool add();
const char *func_name() const { return "max("; }
Item *copy_or_same(THD* thd);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_sum_max>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_sum_max>(thd, this); }
};
@@ -1146,7 +1208,7 @@ public:
}
protected:
- static const int NUM_BIT_COUNTERS= 64;
+ enum bit_counters { NUM_BIT_COUNTERS= 64 };
ulonglong reset_bits,bits;
/*
Marks whether the function is to be computed as a window function.
@@ -1171,8 +1233,8 @@ public:
bool add();
const char *func_name() const { return "bit_or("; }
Item *copy_or_same(THD* thd);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_sum_or>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_sum_or>(thd, this); }
private:
void set_bits_from_counters();
@@ -1188,8 +1250,8 @@ public:
bool add();
const char *func_name() const { return "bit_and("; }
Item *copy_or_same(THD* thd);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_sum_and>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_sum_and>(thd, this); }
private:
void set_bits_from_counters();
@@ -1203,13 +1265,149 @@ public:
bool add();
const char *func_name() const { return "bit_xor("; }
Item *copy_or_same(THD* thd);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_sum_xor>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_sum_xor>(thd, this); }
private:
void set_bits_from_counters();
};
+class sp_head;
+class sp_name;
+class Query_arena;
+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
+ instruction. This cursor is what makes custom stored aggregates possible.
+
+ During computation the function's add method is called. This in turn performs
+ an execution of the function. The function will execute from the current
+ function context (and instruction), if one exists, or from the start if not.
+ See Item_sp for more details.
+
+ Upon encounter of FETCH GROUP NEXT ROW instruction, the function will pause
+ execution. We assume that the user has performed the necessary additions for
+ a row, between two encounters of FETCH GROUP NEXT ROW.
+
+ Example:
+ create aggregate function f1(x INT) returns int
+ begin
+ declare continue handler for not found return s;
+ declare s int default 0
+ loop
+ fetch group next row;
+ set s = s + x;
+ end loop;
+ end
+
+ The function will always stop after an encounter of FETCH GROUP NEXT ROW,
+ except (!) on first encounter, as the value for the first row in the
+ 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
+ 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
+ the values for that particular row have been added to the aggregation.
+
+ To produce values for val_xxx methods we need an extra syntactic construct.
+ We require a continue handler when "no more rows are available". val_xxx
+ methods force a function return by executing the function again, while
+ setting a server flag that no more rows have been found. This implies
+ that val_xxx methods should only be called once per group however.
+
+ Example:
+ DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN ret_val;
+*/
+class Item_sum_sp :public Item_sum,
+ public Item_sp
+{
+ private:
+ bool execute();
+
+public:
+ Item_sum_sp(THD *thd, Name_resolution_context *context_arg, sp_name *name,
+ sp_head *sp);
+
+ Item_sum_sp(THD *thd, Name_resolution_context *context_arg, sp_name *name,
+ sp_head *sp, List<Item> &list);
+ Item_sum_sp(THD *thd, Item_sum_sp *item);
+
+ enum Sumfunctype sum_func () const
+ {
+ return SP_AGGREGATE_FUNC;
+ }
+ Field *create_field_for_create_select(TABLE *table)
+ {
+ return create_table_field_from_handler(table);
+ }
+ bool fix_length_and_dec();
+ bool fix_fields(THD *thd, Item **ref);
+ const char *func_name() const;
+ const Type_handler *type_handler() const;
+ bool add();
+
+ /* val_xx functions */
+ longlong val_int()
+ {
+ if(execute())
+ return 0;
+ return sp_result_field->val_int();
+ }
+
+ double val_real()
+ {
+ if(execute())
+ return 0.0;
+ return sp_result_field->val_real();
+ }
+
+ my_decimal *val_decimal(my_decimal *dec_buf)
+ {
+ if(execute())
+ return NULL;
+ return sp_result_field->val_decimal(dec_buf);
+ }
+
+ String *val_str(String *str)
+ {
+ String buf;
+ char buff[20];
+ buf.set(buff, 20, str->charset());
+ buf.length(0);
+ if (execute())
+ return NULL;
+ /*
+ result_field will set buf pointing to internal buffer
+ of the resul_field. Due to this it will change any time
+ when SP is executed. In order to prevent occasional
+ corruption of returned value, we make here a copy.
+ */
+ sp_result_field->val_str(&buf);
+ str->copy(buf);
+ return str;
+ }
+ void reset_field(){DBUG_ASSERT(0);}
+ void update_field(){DBUG_ASSERT(0);}
+ void clear();
+ void cleanup();
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ {
+ return execute() || sp_result_field->get_date(ltime, fuzzydate);
+ }
+ inline Field *get_sp_result_field()
+ {
+ return sp_result_field;
+ }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_sum_sp>(thd, this); }
+ Item *copy_or_same(THD *thd);
+};
/* Items to get the value of a stored sum function */
@@ -1232,7 +1430,11 @@ public:
void save_in_result_field(bool no_conversions) { DBUG_ASSERT(0); }
bool check_vcol_func_processor(void *arg)
{
- return mark_unsupported_function(name, arg, VCOL_IMPOSSIBLE);
+ return mark_unsupported_function(name.str, arg, VCOL_IMPOSSIBLE);
+ }
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ {
+ return type_handler()->Item_get_date(this, ltime, fuzzydate);
}
};
@@ -1256,14 +1458,13 @@ public:
Item_avg_field_double(THD *thd, Item_sum_avg *item)
:Item_avg_field(thd, item)
{ }
- enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; }
- enum Item_result result_type () const { return REAL_RESULT; }
+ const Type_handler *type_handler() const { return &type_handler_double; }
longlong val_int() { return val_int_from_real(); }
my_decimal *val_decimal(my_decimal *dec) { return val_decimal_from_real(dec); }
String *val_str(String *str) { return val_string_from_real(str); }
double val_real();
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_avg_field_double>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_avg_field_double>(thd, this); }
};
@@ -1277,14 +1478,13 @@ public:
f_scale(item->f_scale),
dec_bin_size(item->dec_bin_size)
{ }
- enum_field_types field_type() const { return MYSQL_TYPE_NEWDECIMAL; }
- enum Item_result result_type () const { return DECIMAL_RESULT; }
+ const Type_handler *type_handler() const { return &type_handler_newdecimal; }
double val_real() { return val_real_from_decimal(); }
longlong val_int() { return val_int_from_decimal(); }
String *val_str(String *str) { return val_string_from_decimal(str); }
my_decimal *val_decimal(my_decimal *);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_avg_field_decimal>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_avg_field_decimal>(thd, this); }
};
@@ -1303,10 +1503,9 @@ public:
my_decimal *val_decimal(my_decimal *dec_buf)
{ return val_decimal_from_real(dec_buf); }
bool is_null() { update_null_value(); return null_value; }
- enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; }
- enum Item_result result_type () const { return REAL_RESULT; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_variance_field>(thd, mem_root, this); }
+ const Type_handler *type_handler() const { return &type_handler_double; }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_variance_field>(thd, this); }
};
@@ -1318,8 +1517,8 @@ public:
{ }
enum Type type() const { return FIELD_STD_ITEM; }
double val_real();
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_std_field>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_std_field>(thd, this); }
};
@@ -1386,6 +1585,10 @@ public:
void update_field() {};
void cleanup();
virtual void print(String *str, enum_query_type query_type);
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ {
+ return type_handler()->Item_get_date(this, ltime, fuzzydate);
+ }
};
@@ -1402,13 +1605,11 @@ class Item_sum_udf_float :public Item_udf_sum
double val_real();
String *val_str(String*str);
my_decimal *val_decimal(my_decimal *);
- enum Item_result result_type () const { return REAL_RESULT; }
- enum Item_result cmp_type () const { return REAL_RESULT; }
- enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; }
+ const Type_handler *type_handler() const { return &type_handler_double; }
bool fix_length_and_dec() { fix_num_length_and_dec(); return FALSE; }
Item *copy_or_same(THD* thd);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_sum_udf_float>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_sum_udf_float>(thd, this); }
};
@@ -1426,12 +1627,11 @@ public:
{ DBUG_ASSERT(fixed == 1); return (double) Item_sum_udf_int::val_int(); }
String *val_str(String*str);
my_decimal *val_decimal(my_decimal *);
- enum Item_result result_type () const { return INT_RESULT; }
- enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; }
+ const Type_handler *type_handler() const { return &type_handler_longlong; }
bool fix_length_and_dec() { decimals=0; max_length=21; return FALSE; }
Item *copy_or_same(THD* thd);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_sum_udf_int>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_sum_udf_int>(thd, this); }
};
@@ -1468,12 +1668,11 @@ public:
return cs->cset->strtoll10(cs, res->ptr(), &end, &err_not_used);
}
my_decimal *val_decimal(my_decimal *dec);
- enum Item_result result_type () const { return STRING_RESULT; }
- enum_field_types field_type() const { return string_field_type(); }
+ const Type_handler *type_handler() const { return string_type_handler(); }
bool fix_length_and_dec();
Item *copy_or_same(THD* thd);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_sum_udf_str>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_sum_udf_str>(thd, this); }
};
@@ -1490,25 +1689,24 @@ public:
double val_real();
longlong val_int();
my_decimal *val_decimal(my_decimal *);
- enum Item_result result_type () const { return DECIMAL_RESULT; }
- enum_field_types field_type() const { return MYSQL_TYPE_NEWDECIMAL; }
+ const Type_handler *type_handler() const { return &type_handler_newdecimal; }
bool fix_length_and_dec() { fix_num_length_and_dec(); return FALSE; }
Item *copy_or_same(THD* thd);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_sum_udf_decimal>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_sum_udf_decimal>(thd, this); }
};
#else /* Dummy functions to get sql_yacc.cc compiled */
-class Item_sum_udf_float :public Item_sum_num
+class Item_sum_udf_float :public Item_sum_double
{
public:
Item_sum_udf_float(THD *thd, udf_func *udf_arg):
- Item_sum_num(thd) {}
+ Item_sum_double(thd) {}
Item_sum_udf_float(THD *thd, udf_func *udf_arg, List<Item> &list):
- Item_sum_num(thd) {}
+ Item_sum_double(thd) {}
Item_sum_udf_float(THD *thd, Item_sum_udf_float *item)
- :Item_sum_num(thd, item) {}
+ :Item_sum_double(thd, item) {}
enum Sumfunctype sum_func () const { return UDF_SUM_FUNC; }
double val_real() { DBUG_ASSERT(fixed == 1); return 0.0; }
void clear() {}
@@ -1517,15 +1715,15 @@ class Item_sum_udf_float :public Item_sum_num
};
-class Item_sum_udf_int :public Item_sum_num
+class Item_sum_udf_int :public Item_sum_double
{
public:
Item_sum_udf_int(THD *thd, udf_func *udf_arg):
- Item_sum_num(thd) {}
+ Item_sum_double(thd) {}
Item_sum_udf_int(THD *thd, udf_func *udf_arg, List<Item> &list):
- Item_sum_num(thd) {}
+ Item_sum_double(thd) {}
Item_sum_udf_int(THD *thd, Item_sum_udf_int *item)
- :Item_sum_num(thd, item) {}
+ :Item_sum_double(thd, item) {}
enum Sumfunctype sum_func () const { return UDF_SUM_FUNC; }
longlong val_int() { DBUG_ASSERT(fixed == 1); return 0; }
double val_real() { DBUG_ASSERT(fixed == 1); return 0; }
@@ -1535,15 +1733,15 @@ public:
};
-class Item_sum_udf_decimal :public Item_sum_num
+class Item_sum_udf_decimal :public Item_sum_double
{
public:
Item_sum_udf_decimal(THD *thd, udf_func *udf_arg):
- Item_sum_num(thd) {}
+ Item_sum_double(thd) {}
Item_sum_udf_decimal(THD *thd, udf_func *udf_arg, List<Item> &list):
- Item_sum_num(thd) {}
+ Item_sum_double(thd) {}
Item_sum_udf_decimal(THD *thd, Item_sum_udf_float *item)
- :Item_sum_num(thd, item) {}
+ :Item_sum_double(thd, item) {}
enum Sumfunctype sum_func () const { return UDF_SUM_FUNC; }
double val_real() { DBUG_ASSERT(fixed == 1); return 0.0; }
my_decimal *val_decimal(my_decimal *) { DBUG_ASSERT(fixed == 1); return 0; }
@@ -1553,20 +1751,19 @@ class Item_sum_udf_decimal :public Item_sum_num
};
-class Item_sum_udf_str :public Item_sum_num
+class Item_sum_udf_str :public Item_sum_double
{
public:
Item_sum_udf_str(THD *thd, udf_func *udf_arg):
- Item_sum_num(thd) {}
+ Item_sum_double(thd) {}
Item_sum_udf_str(THD *thd, udf_func *udf_arg, List<Item> &list):
- Item_sum_num(thd) {}
+ Item_sum_double(thd) {}
Item_sum_udf_str(THD *thd, Item_sum_udf_str *item)
- :Item_sum_num(thd, item) {}
+ :Item_sum_double(thd, item) {}
String *val_str(String *)
{ DBUG_ASSERT(fixed == 1); null_value=1; return 0; }
double val_real() { DBUG_ASSERT(fixed == 1); null_value=1; return 0.0; }
longlong val_int() { DBUG_ASSERT(fixed == 1); null_value=1; return 0; }
- enum Item_result result_type () const { return STRING_RESULT; }
bool fix_length_and_dec() { maybe_null=1; max_length=0; return FALSE; }
enum Sumfunctype sum_func () const { return UDF_SUM_FUNC; }
void clear() {}
@@ -1616,7 +1813,18 @@ class Item_func_group_concat : public Item_sum
bool warning_for_row;
bool always_null;
bool force_copy_fields;
- bool no_appended;
+ /** True if entire result of GROUP_CONCAT has been written to output buffer. */
+ bool result_finalized;
+ /** Limits the rows in the result */
+ Item *row_limit;
+ /** Skips a particular number of rows in from the result*/
+ Item *offset_limit;
+ bool limit_clause;
+ /* copy of the offset limit */
+ ulonglong copy_offset_limit;
+ /*copy of the row limit */
+ ulonglong copy_row_limit;
+
/*
Following is 0 normal object and pointer to original one for copy
(to correctly free resources)
@@ -1630,15 +1838,14 @@ class Item_func_group_concat : public Item_sum
friend int dump_leaf_key(void* key_arg,
element_count count __attribute__((unused)),
void* item_arg);
-protected:
- Field *make_string_field(TABLE *table);
bool repack_tree(THD *thd);
public:
Item_func_group_concat(THD *thd, Name_resolution_context *context_arg,
bool is_distinct, List<Item> *is_select,
- const SQL_I_List<ORDER> &is_order, String *is_separator);
+ const SQL_I_List<ORDER> &is_order, String *is_separator,
+ bool limit_clause, Item *row_limit, Item *offset_limit);
Item_func_group_concat(THD *thd, Item_func_group_concat *item);
~Item_func_group_concat();
@@ -1646,14 +1853,11 @@ public:
enum Sumfunctype sum_func () const {return GROUP_CONCAT_FUNC;}
const char *func_name() const { return "group_concat("; }
- virtual Item_result result_type () const { return STRING_RESULT; }
- virtual Item_result cmp_type () const { return STRING_RESULT; }
- enum_field_types field_type() const
+ const Type_handler *type_handler() const
{
if (too_big_for_varchar())
- return MYSQL_TYPE_BLOB;
- else
- return MYSQL_TYPE_VARCHAR;
+ return &type_handler_blob;
+ return &type_handler_varchar;
}
void clear();
bool add();
@@ -1686,14 +1890,18 @@ public:
{
return val_decimal_from_string(decimal_value);
}
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ {
+ return get_date_from_string(ltime, fuzzydate);
+ }
String* val_str(String* str);
Item *copy_or_same(THD* thd);
void no_rows_in_result() {}
void print(String *str, enum_query_type query_type);
bool change_context_processor(void *cntx)
{ context= (Name_resolution_context *)cntx; return FALSE; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_group_concat>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_group_concat>(thd, this); }
};
#endif /* ITEM_SUM_INCLUDED */
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index 194933a4c54..9bdbcf82f2b 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -30,7 +30,7 @@
#pragma implementation // gcc: Class implementation
#endif
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
/*
It is necessary to include set_var.h instead of item.h because there
@@ -258,8 +258,8 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
break;
case 'w':
tmp= (char*) val + 1;
- if ((weekday= (int) my_strtoll10(val, &tmp, &error)) < 0 ||
- weekday >= 7)
+ if (unlikely((weekday= (int) my_strtoll10(val, &tmp, &error)) < 0 ||
+ weekday >= 7))
goto err;
/* We should use the same 1 - 7 scale for %w as for %W */
if (!weekday)
@@ -280,9 +280,10 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
sunday_first_n_first_week_non_iso= (*ptr=='U' || *ptr== 'V');
strict_week_number= (*ptr=='V' || *ptr=='v');
tmp= (char*) val + MY_MIN(val_len, 2);
- if ((week_number= (int) my_strtoll10(val, &tmp, &error)) < 0 ||
- (strict_week_number && !week_number) ||
- week_number > 53)
+ if (unlikely((week_number=
+ (int) my_strtoll10(val, &tmp, &error)) < 0 ||
+ (strict_week_number && !week_number) ||
+ week_number > 53))
goto err;
val= tmp;
break;
@@ -332,7 +333,7 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
default:
goto err;
}
- if (error) // Error from my_strtoll10
+ if (unlikely(error)) // Error from my_strtoll10
goto err;
}
else if (!my_isspace(cs, *ptr))
@@ -456,7 +457,8 @@ err:
*/
static bool make_date_time(const LEX_CSTRING &format, MYSQL_TIME *l_time,
- timestamp_type type, MY_LOCALE *locale, String *str)
+ timestamp_type type, const MY_LOCALE *locale,
+ String *str)
{
char intbuff[15];
uint hours_i;
@@ -709,8 +711,7 @@ static bool make_date_time(const LEX_CSTRING &format, MYSQL_TIME *l_time,
For example, '1.1' -> '1.100000'
*/
-static bool get_interval_info(const char *str,uint length,CHARSET_INFO *cs,
- uint count, ulonglong *values,
+static bool get_interval_info(const char *str, size_t length,CHARSET_INFO *cs, size_t count, ulonglong *values,
bool transform_msec)
{
const char *end=str+length;
@@ -1000,15 +1001,15 @@ longlong Item_func_quarter::val_int()
longlong Item_func_hour::val_int()
{
DBUG_ASSERT(fixed == 1);
- MYSQL_TIME ltime;
- return get_arg0_time(&ltime) ? 0 : ltime.hour;
+ Time tm(args[0], Time::Options_for_cast());
+ return (null_value= !tm.is_valid_time()) ? 0 : tm.get_mysql_time()->hour;
}
longlong Item_func_minute::val_int()
{
DBUG_ASSERT(fixed == 1);
- MYSQL_TIME ltime;
- return get_arg0_time(&ltime) ? 0 : ltime.minute;
+ Time tm(args[0], Time::Options_for_cast());
+ return (null_value= !tm.is_valid_time()) ? 0 : tm.get_mysql_time()->minute;
}
/**
@@ -1017,8 +1018,8 @@ longlong Item_func_minute::val_int()
longlong Item_func_second::val_int()
{
DBUG_ASSERT(fixed == 1);
- MYSQL_TIME ltime;
- return get_arg0_time(&ltime) ? 0 : ltime.second;
+ Time tm(args[0], Time::Options_for_cast());
+ return (null_value= !tm.is_valid_time()) ? 0 : tm.get_mysql_time()->second;
}
@@ -1090,21 +1091,13 @@ longlong Item_func_yearweek::val_int()
}
-static uint weekday_from_item(Item *item, bool *null_value, bool week_starts_on_sunday)
-{
- MYSQL_TIME ltime;
- if ((*null_value= item->get_date_with_conversion(&ltime, TIME_NO_ZERO_DATE |
- TIME_NO_ZERO_IN_DATE)))
- return 0;
- return calc_weekday(calc_daynr(ltime.year, ltime.month, ltime.day), week_starts_on_sunday) +
- MY_TEST(week_starts_on_sunday);
-}
-
-
longlong Item_func_weekday::val_int()
{
DBUG_ASSERT(fixed == 1);
- return (longlong) weekday_from_item(args[0], &null_value, odbc_type);
+ Datetime dt(current_thd, args[0], TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE);
+ if ((null_value= !dt.is_valid_datetime()))
+ return 0;
+ return dt.weekday(odbc_type) + MY_TEST(odbc_type);
}
bool Item_func_dayname::fix_length_and_dec()
@@ -1123,14 +1116,14 @@ bool Item_func_dayname::fix_length_and_dec()
String* Item_func_dayname::val_str(String* str)
{
DBUG_ASSERT(fixed == 1);
- uint weekday= weekday_from_item(args[0], &null_value, false);
const char *day_name;
uint err;
+ Datetime dt(current_thd, args[0], TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE);
- if (null_value)
+ if ((null_value= !dt.is_valid_datetime()))
return (String*) 0;
- day_name= locale->day_names->type_names[weekday];
+ day_name= locale->day_names->type_names[dt.weekday(false)];
str->copy(day_name, (uint) strlen(day_name), &my_charset_utf8_bin,
collation.collation, &err);
return str;
@@ -1279,24 +1272,20 @@ longlong Item_func_unix_timestamp::val_int_endpoint(bool left_endp, bool *incl_e
longlong Item_func_time_to_sec::int_op()
{
DBUG_ASSERT(fixed == 1);
- MYSQL_TIME ltime;
- if (get_arg0_time(&ltime))
- return 0;
-
- longlong seconds=ltime.hour*3600L+ltime.minute*60+ltime.second;
- return ltime.neg ? -seconds : seconds;
+ Time tm(args[0], Time::Options_for_cast());
+ return ((null_value= !tm.is_valid_time())) ? 0 : tm.to_seconds();
}
my_decimal *Item_func_time_to_sec::decimal_op(my_decimal* buf)
{
DBUG_ASSERT(fixed == 1);
- MYSQL_TIME ltime;
- if (get_arg0_time(&ltime))
+ Time tm(args[0], Time::Options_for_cast());
+ if ((null_value= !tm.is_valid_time()))
return 0;
-
- longlong seconds= ltime.hour*3600L+ltime.minute*60+ltime.second;
- return seconds2my_decimal(ltime.neg, seconds, ltime.second_part, buf);
+ const MYSQL_TIME *ltime= tm.get_mysql_time();
+ longlong seconds= tm.to_seconds_abs();
+ return seconds2my_decimal(ltime->neg, seconds, ltime->second_part, buf);
}
@@ -1483,35 +1472,6 @@ bool get_interval_value(Item *args,interval_type int_type, INTERVAL *interval)
}
-bool Item_temporal_func::fix_length_and_dec()
-{
- uint char_length= mysql_temporal_int_part_length(field_type());
- /*
- We set maybe_null to 1 as default as any bad argument with date or
- time can get us to return NULL.
- */
- maybe_null= (arg_count > 0);
- if (decimals)
- {
- 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;
- }
- }
- sql_mode= current_thd->variables.sql_mode &
- (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE);
- collation.set(field_type() == MYSQL_TYPE_STRING ?
- default_charset() : &my_charset_numeric,
- field_type() == MYSQL_TYPE_STRING ?
- DERIVATION_COERCIBLE : DERIVATION_NUMERIC,
- MY_REPERTOIRE_ASCII);
- fix_char_length(char_length);
- return FALSE;
-}
-
String *Item_temporal_func::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
@@ -1586,7 +1546,7 @@ String *Item_temporal_hybrid_func::val_str_ascii(String *str)
/* Check that the returned timestamp type matches to the function type */
DBUG_ASSERT(field_type() == MYSQL_TYPE_STRING ||
ltime.time_type == MYSQL_TIMESTAMP_NONE ||
- mysql_type_to_time_type(field_type()) == ltime.time_type);
+ ltime.time_type == mysql_timestamp_type());
return str;
}
@@ -1884,7 +1844,14 @@ overflow:
bool Item_func_date_format::fix_length_and_dec()
{
THD* thd= current_thd;
- locale= thd->variables.lc_time_names;
+ if (!is_time_format)
+ {
+ if (arg_count < 3)
+ locale= thd->variables.lc_time_names;
+ else
+ if (args[2]->basic_const_item())
+ locale= args[2]->locale_from_val_str();
+ }
/*
Must use this_item() in case it's a local SP variable
@@ -1898,11 +1865,12 @@ bool Item_func_date_format::fix_length_and_dec()
if (!thd->variables.lc_time_names->is_ascii)
repertoire|= MY_REPERTOIRE_EXTENDED;
collation.set(cs, arg1->collation.derivation, repertoire);
- if (arg1->type() == STRING_ITEM)
+ StringBuffer<STRING_BUFFER_USUAL_SIZE> buffer;
+ String *str;
+ if (args[1]->basic_const_item() && (str= args[1]->val_str(&buffer)))
{ // Optimize the normal case
fixed_length=1;
- max_length= format_length(arg1->val_str(NULL)) *
- collation.collation->mbmaxlen;
+ max_length= format_length(str) * collation.collation->mbmaxlen;
}
else
{
@@ -1927,6 +1895,8 @@ bool Item_func_date_format::eq(const Item *item, bool binary_cmp) const
if (this == item)
return 1;
item_func= (Item_func_date_format*) item;
+ if (arg_count != item_func->arg_count)
+ return 0;
if (!args[0]->eq(item_func->args[0], binary_cmp))
return 0;
/*
@@ -1936,6 +1906,8 @@ bool Item_func_date_format::eq(const Item *item, bool binary_cmp) const
*/
if (!args[1]->eq(item_func->args[1], 1))
return 0;
+ if (arg_count > 2 && !args[2]->eq(item_func->args[2], 1))
+ return 0;
return 1;
}
@@ -2020,15 +1992,18 @@ String *Item_func_date_format::val_str(String *str)
String *format;
MYSQL_TIME l_time;
uint size;
- int is_time_flag = is_time_format ? TIME_TIME_ONLY : 0;
+ const MY_LOCALE *lc= 0;
DBUG_ASSERT(fixed == 1);
- if (get_arg0_date(&l_time, is_time_flag))
+ if ((null_value= args[0]->get_date(&l_time, is_time_format ? TIME_TIME_ONLY : 0)))
return 0;
if (!(format= args[1]->val_str(&format_buffer)) || !format->length())
goto null_date;
+ if (!is_time_format && !(lc= locale) && !(lc= args[2]->locale_from_val_str()))
+ goto null_date; // invalid locale
+
if (fixed_length)
size=max_length;
else
@@ -2046,7 +2021,7 @@ String *Item_func_date_format::val_str(String *str)
if (!make_date_time(format->lex_cstring(), &l_time,
is_time_format ? MYSQL_TIMESTAMP_TIME :
MYSQL_TIMESTAMP_DATE,
- locale, str))
+ lc, str))
return str;
null_date:
@@ -2060,8 +2035,9 @@ bool Item_func_from_unixtime::fix_length_and_dec()
THD *thd= current_thd;
thd->time_zone_used= 1;
tz= thd->variables.time_zone;
- decimals= args[0]->decimals;
- return Item_temporal_func::fix_length_and_dec();
+ fix_attributes_datetime_not_fixed_dec(args[0]->decimals);
+ maybe_null= true;
+ return FALSE;
}
@@ -2088,13 +2064,6 @@ bool Item_func_from_unixtime::get_date(MYSQL_TIME *ltime,
}
-bool Item_func_convert_tz::fix_length_and_dec()
-{
- decimals= args[0]->temporal_precision(MYSQL_TYPE_DATETIME);
- return Item_temporal_func::fix_length_and_dec();
-}
-
-
bool Item_func_convert_tz::get_date(MYSQL_TIME *ltime,
ulonglong fuzzy_date __attribute__((unused)))
{
@@ -2144,6 +2113,13 @@ bool Item_date_add_interval::fix_length_and_dec()
{
enum_field_types arg0_field_type;
+ if (!args[0]->type_handler()->is_traditional_type())
+ {
+ my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0),
+ args[0]->type_handler()->name().ptr(),
+ "interval", func_name());
+ return TRUE;
+ }
/*
The field type for the result of an Item_datefunc is defined as
follows:
@@ -2159,7 +2135,6 @@ bool Item_date_add_interval::fix_length_and_dec()
(This is because you can't know if the string contains a DATE,
MYSQL_TIME or DATETIME argument)
*/
- set_handler_by_field_type(MYSQL_TYPE_STRING);
arg0_field_type= args[0]->field_type();
uint interval_dec= 0;
if (int_type == INTERVAL_MICROSECOND ||
@@ -2172,30 +2147,46 @@ bool Item_date_add_interval::fix_length_and_dec()
if (arg0_field_type == MYSQL_TYPE_DATETIME ||
arg0_field_type == MYSQL_TYPE_TIMESTAMP)
{
- decimals= MY_MAX(args[0]->temporal_precision(MYSQL_TYPE_DATETIME), interval_dec);
- set_handler_by_field_type(MYSQL_TYPE_DATETIME);
+ uint dec= MY_MAX(args[0]->datetime_precision(), interval_dec);
+ set_handler(&type_handler_datetime);
+ fix_attributes_datetime(dec);
}
else if (arg0_field_type == MYSQL_TYPE_DATE)
{
if (int_type <= INTERVAL_DAY || int_type == INTERVAL_YEAR_MONTH)
- set_handler_by_field_type(arg0_field_type);
+ {
+ set_handler(&type_handler_newdate);
+ fix_attributes_date();
+ }
else
{
- decimals= interval_dec;
- set_handler_by_field_type(MYSQL_TYPE_DATETIME);
+ set_handler(&type_handler_datetime2);
+ fix_attributes_datetime(interval_dec);
}
}
else if (arg0_field_type == MYSQL_TYPE_TIME)
{
- decimals= MY_MAX(args[0]->temporal_precision(MYSQL_TYPE_TIME), interval_dec);
+ uint dec= MY_MAX(args[0]->time_precision(), interval_dec);
if (int_type >= INTERVAL_DAY && int_type != INTERVAL_YEAR_MONTH)
- set_handler_by_field_type(arg0_field_type);
+ {
+ set_handler(&type_handler_time2);
+ fix_attributes_time(dec);
+ }
else
- set_handler_by_field_type(MYSQL_TYPE_DATETIME);
+ {
+ set_handler(&type_handler_datetime2);
+ fix_attributes_datetime(dec);
+ }
}
else
- decimals= MY_MAX(args[0]->temporal_precision(MYSQL_TYPE_DATETIME), interval_dec);
- return Item_temporal_func::fix_length_and_dec();
+ {
+ uint dec= MY_MAX(args[0]->datetime_precision(), interval_dec);
+ set_handler(&type_handler_string);
+ collation.set(default_charset(), DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
+ fix_char_length_temporal_not_fixed_dec(MAX_DATETIME_WIDTH, dec);
+ }
+ maybe_null= true;
+ return FALSE;
}
@@ -2438,13 +2429,15 @@ void Item_char_typecast::print(String *str, enum_query_type query_type)
}
-void Item_char_typecast::check_truncation_with_warn(String *src, uint dstlen)
+void Item_char_typecast::check_truncation_with_warn(String *src, size_t dstlen)
{
if (dstlen < src->length())
{
THD *thd= current_thd;
char char_type[40];
ErrConvString err(src);
+ bool save_abort_on_warning= thd->abort_on_warning;
+ thd->abort_on_warning&= !m_suppress_warning_to_error_escalation;
my_snprintf(char_type, sizeof(char_type), "%s(%lu)",
cast_cs == &my_charset_bin ? "BINARY" : "CHAR",
(ulong) cast_length);
@@ -2452,11 +2445,12 @@ void Item_char_typecast::check_truncation_with_warn(String *src, uint dstlen)
ER_TRUNCATED_WRONG_VALUE,
ER_THD(thd, ER_TRUNCATED_WRONG_VALUE), char_type,
err.ptr());
+ thd->abort_on_warning= save_abort_on_warning;
}
}
-String *Item_char_typecast::reuse(String *src, uint32 length)
+String *Item_char_typecast::reuse(String *src, size_t length)
{
DBUG_ASSERT(length <= src->length());
check_truncation_with_warn(src, length);
@@ -2554,7 +2548,23 @@ end:
}
-bool Item_char_typecast::fix_length_and_dec()
+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);
+}
+
+
+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);
+}
+
+
+void Item_char_typecast::fix_length_and_dec_internal(CHARSET_INFO *from_cs)
{
uint32 char_length;
/*
@@ -2584,12 +2594,6 @@ bool Item_char_typecast::fix_length_and_dec()
Note (TODO): we could use repertoire technique here.
*/
- from_cs= ((args[0]->result_type() == INT_RESULT ||
- args[0]->result_type() == DECIMAL_RESULT ||
- args[0]->result_type() == REAL_RESULT) ?
- (cast_cs->mbminlen == 1 ? cast_cs : &my_charset_latin1) :
- args[0]->dynamic_result() ? 0 :
- args[0]->collation.collation);
charset_conversion= !from_cs || (cast_cs->mbmaxlen > 1) ||
(!my_charset_same(from_cs, cast_cs) &&
from_cs != &my_charset_bin &&
@@ -2600,23 +2604,17 @@ bool Item_char_typecast::fix_length_and_dec()
(cast_cs == &my_charset_bin ? 1 :
args[0]->collation.collation->mbmaxlen));
max_length= char_length * cast_cs->mbmaxlen;
- return FALSE;
}
bool Item_time_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
{
- if (get_arg0_time(ltime))
- return 1;
+ Time tm(args[0], Time::Options_for_cast());
+ if ((null_value= !tm.is_valid_time()))
+ return true;
+ tm.copy_to_mysql_time(ltime);
if (decimals < TIME_SECOND_PART_DIGITS)
my_time_trunc(ltime, decimals);
- /*
- MYSQL_TIMESTAMP_TIME value can have non-zero day part,
- which we should not lose.
- */
- if (ltime->time_type != MYSQL_TIMESTAMP_TIME)
- ltime->year= ltime->month= ltime->day= 0;
- ltime->time_type= MYSQL_TIMESTAMP_TIME;
return (fuzzy_date & TIME_TIME_ONLY) ? 0 :
(null_value= check_date_with_warn(ltime, fuzzy_date,
MYSQL_TIMESTAMP_ERROR));
@@ -2692,8 +2690,15 @@ err:
bool Item_func_add_time::fix_length_and_dec()
{
enum_field_types arg0_field_type;
- decimals= MY_MAX(args[0]->decimals, args[1]->decimals);
+ if (!args[0]->type_handler()->is_traditional_type() ||
+ !args[1]->type_handler()->is_traditional_type())
+ {
+ my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0),
+ args[0]->type_handler()->name().ptr(),
+ args[1]->type_handler()->name().ptr(), func_name());
+ return TRUE;
+ }
/*
The field type for the result of an Item_func_add_time function is defined
as follows:
@@ -2704,24 +2709,31 @@ bool Item_func_add_time::fix_length_and_dec()
- Otherwise the result is MYSQL_TYPE_STRING
*/
- set_handler_by_field_type(MYSQL_TYPE_STRING);
arg0_field_type= args[0]->field_type();
if (arg0_field_type == MYSQL_TYPE_DATE ||
arg0_field_type == MYSQL_TYPE_DATETIME ||
arg0_field_type == MYSQL_TYPE_TIMESTAMP ||
is_date)
{
- set_handler_by_field_type(MYSQL_TYPE_DATETIME);
- decimals= MY_MAX(args[0]->temporal_precision(MYSQL_TYPE_DATETIME),
- args[1]->temporal_precision(MYSQL_TYPE_TIME));
+ uint dec= MY_MAX(args[0]->datetime_precision(), args[1]->time_precision());
+ set_handler(&type_handler_datetime2);
+ fix_attributes_datetime(dec);
}
else if (arg0_field_type == MYSQL_TYPE_TIME)
{
- set_handler_by_field_type(MYSQL_TYPE_TIME);
- decimals= MY_MAX(args[0]->temporal_precision(MYSQL_TYPE_TIME),
- args[1]->temporal_precision(MYSQL_TYPE_TIME));
+ uint dec= MY_MAX(args[0]->time_precision(), args[1]->time_precision());
+ set_handler(&type_handler_time2);
+ fix_attributes_time(dec);
+ }
+ else
+ {
+ uint dec= MY_MAX(args[0]->decimals, args[1]->decimals);
+ set_handler(&type_handler_string);
+ collation.set(default_charset(), DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
+ fix_char_length_temporal_not_fixed_dec(MAX_DATETIME_WIDTH, dec);
}
- return Item_temporal_func::fix_length_and_dec();
+ maybe_null= true;
+ return FALSE;
}
/**
@@ -2757,7 +2769,7 @@ bool Item_func_add_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
// ADDTIME function AND the first argument is TIME
if (args[0]->get_time(&l_time1) ||
args[1]->get_time(&l_time2) ||
- l_time2.time_type == MYSQL_TIMESTAMP_DATETIME)
+ l_time2.time_type != MYSQL_TIMESTAMP_TIME)
return (null_value= 1);
is_time= (l_time1.time_type == MYSQL_TIMESTAMP_TIME);
}
@@ -2798,27 +2810,6 @@ bool Item_func_add_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
}
-void Item_func_add_time::print(String *str, enum_query_type query_type)
-{
- if (is_date)
- {
- DBUG_ASSERT(sign > 0);
- str->append(STRING_WITH_LEN("timestamp("));
- }
- else
- {
- if (sign > 0)
- str->append(STRING_WITH_LEN("addtime("));
- else
- str->append(STRING_WITH_LEN("subtime("));
- }
- args[0]->print(str, query_type);
- str->append(',');
- args[1]->print(str, query_type);
- str->append(')');
-}
-
-
/**
TIMEDIFF(t,s) is a time function that calculates the
time value between a start and end time.
@@ -2912,10 +2903,9 @@ bool Item_func_maketime::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
longlong Item_func_microsecond::val_int()
{
DBUG_ASSERT(fixed == 1);
- MYSQL_TIME ltime;
- if (!get_arg0_date(&ltime, TIME_TIME_ONLY))
- return ltime.second_part;
- return 0;
+ Time tm(args[0], Time::Options_for_cast());
+ return ((null_value= !tm.is_valid_time())) ?
+ 0 : tm.get_mysql_time()->second_part;
}
@@ -2926,14 +2916,13 @@ longlong Item_func_timestamp_diff::val_int()
long microseconds;
long months= 0;
int neg= 1;
+ THD *thd= current_thd;
+ ulonglong fuzzydate= TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE;
+
+ null_value= 0;
- null_value= 0;
- if (args[0]->get_date_with_conversion(&ltime1,
- TIME_NO_ZERO_DATE |
- TIME_NO_ZERO_IN_DATE) ||
- args[1]->get_date_with_conversion(&ltime2,
- TIME_NO_ZERO_DATE |
- TIME_NO_ZERO_IN_DATE))
+ if (Datetime(thd, args[0], fuzzydate).copy_to_mysql_time(&ltime1) ||
+ Datetime(thd, args[1], fuzzydate).copy_to_mysql_time(&ltime2))
goto null_date;
if (calc_time_diff(&ltime2,&ltime1, 1,
@@ -3202,19 +3191,23 @@ 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())
+ {
+ my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0),
+ args[0]->type_handler()->name().ptr(),
+ args[1]->type_handler()->name().ptr(), func_name());
+ return TRUE;
+ }
if (agg_arg_charsets(collation, args, 2, MY_COLL_ALLOW_CONV, 1))
return TRUE;
if (collation.collation->mbminlen > 1)
- {
-#if MYSQL_VERSION_ID > 50500
internal_charset= &my_charset_utf8mb4_general_ci;
-#else
- internal_charset= &my_charset_utf8_general_ci;
-#endif
- }
- set_handler_by_field_type(MYSQL_TYPE_DATETIME);
- decimals= TIME_SECOND_PART_DIGITS;
+ maybe_null= true;
+ set_handler(&type_handler_datetime2);
+ fix_attributes_datetime(TIME_SECOND_PART_DIGITS);
+
if ((const_item= args[1]->const_item()))
{
char format_buff[64];
@@ -3228,25 +3221,30 @@ bool Item_func_str_to_date::fix_length_and_dec()
get_date_time_result_type(format->ptr(), format->length());
switch (cached_format_type) {
case DATE_ONLY:
- set_handler_by_field_type(MYSQL_TYPE_DATE);
+ set_handler(&type_handler_newdate);
+ fix_attributes_date();
break;
case TIME_MICROSECOND:
- decimals= 6;
- /* fall through */
+ set_handler(&type_handler_time2);
+ fix_attributes_time(TIME_SECOND_PART_DIGITS);
+ break;
case TIME_ONLY:
- set_handler_by_field_type(MYSQL_TYPE_TIME);
+ set_handler(&type_handler_time2);
+ fix_attributes_time(0);
break;
case DATE_TIME_MICROSECOND:
- decimals= 6;
- /* fall through */
+ set_handler(&type_handler_datetime2);
+ fix_attributes_datetime(TIME_SECOND_PART_DIGITS);
+ break;
case DATE_TIME:
- set_handler_by_field_type(MYSQL_TYPE_DATETIME);
+ set_handler(&type_handler_datetime2);
+ fix_attributes_datetime(0);
break;
}
}
}
- cached_timestamp_type= mysql_type_to_time_type(field_type());
- return Item_temporal_func::fix_length_and_dec();
+ cached_timestamp_type= mysql_timestamp_type();
+ return FALSE;
}
@@ -3285,7 +3283,7 @@ bool Item_func_str_to_date::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
bool Item_func_last_day::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
{
- if (get_arg0_date(ltime, fuzzy_date) ||
+ if (get_arg0_date(ltime, fuzzy_date & ~TIME_TIME_ONLY) ||
(ltime->month == 0))
return (null_value=1);
uint month_idx= ltime->month-1;
diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h
index f25fe3a551f..8b39092e09a 100644
--- a/sql/item_timefunc.h
+++ b/sql/item_timefunc.h
@@ -31,22 +31,35 @@ enum date_time_format_types
};
-static inline uint
-mysql_temporal_int_part_length(enum enum_field_types mysql_type)
+bool get_interval_value(Item *args,interval_type int_type, INTERVAL *interval);
+
+
+class Item_long_func_date_field: public Item_long_func
{
- static uint max_time_type_width[5]=
- { MAX_DATETIME_WIDTH, MAX_DATETIME_WIDTH, MAX_DATE_WIDTH,
- MAX_DATETIME_WIDTH, MIN_TIME_WIDTH };
- return max_time_type_width[mysql_type_to_time_type(mysql_type)+2];
-}
+ bool check_arguments() const
+ { return args[0]->check_type_can_return_date(func_name()); }
+public:
+ Item_long_func_date_field(THD *thd, Item *a)
+ :Item_long_func(thd, a) { }
+};
-bool get_interval_value(Item *args,interval_type int_type, INTERVAL *interval);
+class Item_long_func_time_field: public Item_long_func
+{
+ bool check_arguments() const
+ { return args[0]->check_type_can_return_time(func_name()); }
+public:
+ Item_long_func_time_field(THD *thd, Item *a)
+ :Item_long_func(thd, a) { }
+};
-class Item_func_period_add :public Item_int_func
+
+class Item_func_period_add :public Item_long_func
{
+ bool check_arguments() const
+ { return check_argument_types_can_return_int(0, 2); }
public:
- Item_func_period_add(THD *thd, Item *a, Item *b): Item_int_func(thd, a, b) {}
+ Item_func_period_add(THD *thd, Item *a, Item *b): Item_long_func(thd, a, b) {}
longlong val_int();
const char *func_name() const { return "period_add"; }
bool fix_length_and_dec()
@@ -54,15 +67,17 @@ public:
max_length=6*MY_CHARSET_BIN_MB_MAXLEN;
return FALSE;
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_period_add>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_period_add>(thd, this); }
};
-class Item_func_period_diff :public Item_int_func
+class Item_func_period_diff :public Item_long_func
{
+ bool check_arguments() const
+ { return check_argument_types_can_return_int(0, 2); }
public:
- Item_func_period_diff(THD *thd, Item *a, Item *b): Item_int_func(thd, a, b) {}
+ Item_func_period_diff(THD *thd, Item *a, Item *b): Item_long_func(thd, a, b) {}
longlong val_int();
const char *func_name() const { return "period_diff"; }
bool fix_length_and_dec()
@@ -71,15 +86,15 @@ public:
max_length=6*MY_CHARSET_BIN_MB_MAXLEN;
return FALSE;
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_period_diff>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_period_diff>(thd, this); }
};
-class Item_func_to_days :public Item_int_func
+class Item_func_to_days :public Item_long_func_date_field
{
public:
- Item_func_to_days(THD *thd, Item *a): Item_int_func(thd, a) {}
+ Item_func_to_days(THD *thd, Item *a): Item_long_func_date_field(thd, a) {}
longlong val_int();
const char *func_name() const { return "to_days"; }
bool fix_length_and_dec()
@@ -97,21 +112,23 @@ public:
{
return !has_date_args();
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_to_days>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_to_days>(thd, this); }
};
-class Item_func_to_seconds :public Item_int_func
+class Item_func_to_seconds :public Item_longlong_func
{
+ bool check_arguments() const
+ { return check_argument_types_can_return_date(0, arg_count); }
public:
- Item_func_to_seconds(THD *thd, Item *a): Item_int_func(thd, a) {}
+ Item_func_to_seconds(THD *thd, Item *a): Item_longlong_func(thd, a) {}
longlong val_int();
const char *func_name() const { return "to_seconds"; }
bool fix_length_and_dec()
{
decimals=0;
- max_length=6*MY_CHARSET_BIN_MB_MAXLEN;
+ fix_char_length(12);
maybe_null= 1;
return FALSE;
}
@@ -124,15 +141,15 @@ public:
{
return !has_date_args();
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_to_seconds>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_to_seconds>(thd, this); }
};
-class Item_func_dayofmonth :public Item_int_func
+class Item_func_dayofmonth :public Item_long_func_date_field
{
public:
- Item_func_dayofmonth(THD *thd, Item *a): Item_int_func(thd, a) {}
+ Item_func_dayofmonth(THD *thd, Item *a): Item_long_func_date_field(thd, a) {}
longlong val_int();
const char *func_name() const { return "dayofmonth"; }
bool fix_length_and_dec()
@@ -148,30 +165,18 @@ public:
{
return !has_date_args();
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_dayofmonth>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_dayofmonth>(thd, this); }
};
-class Item_func_month :public Item_func
+class Item_func_month :public Item_long_func
{
public:
- Item_func_month(THD *thd, Item *a): Item_func(thd, a)
- { collation.set_numeric(); }
+ Item_func_month(THD *thd, Item *a): Item_long_func(thd, a)
+ { }
longlong val_int();
- double val_real()
- { DBUG_ASSERT(fixed == 1); return (double) Item_func_month::val_int(); }
- String *val_str(String *str)
- {
- longlong nr= val_int();
- if (null_value)
- return 0;
- str->set(nr, collation.collation);
- return str;
- }
const char *func_name() const { return "month"; }
- enum Item_result result_type () const { return INT_RESULT; }
- enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; }
bool fix_length_and_dec()
{
decimals= 0;
@@ -185,8 +190,8 @@ public:
{
return !has_date_args();
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_month>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_month>(thd, this); }
};
@@ -207,15 +212,15 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_SESSION_FUNC);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_monthname>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_monthname>(thd, this); }
};
-class Item_func_dayofyear :public Item_int_func
+class Item_func_dayofyear :public Item_long_func_date_field
{
public:
- Item_func_dayofyear(THD *thd, Item *a): Item_int_func(thd, a) {}
+ Item_func_dayofyear(THD *thd, Item *a): Item_long_func_date_field(thd, a) {}
longlong val_int();
const char *func_name() const { return "dayofyear"; }
bool fix_length_and_dec()
@@ -231,15 +236,15 @@ public:
{
return !has_date_args();
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_dayofyear>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_dayofyear>(thd, this); }
};
-class Item_func_hour :public Item_int_func
+class Item_func_hour :public Item_long_func_time_field
{
public:
- Item_func_hour(THD *thd, Item *a): Item_int_func(thd, a) {}
+ Item_func_hour(THD *thd, Item *a): Item_long_func_time_field(thd, a) {}
longlong val_int();
const char *func_name() const { return "hour"; }
bool fix_length_and_dec()
@@ -255,15 +260,15 @@ public:
{
return !has_time_args();
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_hour>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_hour>(thd, this); }
};
-class Item_func_minute :public Item_int_func
+class Item_func_minute :public Item_long_func_time_field
{
public:
- Item_func_minute(THD *thd, Item *a): Item_int_func(thd, a) {}
+ Item_func_minute(THD *thd, Item *a): Item_long_func_time_field(thd, a) {}
longlong val_int();
const char *func_name() const { return "minute"; }
bool fix_length_and_dec()
@@ -279,15 +284,15 @@ public:
{
return !has_time_args();
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_minute>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_minute>(thd, this); }
};
-class Item_func_quarter :public Item_int_func
+class Item_func_quarter :public Item_long_func_date_field
{
public:
- Item_func_quarter(THD *thd, Item *a): Item_int_func(thd, a) {}
+ Item_func_quarter(THD *thd, Item *a): Item_long_func_date_field(thd, a) {}
longlong val_int();
const char *func_name() const { return "quarter"; }
bool fix_length_and_dec()
@@ -303,15 +308,15 @@ public:
{
return !has_date_args();
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_quarter>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_quarter>(thd, this); }
};
-class Item_func_second :public Item_int_func
+class Item_func_second :public Item_long_func_time_field
{
public:
- Item_func_second(THD *thd, Item *a): Item_int_func(thd, a) {}
+ Item_func_second(THD *thd, Item *a): Item_long_func_time_field(thd, a) {}
longlong val_int();
const char *func_name() const { return "second"; }
bool fix_length_and_dec()
@@ -327,16 +332,21 @@ public:
{
return !has_time_args();
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_second>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_second>(thd, this); }
};
-class Item_func_week :public Item_int_func
+class Item_func_week :public Item_long_func
{
+ bool check_arguments() const
+ {
+ return args[0]->check_type_can_return_date(func_name()) ||
+ (arg_count > 1 && args[1]->check_type_can_return_int(func_name()));
+ }
public:
- Item_func_week(THD *thd, Item *a): Item_int_func(thd, a) {}
- Item_func_week(THD *thd, Item *a, Item *b): Item_int_func(thd, a, b) {}
+ Item_func_week(THD *thd, Item *a): Item_long_func(thd, a) {}
+ Item_func_week(THD *thd, Item *a, Item *b): Item_long_func(thd, a, b) {}
longlong val_int();
const char *func_name() const { return "week"; }
bool fix_length_and_dec()
@@ -356,14 +366,20 @@ public:
{
return arg_count == 2;
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_week>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_week>(thd, this); }
};
-class Item_func_yearweek :public Item_int_func
+class Item_func_yearweek :public Item_long_func
{
+ bool check_arguments() const
+ {
+ return args[0]->check_type_can_return_date(func_name()) ||
+ args[1]->check_type_can_return_int(func_name());
+ }
public:
- Item_func_yearweek(THD *thd, Item *a, Item *b): Item_int_func(thd, a, b) {}
+ Item_func_yearweek(THD *thd, Item *a, Item *b)
+ :Item_long_func(thd, a, b) {}
longlong val_int();
const char *func_name() const { return "yearweek"; }
bool fix_length_and_dec()
@@ -379,15 +395,15 @@ public:
{
return !has_date_args();
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_yearweek>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_yearweek>(thd, this); }
};
-class Item_func_year :public Item_int_func
+class Item_func_year :public Item_long_func_date_field
{
public:
- Item_func_year(THD *thd, Item *a): Item_int_func(thd, a) {}
+ Item_func_year(THD *thd, Item *a): Item_long_func_date_field(thd, a) {}
longlong val_int();
const char *func_name() const { return "year"; }
enum_monotonicity_info get_monotonicity_info() const;
@@ -405,22 +421,26 @@ public:
{
return !has_date_args();
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_year>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_year>(thd, this); }
};
-class Item_func_weekday :public Item_int_func
+class Item_func_weekday :public Item_long_func
{
bool odbc_type;
public:
Item_func_weekday(THD *thd, Item *a, bool type_arg):
- Item_int_func(thd, a), odbc_type(type_arg) { }
+ Item_long_func(thd, a), odbc_type(type_arg) { }
longlong val_int();
const char *func_name() const
{
return (odbc_type ? "dayofweek" : "weekday");
}
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ {
+ return type_handler()->Item_get_date(this, ltime, fuzzydate);
+ }
bool fix_length_and_dec()
{
decimals= 0;
@@ -434,8 +454,8 @@ public:
{
return !has_date_args();
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_weekday>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_weekday>(thd, this); }
};
class Item_func_dayname :public Item_str_func
@@ -445,6 +465,7 @@ class Item_func_dayname :public Item_str_func
Item_func_dayname(THD *thd, Item *a): Item_str_func(thd, a) {}
const char *func_name() const { return "dayname"; }
String *val_str(String *str);
+ const Type_handler *type_handler() const { return &type_handler_varchar; }
bool fix_length_and_dec();
bool check_partition_func_processor(void *int_arg) {return TRUE;}
bool check_vcol_func_processor(void *arg)
@@ -455,39 +476,40 @@ class Item_func_dayname :public Item_str_func
{
return !has_date_args();
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_dayname>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_dayname>(thd, this); }
};
class Item_func_seconds_hybrid: public Item_func_numhybrid
{
-protected:
- virtual enum_field_types arg0_expected_type() const = 0;
public:
Item_func_seconds_hybrid(THD *thd): Item_func_numhybrid(thd) {}
Item_func_seconds_hybrid(THD *thd, Item *a): Item_func_numhybrid(thd, a) {}
- bool fix_length_and_dec()
+ void fix_length_and_dec_generic(uint dec)
{
- if (arg_count)
- decimals= args[0]->temporal_precision(arg0_expected_type());
- set_if_smaller(decimals, TIME_SECOND_PART_DIGITS);
+ DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+ decimals= dec;
max_length=17 + (decimals ? decimals + 1 : 0);
maybe_null= true;
- set_handler_by_result_type(decimals ? DECIMAL_RESULT : INT_RESULT);
- return FALSE;
+ if (decimals)
+ set_handler(&type_handler_newdecimal);
+ else
+ set_handler(type_handler_long_or_longlong());
}
double real_op() { DBUG_ASSERT(0); return 0; }
String *str_op(String *str) { DBUG_ASSERT(0); return 0; }
- bool date_op(MYSQL_TIME *ltime, uint fuzzydate) { DBUG_ASSERT(0); return true; }
+ bool date_op(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ {
+ DBUG_ASSERT(0);
+ return true;
+ }
};
class Item_func_unix_timestamp :public Item_func_seconds_hybrid
{
bool get_timestamp_value(my_time_t *seconds, ulong *second_part);
-protected:
- enum_field_types arg0_expected_type() const { return MYSQL_TYPE_DATETIME; }
public:
Item_func_unix_timestamp(THD *thd): Item_func_seconds_hybrid(thd) {}
Item_func_unix_timestamp(THD *thd, Item *a):
@@ -511,17 +533,20 @@ public:
return FALSE;
return mark_unsupported_function(func_name(), "()", arg, VCOL_TIME_FUNC);
}
+ bool fix_length_and_dec()
+ {
+ fix_length_and_dec_generic(arg_count ? args[0]->datetime_precision() : 0);
+ return FALSE;
+ }
longlong int_op();
my_decimal *decimal_op(my_decimal* buf);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_unix_timestamp>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_unix_timestamp>(thd, this); }
};
class Item_func_time_to_sec :public Item_func_seconds_hybrid
{
-protected:
- enum_field_types arg0_expected_type() const { return MYSQL_TYPE_TIME; }
public:
Item_func_time_to_sec(THD *thd, Item *item):
Item_func_seconds_hybrid(thd, item) {}
@@ -532,42 +557,31 @@ public:
{
return !has_time_args();
}
+ bool fix_length_and_dec()
+ {
+ fix_length_and_dec_generic(args[0]->time_precision());
+ return FALSE;
+ }
longlong int_op();
my_decimal *decimal_op(my_decimal* buf);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_time_to_sec>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_time_to_sec>(thd, this); }
};
class Item_temporal_func: public Item_func
{
- sql_mode_t sql_mode;
public:
Item_temporal_func(THD *thd): Item_func(thd) {}
Item_temporal_func(THD *thd, Item *a): Item_func(thd, a) {}
Item_temporal_func(THD *thd, Item *a, Item *b): Item_func(thd, a, b) {}
Item_temporal_func(THD *thd, Item *a, Item *b, Item *c): Item_func(thd, a, b, c) {}
- enum Item_result result_type () const { return STRING_RESULT; }
- Item_result cmp_type() const { return TIME_RESULT; }
String *val_str(String *str);
longlong val_int() { return val_int_from_date(); }
double val_real() { return val_real_from_date(); }
bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date) { DBUG_ASSERT(0); return 1; }
my_decimal *val_decimal(my_decimal *decimal_value)
{ return val_decimal_from_date(decimal_value); }
- Field *create_field_for_create_select(TABLE *table)
- { return tmp_table_field_from_field_type(table, false, false); }
-#if MARIADB_VERSION_ID > 100300
-#error This code should be removed in 10.3, to use the derived save_in_field()
-#else
- int save_in_field(Field *field, bool no_conversions)
- {
- return field_type() == MYSQL_TYPE_TIME ?
- save_time_in_field(field) :
- save_date_in_field(field);
- }
-#endif
- bool fix_length_and_dec();
};
@@ -575,32 +589,20 @@ public:
Abstract class for functions returning TIME, DATE, DATETIME or string values,
whose data type depends on parameters and is set at fix_fields time.
*/
-class Item_temporal_hybrid_func: public Item_temporal_func,
- public Type_handler_hybrid_field_type
+class Item_temporal_hybrid_func: public Item_hybrid_func
{
protected:
String ascii_buf; // Conversion buffer
public:
Item_temporal_hybrid_func(THD *thd, Item *a, Item *b):
- Item_temporal_func(thd, a, b) {}
- enum_field_types field_type() const
- { return Type_handler_hybrid_field_type::field_type(); }
- enum Item_result result_type () const
- { return Type_handler_hybrid_field_type::result_type(); }
- enum Item_result cmp_type () const
- { return Type_handler_hybrid_field_type::cmp_type(); }
- CHARSET_INFO *charset_for_protocol() const
- {
- /*
- Can return TIME, DATE, DATETIME or VARCHAR depending on arguments.
- Send using "binary" when TIME, DATE or DATETIME,
- or using collation.collation when VARCHAR
- (which is fixed from @@collation_connection in fix_length_and_dec).
- */
- DBUG_ASSERT(fixed == 1);
- return Item_temporal_hybrid_func::field_type() == MYSQL_TYPE_STRING ?
- collation.collation : &my_charset_bin;
- }
+ Item_hybrid_func(thd, a, b) {}
+
+ longlong val_int() { return val_int_from_date(); }
+ double val_real() { return val_real_from_date(); }
+ bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date)= 0;
+ my_decimal *val_decimal(my_decimal *decimal_value)
+ { return val_decimal_from_date(decimal_value); }
+
/**
Fix the returned timestamp to match field_type(),
which is important for val_str().
@@ -625,7 +627,14 @@ class Item_datefunc :public Item_temporal_func
public:
Item_datefunc(THD *thd): Item_temporal_func(thd) { }
Item_datefunc(THD *thd, Item *a): Item_temporal_func(thd, a) { }
- enum_field_types field_type() const { return MYSQL_TYPE_DATE; }
+ Item_datefunc(THD *thd, Item *a, Item *b): Item_temporal_func(thd, a, b) { }
+ const Type_handler *type_handler() const { return &type_handler_newdate; }
+ bool fix_length_and_dec()
+ {
+ fix_attributes_date();
+ maybe_null= (arg_count > 0);
+ return FALSE;
+ }
};
@@ -637,7 +646,7 @@ public:
Item_timefunc(THD *thd, Item *a, Item *b): Item_temporal_func(thd, a, b) {}
Item_timefunc(THD *thd, Item *a, Item *b, Item *c):
Item_temporal_func(thd, a, b ,c) {}
- enum_field_types field_type() const { return MYSQL_TYPE_TIME; }
+ const Type_handler *type_handler() const { return &type_handler_time2; }
};
@@ -648,7 +657,7 @@ public:
Item_datetimefunc(THD *thd, Item *a): Item_temporal_func(thd, a) {}
Item_datetimefunc(THD *thd, Item *a, Item *b, Item *c):
Item_temporal_func(thd, a, b ,c) {}
- enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; }
+ const Type_handler *type_handler() const { return &type_handler_datetime2; }
};
@@ -662,6 +671,7 @@ public:
Item_func_curtime(THD *thd, uint dec): Item_timefunc(thd), last_query_id(0)
{ decimals= dec; }
bool fix_fields(THD *, Item **);
+ bool fix_length_and_dec() { fix_attributes_time(decimals); return FALSE; }
bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
/*
Abstract method that defines which time zone is used for conversion.
@@ -683,8 +693,8 @@ public:
Item_func_curtime_local(THD *thd, uint dec): Item_func_curtime(thd, dec) {}
const char *func_name() const { return "curtime"; }
virtual void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_curtime_local>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_curtime_local>(thd, this); }
};
@@ -694,8 +704,8 @@ public:
Item_func_curtime_utc(THD *thd, uint dec): Item_func_curtime(thd, dec) {}
const char *func_name() const { return "utc_time"; }
virtual void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_curtime_utc>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_curtime_utc>(thd, this); }
};
@@ -722,8 +732,8 @@ public:
Item_func_curdate_local(THD *thd): Item_func_curdate(thd) {}
const char *func_name() const { return "curdate"; }
void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_curdate_local>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_curdate_local>(thd, this); }
};
@@ -733,8 +743,8 @@ public:
Item_func_curdate_utc(THD *thd): Item_func_curdate(thd) {}
const char *func_name() const { return "utc_date"; }
void store_now_in_TIME(THD* thd, MYSQL_TIME *now_time);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_curdate_utc>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_curdate_utc>(thd, this); }
};
@@ -748,6 +758,8 @@ public:
Item_func_now(THD *thd, uint dec): Item_datetimefunc(thd), last_query_id(0)
{ decimals= dec; }
bool fix_fields(THD *, Item **);
+ bool fix_length_and_dec()
+ { fix_attributes_datetime(decimals); return FALSE;}
bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
virtual void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time)=0;
bool check_vcol_func_processor(void *arg)
@@ -770,8 +782,8 @@ public:
int save_in_field(Field *field, bool no_conversions);
virtual void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time);
virtual enum Functype functype() const { return NOW_FUNC; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_now_local>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_now_local>(thd, this); }
};
@@ -787,8 +799,8 @@ public:
return mark_unsupported_function(func_name(), "()", arg,
VCOL_TIME_FUNC | VCOL_NON_DETERMINISTIC);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_now_utc>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_now_utc>(thd, this); }
};
@@ -811,13 +823,15 @@ public:
VCOL_TIME_FUNC | VCOL_NON_DETERMINISTIC);
}
virtual enum Functype functype() const { return SYSDATE_FUNC; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_sysdate_local>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_sysdate_local>(thd, this); }
};
class Item_func_from_days :public Item_datefunc
{
+ bool check_arguments() const
+ { return args[0]->check_type_can_return_int(func_name()); }
public:
Item_func_from_days(THD *thd, Item *a): Item_datefunc(thd, a) {}
const char *func_name() const { return "from_days"; }
@@ -828,39 +842,59 @@ public:
{
return has_date_args() || has_time_args();
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_from_days>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_from_days>(thd, this); }
};
class Item_func_date_format :public Item_str_func
{
- MY_LOCALE *locale;
+ bool check_arguments() const
+ {
+ return args[0]->check_type_can_return_date(func_name()) ||
+ check_argument_types_can_return_text(1, arg_count);
+ }
+ const MY_LOCALE *locale;
int fixed_length;
- const bool is_time_format;
String value;
+protected:
+ bool is_time_format;
public:
- Item_func_date_format(THD *thd, Item *a, Item *b, bool is_time_format_arg):
- Item_str_func(thd, a, b), is_time_format(is_time_format_arg) {}
+ Item_func_date_format(THD *thd, Item *a, Item *b):
+ Item_str_func(thd, a, b), locale(0), is_time_format(false) {}
+ Item_func_date_format(THD *thd, Item *a, Item *b, Item *c):
+ Item_str_func(thd, a, b, c), locale(0), is_time_format(false) {}
String *val_str(String *str);
- const char *func_name() const
- { return is_time_format ? "time_format" : "date_format"; }
+ const char *func_name() const { return "date_format"; }
bool fix_length_and_dec();
uint format_length(const String *format);
bool eq(const Item *item, bool binary_cmp) const;
bool check_vcol_func_processor(void *arg)
{
- if (is_time_format)
+ if (arg_count > 2)
return false;
return mark_unsupported_function(func_name(), "()", arg, VCOL_SESSION_FUNC);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_date_format>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_date_format>(thd, this); }
+};
+
+class Item_func_time_format: public Item_func_date_format
+{
+public:
+ Item_func_time_format(THD *thd, Item *a, Item *b):
+ Item_func_date_format(thd, a, b) { is_time_format= true; }
+ const char *func_name() const { return "time_format"; }
+ bool check_vcol_func_processor(void *arg) { return false; }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_time_format>(thd, this); }
};
class Item_func_from_unixtime :public Item_datetimefunc
{
+ bool check_arguments() const
+ { return args[0]->check_type_can_return_decimal(func_name()); }
Time_zone *tz;
public:
Item_func_from_unixtime(THD *thd, Item *a): Item_datetimefunc(thd, a) {}
@@ -871,8 +905,8 @@ class Item_func_from_unixtime :public Item_datetimefunc
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_SESSION_FUNC);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_from_unixtime>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_from_unixtime>(thd, this); }
};
@@ -892,6 +926,11 @@ class Time_zone;
*/
class Item_func_convert_tz :public Item_datetimefunc
{
+ bool check_arguments() const
+ {
+ return args[0]->check_type_can_return_date(func_name()) ||
+ check_argument_types_can_return_text(1, arg_count);
+ }
/*
If time zone parameters are constants we are caching objects that
represent them (we use separate from_tz_cached/to_tz_cached members
@@ -904,27 +943,35 @@ class Item_func_convert_tz :public Item_datetimefunc
Item_func_convert_tz(THD *thd, Item *a, Item *b, Item *c):
Item_datetimefunc(thd, a, b, c), from_tz_cached(0), to_tz_cached(0) {}
const char *func_name() const { return "convert_tz"; }
- bool fix_length_and_dec();
+ bool fix_length_and_dec()
+ {
+ fix_attributes_datetime(args[0]->datetime_precision());
+ maybe_null= true;
+ return FALSE;
+ }
bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
void cleanup();
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_convert_tz>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_convert_tz>(thd, this); }
};
class Item_func_sec_to_time :public Item_timefunc
{
+ bool check_arguments() const
+ { return args[0]->check_type_can_return_decimal(func_name()); }
public:
Item_func_sec_to_time(THD *thd, Item *item): Item_timefunc(thd, item) {}
bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
bool fix_length_and_dec()
{
- decimals= MY_MIN(args[0]->decimals, TIME_SECOND_PART_DIGITS);
- return Item_timefunc::fix_length_and_dec();
+ fix_attributes_time(args[0]->decimals);
+ maybe_null= true;
+ return FALSE;
}
const char *func_name() const { return "sec_to_time"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_sec_to_time>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_sec_to_time>(thd, this); }
};
@@ -944,8 +991,8 @@ public:
void print(String *str, enum_query_type query_type);
enum precedence precedence() const { return ADDINTERVAL_PRECEDENCE; }
bool need_parentheses_in_default() { return true; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_date_add_interval>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_date_add_interval>(thd, this); }
};
@@ -972,7 +1019,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), int_type(type_arg) {}
- enum_field_types field_type() const
+ const Type_handler *type_handler() const
{
switch (int_type) {
case INTERVAL_YEAR:
@@ -992,16 +1039,16 @@ class Item_extract :public Item_int_func
case INTERVAL_SECOND:
case INTERVAL_MICROSECOND:
case INTERVAL_SECOND_MICROSECOND:
- return MYSQL_TYPE_LONG;
+ return &type_handler_long;
case INTERVAL_DAY_MICROSECOND:
case INTERVAL_HOUR_MICROSECOND:
case INTERVAL_MINUTE_MICROSECOND:
- return MYSQL_TYPE_LONGLONG;
+ return &type_handler_longlong;
case INTERVAL_LAST:
break;
}
DBUG_ASSERT(0);
- return MYSQL_TYPE_LONGLONG;
+ return &type_handler_longlong;
}
longlong val_int();
enum Functype functype() const { return EXTRACT_FUNC; }
@@ -1052,11 +1099,8 @@ class Item_extract :public Item_int_func
}
return true;
}
- Field *create_field_for_create_select(TABLE *table)
- { return tmp_table_field_from_field_type(table, false, false); }
-
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_extract>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_extract>(thd, this); }
};
@@ -1066,23 +1110,37 @@ class Item_char_typecast :public Item_str_func
CHARSET_INFO *cast_cs, *from_cs;
bool charset_conversion;
String tmp_value;
+ bool m_suppress_warning_to_error_escalation;
bool has_explicit_length() const { return cast_length != ~0U; }
- String *reuse(String *src, uint32 length);
+ String *reuse(String *src, size_t length);
String *copy(String *src, CHARSET_INFO *cs);
uint adjusted_length_with_warn(uint length);
- void check_truncation_with_warn(String *src, uint dstlen);
+ void check_truncation_with_warn(String *src, size_t dstlen);
+ void fix_length_and_dec_internal(CHARSET_INFO *fromcs);
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_str_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);
- bool fix_length_and_dec();
+ 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;
+ }
+ bool fix_length_and_dec()
+ {
+ return args[0]->type_handler()->Item_char_typecast_fix_length_and_dec(this);
+ }
void print(String *str, enum_query_type query_type);
bool need_parentheses_in_default() { return true; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_char_typecast>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_char_typecast>(thd, this); }
};
@@ -1092,12 +1150,6 @@ public:
Item_temporal_typecast(THD *thd, Item *a): Item_temporal_func(thd, a) {}
virtual const char *cast_type() const = 0;
void print(String *str, enum_query_type query_type);
- bool fix_length_and_dec()
- {
- if (decimals == NOT_FIXED_DEC)
- decimals= args[0]->temporal_precision(field_type());
- return Item_temporal_func::fix_length_and_dec();
- }
};
class Item_date_typecast :public Item_temporal_typecast
@@ -1107,9 +1159,13 @@ public:
const char *func_name() const { return "cast_as_date"; }
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
const char *cast_type() const { return "date"; }
- enum_field_types field_type() const { return MYSQL_TYPE_DATE; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_date_typecast>(thd, mem_root, this); }
+ const Type_handler *type_handler() const { return &type_handler_newdate; }
+ bool fix_length_and_dec()
+ {
+ return args[0]->type_handler()->Item_date_typecast_fix_length_and_dec(this);
+ }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_date_typecast>(thd, this); }
};
@@ -1121,9 +1177,14 @@ public:
const char *func_name() const { return "cast_as_time"; }
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
const char *cast_type() const { return "time"; }
- enum_field_types field_type() const { return MYSQL_TYPE_TIME; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_time_typecast>(thd, mem_root, this); }
+ const Type_handler *type_handler() const { return &type_handler_time2; }
+ bool fix_length_and_dec()
+ {
+ return args[0]->type_handler()->
+ Item_time_typecast_fix_length_and_dec(this);
+ }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_time_typecast>(thd, this); }
};
@@ -1134,23 +1195,29 @@ public:
Item_temporal_typecast(thd, a) { decimals= dec_arg; }
const char *func_name() const { return "cast_as_datetime"; }
const char *cast_type() const { return "datetime"; }
- enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; }
+ const Type_handler *type_handler() const { return &type_handler_datetime2; }
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_datetime_typecast>(thd, mem_root, this); }
+ bool fix_length_and_dec()
+ {
+ return args[0]->type_handler()->
+ Item_datetime_typecast_fix_length_and_dec(this);
+ }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_datetime_typecast>(thd, this); }
};
-class Item_func_makedate :public Item_temporal_func
+class Item_func_makedate :public Item_datefunc
{
+ bool check_arguments() const
+ { return check_argument_types_can_return_int(0, arg_count); }
public:
Item_func_makedate(THD *thd, Item *a, Item *b):
- Item_temporal_func(thd, a, b) {}
+ Item_datefunc(thd, a, b) {}
const char *func_name() const { return "makedate"; }
- enum_field_types field_type() const { return MYSQL_TYPE_DATE; }
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_makedate>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_makedate>(thd, this); }
};
@@ -1165,56 +1232,68 @@ public:
{ sign= neg_arg ? -1 : 1; }
bool fix_length_and_dec();
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
- void print(String *str, enum_query_type query_type);
- const char *func_name() const { return "add_time"; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_add_time>(thd, mem_root, this); }
+ const char *func_name() const
+ {
+ return is_date ? "timestamp" : sign > 0 ? "addtime" : "subtime";
+ }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_add_time>(thd, this); }
};
class Item_func_timediff :public Item_timefunc
{
+ bool check_arguments() const
+ { return check_argument_types_can_return_time(0, arg_count); }
public:
Item_func_timediff(THD *thd, Item *a, Item *b): Item_timefunc(thd, a, b) {}
const char *func_name() const { return "timediff"; }
bool fix_length_and_dec()
{
- decimals= MY_MAX(args[0]->temporal_precision(MYSQL_TYPE_TIME),
- args[1]->temporal_precision(MYSQL_TYPE_TIME));
- return Item_timefunc::fix_length_and_dec();
+ uint dec= MY_MAX(args[0]->time_precision(), args[1]->time_precision());
+ fix_attributes_time(dec);
+ maybe_null= true;
+ return FALSE;
}
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_timediff>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_timediff>(thd, this); }
};
class Item_func_maketime :public Item_timefunc
{
+ bool check_arguments() const
+ {
+ return check_argument_types_can_return_int(0, 2) ||
+ args[2]->check_type_can_return_decimal(func_name());
+ }
public:
Item_func_maketime(THD *thd, Item *a, Item *b, Item *c):
Item_timefunc(thd, a, b, c)
{}
bool fix_length_and_dec()
{
- decimals= MY_MIN(args[2]->decimals, TIME_SECOND_PART_DIGITS);
- return Item_timefunc::fix_length_and_dec();
+ fix_attributes_time(args[2]->decimals);
+ maybe_null= true;
+ return FALSE;
}
const char *func_name() const { return "maketime"; }
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_maketime>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_maketime>(thd, this); }
};
-class Item_func_microsecond :public Item_int_func
+class Item_func_microsecond :public Item_long_func_time_field
{
public:
- Item_func_microsecond(THD *thd, Item *a): Item_int_func(thd, a) {}
+ Item_func_microsecond(THD *thd, Item *a): Item_long_func_time_field(thd, a) {}
longlong val_int();
const char *func_name() const { return "microsecond"; }
bool fix_length_and_dec()
{
decimals=0;
maybe_null=1;
+ fix_char_length(6);
return FALSE;
}
bool check_partition_func_processor(void *int_arg) {return FALSE;}
@@ -1223,17 +1302,19 @@ public:
{
return !has_time_args();
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_microsecond>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_microsecond>(thd, this); }
};
-class Item_func_timestamp_diff :public Item_int_func
+class Item_func_timestamp_diff :public Item_longlong_func
{
+ bool check_arguments() const
+ { return check_argument_types_can_return_date(0, arg_count); }
const interval_type int_type;
public:
Item_func_timestamp_diff(THD *thd, Item *a, Item *b, interval_type type_arg):
- Item_int_func(thd, a, b), int_type(type_arg) {}
+ Item_longlong_func(thd, a, b), int_type(type_arg) {}
const char *func_name() const { return "timestampdiff"; }
longlong val_int();
bool fix_length_and_dec()
@@ -1243,8 +1324,8 @@ public:
return FALSE;
}
virtual void print(String *str, enum_query_type query_type);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_timestamp_diff>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_timestamp_diff>(thd, this); }
};
@@ -1270,8 +1351,8 @@ public:
return FALSE;
}
virtual void print(String *str, enum_query_type query_type);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_get_format>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_get_format>(thd, this); }
};
@@ -1290,19 +1371,21 @@ public:
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
const char *func_name() const { return "str_to_date"; }
bool fix_length_and_dec();
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_str_to_date>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_str_to_date>(thd, this); }
};
class Item_func_last_day :public Item_datefunc
{
+ bool check_arguments() const
+ { return args[0]->check_type_can_return_date(func_name()); }
public:
Item_func_last_day(THD *thd, Item *a): Item_datefunc(thd, a) {}
const char *func_name() const { return "last_day"; }
bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_last_day>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_last_day>(thd, this); }
};
#endif /* ITEM_TIMEFUNC_INCLUDED */
diff --git a/sql/item_vers.cc b/sql/item_vers.cc
new file mode 100644
index 00000000000..cfedc6b0f81
--- /dev/null
+++ b/sql/item_vers.cc
@@ -0,0 +1,179 @@
+/* Copyright (c) 2017, 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-1301, USA */
+
+
+/**
+ @brief
+ System Versioning items
+*/
+
+#include "mariadb.h"
+#include "sql_priv.h"
+
+#include "sql_class.h"
+#include "tztime.h"
+#include "item.h"
+
+Item_func_trt_ts::Item_func_trt_ts(THD *thd, Item* a, TR_table::field_id_t _trt_field) :
+ Item_datetimefunc(thd, a),
+ trt_field(_trt_field)
+{
+ decimals= 6;
+ null_value= true;
+ DBUG_ASSERT(arg_count == 1 && args[0]);
+}
+
+
+bool
+Item_func_trt_ts::get_date(MYSQL_TIME *res, ulonglong fuzzy_date)
+{
+ THD *thd= current_thd; // can it differ from constructor's?
+ DBUG_ASSERT(thd);
+ DBUG_ASSERT(args[0]);
+ if (args[0]->result_type() != INT_RESULT)
+ {
+ my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0),
+ args[0]->type_handler()->name().ptr(),
+ func_name());
+ return true;
+ }
+ ulonglong trx_id= args[0]->val_uint();
+ if (trx_id == ULONGLONG_MAX)
+ {
+ null_value= false;
+ thd->variables.time_zone->gmt_sec_to_TIME(res, TIMESTAMP_MAX_VALUE);
+ res->second_part= TIME_MAX_SECOND_PART;
+ return false;
+ }
+
+ TR_table trt(thd);
+
+ null_value= !trt.query(trx_id);
+ if (null_value)
+ return true;
+
+ return trt[trt_field]->get_date(res, fuzzy_date);
+}
+
+
+Item_func_trt_id::Item_func_trt_id(THD *thd, Item* a, TR_table::field_id_t _trt_field,
+ bool _backwards) :
+ Item_longlong_func(thd, a),
+ trt_field(_trt_field),
+ backwards(_backwards)
+{
+ decimals= 0;
+ unsigned_flag= 1;
+ null_value= true;
+ DBUG_ASSERT(arg_count == 1 && args[0]);
+}
+
+Item_func_trt_id::Item_func_trt_id(THD *thd, Item* a, Item* b, TR_table::field_id_t _trt_field) :
+ Item_longlong_func(thd, a, b),
+ trt_field(_trt_field),
+ backwards(false)
+{
+ decimals= 0;
+ unsigned_flag= 1;
+ null_value= true;
+ DBUG_ASSERT(arg_count == 2 && args[0] && args[1]);
+}
+
+longlong
+Item_func_trt_id::get_by_trx_id(ulonglong trx_id)
+{
+ THD *thd= current_thd;
+ DBUG_ASSERT(thd);
+
+ if (trx_id == ULONGLONG_MAX)
+ {
+ null_value= true;
+ return 0;
+ }
+
+ TR_table trt(thd);
+ null_value= !trt.query(trx_id);
+ if (null_value)
+ return 0;
+
+ return trt[trt_field]->val_int();
+}
+
+longlong
+Item_func_trt_id::get_by_commit_ts(MYSQL_TIME &commit_ts, bool backwards)
+{
+ THD *thd= current_thd;
+ DBUG_ASSERT(thd);
+
+ TR_table trt(thd);
+ null_value= !trt.query(commit_ts, backwards);
+ if (null_value)
+ return backwards ? ULONGLONG_MAX : 0;
+
+ return trt[trt_field]->val_int();
+}
+
+longlong
+Item_func_trt_id::val_int()
+{
+ if (args[0]->is_null())
+ {
+ if (arg_count < 2 || trt_field == TR_table::FLD_TRX_ID)
+ {
+ null_value= true;
+ return 0;
+ }
+ return get_by_trx_id(args[1]->val_uint());
+ }
+ else
+ {
+ MYSQL_TIME commit_ts;
+ if (args[0]->get_date(&commit_ts, 0))
+ {
+ null_value= true;
+ return 0;
+ }
+ if (arg_count > 1)
+ {
+ backwards= args[1]->val_bool();
+ DBUG_ASSERT(arg_count == 2);
+ }
+ return get_by_commit_ts(commit_ts, backwards);
+ }
+}
+
+Item_func_trt_trx_sees::Item_func_trt_trx_sees(THD *thd, Item* a, Item* b) :
+ Item_bool_func(thd, a, b),
+ accept_eq(false)
+{
+ null_value= true;
+ DBUG_ASSERT(arg_count == 2 && args[0] && args[1]);
+}
+
+longlong
+Item_func_trt_trx_sees::val_int()
+{
+ THD *thd= current_thd;
+ DBUG_ASSERT(thd);
+
+ DBUG_ASSERT(arg_count > 1);
+ ulonglong trx_id1= args[0]->val_uint();
+ ulonglong trx_id0= args[1]->val_uint();
+ bool result= accept_eq;
+
+ TR_table trt(thd);
+ null_value= trt.query_sees(result, trx_id1, trx_id0);
+ return result;
+}
diff --git a/sql/item_vers.h b/sql/item_vers.h
new file mode 100644
index 00000000000..8b9c0e6056c
--- /dev/null
+++ b/sql/item_vers.h
@@ -0,0 +1,116 @@
+#ifndef ITEM_VERS_INCLUDED
+#define ITEM_VERS_INCLUDED
+/* Copyright (c) 2017, 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-1301, USA */
+
+
+/* System Versioning items */
+
+#ifdef USE_PRAGMA_INTERFACE
+#pragma interface /* gcc class implementation */
+#endif
+
+class Item_func_trt_ts: public Item_datetimefunc
+{
+ TR_table::field_id_t trt_field;
+public:
+ Item_func_trt_ts(THD *thd, Item* a, TR_table::field_id_t _trt_field);
+ const char *func_name() const
+ {
+ if (trt_field == TR_table::FLD_BEGIN_TS)
+ {
+ return "trt_begin_ts";
+ }
+ return "trt_commit_ts";
+ }
+ bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_trt_ts>(thd, this); }
+ bool fix_length_and_dec()
+ { fix_attributes_datetime(decimals); return FALSE; }
+};
+
+class Item_func_trt_id : public Item_longlong_func
+{
+ TR_table::field_id_t trt_field;
+ bool backwards;
+
+ longlong get_by_trx_id(ulonglong trx_id);
+ longlong get_by_commit_ts(MYSQL_TIME &commit_ts, bool backwards);
+
+public:
+ Item_func_trt_id(THD *thd, Item* a, TR_table::field_id_t _trt_field, bool _backwards= false);
+ Item_func_trt_id(THD *thd, Item* a, Item* b, TR_table::field_id_t _trt_field);
+
+ const char *func_name() const
+ {
+ switch (trt_field)
+ {
+ case TR_table::FLD_TRX_ID:
+ return "trt_trx_id";
+ case TR_table::FLD_COMMIT_ID:
+ return "trt_commit_id";
+ case TR_table::FLD_ISO_LEVEL:
+ return "trt_iso_level";
+ default:
+ DBUG_ASSERT(0);
+ }
+ return NULL;
+ }
+
+ bool fix_length_and_dec()
+ {
+ bool res= Item_int_func::fix_length_and_dec();
+ max_length= 20;
+ return res;
+ }
+
+ longlong val_int();
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_trt_id>(thd, this); }
+};
+
+class Item_func_trt_trx_sees : public Item_bool_func
+{
+protected:
+ bool accept_eq;
+
+public:
+ Item_func_trt_trx_sees(THD *thd, Item* a, Item* b);
+ const char *func_name() const
+ {
+ return "trt_trx_sees";
+ }
+ longlong val_int();
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_trt_trx_sees>(thd, this); }
+};
+
+class Item_func_trt_trx_sees_eq :
+ public Item_func_trt_trx_sees
+{
+public:
+ Item_func_trt_trx_sees_eq(THD *thd, Item* a, Item* b) :
+ Item_func_trt_trx_sees(thd, a, b)
+ {
+ accept_eq= true;
+ }
+ const char *func_name() const
+ {
+ return "trt_trx_sees_eq";
+ }
+};
+
+#endif /* ITEM_VERS_INCLUDED */
diff --git a/sql/item_windowfunc.cc b/sql/item_windowfunc.cc
index bb4a8a9f3af..e3e2af8587f 100644
--- a/sql/item_windowfunc.cc
+++ b/sql/item_windowfunc.cc
@@ -1,6 +1,21 @@
+/*
+ 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
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mariadb.h"
#include "item_windowfunc.h"
-#include "my_dbug.h"
-#include "my_global.h"
#include "sql_select.h" // test if group changed
@@ -13,7 +28,7 @@ Item_window_func::resolve_window_name(THD *thd)
return false;
}
DBUG_ASSERT(window_name != NULL && window_spec == NULL);
- char *ref_name= window_name->str;
+ const char *ref_name= window_name->str;
/* !TODO: Add the code to resolve ref_name in outer queries */
/*
@@ -26,7 +41,7 @@ Item_window_func::resolve_window_name(THD *thd)
Window_spec *win_spec;
while((win_spec= it++))
{
- char *win_spec_name= win_spec->name();
+ const char *win_spec_name= win_spec->name();
if (win_spec_name &&
my_strcasecmp(system_charset_info, ref_name, win_spec_name) == 0)
{
@@ -93,6 +108,7 @@ Item_window_func::fix_fields(THD *thd, Item **ref)
my_error(ER_NO_ORDER_LIST_IN_WINDOW_SPEC, MYF(0), window_func()->func_name());
return true;
}
+
/*
TODO: why the last parameter is 'ref' in this call? What if window_func
decides to substitute itself for something else and does *ref=.... ?
@@ -154,10 +170,46 @@ void Item_window_func::split_sum_func(THD *thd, Ref_ptr_array ref_pointer_array,
window_func()->setup_caches(thd);
}
+bool Item_window_func::check_result_type_of_order_item()
+{
+ switch (window_func()->sum_func()) {
+ case Item_sum::PERCENTILE_CONT_FUNC:
+ {
+ Item_result rtype= window_spec->order_list->first->item[0]->cmp_type();
+ // TODO (varun) : support date type in percentile_cont function
+ if (rtype != REAL_RESULT && rtype != INT_RESULT &&
+ rtype != DECIMAL_RESULT && rtype != TIME_RESULT)
+ {
+ my_error(ER_WRONG_TYPE_FOR_PERCENTILE_FUNC, MYF(0), window_func()->func_name());
+ return true;
+ }
+ return false;
+ }
+ 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());
+ func->Type_std_attributes::set(src_item);
+ Type_std_attributes::set(src_item);
+ return false;
+ }
+ default:
+ break;
+ }
+ return FALSE;
+}
/*
This must be called before attempting to compute the window function values.
-
@detail
If we attempt to do it in fix_fields(), partition_fields will refer
to the original window function arguments.
@@ -180,6 +232,71 @@ void Item_sum_dense_rank::setup_window_func(THD *thd, Window_spec *window_spec)
clear();
}
+void Item_sum_percentile_disc::setup_window_func(THD *thd, Window_spec *window_spec)
+{
+ order_item= window_spec->order_list->first->item[0];
+ if (!(value= order_item->get_cache(thd)))
+ return;
+ value->setup(thd, order_item);
+ value->store(order_item);
+}
+
+void Item_sum_percentile_cont::setup_window_func(THD *thd, Window_spec *window_spec)
+{
+ order_item= window_spec->order_list->first->item[0];
+ /* TODO(varun): need to discuss and finalise what type should we
+ return for percentile cont functions
+ */
+ if (!(ceil_value= order_item->get_cache(thd)))
+ return;
+ ceil_value->setup(thd, order_item);
+ ceil_value->store(order_item);
+
+ if (!(floor_value= order_item->get_cache(thd)))
+ return;
+ floor_value->setup(thd, order_item);
+ floor_value->store(order_item);
+}
+bool Item_sum_percentile_cont::fix_fields(THD *thd, Item **ref)
+{
+ bool res;
+ res= Item_sum_num::fix_fields(thd, ref);
+ if (res)
+ return res;
+
+ switch(args[0]->cmp_type())
+ {
+ case DECIMAL_RESULT:
+ case REAL_RESULT:
+ case INT_RESULT:
+ break;
+ default:
+ my_error(ER_WRONG_TYPE_OF_ARGUMENT, MYF(0), func_name());
+ return TRUE;
+ }
+ return res;
+}
+bool Item_sum_percentile_disc::fix_fields(THD *thd, Item **ref)
+{
+ bool res;
+ res= Item_sum_num::fix_fields(thd, ref);
+ if (res)
+ return res;
+
+ switch(args[0]->cmp_type())
+ {
+ case DECIMAL_RESULT:
+ case REAL_RESULT:
+ case INT_RESULT:
+ break;
+ default:
+ my_error(ER_WRONG_TYPE_OF_ARGUMENT, MYF(0), func_name());
+ return TRUE;
+ }
+ return res;
+
+}
+
bool Item_sum_dense_rank::add()
{
if (peer_tracker->check_if_next_group() || first_add)
@@ -232,46 +349,19 @@ bool Item_sum_hybrid_simple::fix_fields(THD *thd, Item **ref)
for (uint i= 0; i < arg_count; i++)
{
- Item *item= args[i];
- // 'item' can be changed during fix_fields
- if ((!item->fixed && item->fix_fields(thd, args + i)) ||
- (item= args[i])->check_cols(1))
+ if (args[i]->fix_fields_if_needed_for_scalar(thd, &args[i]))
return TRUE;
- with_window_func|= item->with_window_func;
+ with_window_func|= args[i]->with_window_func;
}
- Type_std_attributes::set(args[0]);
- for (uint i= 0; i < arg_count && !with_subselect; i++)
- with_subselect= with_subselect || args[i]->with_subselect;
-
- Item *item2= args[0]->real_item();
- if (item2->type() == Item::FIELD_ITEM)
- set_handler_by_field_type(((Item_field*) item2)->field->type());
- else if (args[0]->cmp_type() == TIME_RESULT)
- set_handler_by_field_type(item2->field_type());
- else
- set_handler_by_result_type(item2->result_type(),
- max_length, collation.collation);
- switch (Item_sum_hybrid_simple::result_type()) {
- case INT_RESULT:
- case DECIMAL_RESULT:
- case STRING_RESULT:
- break;
- case REAL_RESULT:
- max_length= float_length(decimals);
- break;
- case ROW_RESULT:
- case TIME_RESULT:
- DBUG_ASSERT(0); // XXX(cvicentiu) Should this never happen?
- return TRUE;
- };
+ for (uint i= 0; i < arg_count && !m_with_subquery; i++)
+ m_with_subquery|= args[i]->with_subquery();
+
+ if (fix_length_and_dec())
+ return true;
+
setup_hybrid(thd, args[0]);
- /* MIN/MAX can return NULL for empty set indepedent of the used column */
- maybe_null= 1;
result_field=0;
- null_value=1;
- if (fix_length_and_dec())
- return TRUE;
if (check_sum_func(thd, ref))
return TRUE;
@@ -283,6 +373,14 @@ bool Item_sum_hybrid_simple::fix_fields(THD *thd, Item **ref)
return FALSE;
}
+
+bool Item_sum_hybrid_simple::fix_length_and_dec()
+{
+ maybe_null= null_value= true;
+ return args[0]->type_handler()->Item_sum_hybrid_fix_length_and_dec(this);
+}
+
+
bool Item_sum_hybrid_simple::add()
{
value->store(args[0]);
@@ -293,7 +391,7 @@ bool Item_sum_hybrid_simple::add()
void Item_sum_hybrid_simple::setup_hybrid(THD *thd, Item *item)
{
- if (!(value= Item_cache::get_cache(thd, item, item->cmp_type())))
+ if (!(value= item->get_cache(thd)))
return;
value->setup(thd, item);
value->store(item);
@@ -347,6 +445,17 @@ Item_sum_hybrid_simple::val_str(String *str)
return retval;
}
+bool Item_sum_hybrid_simple::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+{
+ DBUG_ASSERT(fixed == 1);
+ if (null_value)
+ return true;
+ bool retval= value->get_date(ltime, fuzzydate);
+ if ((null_value= value->null_value))
+ DBUG_ASSERT(retval == true);
+ return retval;
+}
+
Field *Item_sum_hybrid_simple::create_tmp_field(bool group, TABLE *table)
{
DBUG_ASSERT(0);
@@ -355,7 +464,7 @@ Field *Item_sum_hybrid_simple::create_tmp_field(bool group, TABLE *table)
void Item_sum_hybrid_simple::reset_field()
{
- switch(Item_sum_hybrid_simple::result_type()) {
+ switch(result_type()) {
case STRING_RESULT:
{
char buff[MAX_FIELD_WIDTH];
@@ -441,6 +550,11 @@ void Item_sum_hybrid_simple::update_field()
void Item_window_func::print(String *str, enum_query_type query_type)
{
+ if (only_single_element_order_list())
+ {
+ print_for_percentile_functions(str, query_type);
+ return;
+ }
window_func()->print(str, query_type);
str->append(" over ");
if (!window_spec)
@@ -448,3 +562,15 @@ void Item_window_func::print(String *str, enum_query_type query_type)
else
window_spec->print(str, query_type);
}
+void Item_window_func::print_for_percentile_functions(String *str, enum_query_type query_type)
+{
+ window_func()->print(str, query_type);
+ str->append(" within group ");
+ str->append('(');
+ window_spec->print_order(str,query_type);
+ str->append(')');
+ str->append(" over ");
+ str->append('(');
+ window_spec->print_partition(str,query_type);
+ str->append(')');
+}
diff --git a/sql/item_windowfunc.h b/sql/item_windowfunc.h
index 36b0d087f12..81bfa8d7cd0 100644
--- a/sql/item_windowfunc.h
+++ b/sql/item_windowfunc.h
@@ -1,7 +1,22 @@
+/*
+ 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
+ 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 ITEM_WINDOWFUNC_INCLUDED
#define ITEM_WINDOWFUNC_INCLUDED
-#include "my_global.h"
#include "item.h"
class Window_spec;
@@ -9,6 +24,7 @@ class Window_spec;
int test_if_group_changed(List<Cached_item> &list);
+
/* A wrapper around test_if_group_changed */
class Group_bound_tracker
{
@@ -16,10 +32,10 @@ public:
Group_bound_tracker(THD *thd, SQL_I_List<ORDER> *list)
{
- for (ORDER *curr = list->first; curr; curr=curr->next)
+ for (ORDER *curr = list->first; curr; curr=curr->next)
{
- Cached_item *tmp= new_Cached_item(thd, curr->item[0], TRUE);
- group_fields.push_back(tmp);
+ Cached_item *tmp= new_Cached_item(thd, curr->item[0], TRUE);
+ group_fields.push_back(tmp);
}
}
@@ -129,8 +145,8 @@ public:
return "row_number";
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_sum_row_number>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_sum_row_number>(thd, this); }
};
@@ -203,8 +219,8 @@ public:
}
Item_sum_int::cleanup();
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_sum_rank>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_sum_rank>(thd, this); }
};
@@ -272,42 +288,33 @@ class Item_sum_dense_rank: public Item_sum_int
}
Item_sum_int::cleanup();
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_sum_dense_rank>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_sum_dense_rank>(thd, this); }
};
-class Item_sum_hybrid_simple : public Item_sum,
- public Type_handler_hybrid_field_type
+class Item_sum_hybrid_simple : public Item_sum_hybrid
{
public:
Item_sum_hybrid_simple(THD *thd, Item *arg):
- Item_sum(thd, arg),
- Type_handler_hybrid_field_type(MYSQL_TYPE_LONGLONG),
+ Item_sum_hybrid(thd, arg),
value(NULL)
- { collation.set(&my_charset_bin); }
+ { }
Item_sum_hybrid_simple(THD *thd, Item *arg1, Item *arg2):
- Item_sum(thd, arg1, arg2),
- Type_handler_hybrid_field_type(MYSQL_TYPE_LONGLONG),
+ Item_sum_hybrid(thd, arg1, arg2),
value(NULL)
- { collation.set(&my_charset_bin); }
+ { }
bool add();
bool fix_fields(THD *, Item **);
+ bool fix_length_and_dec();
void setup_hybrid(THD *thd, Item *item);
double val_real();
longlong val_int();
my_decimal *val_decimal(my_decimal *);
void reset_field();
String *val_str(String *);
- /* TODO(cvicentiu) copied from Item_sum_hybrid, what does it do? */
- bool keep_field_type(void) const { return 1; }
- enum Item_result result_type() const
- { return Type_handler_hybrid_field_type::result_type(); }
- enum Item_result cmp_type() const
- { return Type_handler_hybrid_field_type::cmp_type(); }
- enum enum_field_types field_type() const
- { return Type_handler_hybrid_field_type::field_type(); }
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
void update_field();
Field *create_tmp_field(bool group, TABLE *table);
void clear()
@@ -341,8 +348,8 @@ class Item_sum_first_value : public Item_sum_hybrid_simple
return "first_value";
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_sum_first_value>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_sum_first_value>(thd, this); }
};
/*
@@ -367,8 +374,8 @@ class Item_sum_last_value : public Item_sum_hybrid_simple
return "last_value";
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_sum_last_value>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_sum_last_value>(thd, this); }
};
class Item_sum_nth_value : public Item_sum_hybrid_simple
@@ -387,8 +394,8 @@ class Item_sum_nth_value : public Item_sum_hybrid_simple
return "nth_value";
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_sum_nth_value>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_sum_nth_value>(thd, this); }
};
class Item_sum_lead : public Item_sum_hybrid_simple
@@ -407,8 +414,8 @@ class Item_sum_lead : public Item_sum_hybrid_simple
return "lead";
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_sum_lead>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_sum_lead>(thd, this); }
};
class Item_sum_lag : public Item_sum_hybrid_simple
@@ -427,31 +434,42 @@ class Item_sum_lag : public Item_sum_hybrid_simple
return "lag";
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_sum_lag>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_sum_lag>(thd, this); }
};
-/*
- A base window function (aggregate) that also holds a counter for the number
- of rows.
-*/
-class Item_sum_window_with_row_count : public Item_sum_num
-{
- public:
- Item_sum_window_with_row_count(THD *thd) : Item_sum_num(thd),
- partition_row_count_(0) {}
-
- Item_sum_window_with_row_count(THD *thd, Item *arg) :
- Item_sum_num(thd, arg), partition_row_count_(0) {};
-
- void set_row_count(ulonglong count) { partition_row_count_ = count; }
- protected:
+class Partition_row_count
+{
+public:
+ Partition_row_count() :partition_row_count_(0) { }
+ void set_partition_row_count(ulonglong count)
+ {
+ partition_row_count_ = count;
+ }
+ double calc_val_real(bool *null_value,
+ ulonglong current_row_count)
+ {
+ if ((*null_value= (partition_row_count_ == 0)))
+ return 0;
+ return static_cast<double>(current_row_count) / partition_row_count_;
+ }
+protected:
longlong get_row_count() { return partition_row_count_; }
- private:
ulonglong partition_row_count_;
};
+
+class Current_row_count
+{
+public:
+ Current_row_count() :current_row_count_(0) { }
+protected:
+ ulonglong get_row_number() { return current_row_count_ ; }
+ ulonglong current_row_count_;
+};
+
+
/*
@detail
"The relative rank of a row R is defined as (RK-1)/(NR-1), where RK is
@@ -463,11 +481,12 @@ class Item_sum_window_with_row_count : public Item_sum_num
This is held within the row_count context.
- Second pass to compute rank of current row and the value of the function
*/
-class Item_sum_percent_rank: public Item_sum_window_with_row_count
+class Item_sum_percent_rank: public Item_sum_double,
+ public Partition_row_count
{
public:
Item_sum_percent_rank(THD *thd)
- : Item_sum_window_with_row_count(thd), cur_rank(1), peer_tracker(NULL) {}
+ : Item_sum_double(thd), cur_rank(1), peer_tracker(NULL) {}
longlong val_int()
{
@@ -510,8 +529,7 @@ class Item_sum_percent_rank: public Item_sum_window_with_row_count
row_number= 0;
}
bool add();
- enum Item_result result_type () const { return REAL_RESULT; }
- enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; }
+ const Type_handler *type_handler() const { return &type_handler_double; }
bool fix_length_and_dec()
{
@@ -521,8 +539,14 @@ class Item_sum_percent_rank: public Item_sum_window_with_row_count
}
void setup_window_func(THD *thd, Window_spec *window_spec);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_sum_percent_rank>(thd, mem_root, this); }
+
+ void set_partition_row_count(ulonglong count)
+ {
+ Partition_row_count::set_partition_row_count(count);
+ }
+
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_sum_percent_rank>(thd, this); }
private:
longlong cur_rank; // Current rank of the current row.
@@ -555,22 +579,17 @@ class Item_sum_percent_rank: public Item_sum_window_with_row_count
two passes.
*/
-class Item_sum_cume_dist: public Item_sum_window_with_row_count
+class Item_sum_cume_dist: public Item_sum_double,
+ public Partition_row_count,
+ public Current_row_count
{
public:
- Item_sum_cume_dist(THD *thd) : Item_sum_window_with_row_count(thd),
- current_row_count_(0) {}
+ Item_sum_cume_dist(THD *thd) :Item_sum_double(thd) { }
+ Item_sum_cume_dist(THD *thd, Item *arg) :Item_sum_double(thd, arg) { }
double val_real()
{
- if (get_row_count() == 0)
- {
- null_value= true;
- return 0;
- }
- ulonglong partition_row_count= get_row_count();
- null_value= false;
- return static_cast<double>(current_row_count_) / partition_row_count;
+ return calc_val_real(&null_value, current_row_count_);
}
bool add()
@@ -587,7 +606,7 @@ class Item_sum_cume_dist: public Item_sum_window_with_row_count
void clear()
{
current_row_count_= 0;
- set_row_count(0);
+ partition_row_count_= 0;
}
const char*func_name() const
@@ -596,8 +615,7 @@ class Item_sum_cume_dist: public Item_sum_window_with_row_count
}
void update_field() {}
- enum Item_result result_type () const { return REAL_RESULT; }
- enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; }
+ const Type_handler *type_handler() const { return &type_handler_double; }
bool fix_length_and_dec()
{
@@ -606,25 +624,24 @@ class Item_sum_cume_dist: public Item_sum_window_with_row_count
return FALSE;
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_sum_cume_dist>(thd, mem_root, this); }
+ void set_partition_row_count(ulonglong count)
+ {
+ Partition_row_count::set_partition_row_count(count);
+ }
+
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_sum_cume_dist>(thd, this); }
- private:
- ulonglong current_row_count_;
};
-class Item_sum_ntile : public Item_sum_window_with_row_count
+class Item_sum_ntile : public Item_sum_int,
+ public Partition_row_count,
+ public Current_row_count
{
public:
Item_sum_ntile(THD* thd, Item* num_quantiles_expr) :
- Item_sum_window_with_row_count(thd, num_quantiles_expr),
- current_row_count_(0),
- n_old_val_(0) {};
-
- double val_real()
- {
- return (double) val_int();
- }
+ Item_sum_int(thd, num_quantiles_expr), n_old_val_(0)
+ { }
longlong val_int()
{
@@ -667,7 +684,7 @@ class Item_sum_ntile : public Item_sum_window_with_row_count
void clear()
{
current_row_count_= 0;
- set_row_count(0);
+ partition_row_count_= 0;
n_old_val_= 0;
}
@@ -678,29 +695,324 @@ class Item_sum_ntile : public Item_sum_window_with_row_count
void update_field() {}
- enum Item_result result_type () const { return INT_RESULT; }
- enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; }
-
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_sum_ntile>(thd, mem_root, this); }
+ void set_partition_row_count(ulonglong count)
+ {
+ Partition_row_count::set_partition_row_count(count);
+ }
+
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_sum_ntile>(thd, this); }
private:
longlong get_num_quantiles() { return args[0]->val_int(); }
- ulong current_row_count_;
ulonglong n_old_val_;
};
+class Item_sum_percentile_disc : public Item_sum_num,
+ public Type_handler_hybrid_field_type,
+ public Partition_row_count,
+ public Current_row_count
+{
+public:
+ Item_sum_percentile_disc(THD *thd, Item* arg) : Item_sum_num(thd, arg),
+ Type_handler_hybrid_field_type(&type_handler_longlong),
+ value(NULL), val_calculated(FALSE), first_call(TRUE),
+ prev_value(0), order_item(NULL){}
+
+ double val_real()
+ {
+ if (get_row_count() == 0 || get_arg(0)->is_null())
+ {
+ null_value= true;
+ return 0;
+ }
+ null_value= false;
+ return value->val_real();
+ }
+
+ longlong val_int()
+ {
+ if (get_row_count() == 0 || get_arg(0)->is_null())
+ {
+ null_value= true;
+ return 0;
+ }
+ null_value= false;
+ return value->val_int();
+ }
+
+ my_decimal* val_decimal(my_decimal* dec)
+ {
+ if (get_row_count() == 0 || get_arg(0)->is_null())
+ {
+ null_value= true;
+ return 0;
+ }
+ null_value= false;
+ return value->val_decimal(dec);
+ }
+
+ String* val_str(String *str)
+ {
+ if (get_row_count() == 0 || get_arg(0)->is_null())
+ {
+ null_value= true;
+ return 0;
+ }
+ null_value= false;
+ return value->val_str(str);
+ }
+
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ {
+ if (get_row_count() == 0 || get_arg(0)->is_null())
+ {
+ null_value= true;
+ return 0;
+ }
+ null_value= false;
+ return value->get_date(ltime, fuzzydate);
+ }
+
+ bool add()
+ {
+ Item *arg= get_arg(0);
+ if (arg->is_null())
+ return false;
+
+ if (first_call)
+ {
+ prev_value= arg->val_real();
+ if (prev_value > 1 || prev_value < 0)
+ {
+ my_error(ER_ARGUMENT_OUT_OF_RANGE, MYF(0), func_name());
+ return true;
+ }
+ first_call= false;
+ }
+
+ double arg_val= arg->val_real();
+
+ if (prev_value != arg_val)
+ {
+ my_error(ER_ARGUMENT_NOT_CONSTANT, MYF(0), func_name());
+ return true;
+ }
+
+ if (val_calculated)
+ return false;
+
+ value->store(order_item);
+ value->cache_value();
+ if (value->null_value)
+ return false;
+
+ current_row_count_++;
+ double val= calc_val_real(&null_value, current_row_count_);
+
+ if (val >= prev_value && !val_calculated)
+ val_calculated= true;
+ return false;
+ }
+
+ enum Sumfunctype sum_func() const
+ {
+ return PERCENTILE_DISC_FUNC;
+ }
+
+ void clear()
+ {
+ val_calculated= false;
+ first_call= true;
+ value->clear();
+ partition_row_count_= 0;
+ current_row_count_= 0;
+ }
+
+ const char*func_name() const
+ {
+ return "percentile_disc";
+ }
+
+ void update_field() {}
+ const Type_handler *type_handler() const
+ {return Type_handler_hybrid_field_type::type_handler();}
+
+ bool fix_length_and_dec()
+ {
+ decimals = 10; // TODO-cvicentiu find out how many decimals the standard
+ // requires.
+ return FALSE;
+ }
+
+ void set_partition_row_count(ulonglong count)
+ {
+ Partition_row_count::set_partition_row_count(count);
+ }
+
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_sum_percentile_disc>(thd, this); }
+ void setup_window_func(THD *thd, Window_spec *window_spec);
+ void setup_hybrid(THD *thd, Item *item);
+ bool fix_fields(THD *thd, Item **ref);
+
+private:
+ Item_cache *value;
+ bool val_calculated;
+ bool first_call;
+ double prev_value;
+ Item *order_item;
+};
+
+class Item_sum_percentile_cont : public Item_sum_double,
+ public Partition_row_count,
+ public Current_row_count
+{
+public:
+ Item_sum_percentile_cont(THD *thd, Item* arg) : Item_sum_double(thd, arg),
+ floor_value(NULL), ceil_value(NULL), first_call(TRUE),prev_value(0),
+ ceil_val_calculated(FALSE), floor_val_calculated(FALSE), order_item(NULL){}
+
+ double val_real()
+ {
+ if (get_row_count() == 0 || get_arg(0)->is_null())
+ {
+ null_value= true;
+ return 0;
+ }
+ null_value= false;
+ double val= 1 + prev_value * (get_row_count()-1);
+
+ /*
+ Applying the formula to get the value
+ If (CRN = FRN = RN) then the result is (value of expression from row at RN)
+ Otherwise the result is
+ (CRN - RN) * (value of expression for row at FRN) +
+ (RN - FRN) * (value of expression for row at CRN)
+ */
+
+ if(ceil(val) == floor(val))
+ return floor_value->val_real();
+
+ double ret_val= ((val - floor(val)) * ceil_value->val_real()) +
+ ((ceil(val) - val) * floor_value->val_real());
+
+ return ret_val;
+ }
+
+ bool add()
+ {
+ Item *arg= get_arg(0);
+ if (arg->is_null())
+ return false;
+
+ if (first_call)
+ {
+ first_call= false;
+ prev_value= arg->val_real();
+ if (prev_value > 1 || prev_value < 0)
+ {
+ my_error(ER_ARGUMENT_OUT_OF_RANGE, MYF(0), func_name());
+ return true;
+ }
+ }
+
+ double arg_val= arg->val_real();
+ if (prev_value != arg_val)
+ {
+ my_error(ER_ARGUMENT_NOT_CONSTANT, MYF(0), func_name());
+ return true;
+ }
+
+ if (!floor_val_calculated)
+ {
+ floor_value->store(order_item);
+ floor_value->cache_value();
+ if (floor_value->null_value)
+ return false;
+ }
+ if (floor_val_calculated && !ceil_val_calculated)
+ {
+ ceil_value->store(order_item);
+ ceil_value->cache_value();
+ if (ceil_value->null_value)
+ return false;
+ }
+
+ current_row_count_++;
+ double val= 1 + prev_value * (get_row_count()-1);
+
+ if (!floor_val_calculated && get_row_number() == floor(val))
+ floor_val_calculated= true;
+
+ if (!ceil_val_calculated && get_row_number() == ceil(val))
+ ceil_val_calculated= true;
+ return false;
+ }
+
+ enum Sumfunctype sum_func() const
+ {
+ return PERCENTILE_CONT_FUNC;
+ }
+
+ void clear()
+ {
+ first_call= true;
+ floor_value->clear();
+ ceil_value->clear();
+ floor_val_calculated= false;
+ ceil_val_calculated= false;
+ partition_row_count_= 0;
+ current_row_count_= 0;
+ }
+
+ const char*func_name() const
+ {
+ return "percentile_cont";
+ }
+ void update_field() {}
+
+ bool fix_length_and_dec()
+ {
+ decimals = 10; // TODO-cvicentiu find out how many decimals the standard
+ // requires.
+ return FALSE;
+ }
+
+ void set_partition_row_count(ulonglong count)
+ {
+ Partition_row_count::set_partition_row_count(count);
+ }
+
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_sum_percentile_cont>(thd, this); }
+ void setup_window_func(THD *thd, Window_spec *window_spec);
+ void setup_hybrid(THD *thd, Item *item);
+ bool fix_fields(THD *thd, Item **ref);
+
+private:
+ Item_cache *floor_value;
+ Item_cache *ceil_value;
+ bool first_call;
+ double prev_value;
+ bool ceil_val_calculated;
+ bool floor_val_calculated;
+ Item *order_item;
+};
+
+
+
class Item_window_func : public Item_func_or_sum
{
/* Window function parameters as we've got them from the parser */
public:
- LEX_STRING *window_name;
+ LEX_CSTRING *window_name;
public:
Window_spec *window_spec;
public:
- Item_window_func(THD *thd, Item_sum *win_func, LEX_STRING *win_name)
+ Item_window_func(THD *thd, Item_sum *win_func, LEX_CSTRING *win_name)
: Item_func_or_sum(thd, (Item *) win_func),
window_name(win_name), window_spec(NULL),
force_return_blank(true),
@@ -744,6 +1056,8 @@ public:
case Item_sum::PERCENT_RANK_FUNC:
case Item_sum::CUME_DIST_FUNC:
case Item_sum::NTILE_FUNC:
+ case Item_sum::PERCENTILE_CONT_FUNC:
+ case Item_sum::PERCENTILE_DISC_FUNC:
return true;
default:
return false;
@@ -770,6 +1084,8 @@ public:
case Item_sum::PERCENT_RANK_FUNC:
case Item_sum::CUME_DIST_FUNC:
case Item_sum::NTILE_FUNC:
+ case Item_sum::PERCENTILE_CONT_FUNC:
+ case Item_sum::PERCENTILE_DISC_FUNC:
return true;
default:
return false;
@@ -795,21 +1111,38 @@ public:
case Item_sum::CUME_DIST_FUNC:
case Item_sum::LAG_FUNC:
case Item_sum::LEAD_FUNC:
+ case Item_sum::PERCENTILE_CONT_FUNC:
+ case Item_sum::PERCENTILE_DISC_FUNC:
return true;
default:
return false;
}
}
+ bool only_single_element_order_list() const
+ {
+ switch (window_func()->sum_func()){
+ case Item_sum::PERCENTILE_CONT_FUNC:
+ case Item_sum::PERCENTILE_DISC_FUNC:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ bool check_result_type_of_order_item();
+
+
+
/*
Computation functions.
TODO: consoder merging these with class Group_bound_tracker.
*/
void setup_partition_border_check(THD *thd);
- enum_field_types field_type() const
- {
- return ((Item_sum *) args[0])->field_type();
+ const Type_handler *type_handler() const
+ {
+ return ((Item_sum *) args[0])->type_handler();
}
enum Item::Type type() const { return Item::WINDOW_FUNC_ITEM; }
@@ -835,6 +1168,7 @@ private:
*/
bool force_return_blank;
bool read_value_from_result_field;
+ void print_for_percentile_functions(String *str, enum_query_type query_type);
public:
void set_phase_to_initial()
@@ -952,13 +1286,35 @@ public:
return res;
}
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ {
+ bool res;
+ if (force_return_blank)
+ {
+ null_value= true;
+ res= true;
+ }
+ else if (read_value_from_result_field)
+ {
+ if ((null_value= result_field->is_null()))
+ res= true;
+ else
+ res= result_field->get_date(ltime, fuzzydate);
+ }
+ else
+ {
+ res= window_func()->get_date(ltime, fuzzydate);
+ null_value= window_func()->null_value;
+ }
+ return res;
+ }
+
void split_sum_func(THD *thd, Ref_ptr_array ref_pointer_array,
List<Item> &fields, uint flags);
bool fix_length_and_dec()
{
- decimals = window_func()->decimals;
- unsigned_flag= window_func()->unsigned_flag;
+ Type_std_attributes::set(window_func());
return FALSE;
}
@@ -970,7 +1326,7 @@ public:
void print(String *str, enum_query_type query_type);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; }
+ Item *get_copy(THD *thd) { return 0; }
};
diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc
index f0d20b045d0..885ef1c397d 100644
--- a/sql/item_xmlfunc.cc
+++ b/sql/item_xmlfunc.cc
@@ -14,7 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
/*
It is necessary to include set_var.h instead of item.h because there
@@ -248,8 +248,8 @@ public:
Item_nodeset_func(thd, pxml) {}
const char *func_name() const { return "xpath_rootelement"; }
String *val_nodeset(String *nodeset);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_nodeset_func_rootelement>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_nodeset_func_rootelement>(thd, this); }
};
@@ -261,8 +261,8 @@ public:
Item_nodeset_func(thd, a, b, pxml) {}
const char *func_name() const { return "xpath_union"; }
String *val_nodeset(String *nodeset);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_nodeset_func_union>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_nodeset_func_union>(thd, this); }
};
@@ -295,8 +295,8 @@ public:
Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {}
const char *func_name() const { return "xpath_selfbyname"; }
String *val_nodeset(String *nodeset);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_nodeset_func_selfbyname>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_nodeset_func_selfbyname>(thd, this); }
};
@@ -309,8 +309,8 @@ public:
Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {}
const char *func_name() const { return "xpath_childbyname"; }
String *val_nodeset(String *nodeset);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_nodeset_func_childbyname>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_nodeset_func_childbyname>(thd, this); }
};
@@ -325,8 +325,8 @@ public:
need_self(need_self_arg) {}
const char *func_name() const { return "xpath_descendantbyname"; }
String *val_nodeset(String *nodeset);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_nodeset_func_descendantbyname>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_nodeset_func_descendantbyname>(thd, this); }
};
@@ -341,8 +341,8 @@ public:
need_self(need_self_arg) {}
const char *func_name() const { return "xpath_ancestorbyname"; }
String *val_nodeset(String *nodeset);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_nodeset_func_ancestorbyname>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_nodeset_func_ancestorbyname>(thd, this); }
};
@@ -355,8 +355,8 @@ public:
Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {}
const char *func_name() const { return "xpath_parentbyname"; }
String *val_nodeset(String *nodeset);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_nodeset_func_parentbyname>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_nodeset_func_parentbyname>(thd, this); }
};
@@ -369,8 +369,8 @@ public:
Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {}
const char *func_name() const { return "xpath_attributebyname"; }
String *val_nodeset(String *nodeset);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_nodeset_func_attributebyname>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_nodeset_func_attributebyname>(thd, this); }
};
@@ -386,8 +386,8 @@ public:
Item_nodeset_func(thd, a, b, pxml) {}
const char *func_name() const { return "xpath_predicate"; }
String *val_nodeset(String *nodeset);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_nodeset_func_predicate>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_nodeset_func_predicate>(thd, this); }
};
@@ -399,8 +399,8 @@ public:
Item_nodeset_func(thd, a, b, pxml) { }
const char *func_name() const { return "xpath_elementbyindex"; }
String *val_nodeset(String *nodeset);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_nodeset_func_elementbyindex>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_nodeset_func_elementbyindex>(thd, this); }
};
@@ -427,8 +427,8 @@ public:
}
return args[0]->val_real() ? 1 : 0;
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_xpath_cast_bool>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_xpath_cast_bool>(thd, this); }
};
@@ -441,8 +441,8 @@ public:
Item_xpath_cast_number(THD *thd, Item *a): Item_real_func(thd, a) {}
const char *func_name() const { return "xpath_cast_number"; }
virtual double val_real() { return args[0]->val_real(); }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_xpath_cast_number>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_xpath_cast_number>(thd, this); }
};
@@ -457,19 +457,19 @@ public:
Item_nodeset_func(thd, pxml), string_cache(str_arg) { }
String *val_nodeset(String *res)
{ return string_cache; }
- bool fix_length_and_dec() { max_length= MAX_BLOB_WIDTH; return FALSE; }
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_nodeset_context_cache>(thd, mem_root, this); }
+ 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); }
};
-class Item_func_xpath_position :public Item_int_func
+class Item_func_xpath_position :public Item_long_func
{
String *pxml;
String tmp_value;
public:
Item_func_xpath_position(THD *thd, Item *a, String *p):
- Item_int_func(thd, a), pxml(p) {}
+ Item_long_func(thd, a), pxml(p) {}
const char *func_name() const { return "xpath_position"; }
bool fix_length_and_dec() { max_length=10; return FALSE; }
longlong val_int()
@@ -479,18 +479,18 @@ public:
return ((MY_XPATH_FLT*)flt->ptr())->pos + 1;
return 0;
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_xpath_position>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_xpath_position>(thd, this); }
};
-class Item_func_xpath_count :public Item_int_func
+class Item_func_xpath_count :public Item_long_func
{
String *pxml;
String tmp_value;
public:
Item_func_xpath_count(THD *thd, Item *a, String *p):
- Item_int_func(thd, a), pxml(p) {}
+ Item_long_func(thd, a), pxml(p) {}
const char *func_name() const { return "xpath_count"; }
bool fix_length_and_dec() { max_length=10; return FALSE; }
longlong val_int()
@@ -502,8 +502,8 @@ public:
return predicate_supplied_context_size;
return res->length() / sizeof(MY_XPATH_FLT);
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_xpath_count>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_xpath_count>(thd, this); }
};
@@ -547,8 +547,8 @@ public:
}
return sum;
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_xpath_sum>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_xpath_sum>(thd, this); }
};
@@ -625,8 +625,8 @@ public:
}
return 0;
}
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_nodeset_to_const_comparator>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_nodeset_to_const_comparator>(thd, this); }
};
@@ -2469,6 +2469,21 @@ static int my_xpath_parse_UnaryExpr(MY_XPATH *xpath)
}
+/**
+ A helper class to make a null-terminated string from XPath fragments.
+ The string is allocated on the THD memory root.
+*/
+class XPath_cstring_null_terminated: public LEX_CSTRING
+{
+public:
+ XPath_cstring_null_terminated(THD *thd, const char *str, size_t length)
+ {
+ if (thd->make_lex_string(this, str, length))
+ static_cast<LEX_CSTRING>(*this)= empty_clex_str;
+ }
+};
+
+
/*
Scan Number
@@ -2503,14 +2518,15 @@ static int my_xpath_parse_Number(MY_XPATH *xpath)
thd= xpath->thd;
if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_DOT))
{
- xpath->item= new (thd->mem_root) Item_int(thd, xpath->prevtok.beg,
- (uint)(xpath->prevtok.end - xpath->prevtok.beg));
- return 1;
+ XPath_cstring_null_terminated nr(thd, beg, xpath->prevtok.end - beg);
+ xpath->item= new (thd->mem_root) Item_int(thd, nr.str, (uint) nr.length);
+ }
+ else
+ {
+ my_xpath_parse_term(xpath, MY_XPATH_LEX_DIGITS);
+ XPath_cstring_null_terminated nr(thd, beg, xpath->prevtok.end - beg);
+ xpath->item= new (thd->mem_root) Item_float(thd, nr.str, (uint) nr.length);
}
- my_xpath_parse_term(xpath, MY_XPATH_LEX_DIGITS);
-
- xpath->item= new (thd->mem_root) Item_float(thd, beg,
- (uint)(xpath->prevtok.end - beg));
return 1;
}
@@ -2600,7 +2616,7 @@ my_xpath_parse_QName(MY_XPATH *xpath)
static int
my_xpath_parse_VariableReference(MY_XPATH *xpath)
{
- LEX_STRING name;
+ LEX_CSTRING name;
int user_var;
const char *dollar_pos;
THD *thd= xpath->thd;
@@ -2615,19 +2631,22 @@ my_xpath_parse_VariableReference(MY_XPATH *xpath)
name.str= (char*) xpath->prevtok.beg;
if (user_var)
- xpath->item= new (thd->mem_root) Item_func_get_user_var(thd, name);
+ xpath->item= new (thd->mem_root) Item_func_get_user_var(thd, &name);
else
{
sp_variable *spv;
- sp_pcontext *spc;
+ const Sp_rcontext_handler *rh;
LEX *lex;
+ /*
+ We call lex->find_variable() rather than thd->lex->spcont->find_variable()
+ to make sure package body variables are properly supported.
+ */
if ((lex= thd->lex) &&
- (spc= lex->spcont) &&
- (spv= spc->find_variable(name, false)))
+ (spv= lex->find_variable(&name, &rh)))
{
Item_splocal *splocal= new (thd->mem_root)
- Item_splocal(thd, name, spv->offset, spv->sql_type(), 0);
-#ifndef DBUG_OFF
+ Item_splocal(thd, rh, &name, spv->offset, spv->type_handler(), 0);
+#ifdef DBUG_ASSERT_EXISTS
if (splocal)
splocal->m_sp= lex->sphead;
#endif
diff --git a/sql/item_xmlfunc.h b/sql/item_xmlfunc.h
index e00f071fc0c..ce34697d9bd 100644
--- a/sql/item_xmlfunc.h
+++ b/sql/item_xmlfunc.h
@@ -96,8 +96,8 @@ public:
Item_xml_str_func(thd, a, b) {}
const char *func_name() const { return "extractvalue"; }
String *val_str(String *);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_xml_extractvalue>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_xml_extractvalue>(thd, this); }
};
@@ -112,8 +112,8 @@ public:
Item_xml_str_func(thd, a, b, c) {}
const char *func_name() const { return "updatexml"; }
String *val_str(String *);
- Item *get_copy(THD *thd, MEM_ROOT *mem_root)
- { return get_item_copy<Item_func_xml_update>(thd, mem_root, this); }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_xml_update>(thd, this); }
};
#endif /* ITEM_XMLFUNC_INCLUDED */
diff --git a/sql/key.cc b/sql/key.cc
index 18806efc18f..adff6975631 100644
--- a/sql/key.cc
+++ b/sql/key.cc
@@ -17,7 +17,7 @@
/* Functions to handle keys and fields in forms */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "key.h" // key_rec_cmp
#include "field.h" // Field
@@ -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, uchar *from_record, KEY *key_info,
+void key_copy(uchar *to_key, const uchar *from_record, KEY *key_info,
uint key_length, bool with_zerofill)
{
uint length;
@@ -320,7 +320,7 @@ bool key_cmp_if_same(TABLE *table,const uchar *key,uint idx,uint key_length)
FIELDFLAG_PACK)))
{
CHARSET_INFO *cs= key_part->field->charset();
- uint char_length= key_part->length / cs->mbmaxlen;
+ size_t char_length= key_part->length / cs->mbmaxlen;
const uchar *pos= table->record[0] + key_part->offset;
if (length > char_length)
{
@@ -386,7 +386,7 @@ void field_unpack(String *to, Field *field, const uchar *rec, uint max_length,
which can break a multi-byte characters in the middle.
Align, returning not more than "char_length" characters.
*/
- uint charpos, char_length= max_length / cs->mbmaxlen;
+ size_t charpos, char_length= max_length / cs->mbmaxlen;
if ((charpos= my_charpos(cs, tmp.ptr(),
tmp.ptr() + tmp.length(),
char_length)) < tmp.length())
@@ -428,6 +428,8 @@ void key_unpack(String *to, TABLE *table, KEY *key)
key_part < key_part_end;
key_part++)
{
+ if (key_part->field->invisible > INVISIBLE_USER)
+ continue;
if (to->length())
to->append('-');
if (key_part->null_bit)
@@ -698,7 +700,7 @@ ulong key_hashnr(KEY *key_info, uint used_key_parts, const uchar *key)
{
uchar *pos= (uchar*)key;
CHARSET_INFO *UNINIT_VAR(cs);
- uint UNINIT_VAR(length), UNINIT_VAR(pack_length);
+ size_t UNINIT_VAR(length), UNINIT_VAR(pack_length);
bool is_string= TRUE;
key+= key_part->length;
@@ -755,7 +757,7 @@ ulong key_hashnr(KEY *key_info, uint used_key_parts, const uchar *key)
{
if (cs->mbmaxlen > 1)
{
- uint char_length= my_charpos(cs, pos + pack_length,
+ size_t char_length= my_charpos(cs, pos + pack_length,
pos + pack_length + length,
length / cs->mbmaxlen);
set_if_smaller(length, char_length);
@@ -802,7 +804,7 @@ bool key_buf_cmp(KEY *key_info, uint used_key_parts,
uchar *pos1= (uchar*)key1;
uchar *pos2= (uchar*)key2;
CHARSET_INFO *UNINIT_VAR(cs);
- uint UNINIT_VAR(length1), UNINIT_VAR(length2), UNINIT_VAR(pack_length);
+ size_t UNINIT_VAR(length1), UNINIT_VAR(length2), UNINIT_VAR(pack_length);
bool is_string= TRUE;
key1+= key_part->length;
@@ -866,13 +868,13 @@ bool key_buf_cmp(KEY *key_info, uint used_key_parts,
Compare the strings taking into account length in characters
and collation
*/
- uint byte_len1= length1, byte_len2= length2;
+ size_t byte_len1= length1, byte_len2= length2;
if (cs->mbmaxlen > 1)
{
- uint char_length1= my_charpos(cs, pos1 + pack_length,
+ size_t char_length1= my_charpos(cs, pos1 + pack_length,
pos1 + pack_length + length1,
length1 / cs->mbmaxlen);
- uint char_length2= my_charpos(cs, pos2 + pack_length,
+ size_t char_length2= my_charpos(cs, pos2 + pack_length,
pos2 + pack_length + length2,
length2 / cs->mbmaxlen);
set_if_smaller(length1, char_length1);
diff --git a/sql/key.h b/sql/key.h
index 950c45b5512..45f58c75655 100644
--- a/sql/key.h
+++ b/sql/key.h
@@ -16,8 +16,6 @@
#ifndef KEY_INCLUDED
#define KEY_INCLUDED
-#include "my_global.h" /* uchar */
-
class Field;
class String;
struct TABLE;
@@ -27,8 +25,8 @@ 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, uchar *from_record, KEY *key_info, uint key_length,
- bool with_zerofill= FALSE);
+void key_copy(uchar *to_key, const uchar *from_record, 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);
bool key_cmp_if_same(TABLE *form,const uchar *key,uint index,uint key_length);
diff --git a/sql/keycaches.cc b/sql/keycaches.cc
index 43ec85d707c..60049cdd67d 100644
--- a/sql/keycaches.cc
+++ b/sql/keycaches.cc
@@ -13,6 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
+#include "mariadb.h"
#include "keycaches.h"
/****************************************************************************
@@ -29,17 +30,17 @@ class NAMED_ILINK :public ilink
{
public:
const char *name;
- uint name_length;
+ size_t name_length;
uchar* data;
NAMED_ILINK(I_List<NAMED_ILINK> *links, const char *name_arg,
- uint name_length_arg, uchar* data_arg)
+ 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));
links->push_back(this);
}
- inline bool cmp(const char *name_cmp, uint length)
+ inline bool cmp(const char *name_cmp, size_t length)
{
return length == name_length && !memcmp(name, name_cmp, length);
}
@@ -49,7 +50,7 @@ public:
}
};
-uchar* find_named(I_List<NAMED_ILINK> *list, const char *name, uint length,
+uchar* find_named(I_List<NAMED_ILINK> *list, const char *name, size_t length,
NAMED_ILINK **found)
{
I_List_iterator<NAMED_ILINK> it(*list);
@@ -67,7 +68,7 @@ uchar* find_named(I_List<NAMED_ILINK> *list, const char *name, uint length,
}
-bool NAMED_ILIST::delete_element(const char *name, uint length, void (*free_element)(const char *name, uchar*))
+bool NAMED_ILIST::delete_element(const char *name, size_t length, void (*free_element)(const char *name, void*))
{
I_List_iterator<NAMED_ILINK> it(*this);
NAMED_ILINK *element;
@@ -99,11 +100,11 @@ void NAMED_ILIST::delete_elements(void (*free_element)(const char *name, void*))
/* Key cache functions */
-LEX_STRING default_key_cache_base= {C_STRING_WITH_LEN("default")};
+LEX_CSTRING default_key_cache_base= {STRING_WITH_LEN("default")};
KEY_CACHE zero_key_cache; ///< @@nonexistent_cache.param->value_ptr() points here
-KEY_CACHE *get_key_cache(const LEX_STRING *cache_name)
+KEY_CACHE *get_key_cache(const LEX_CSTRING *cache_name)
{
if (!cache_name || ! cache_name->length)
cache_name= &default_key_cache_base;
@@ -111,11 +112,11 @@ KEY_CACHE *get_key_cache(const LEX_STRING *cache_name)
cache_name->str, cache_name->length, 0));
}
-KEY_CACHE *create_key_cache(const char *name, uint length)
+KEY_CACHE *create_key_cache(const char *name, size_t length)
{
KEY_CACHE *key_cache;
DBUG_ENTER("create_key_cache");
- DBUG_PRINT("enter",("name: %.*s", length, name));
+ DBUG_PRINT("enter",("name: %.*s", (int)length, name));
if ((key_cache= (KEY_CACHE*) my_malloc(sizeof(KEY_CACHE),
MYF(MY_ZEROFILL | MY_WME))))
@@ -143,12 +144,12 @@ KEY_CACHE *create_key_cache(const char *name, uint length)
}
-KEY_CACHE *get_or_create_key_cache(const char *name, uint length)
+KEY_CACHE *get_or_create_key_cache(const char *name, size_t length)
{
- LEX_STRING key_cache_name;
+ LEX_CSTRING key_cache_name;
KEY_CACHE *key_cache;
- key_cache_name.str= (char *) name;
+ key_cache_name.str= name;
key_cache_name.length= length;
if (!(key_cache= get_key_cache(&key_cache_name)))
key_cache= create_key_cache(name, length);
@@ -179,9 +180,9 @@ bool process_key_caches(process_key_cache_t func, void *param)
/* Rpl_filter functions */
-LEX_STRING default_rpl_filter_base= {C_STRING_WITH_LEN("")};
+LEX_CSTRING default_rpl_filter_base= {STRING_WITH_LEN("")};
-Rpl_filter *get_rpl_filter(LEX_STRING *filter_name)
+Rpl_filter *get_rpl_filter(LEX_CSTRING *filter_name)
{
if (!filter_name->length)
filter_name= &default_rpl_filter_base;
@@ -189,11 +190,11 @@ Rpl_filter *get_rpl_filter(LEX_STRING *filter_name)
filter_name->str, filter_name->length, 0));
}
-Rpl_filter *create_rpl_filter(const char *name, uint length)
+Rpl_filter *create_rpl_filter(const char *name, size_t length)
{
Rpl_filter *filter;
DBUG_ENTER("create_rpl_filter");
- DBUG_PRINT("enter",("name: %.*s", length, name));
+ DBUG_PRINT("enter",("name: %.*s", (int)length, name));
filter= new Rpl_filter;
if (filter)
@@ -208,9 +209,9 @@ Rpl_filter *create_rpl_filter(const char *name, uint length)
}
-Rpl_filter *get_or_create_rpl_filter(const char *name, uint length)
+Rpl_filter *get_or_create_rpl_filter(const char *name, size_t length)
{
- LEX_STRING rpl_filter_name;
+ LEX_CSTRING rpl_filter_name;
Rpl_filter *filter;
rpl_filter_name.str= (char *) name;
diff --git a/sql/keycaches.h b/sql/keycaches.h
index e06c8da3d0d..68c3dd3a2b0 100644
--- a/sql/keycaches.h
+++ b/sql/keycaches.h
@@ -31,27 +31,27 @@ class NAMED_ILIST: public I_List<NAMED_ILINK>
{
public:
void delete_elements(void (*free_element)(const char*, void*));
- bool delete_element(const char *name, uint length, void (*free_element)(const char*, uchar*));
+ bool delete_element(const char *name, size_t length, void (*free_element)(const char*, void*));
};
/* For key cache */
-extern LEX_STRING default_key_cache_base;
+extern LEX_CSTRING default_key_cache_base;
extern KEY_CACHE zero_key_cache;
extern NAMED_ILIST key_caches;
-KEY_CACHE *create_key_cache(const char *name, uint length);
-KEY_CACHE *get_key_cache(const LEX_STRING *cache_name);
-KEY_CACHE *get_or_create_key_cache(const char *name, uint length);
+KEY_CACHE *create_key_cache(const char *name, size_t length);
+KEY_CACHE *get_key_cache(const LEX_CSTRING *cache_name);
+KEY_CACHE *get_or_create_key_cache(const char *name, size_t length);
void free_key_cache(const char *name, void *key_cache);
bool process_key_caches(process_key_cache_t func, void *param);
/* For Rpl_filter */
-extern LEX_STRING default_rpl_filter_base;
+extern LEX_CSTRING default_rpl_filter_base;
extern NAMED_ILIST rpl_filters;
-Rpl_filter *create_rpl_filter(const char *name, uint length);
-Rpl_filter *get_rpl_filter(LEX_STRING *filter_name);
-Rpl_filter *get_or_create_rpl_filter(const char *name, uint length);
+Rpl_filter *create_rpl_filter(const char *name, size_t length);
+Rpl_filter *get_rpl_filter(LEX_CSTRING *filter_name);
+Rpl_filter *get_or_create_rpl_filter(const char *name, size_t length);
void free_all_rpl_filters(void);
#endif /* KEYCACHES_INCLUDED */
diff --git a/sql/lex.h b/sql/lex.h
index ca7c6635329..070ac5cc147 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -82,14 +82,15 @@ static SYMBOL symbols[] = {
{ "AVG_ROW_LENGTH", SYM(AVG_ROW_LENGTH)},
{ "BACKUP", SYM(BACKUP_SYM)},
{ "BEFORE", SYM(BEFORE_SYM)},
- { "BEGIN", SYM(BEGIN_SYM)},
+ { "BEGIN", SYM(BEGIN_MARIADB_SYM)},
{ "BETWEEN", SYM(BETWEEN_SYM)},
{ "BIGINT", SYM(BIGINT)},
{ "BINARY", SYM(BINARY)},
{ "BINLOG", SYM(BINLOG_SYM)},
{ "BIT", SYM(BIT_SYM)},
- { "BLOB", SYM(BLOB_SYM)},
+ { "BLOB", SYM(BLOB_MARIADB_SYM)},
{ "BLOCK", SYM(BLOCK_SYM)},
+ { "BODY", SYM(BODY_MARIADB_SYM)},
{ "BOOL", SYM(BOOL_SYM)},
{ "BOOLEAN", SYM(BOOLEAN_SYM)},
{ "BOTH", SYM(BOTH)},
@@ -114,6 +115,7 @@ static SYMBOL symbols[] = {
{ "CIPHER", SYM(CIPHER_SYM)},
{ "CLASS_ORIGIN", SYM(CLASS_ORIGIN_SYM)},
{ "CLIENT", SYM(CLIENT_SYM)},
+ { "CLOB", SYM(CLOB_MARIADB_SYM)},
{ "CLOSE", SYM(CLOSE_SYM)},
{ "COALESCE", SYM(COALESCE)},
{ "CODE", SYM(CODE_SYM)},
@@ -143,7 +145,7 @@ static SYMBOL symbols[] = {
{ "CONSTRAINT_SCHEMA", SYM(CONSTRAINT_SCHEMA_SYM)},
{ "CONTAINS", SYM(CONTAINS_SYM)},
{ "CONTEXT", SYM(CONTEXT_SYM)},
- { "CONTINUE", SYM(CONTINUE_SYM)},
+ { "CONTINUE", SYM(CONTINUE_MARIADB_SYM)},
{ "CONTRIBUTORS", SYM(CONTRIBUTORS_SYM)},
{ "CONVERT", SYM(CONVERT_SYM)},
{ "CPU", SYM(CPU_SYM)},
@@ -159,6 +161,7 @@ static SYMBOL symbols[] = {
{ "CURRENT_USER", SYM(CURRENT_USER)},
{ "CURSOR", SYM(CURSOR_SYM)},
{ "CURSOR_NAME", SYM(CURSOR_NAME_SYM)},
+ { "CYCLE", SYM(CYCLE_SYM)},
{ "DATA", SYM(DATA_SYM)},
{ "DATABASE", SYM(DATABASE)},
{ "DATABASES", SYM(DATABASES)},
@@ -173,7 +176,7 @@ static SYMBOL symbols[] = {
{ "DEALLOCATE", SYM(DEALLOCATE_SYM)},
{ "DEC", SYM(DECIMAL_SYM)},
{ "DECIMAL", SYM(DECIMAL_SYM)},
- { "DECLARE", SYM(DECLARE_SYM)},
+ { "DECLARE", SYM(DECLARE_MARIADB_SYM)},
{ "DEFAULT", SYM(DEFAULT)},
{ "DEFINER", SYM(DEFINER_SYM)},
{ "DELAYED", SYM(DELAYED_SYM)},
@@ -202,7 +205,8 @@ static SYMBOL symbols[] = {
{ "DYNAMIC", SYM(DYNAMIC_SYM)},
{ "EACH", SYM(EACH_SYM)},
{ "ELSE", SYM(ELSE)},
- { "ELSEIF", SYM(ELSEIF_SYM)},
+ { "ELSEIF", SYM(ELSEIF_MARIADB_SYM)},
+ { "ELSIF", SYM(ELSIF_MARIADB_SYM)},
{ "ENABLE", SYM(ENABLE_SYM)},
{ "ENCLOSED", SYM(ENCLOSED)},
{ "END", SYM(END)},
@@ -218,11 +222,13 @@ static SYMBOL symbols[] = {
{ "EVENTS", SYM(EVENTS_SYM)},
{ "EVERY", SYM(EVERY_SYM)},
{ "EXAMINED", SYM(EXAMINED_SYM)},
+ { "EXCEPT", SYM(EXCEPT_SYM)},
{ "EXCHANGE", SYM(EXCHANGE_SYM)},
{ "EXCLUDE", SYM(EXCLUDE_SYM)},
{ "EXECUTE", SYM(EXECUTE_SYM)},
+ { "EXCEPTION", SYM(EXCEPTION_MARIADB_SYM)},
{ "EXISTS", SYM(EXISTS)},
- { "EXIT", SYM(EXIT_SYM)},
+ { "EXIT", SYM(EXIT_MARIADB_SYM)},
{ "EXPANSION", SYM(EXPANSION_SYM)},
{ "EXPORT", SYM(EXPORT_SYM)},
{ "EXPLAIN", SYM(DESCRIBE)},
@@ -258,6 +264,7 @@ static SYMBOL symbols[] = {
{ "GET_FORMAT", SYM(GET_FORMAT)},
{ "GET", SYM(GET_SYM)},
{ "GLOBAL", SYM(GLOBAL_SYM)},
+ { "GOTO", SYM(GOTO_MARIADB_SYM)},
{ "GRANT", SYM(GRANT)},
{ "GRANTS", SYM(GRANTS)},
{ "GROUP", SYM(GROUP_SYM)},
@@ -267,6 +274,7 @@ static SYMBOL symbols[] = {
{ "HAVING", SYM(HAVING)},
{ "HELP", SYM(HELP_SYM)},
{ "HIGH_PRIORITY", SYM(HIGH_PRIORITY)},
+ { "HISTORY", SYM(HISTORY_SYM)},
{ "HOST", SYM(HOST_SYM)},
{ "HOSTS", SYM(HOSTS_SYM)},
{ "HOUR", SYM(HOUR_SYM)},
@@ -281,7 +289,9 @@ static SYMBOL symbols[] = {
{ "IGNORE_SERVER_IDS", SYM(IGNORE_SERVER_IDS_SYM)},
{ "IMMEDIATE", SYM(IMMEDIATE_SYM)},
{ "IMPORT", SYM(IMPORT)},
+ { "INTERSECT", SYM(INTERSECT_SYM)},
{ "IN", SYM(IN_SYM)},
+ { "INCREMENT", SYM(INCREMENT_SYM)},
{ "INDEX", SYM(INDEX_SYM)},
{ "INDEXES", SYM(INDEXES)},
{ "INFILE", SYM(INFILE)},
@@ -300,12 +310,14 @@ static SYMBOL symbols[] = {
{ "INT8", SYM(BIGINT)},
{ "INTEGER", SYM(INT_SYM)},
{ "INTERVAL", SYM(INTERVAL_SYM)},
+ { "INVISIBLE", SYM(INVISIBLE_SYM)},
{ "INTO", SYM(INTO)},
{ "IO", SYM(IO_SYM)},
{ "IO_THREAD", SYM(RELAY_THREAD)},
{ "IPC", SYM(IPC_SYM)},
{ "IS", SYM(IS)},
{ "ISOLATION", SYM(ISOLATION)},
+ { "ISOPEN", SYM(ISOPEN_SYM)},
{ "ISSUER", SYM(ISSUER_SYM)},
{ "ITERATE", SYM(ITERATE_SYM)},
{ "INVOKER", SYM(INVOKER_SYM)},
@@ -318,6 +330,7 @@ static SYMBOL symbols[] = {
{ "LANGUAGE", SYM(LANGUAGE_SYM)},
{ "LAST", SYM(LAST_SYM)},
{ "LAST_VALUE", SYM(LAST_VALUE)},
+ { "LASTVAL", SYM(LASTVAL_SYM)},
{ "LEADING", SYM(LEADING)},
{ "LEAVE", SYM(LEAVE_SYM)},
{ "LEAVES", SYM(LEAVES)},
@@ -373,7 +386,7 @@ static SYMBOL symbols[] = {
{ "MAX_STATEMENT_TIME", SYM(MAX_STATEMENT_TIME_SYM)},
{ "MAX_UPDATES_PER_HOUR", SYM(MAX_UPDATES_PER_HOUR)},
{ "MAX_USER_CONNECTIONS", SYM(MAX_USER_CONNECTIONS_SYM)},
- { "MAXVALUE", SYM(MAX_VALUE_SYM)},
+ { "MAXVALUE", SYM(MAXVALUE_SYM)},
{ "MEDIUM", SYM(MEDIUM_SYM)},
{ "MEDIUMBLOB", SYM(MEDIUMBLOB)},
{ "MEDIUMINT", SYM(MEDIUMINT)},
@@ -387,6 +400,7 @@ static SYMBOL symbols[] = {
{ "MINUTE", SYM(MINUTE_SYM)},
{ "MINUTE_MICROSECOND", SYM(MINUTE_MICROSECOND_SYM)},
{ "MINUTE_SECOND", SYM(MINUTE_SECOND_SYM)},
+ { "MINVALUE", SYM(MINVALUE_SYM)},
{ "MIN_ROWS", SYM(MIN_ROWS)},
{ "MOD", SYM(MOD_SYM)},
{ "MODE", SYM(MODE_SYM)},
@@ -406,16 +420,24 @@ static SYMBOL symbols[] = {
{ "NCHAR", SYM(NCHAR_SYM)},
{ "NEW", SYM(NEW_SYM)},
{ "NEXT", SYM(NEXT_SYM)},
+ { "NEXTVAL", SYM(NEXTVAL_SYM)},
{ "NO", SYM(NO_SYM)},
+ { "NOMAXVALUE", SYM(NOMAXVALUE_SYM)},
+ { "NOMINVALUE", SYM(NOMINVALUE_SYM)},
+ { "NOCACHE", SYM(NOCACHE_SYM)},
+ { "NOCYCLE", SYM(NOCYCLE_SYM)},
{ "NO_WAIT", SYM(NO_WAIT_SYM)},
+ { "NOWAIT", SYM(NOWAIT_SYM)},
{ "NODEGROUP", SYM(NODEGROUP_SYM)},
{ "NONE", SYM(NONE_SYM)},
{ "NOT", SYM(NOT_SYM)},
+ { "NOTFOUND", SYM(NOTFOUND_SYM)},
{ "NO_WRITE_TO_BINLOG", SYM(NO_WRITE_TO_BINLOG)},
{ "NULL", SYM(NULL_SYM)},
- { "NUMBER", SYM(NUMBER_SYM)},
+ { "NUMBER", SYM(NUMBER_MARIADB_SYM)},
{ "NUMERIC", SYM(NUMERIC_SYM)},
{ "NVARCHAR", SYM(NVARCHAR_SYM)},
+ { "OF", SYM(OF_SYM)},
{ "OFFSET", SYM(OFFSET_SYM)},
{ "OLD_PASSWORD", SYM(OLD_PASSWORD_SYM)},
{ "ON", SYM(ON)},
@@ -429,17 +451,19 @@ static SYMBOL symbols[] = {
{ "OPTIONALLY", SYM(OPTIONALLY)},
{ "OR", SYM(OR_SYM)},
{ "ORDER", SYM(ORDER_SYM)},
- { "OTHERS", SYM(OTHERS_SYM)},
+ { "OTHERS", SYM(OTHERS_MARIADB_SYM)},
{ "OUT", SYM(OUT_SYM)},
{ "OUTER", SYM(OUTER)},
{ "OUTFILE", SYM(OUTFILE)},
{ "OVER", SYM(OVER_SYM)},
{ "OWNER", SYM(OWNER_SYM)},
+ { "PACKAGE", SYM(PACKAGE_MARIADB_SYM)},
{ "PACK_KEYS", SYM(PACK_KEYS_SYM)},
{ "PAGE", SYM(PAGE_SYM)},
{ "PAGE_CHECKSUM", SYM(PAGE_CHECKSUM_SYM)},
{ "PARSER", SYM(PARSER_SYM)},
{ "PARSE_VCOL_EXPR", SYM(PARSE_VCOL_EXPR_SYM)},
+ { "PERIOD", SYM(PERIOD_SYM)},
{ "PARTIAL", SYM(PARTIAL)},
{ "PARTITION", SYM(PARTITION_SYM)},
{ "PARTITIONING", SYM(PARTITIONING_SYM)},
@@ -458,6 +482,7 @@ static SYMBOL symbols[] = {
{ "PREPARE", SYM(PREPARE_SYM)},
{ "PRESERVE", SYM(PRESERVE_SYM)},
{ "PREV", SYM(PREV_SYM)},
+ { "PREVIOUS", SYM(PREVIOUS_SYM)},
{ "PRIMARY", SYM(PRIMARY_SYM)},
{ "PRIVILEGES", SYM(PRIVILEGES)},
{ "PROCEDURE", SYM(PROCEDURE_SYM)},
@@ -470,7 +495,9 @@ static SYMBOL symbols[] = {
{ "QUARTER", SYM(QUARTER_SYM)},
{ "QUERY", SYM(QUERY_SYM)},
{ "QUICK", SYM(QUICK)},
+ { "RAISE", SYM(RAISE_MARIADB_SYM)},
{ "RANGE", SYM(RANGE_SYM)},
+ { "RAW", SYM(RAW_MARIADB_SYM)},
{ "READ", SYM(READ_SYM)},
{ "READ_ONLY", SYM(READ_ONLY_SYM)},
{ "READ_WRITE", SYM(READ_WRITE_SYM)},
@@ -502,13 +529,15 @@ static SYMBOL symbols[] = {
{ "REQUIRE", SYM(REQUIRE_SYM)},
{ "RESET", SYM(RESET_SYM)},
{ "RESIGNAL", SYM(RESIGNAL_SYM)},
+ { "RESTART", SYM(RESTART_SYM)},
{ "RESTORE", SYM(RESTORE_SYM)},
{ "RESTRICT", SYM(RESTRICT)},
{ "RESUME", SYM(RESUME_SYM)},
{ "RETURNED_SQLSTATE",SYM(RETURNED_SQLSTATE_SYM)},
- { "RETURN", SYM(RETURN_SYM)},
+ { "RETURN", SYM(RETURN_MARIADB_SYM)},
{ "RETURNING", SYM(RETURNING_SYM)},
{ "RETURNS", SYM(RETURNS_SYM)},
+ { "REUSE", SYM(REUSE_SYM)},
{ "REVERSE", SYM(REVERSE_SYM)},
{ "REVOKE", SYM(REVOKE)},
{ "RIGHT", SYM(RIGHT)},
@@ -518,7 +547,9 @@ static SYMBOL symbols[] = {
{ "ROLLUP", SYM(ROLLUP_SYM)},
{ "ROUTINE", SYM(ROUTINE_SYM)},
{ "ROW", SYM(ROW_SYM)},
+ { "ROWCOUNT", SYM(ROWCOUNT_SYM)}, /* Oracle-N */
{ "ROWS", SYM(ROWS_SYM)},
+ { "ROWTYPE", SYM(ROWTYPE_MARIADB_SYM)},
{ "ROW_COUNT", SYM(ROW_COUNT_SYM)},
{ "ROW_FORMAT", SYM(ROW_FORMAT_SYM)},
{ "RTREE", SYM(RTREE_SYM)},
@@ -533,11 +564,13 @@ static SYMBOL symbols[] = {
{ "SELECT", SYM(SELECT_SYM)},
{ "SENSITIVE", SYM(SENSITIVE_SYM)},
{ "SEPARATOR", SYM(SEPARATOR_SYM)},
+ { "SEQUENCE", SYM(SEQUENCE_SYM)},
{ "SERIAL", SYM(SERIAL_SYM)},
{ "SERIALIZABLE", SYM(SERIALIZABLE_SYM)},
{ "SESSION", SYM(SESSION_SYM)},
{ "SERVER", SYM(SERVER_SYM)},
{ "SET", SYM(SET)},
+ { "SETVAL", SYM(SETVAL_SYM)},
{ "SHARE", SYM(SHARE_SYM)},
{ "SHOW", SYM(SHOW)},
{ "SHUTDOWN", SYM(SHUTDOWN)},
@@ -600,6 +633,8 @@ static SYMBOL symbols[] = {
{ "SUSPEND", SYM(SUSPEND_SYM)},
{ "SWAPS", SYM(SWAPS_SYM)},
{ "SWITCHES", SYM(SWITCHES_SYM)},
+ { "SYSTEM", SYM(SYSTEM)},
+ { "SYSTEM_TIME", SYM(SYSTEM_TIME_SYM)},
{ "TABLE", SYM(TABLE_SYM)},
{ "TABLE_NAME", SYM(TABLE_NAME_SYM)},
{ "TABLES", SYM(TABLES)},
@@ -659,11 +694,13 @@ static SYMBOL symbols[] = {
{ "VARBINARY", SYM(VARBINARY)},
{ "VARCHAR", SYM(VARCHAR)},
{ "VARCHARACTER", SYM(VARCHAR)},
+ { "VARCHAR2", SYM(VARCHAR2_MARIADB_SYM)},
{ "VARIABLES", SYM(VARIABLES)},
{ "VARYING", SYM(VARYING)},
{ "VIA", SYM(VIA_SYM)},
{ "VIEW", SYM(VIEW_SYM)},
{ "VIRTUAL", SYM(VIRTUAL_SYM)},
+ { "VERSIONING", SYM(VERSIONING_SYM)},
{ "WAIT", SYM(WAIT_SYM)},
{ "WARNINGS", SYM(WARNINGS)},
{ "WEEK", SYM(WEEK_SYM)},
@@ -673,6 +710,8 @@ static SYMBOL symbols[] = {
{ "WHILE", SYM(WHILE_SYM)},
{ "WINDOW", SYM(WINDOW_SYM)},
{ "WITH", SYM(WITH)},
+ { "WITHIN", SYM(WITHIN)},
+ { "WITHOUT", SYM(WITHOUT)},
{ "WORK", SYM(WORK_SYM)},
{ "WRAPPER", SYM(WRAPPER_SYM)},
{ "WRITE", SYM(WRITE_SYM)},
@@ -683,7 +722,7 @@ static SYMBOL symbols[] = {
{ "YEAR", SYM(YEAR_SYM)},
{ "YEAR_MONTH", SYM(YEAR_MONTH_SYM)},
{ "ZEROFILL", SYM(ZEROFILL)},
- { "||", SYM(OR_OR_SYM)}
+ { "||", SYM(OR2_SYM)}
};
@@ -699,6 +738,8 @@ static SYMBOL sql_functions[] = {
{ "CURTIME", SYM(CURTIME)},
{ "DATE_ADD", SYM(DATE_ADD_INTERVAL)},
{ "DATE_SUB", SYM(DATE_SUB_INTERVAL)},
+ { "DATE_FORMAT", SYM(DATE_FORMAT_SYM)},
+ { "DECODE", SYM(DECODE_MARIADB_SYM)},
{ "DENSE_RANK", SYM(DENSE_RANK_SYM)},
{ "EXTRACT", SYM(EXTRACT_SYM)},
{ "FIRST_VALUE", SYM(FIRST_VALUE_SYM)},
@@ -706,6 +747,7 @@ static SYMBOL sql_functions[] = {
{ "LAG", SYM(LAG_SYM)},
{ "LEAD", SYM(LEAD_SYM)},
{ "MAX", SYM(MAX_SYM)},
+ { "MEDIAN", SYM(MEDIAN_SYM)},
{ "MID", SYM(SUBSTRING)}, /* unireg function */
{ "MIN", SYM(MIN_SYM)},
{ "NOW", SYM(NOW_SYM)},
@@ -713,6 +755,8 @@ static SYMBOL sql_functions[] = {
{ "NTILE", SYM(NTILE_SYM)},
{ "POSITION", SYM(POSITION_SYM)},
{ "PERCENT_RANK", SYM(PERCENT_RANK_SYM)},
+ { "PERCENTILE_CONT", SYM(PERCENTILE_CONT_SYM)},
+ { "PERCENTILE_DISC", SYM(PERCENTILE_DISC_SYM)},
{ "RANK", SYM(RANK_SYM)},
{ "ROW_NUMBER", SYM(ROW_NUMBER_SYM)},
{ "SESSION_USER", SYM(USER_SYM)},
@@ -727,6 +771,7 @@ static SYMBOL sql_functions[] = {
{ "SYSDATE", SYM(SYSDATE)},
{ "SYSTEM_USER", SYM(USER_SYM)},
{ "TRIM", SYM(TRIM)},
+ { "TRIM_ORACLE", SYM(TRIM_ORACLE)},
{ "VARIANCE", SYM(VARIANCE_SYM)},
{ "VAR_POP", SYM(VARIANCE_SYM)},
{ "VAR_SAMP", SYM(VAR_SAMP_SYM)},
diff --git a/sql/lex_string.h b/sql/lex_string.h
new file mode 100644
index 00000000000..a5209165be0
--- /dev/null
+++ b/sql/lex_string.h
@@ -0,0 +1,65 @@
+/*
+ Copyright (c) 2018, 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-1301 USA */
+
+
+#ifndef LEX_STRING_INCLUDED
+#define LEX_STRING_INCLUDED
+
+typedef struct st_mysql_const_lex_string LEX_CSTRING;
+
+/* Functions to compare if two lex strings are equal */
+
+static inline bool lex_string_cmp(CHARSET_INFO *charset, const LEX_CSTRING *a,
+ const LEX_CSTRING *b)
+{
+ return my_strcasecmp(charset, a->str, b->str);
+}
+
+/*
+ Compare to LEX_CSTRING's and return 0 if equal
+*/
+
+static inline bool cmp(const LEX_CSTRING *a, const LEX_CSTRING *b)
+{
+ return (a->length != b->length ||
+ memcmp(a->str, b->str, a->length));
+}
+
+/*
+ Compare if two LEX_CSTRING are equal. Assumption is that
+ character set is ASCII (like for plugin names)
+*/
+
+static inline bool lex_string_eq(const LEX_CSTRING *a, const LEX_CSTRING *b)
+{
+ if (a->length != b->length)
+ return 0; /* Different */
+ return strcasecmp(a->str, b->str) == 0;
+}
+
+/*
+ To be used when calling lex_string_eq with STRING_WITH_LEN() as second
+ argument
+*/
+
+static inline bool lex_string_eq(const LEX_CSTRING *a, const char *b, size_t b_length)
+{
+ if (a->length != b_length)
+ return 0; /* Different */
+ return strcasecmp(a->str, b) == 0;
+}
+
+#endif /* LEX_STRING_INCLUDED */
diff --git a/sql/lock.cc b/sql/lock.cc
index c882a84f3b8..7dcba179a36 100644
--- a/sql/lock.cc
+++ b/sql/lock.cc
@@ -70,7 +70,7 @@
in case external_lock() fails.
*/
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "debug_sync.h"
#include "lock.h"
@@ -252,16 +252,11 @@ static void track_table_access(THD *thd, TABLE **tables, size_t count)
{
if (thd->variables.session_track_transaction_info > TX_TRACK_NONE)
{
- Transaction_state_tracker *tst= (Transaction_state_tracker *)
- thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER);
-
while (count--)
{
- TABLE *t= tables[count];
-
- if (t)
- tst->add_trx_state(thd, t->reginfo.lock_type,
- t->file->has_transactions());
+ if (TABLE *t= tables[count])
+ thd->session_tracker.transaction_info.add_trx_state(thd,
+ t->reginfo.lock_type, t->file->has_transaction_manager());
}
}
}
@@ -294,7 +289,8 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags)
if (lock_tables_check(thd, tables, count, flags))
DBUG_RETURN(NULL);
- if (!(thd->variables.option_bits & OPTION_TABLE_LOCK))
+ if (!(thd->variables.option_bits & OPTION_TABLE_LOCK) &&
+ !(flags & MYSQL_LOCK_USE_MALLOC))
gld_flags|= GET_LOCK_ON_THD;
if (! (sql_lock= get_lock_data(thd, tables, count, gld_flags)))
@@ -392,7 +388,7 @@ static int lock_external(THD *thd, TABLE **tables, uint count)
(*tables)->reginfo.lock_type <= TL_READ_NO_INSERT))
lock_type=F_RDLCK;
- if ((error=(*tables)->file->ha_external_lock(thd,lock_type)))
+ if (unlikely((error=(*tables)->file->ha_external_lock(thd,lock_type))))
{
(*tables)->file->print_error(error, MYF(0));
while (--i)
@@ -415,14 +411,18 @@ static int lock_external(THD *thd, TABLE **tables, uint count)
void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock)
{
mysql_unlock_tables(thd, sql_lock,
- thd->variables.option_bits & OPTION_TABLE_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)
{
- DBUG_ENTER("mysql_unlock_tables");
bool errors= thd->is_error();
+ PSI_stage_info org_stage;
+ DBUG_ENTER("mysql_unlock_tables");
+
+ thd->backup_stage(&org_stage);
THD_STAGE_INFO(thd, stage_unlocking_tables);
if (sql_lock->table_count)
@@ -430,9 +430,13 @@ void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock, bool free_lock)
if (sql_lock->lock_count)
thr_multi_unlock(sql_lock->locks, sql_lock->lock_count, 0);
if (free_lock)
+ {
+ DBUG_ASSERT(!(sql_lock->flags & GET_LOCK_ON_THD));
my_free(sql_lock);
- if (!errors)
+ }
+ if (likely(!errors))
thd->clear_error();
+ THD_STAGE_INFO(thd, org_stage);
DBUG_VOID_RETURN;
}
@@ -442,10 +446,10 @@ 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)
+void 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);
+ get_lock_data(thd, table, count, GET_LOCK_UNLOCK | GET_LOCK_ON_THD | flag);
if (sql_lock)
mysql_unlock_tables(thd, sql_lock, 0);
}
@@ -542,7 +546,7 @@ void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table)
DBUG_ASSERT(table->lock_position == i);
/* Unlock the table. */
- mysql_unlock_some_tables(thd, &table, /* table count */ 1);
+ mysql_unlock_some_tables(thd, &table, /* table count */ 1, 0);
/* Decrement table_count in advance, making below expressions easier */
old_tables= --locked->table_count;
@@ -648,6 +652,7 @@ MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b)
sql_lock->table_count=a->table_count+b->table_count;
sql_lock->locks=(THR_LOCK_DATA**) (sql_lock+1);
sql_lock->table=(TABLE**) (sql_lock->locks+sql_lock->lock_count*2);
+ sql_lock->flags= 0;
memcpy(sql_lock->locks,a->locks,a->lock_count*sizeof(*a->locks));
memcpy(sql_lock->locks+a->lock_count,b->locks,
b->lock_count*sizeof(*b->locks));
@@ -700,7 +705,7 @@ static int unlock_external(THD *thd, TABLE **table,uint count)
if ((*table)->current_lock != F_UNLCK)
{
(*table)->current_lock = F_UNLCK;
- if ((error=(*table)->file->ha_external_lock(thd, F_UNLCK)))
+ if (unlikely((error=(*table)->file->ha_external_lock(thd, F_UNLCK))))
{
error_code= error;
(*table)->file->print_error(error, MYF(0));
@@ -720,6 +725,8 @@ static int unlock_external(THD *thd, TABLE **table,uint count)
@param flags One of:
- GET_LOCK_UNLOCK : If we should send TL_IGNORE to store lock
- GET_LOCK_STORE_LOCKS : Store lock info in TABLE
+ - GET_LOCK_SKIP_SEQUENCES : Ignore sequences (for temporary unlock)
+ - GET_LOCK_ON_THD : Store lock in thd->mem_root
*/
MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags)
@@ -737,7 +744,8 @@ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags)
TABLE *t= table_ptr[i];
if (t->s->tmp_table != NON_TRANSACTIONAL_TMP_TABLE &&
- t->s->tmp_table != INTERNAL_TMP_TABLE)
+ t->s->tmp_table != INTERNAL_TMP_TABLE &&
+ (!(flags & GET_LOCK_SKIP_SEQUENCES) || t->s->sequence == 0))
{
lock_count+= t->file->lock_count();
table_count++;
@@ -760,6 +768,7 @@ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags)
locks= locks_buf= sql_lock->locks= (THR_LOCK_DATA**) (sql_lock + 1);
to= table_buf= sql_lock->table= (TABLE**) (locks + lock_count * 2);
sql_lock->table_count= table_count;
+ sql_lock->flags= flags;
for (i=0 ; i < count ; i++)
{
@@ -768,7 +777,8 @@ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags)
THR_LOCK_DATA **locks_start;
table= table_ptr[i];
if (table->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE ||
- table->s->tmp_table == INTERNAL_TMP_TABLE)
+ table->s->tmp_table == INTERNAL_TMP_TABLE ||
+ ((flags & GET_LOCK_SKIP_SEQUENCES) && table->s->sequence))
continue;
lock_type= table->reginfo.lock_type;
DBUG_ASSERT(lock_type != TL_WRITE_DEFAULT && lock_type != TL_READ_DEFAULT);
@@ -892,7 +902,7 @@ bool lock_object_name(THD *thd, MDL_key::enum_mdl_namespace mdl_type,
MDL_request schema_request;
MDL_request mdl_request;
- DBUG_ASSERT(ok_for_lower_case_names(db));
+ DBUG_SLOW_ASSERT(ok_for_lower_case_names(db));
if (thd->locked_tables_mode)
{
@@ -1063,8 +1073,8 @@ void Global_read_lock::unlock_global_read_lock(THD *thd)
int ret = wsrep->resync(wsrep);
if (ret != WSREP_OK)
{
- WSREP_WARN("resync failed %d for FTWRL: db: %s, query: %s", ret,
- (thd->db ? thd->db : "(null)"), thd->query());
+ WSREP_WARN("resync failed %d for FTWRL: db: %s, query: %s",
+ ret, thd->get_db(), thd->query());
DBUG_VOID_RETURN;
}
}
@@ -1150,7 +1160,7 @@ bool Global_read_lock::make_global_read_lock_block_commit(THD *thd)
if (rcode != WSREP_OK)
{
WSREP_WARN("FTWRL desync failed %d for schema: %s, query: %s",
- rcode, (thd->db ? thd->db : "(null)"), thd->query());
+ rcode, thd->get_db(), thd->query());
my_message(ER_LOCK_DEADLOCK, "wsrep desync failed for FTWRL", MYF(0));
DBUG_RETURN(TRUE);
}
diff --git a/sql/lock.h b/sql/lock.h
index 460cbd7df8f..e5036ea032c 100644
--- a/sql/lock.h
+++ b/sql/lock.h
@@ -31,7 +31,7 @@ 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);
+void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count, uint flag);
void 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);
@@ -46,6 +46,7 @@ bool lock_object_name(THD *thd, MDL_key::enum_mdl_namespace mdl_type,
#define GET_LOCK_STORE_LOCKS 1
#define GET_LOCK_ACTION_MASK 1
#define GET_LOCK_ON_THD (1 << 1)
+#define GET_LOCK_SKIP_SEQUENCES (1 << 2)
MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags);
void reset_lock_data(MYSQL_LOCK *sql_lock, bool unlock);
diff --git a/sql/log.cc b/sql/log.cc
index cb484210c50..c77500bb9f5 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -25,7 +25,7 @@
Abort logging when we get an error in reading or writing log files
*/
-#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
#include "log.h"
#include "sql_base.h" // open_log_table
@@ -39,11 +39,9 @@
#include "rpl_filter.h"
#include "rpl_rli.h"
#include "sql_audit.h"
-#include "log_slow.h"
#include "mysqld.h"
#include <my_dir.h>
-#include <stdarg.h>
#include <m_ctype.h> // For test_if_number
#include <set_var.h> // for Sys_last_gtid_ptr
@@ -53,16 +51,20 @@
#endif
#include "sql_plugin.h"
-#include "rpl_handler.h"
#include "debug_sync.h"
#include "sql_show.h"
#include "my_pthread.h"
+#include "semisync_master.h"
#include "wsrep_mysqld.h"
+#include "sp_rcontext.h"
+#include "sp_head.h"
/* max size of the log message */
#define MAX_LOG_BUFFER_SIZE 1024
#define MAX_TIME_SIZE 32
#define MY_OFF_T_UNDEF (~(my_off_t)0UL)
+/* Truncate cache log files bigger than this */
+#define CACHE_FILE_TRUNC_SIZE 65536
#define FLAGSTR(V,F) ((V)&(F)?#F" ":"")
@@ -87,8 +89,8 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all);
static int binlog_prepare(handlerton *hton, THD *thd, bool all);
static int binlog_start_consistent_snapshot(handlerton *hton, THD *thd);
-static LEX_STRING const write_error_msg=
- { C_STRING_WITH_LEN("error writing to the binary log") };
+static const LEX_CSTRING write_error_msg=
+ { STRING_WITH_LEN("error writing to the binary log") };
static my_bool opt_optimize_thread_scheduling= TRUE;
ulong binlog_checksum_options;
@@ -109,6 +111,13 @@ static ulonglong binlog_status_group_commit_trigger_timeout;
static char binlog_snapshot_file[FN_REFLEN];
static ulonglong binlog_snapshot_position;
+static const char *fatal_log_error=
+ "Could not use %s for logging (error %d). "
+ "Turning logging off for the whole duration of the MariaDB server process. "
+ "To turn it on again: fix the cause, shutdown the MariaDB server and "
+ "restart it.";
+
+
static SHOW_VAR binlog_status_vars_detail[]=
{
{"commits",
@@ -252,7 +261,8 @@ void make_default_log_name(char **out, const char* log_ext, bool once)
class binlog_cache_data
{
public:
- binlog_cache_data(): m_pending(0), before_stmt_pos(MY_OFF_T_UNDEF),
+ binlog_cache_data(): m_pending(0), status(0),
+ before_stmt_pos(MY_OFF_T_UNDEF),
incident(FALSE), changes_to_non_trans_temp_table_flag(FALSE),
saved_max_binlog_cache_size(0), ptr_binlog_cache_use(0),
ptr_binlog_cache_disk_use(0)
@@ -264,9 +274,22 @@ public:
close_cached_file(&cache_log);
}
+ /*
+ Return 1 if there is no relevant entries in the cache
+
+ This is:
+ - Cache is empty
+ - There are row or critical (DDL?) events in the cache
+
+ The status test is needed to avoid writing entries with only
+ a table map entry, which would crash in do_apply_event() on the slave
+ as it assumes that there is always a row entry after a table map.
+ */
bool empty() const
{
- return pending() == NULL && my_b_tell(&cache_log) == 0;
+ return (pending() == NULL &&
+ (my_b_write_tell(&cache_log) == 0 ||
+ ((status & (LOGGED_ROW_EVENT | LOGGED_CRITICAL)) == 0)));
}
Rows_log_event *pending() const
@@ -301,23 +324,19 @@ public:
void reset()
{
- compute_statistics();
- truncate(0);
- if(cache_log.file != -1)
+ bool cache_was_empty= empty();
+ bool truncate_file= (cache_log.file != -1 &&
+ my_b_write_tell(&cache_log) > CACHE_FILE_TRUNC_SIZE);
+ truncate(0,1); // Forget what's in cache
+ if (!cache_was_empty)
+ compute_statistics();
+ if (truncate_file)
my_chsize(cache_log.file, 0, 0, MYF(MY_WME));
changes_to_non_trans_temp_table_flag= FALSE;
+ status= 0;
incident= FALSE;
before_stmt_pos= MY_OFF_T_UNDEF;
- /*
- The truncate function calls reinit_io_cache that calls
- my_b_flush_io_cache which may increase disk_writes. This breaks
- the disk_writes use by the binary log which aims to compute the
- ratio between in-memory cache usage and disk cache usage. To
- avoid this undesirable behavior, we reset the variable after
- truncating the cache.
- */
- cache_log.disk_writes= 0;
DBUG_ASSERT(empty());
}
@@ -378,6 +397,11 @@ public:
cache_log.end_of_file= saved_max_binlog_cache_size;
}
+ void add_status(enum_logged_status status_arg)
+ {
+ status|= status_arg;
+ }
+
/*
Cache to store data before copying it to the binary log.
*/
@@ -391,6 +415,13 @@ private:
Rows_log_event *m_pending;
/*
+ Bit flags for what has been writting to cache. Used to
+ discard logs without any data changes.
+ see enum_logged_status;
+ */
+ uint32 status;
+
+ /*
Binlog position before the start of the current statement.
*/
my_off_t before_stmt_pos;
@@ -412,11 +443,16 @@ private:
*/
void compute_statistics()
{
- if (!empty())
+ statistic_increment(*ptr_binlog_cache_use, &LOCK_status);
+ if (cache_log.disk_writes != 0)
{
- statistic_increment(*ptr_binlog_cache_use, &LOCK_status);
- if (cache_log.disk_writes != 0)
- statistic_increment(*ptr_binlog_cache_disk_use, &LOCK_status);
+#ifdef REAL_STATISTICS
+ statistic_add(*ptr_binlog_cache_disk_use,
+ cache_log.disk_writes, &LOCK_status);
+#else
+ statistic_increment(*ptr_binlog_cache_disk_use, &LOCK_status);
+#endif
+ cache_log.disk_writes= 0;
}
}
@@ -445,7 +481,7 @@ private:
It truncates the cache to a certain position. This includes deleting the
pending event.
*/
- void truncate(my_off_t pos)
+ void truncate(my_off_t pos, bool reset_cache=0)
{
DBUG_PRINT("info", ("truncating to position %lu", (ulong) pos));
cache_log.error=0;
@@ -454,7 +490,7 @@ private:
delete pending();
set_pending(0);
}
- reinit_io_cache(&cache_log, WRITE_CACHE, pos, 0, 0);
+ reinit_io_cache(&cache_log, WRITE_CACHE, pos, 0, reset_cache);
cache_log.end_of_file= saved_max_binlog_cache_size;
}
@@ -462,6 +498,13 @@ private:
binlog_cache_data(const binlog_cache_data& info);
};
+
+void Log_event_writer::add_status(enum_logged_status status)
+{
+ if (likely(cache_data))
+ cache_data->add_status(status);
+}
+
class binlog_cache_mngr {
public:
binlog_cache_mngr(my_off_t param_max_binlog_stmt_cache_size,
@@ -568,19 +611,19 @@ int check_if_log_table(const TABLE_LIST *table,
const char *error_msg)
{
int result= 0;
- if (table->db_length == 5 &&
- !my_strcasecmp(table_alias_charset, table->db, "mysql"))
+ if (table->db.length == 5 &&
+ !my_strcasecmp(table_alias_charset, table->db.str, "mysql"))
{
- const char *table_name= table->table_name;
+ const char *table_name= table->table_name.str;
- if (table->table_name_length == 11 &&
+ if (table->table_name.length == 11 &&
!my_strcasecmp(table_alias_charset, table_name, "general_log"))
{
result= QUERY_LOG_GENERAL;
goto end;
}
- if (table->table_name_length == 8 &&
+ if (table->table_name.length == 8 &&
!my_strcasecmp(table_alias_charset, table_name, "slow_log"))
{
result= QUERY_LOG_SLOW;
@@ -648,10 +691,9 @@ void Log_to_csv_event_handler::cleanup()
*/
bool Log_to_csv_event_handler::
- log_general(THD *thd, my_hrtime_t event_time, const char *user_host,
- uint user_host_len, my_thread_id thread_id_arg,
- const char *command_type, uint command_type_len,
- const char *sql_text, uint sql_text_len,
+ log_general(THD *thd, my_hrtime_t event_time, const char *user_host, size_t user_host_len, my_thread_id thread_id_arg,
+ const char *command_type, size_t command_type_len,
+ const char *sql_text, size_t sql_text_len,
CHARSET_INFO *client_cs)
{
TABLE_LIST table_list;
@@ -676,9 +718,7 @@ bool Log_to_csv_event_handler::
save_thd_options= thd->variables.option_bits;
thd->variables.option_bits&= ~OPTION_BIN_LOG;
- table_list.init_one_table(MYSQL_SCHEMA_NAME.str, MYSQL_SCHEMA_NAME.length,
- GENERAL_LOG_NAME.str, GENERAL_LOG_NAME.length,
- GENERAL_LOG_NAME.str,
+ table_list.init_one_table(&MYSQL_SCHEMA_NAME, &GENERAL_LOG_NAME, 0,
TL_WRITE_CONCURRENT_INSERT);
/*
@@ -814,9 +854,9 @@ err:
bool Log_to_csv_event_handler::
log_slow(THD *thd, my_hrtime_t current_time,
- const char *user_host, uint user_host_len,
+ const char *user_host, size_t user_host_len,
ulonglong query_utime, ulonglong lock_utime, bool is_command,
- const char *sql_text, uint sql_text_len)
+ const char *sql_text, size_t sql_text_len)
{
TABLE_LIST table_list;
TABLE *table;
@@ -841,9 +881,7 @@ bool Log_to_csv_event_handler::
*/
save_time_zone_used= thd->time_zone_used;
- table_list.init_one_table(MYSQL_SCHEMA_NAME.str, MYSQL_SCHEMA_NAME.length,
- SLOW_LOG_NAME.str, SLOW_LOG_NAME.length,
- SLOW_LOG_NAME.str,
+ table_list.init_one_table(&MYSQL_SCHEMA_NAME, &SLOW_LOG_NAME, 0,
TL_WRITE_CONCURRENT_INSERT);
if (!(table= open_log_table(thd, &table_list, &open_tables_backup)))
@@ -897,9 +935,9 @@ bool Log_to_csv_event_handler::
goto err;
/* fill database field */
- if (thd->db)
+ if (thd->db.str)
{
- if (table->field[6]->store(thd->db, thd->db_length, client_cs))
+ if (table->field[6]->store(thd->db.str, thd->db.length, client_cs))
goto err;
table->field[6]->set_notnull();
}
@@ -979,7 +1017,7 @@ int Log_to_csv_event_handler::
{
TABLE_LIST table_list;
TABLE *table;
- LEX_STRING *UNINIT_VAR(log_name);
+ LEX_CSTRING *UNINIT_VAR(log_name);
int result;
Open_tables_backup open_tables_backup;
@@ -995,9 +1033,7 @@ int Log_to_csv_event_handler::
log_name= &SLOW_LOG_NAME;
}
- table_list.init_one_table(MYSQL_SCHEMA_NAME.str, MYSQL_SCHEMA_NAME.length,
- log_name->str, log_name->length, log_name->str,
- TL_WRITE_CONCURRENT_INSERT);
+ table_list.init_one_table(&MYSQL_SCHEMA_NAME, log_name, 0, TL_WRITE_CONCURRENT_INSERT);
table= open_log_table(thd, &table_list, &open_tables_backup);
if (table)
@@ -1037,9 +1073,9 @@ void Log_to_file_event_handler::init_pthread_objects()
bool Log_to_file_event_handler::
log_slow(THD *thd, my_hrtime_t current_time,
- const char *user_host, uint user_host_len,
+ const char *user_host, size_t user_host_len,
ulonglong query_utime, ulonglong lock_utime, bool is_command,
- const char *sql_text, uint sql_text_len)
+ const char *sql_text, size_t sql_text_len)
{
Silence_log_table_errors error_handler;
thd->push_internal_handler(&error_handler);
@@ -1058,10 +1094,9 @@ bool Log_to_file_event_handler::
*/
bool Log_to_file_event_handler::
- log_general(THD *thd, my_hrtime_t event_time, const char *user_host,
- uint user_host_len, my_thread_id thread_id_arg,
- const char *command_type, uint command_type_len,
- const char *sql_text, uint sql_text_len,
+ log_general(THD *thd, my_hrtime_t event_time, const char *user_host, size_t user_host_len, my_thread_id thread_id_arg,
+ const char *command_type, size_t command_type_len,
+ const char *sql_text, size_t sql_text_len,
CHARSET_INFO *client_cs)
{
Silence_log_table_errors error_handler;
@@ -1264,7 +1299,7 @@ bool LOGGER::flush_general_log()
TRUE error occurred
*/
-bool LOGGER::slow_log_print(THD *thd, const char *query, uint query_length,
+bool LOGGER::slow_log_print(THD *thd, const char *query, size_t query_length,
ulonglong current_utime)
{
@@ -1313,7 +1348,7 @@ bool LOGGER::slow_log_print(THD *thd, const char *query, uint query_length,
{
is_command= TRUE;
query= command_name[thd->get_command()].str;
- query_length= command_name[thd->get_command()].length;
+ query_length= (uint)command_name[thd->get_command()].length;
}
for (current_handler= slow_log_handler_list; *current_handler ;)
@@ -1328,7 +1363,7 @@ bool LOGGER::slow_log_print(THD *thd, const char *query, uint query_length,
}
bool LOGGER::general_log_write(THD *thd, enum enum_server_command command,
- const char *query, uint query_length)
+ const char *query, size_t query_length)
{
bool error= FALSE;
Log_event_handler **current_handler= general_log_handler_list;
@@ -1345,8 +1380,8 @@ bool LOGGER::general_log_write(THD *thd, enum enum_server_command command,
mysql_audit_general_log(thd, hrtime_to_time(current_time),
user_host_buff, user_host_len,
command_name[(uint) command].str,
- command_name[(uint) command].length,
- query, query_length);
+ (uint)command_name[(uint) command].length,
+ query, (uint)query_length);
if (opt_log && log_command(thd, command))
{
@@ -1368,7 +1403,7 @@ bool LOGGER::general_log_write(THD *thd, enum enum_server_command command,
bool LOGGER::general_log_print(THD *thd, enum enum_server_command command,
const char *format, va_list args)
{
- uint message_buff_len= 0;
+ size_t message_buff_len= 0;
char message_buff[MAX_LOG_BUFFER_SIZE];
/* prepare message */
@@ -1969,7 +2004,9 @@ static bool trans_cannot_safely_rollback(THD *thd, bool all)
static int binlog_commit(handlerton *hton, THD *thd, bool all)
{
int error= 0;
+ PSI_stage_info org_stage;
DBUG_ENTER("binlog_commit");
+
binlog_cache_mngr *const cache_mngr=
(binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
@@ -1986,6 +2023,9 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all)
YESNO(thd->transaction.all.modified_non_trans_table),
YESNO(thd->transaction.stmt.modified_non_trans_table)));
+
+ thd->backup_stage(&org_stage);
+ THD_STAGE_INFO(thd, stage_binlog_write);
if (!cache_mngr->stmt_cache.empty())
{
error= binlog_commit_flush_stmt_cache(thd, all, cache_mngr);
@@ -1997,6 +2037,7 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all)
we're here because cache_log was flushed in MYSQL_BIN_LOG::log_xid()
*/
cache_mngr->reset(false, true);
+ THD_STAGE_INFO(thd, org_stage);
DBUG_RETURN(error);
}
@@ -2006,7 +2047,7 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all)
- We are in a transaction and a full transaction is committed.
Otherwise, we accumulate the changes.
*/
- if (!error && ending_trans(thd, all))
+ if (likely(!error) && ending_trans(thd, all))
error= binlog_commit_flush_trx_cache(thd, all, cache_mngr);
/*
@@ -2015,6 +2056,7 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all)
if (!all)
cache_mngr->trx_cache.set_prev_position(MY_OFF_T_UNDEF);
+ THD_STAGE_INFO(thd, org_stage);
DBUG_RETURN(error);
}
@@ -2083,7 +2125,7 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all)
*/
error |= binlog_truncate_trx_cache(thd, cache_mngr, all);
}
- else if (!error)
+ else if (likely(!error))
{
if (ending_trans(thd, all) && trans_cannot_safely_rollback(thd, all))
error= binlog_rollback_flush_trx_cache(thd, all, cache_mngr);
@@ -2135,7 +2177,7 @@ void MYSQL_BIN_LOG::set_write_error(THD *thd, bool is_transactional)
write_error= 1;
- if (check_write_error(thd))
+ if (unlikely(check_write_error(thd)))
DBUG_VOID_RETURN;
if (my_errno == EFBIG)
@@ -2163,7 +2205,7 @@ bool MYSQL_BIN_LOG::check_write_error(THD *thd)
bool checked= FALSE;
- if (!thd->is_error())
+ if (likely(!thd->is_error()))
DBUG_RETURN(checked);
switch (thd->get_stmt_da()->sql_errno())
@@ -2213,8 +2255,7 @@ static int binlog_savepoint_set(handlerton *hton, THD *thd, void *sv)
String log_query(buf, sizeof(buf), &my_charset_bin);
if (log_query.copy(STRING_WITH_LEN("SAVEPOINT "), &my_charset_bin) ||
- append_identifier(thd, &log_query,
- thd->lex->ident.str, thd->lex->ident.length))
+ append_identifier(thd, &log_query, &thd->lex->ident))
DBUG_RETURN(1);
int errcode= query_error_code(thd, thd->killed == NOT_KILLED);
Query_log_event qinfo(thd, log_query.c_ptr_safe(), log_query.length(),
@@ -2232,7 +2273,7 @@ static int binlog_savepoint_set(handlerton *hton, THD *thd, void *sv)
or "RELEASE S" without the preceding "SAVEPOINT S" in the binary
log.
*/
- if (!(error= mysql_bin_log.write(&qinfo)))
+ if (likely(!(error= mysql_bin_log.write(&qinfo))))
binlog_trans_log_savepos(thd, (my_off_t*) sv);
DBUG_RETURN(error);
@@ -2253,8 +2294,7 @@ static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv)
char buf[1024];
String log_query(buf, sizeof(buf), &my_charset_bin);
if (log_query.copy(STRING_WITH_LEN("ROLLBACK TO "), &my_charset_bin) ||
- append_identifier(thd, &log_query,
- thd->lex->ident.str, thd->lex->ident.length))
+ append_identifier(thd, &log_query, &thd->lex->ident))
DBUG_RETURN(1);
int errcode= query_error_code(thd, thd->killed == NOT_KILLED);
Query_log_event qinfo(thd, log_query.ptr(), log_query.length(),
@@ -2338,7 +2378,7 @@ 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, IO_SIZE*2, READ_CACHE, 0, 0,
+ if (init_io_cache(log, file, (size_t)binlog_file_cache_size, READ_CACHE, 0, 0,
MYF(MY_WME|MY_DONT_CHECK_FILESIZE)))
{
sql_print_error("Failed to create a cache on log (file '%s')",
@@ -2431,7 +2471,8 @@ static int find_uniq_filename(char *name, ulong next_log_number)
length= (size_t) (end - start + 1);
if ((DBUG_EVALUATE_IF("error_unique_log_filename", 1,
- !(dir_info= my_dir(buff,MYF(MY_DONT_SORT))))))
+ unlikely(!(dir_info= my_dir(buff,
+ MYF(MY_DONT_SORT)))))))
{ // This shouldn't happen
strmov(end,".1"); // use name+1
DBUG_RETURN(1);
@@ -2619,11 +2660,11 @@ bool MYSQL_LOG::open(
if (log_type == LOG_NORMAL)
{
char *end;
- int len=my_snprintf(buff, sizeof(buff), "%s, Version: %s (%s). "
+ size_t len=my_snprintf(buff, sizeof(buff), "%s, Version: %s (%s). "
#ifdef EMBEDDED_LIBRARY
"embedded library\n",
my_progname, server_version, MYSQL_COMPILATION_COMMENT
-#elif _WIN32
+#elif defined(_WIN32)
"started with:\nTCP Port: %d, Named Pipe: %s\n",
my_progname, server_version, MYSQL_COMPILATION_COMMENT,
mysqld_port, mysqld_unix_port
@@ -2633,7 +2674,7 @@ bool MYSQL_LOG::open(
mysqld_port, mysqld_unix_port
#endif
);
- end= strnmov(buff + len, "Time Id Command Argument\n",
+ end= strnmov(buff + len, "Time\t\t Id Command\tArgument\n",
sizeof(buff) - len);
if (my_b_write(&log_file, (uchar*) buff, (uint) (end-buff)) ||
flush_io_cache(&log_file))
@@ -2644,10 +2685,7 @@ bool MYSQL_LOG::open(
DBUG_RETURN(0);
err:
- sql_print_error("Could not use %s for logging (error %d). \
-Turning logging off for the whole duration of the MySQL server process. \
-To turn it on again: fix the cause, \
-shutdown the MySQL server and restart it.", name, errno);
+ sql_print_error(fatal_log_error, name, errno);
if (file >= 0)
mysql_file_close(file, MYF(0));
end_io_cache(&log_file);
@@ -2744,10 +2782,10 @@ int MYSQL_LOG::generate_new_name(char *new_name, const char *log_name,
if (!fn_ext(log_name)[0])
{
if (DBUG_EVALUATE_IF("binlog_inject_new_name_error", TRUE, FALSE) ||
- find_uniq_filename(new_name, next_log_number))
+ unlikely(find_uniq_filename(new_name, next_log_number)))
{
THD *thd= current_thd;
- if (thd)
+ if (unlikely(thd))
my_error(ER_NO_UNIQUE_LOGFILE, MYF(ME_FATALERROR), log_name);
sql_print_error(ER_DEFAULT(ER_NO_UNIQUE_LOGFILE), log_name);
return 1;
@@ -2829,15 +2867,14 @@ void MYSQL_QUERY_LOG::reopen_file()
TRUE - error occurred
*/
-bool MYSQL_QUERY_LOG::write(time_t event_time, const char *user_host,
- uint user_host_len, my_thread_id thread_id_arg,
- const char *command_type, uint command_type_len,
- const char *sql_text, uint sql_text_len)
+bool MYSQL_QUERY_LOG::write(time_t event_time, const char *user_host, size_t user_host_len, my_thread_id thread_id_arg,
+ const char *command_type, size_t command_type_len,
+ const char *sql_text, size_t sql_text_len)
{
char buff[32];
char local_time_buff[MAX_TIME_SIZE];
struct tm start;
- uint time_buff_len= 0;
+ size_t time_buff_len= 0;
mysql_mutex_lock(&LOCK_log);
@@ -2848,27 +2885,27 @@ bool MYSQL_QUERY_LOG::write(time_t event_time, const char *user_host,
DBUG_EXECUTE_IF("reset_log_last_time", last_time= 0;);
/* Note that my_b_write() assumes it knows the length for this */
- if (event_time != last_time)
- {
- last_time= event_time;
+ if (event_time != last_time)
+ {
+ last_time= event_time;
- localtime_r(&event_time, &start);
+ localtime_r(&event_time, &start);
- time_buff_len= my_snprintf(local_time_buff, MAX_TIME_SIZE,
- "%02d%02d%02d %2d:%02d:%02d\t",
- start.tm_year % 100, start.tm_mon + 1,
- start.tm_mday, start.tm_hour,
- start.tm_min, start.tm_sec);
+ time_buff_len= my_snprintf(local_time_buff, MAX_TIME_SIZE,
+ "%02d%02d%02d %2d:%02d:%02d\t",
+ start.tm_year % 100, start.tm_mon + 1,
+ start.tm_mday, start.tm_hour,
+ start.tm_min, start.tm_sec);
- if (my_b_write(&log_file, (uchar*) local_time_buff, time_buff_len))
- goto err;
- }
- else
- if (my_b_write(&log_file, (uchar*) "\t\t" ,2) < 0)
- goto err;
+ if (my_b_write(&log_file, (uchar*) local_time_buff, time_buff_len))
+ goto err;
+ }
+ else
+ if (my_b_write(&log_file, (uchar*) "\t\t" ,2) < 0)
+ goto err;
- /* command_type, thread_id */
- size_t length= my_snprintf(buff, 32, "%5llu ", thread_id_arg);
+ /* command_type, thread_id */
+ size_t length= my_snprintf(buff, 32, "%6llu ", thread_id_arg);
if (my_b_write(&log_file, (uchar*) buff, length))
goto err;
@@ -2931,18 +2968,17 @@ err:
*/
bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time,
- const char *user_host,
- uint user_host_len, ulonglong query_utime,
+ const char *user_host, size_t user_host_len, ulonglong query_utime,
ulonglong lock_utime, bool is_command,
- const char *sql_text, uint sql_text_len)
+ const char *sql_text, size_t sql_text_len)
{
bool error= 0;
+ char llbuff[22];
DBUG_ENTER("MYSQL_QUERY_LOG::write");
mysql_mutex_lock(&LOCK_log);
if (is_open())
{ // Safety against reopen
- int tmp_errno= 0;
char buff[80], *end;
char query_time_buff[22+7], lock_time_buff[22+7];
size_t buff_len;
@@ -2964,37 +3000,50 @@ bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time,
/* Note that my_b_write() assumes it knows the length for this */
if (my_b_write(&log_file, (uchar*) buff, buff_len))
- tmp_errno= errno;
+ goto err;
}
const uchar uh[]= "# User@Host: ";
- if (my_b_write(&log_file, uh, sizeof(uh) - 1))
- tmp_errno= errno;
- if (my_b_write(&log_file, (uchar*) user_host, user_host_len))
- tmp_errno= errno;
- if (my_b_write(&log_file, (uchar*) "\n", 1))
- tmp_errno= errno;
- }
+ if (my_b_write(&log_file, uh, sizeof(uh) - 1) ||
+ my_b_write(&log_file, (uchar*) user_host, user_host_len) ||
+ my_b_write(&log_file, (uchar*) "\n", 1))
+ goto err;
/* For slow query log */
sprintf(query_time_buff, "%.6f", ulonglong2double(query_utime)/1000000.0);
sprintf(lock_time_buff, "%.6f", ulonglong2double(lock_utime)/1000000.0);
if (my_b_printf(&log_file,
- "# Thread_id: %lu Schema: %s QC_hit: %s\n" \
- "# Query_time: %s Lock_time: %s Rows_sent: %lu Rows_examined: %lu\n" \
- "# Rows_affected: %lu\n",
- (ulong) thd->thread_id, (thd->db ? thd->db : ""),
+ "# Thread_id: %lu Schema: %s QC_hit: %s\n"
+ "# Query_time: %s Lock_time: %s Rows_sent: %lu Rows_examined: %lu\n"
+ "# Rows_affected: %lu Bytes_sent: %lu\n",
+ (ulong) thd->thread_id, thd->get_db(),
((thd->query_plan_flags & QPLAN_QC) ? "Yes" : "No"),
query_time_buff, lock_time_buff,
(ulong) thd->get_sent_row_count(),
(ulong) thd->get_examined_row_count(),
- thd->get_stmt_da()->is_ok() ?
- (ulong) thd->get_stmt_da()->affected_rows() :
- 0) == (size_t) -1)
- tmp_errno= errno;
+ (ulong) thd->get_affected_rows(),
+ (ulong) (thd->status_var.bytes_sent - thd->bytes_sent_old)))
+ goto err;
+
+ if ((thd->variables.log_slow_verbosity & LOG_SLOW_VERBOSITY_QUERY_PLAN)
+ && thd->tmp_tables_used &&
+ my_b_printf(&log_file,
+ "# Tmp_tables: %lu Tmp_disk_tables: %lu "
+ "Tmp_table_sizes: %s\n",
+ (ulong) thd->tmp_tables_used,
+ (ulong) thd->tmp_tables_disk_used,
+ llstr(thd->tmp_tables_size, llbuff)))
+ goto err;
+
+ if (thd->spcont &&
+ my_b_printf(&log_file, "# Stored_routine: %s\n",
+ ErrConvDQName(thd->spcont->m_sp).ptr()))
+ goto err;
+
if ((thd->variables.log_slow_verbosity & LOG_SLOW_VERBOSITY_QUERY_PLAN) &&
(thd->query_plan_flags &
(QPLAN_FULL_SCAN | QPLAN_FULL_JOIN | QPLAN_TMP_TABLE |
- QPLAN_TMP_DISK | QPLAN_FILESORT | QPLAN_FILESORT_DISK)) &&
+ QPLAN_TMP_DISK | QPLAN_FILESORT | QPLAN_FILESORT_DISK |
+ QPLAN_FILESORT_PRIORITY_QUEUE)) &&
my_b_printf(&log_file,
"# Full_scan: %s Full_join: %s "
"Tmp_table: %s Tmp_table_on_disk: %s\n"
@@ -3002,30 +3051,31 @@ bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time,
"Priority_queue: %s\n",
((thd->query_plan_flags & QPLAN_FULL_SCAN) ? "Yes" : "No"),
((thd->query_plan_flags & QPLAN_FULL_JOIN) ? "Yes" : "No"),
- ((thd->query_plan_flags & QPLAN_TMP_TABLE) ? "Yes" : "No"),
- ((thd->query_plan_flags & QPLAN_TMP_DISK) ? "Yes" : "No"),
+ (thd->tmp_tables_used ? "Yes" : "No"),
+ (thd->tmp_tables_disk_used ? "Yes" : "No"),
((thd->query_plan_flags & QPLAN_FILESORT) ? "Yes" : "No"),
((thd->query_plan_flags & QPLAN_FILESORT_DISK) ?
"Yes" : "No"),
thd->query_plan_fsort_passes,
((thd->query_plan_flags & QPLAN_FILESORT_PRIORITY_QUEUE) ?
"Yes" : "No")
- ) == (size_t) -1)
- tmp_errno= errno;
+ ))
+ goto err;
if (thd->variables.log_slow_verbosity & LOG_SLOW_VERBOSITY_EXPLAIN &&
thd->lex->explain)
{
StringBuffer<128> buf;
DBUG_ASSERT(!thd->free_list);
if (!print_explain_for_slow_log(thd->lex, thd, &buf))
- my_b_printf(&log_file, "%s", buf.c_ptr_safe());
+ if (my_b_printf(&log_file, "%s", buf.c_ptr_safe()))
+ goto err;
thd->free_items();
}
- if (thd->db && strcmp(thd->db, db))
+ if (thd->db.str && strcmp(thd->db.str, db))
{ // Database changed
- if (my_b_printf(&log_file,"use %s;\n",thd->db) == (size_t) -1)
- tmp_errno= errno;
- strmov(db,thd->db);
+ if (my_b_printf(&log_file,"use %s;\n",thd->db.str))
+ goto err;
+ strmov(db,thd->db.str);
}
if (thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt)
{
@@ -3060,7 +3110,7 @@ bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time,
*end='\n';
if (my_b_write(&log_file, (uchar*) "SET ", 4) ||
my_b_write(&log_file, (uchar*) buff + 1, (uint) (end-buff)))
- tmp_errno= errno;
+ goto err;
}
if (is_command)
{
@@ -3069,24 +3119,27 @@ bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time,
DBUG_EXECUTE_IF("simulate_slow_log_write_error",
{DBUG_SET("+d,simulate_file_write_error");});
if(my_b_write(&log_file, (uchar*) buff, buff_len))
- tmp_errno= errno;
+ goto err;
}
if (my_b_write(&log_file, (uchar*) sql_text, sql_text_len) ||
my_b_write(&log_file, (uchar*) ";\n",2) ||
flush_io_cache(&log_file))
- tmp_errno= errno;
- if (tmp_errno)
- {
- error= 1;
- if (! write_error)
- {
- write_error= 1;
- sql_print_error(ER_DEFAULT(ER_ERROR_ON_WRITE), name, tmp_errno);
- }
+ goto err;
+
}
}
+end:
mysql_mutex_unlock(&LOCK_log);
DBUG_RETURN(error);
+
+err:
+ error= 1;
+ if (!write_error)
+ {
+ write_error= 1;
+ sql_print_error(ER_THD(thd, ER_ERROR_ON_WRITE), name, errno);
+ }
+ goto end;
}
@@ -3143,7 +3196,7 @@ MYSQL_BIN_LOG::MYSQL_BIN_LOG(uint *sync_period)
group_commit_trigger_lock_wait(0),
sync_period_ptr(sync_period), sync_counter(0),
state_file_deleted(false), binlog_state_recover_done(false),
- is_relay_log(0), signal_cnt(0),
+ is_relay_log(0), relay_signal_cnt(0),
checksum_alg_reset(BINLOG_CHECKSUM_ALG_UNDEF),
relay_log_checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF),
description_event_for_exec(0), description_event_for_queue(0),
@@ -3213,7 +3266,8 @@ void MYSQL_BIN_LOG::cleanup()
mysql_mutex_destroy(&LOCK_xid_list);
mysql_mutex_destroy(&LOCK_binlog_background_thread);
mysql_mutex_destroy(&LOCK_binlog_end_pos);
- mysql_cond_destroy(&update_cond);
+ mysql_cond_destroy(&COND_relay_log_updated);
+ mysql_cond_destroy(&COND_bin_log_updated);
mysql_cond_destroy(&COND_queue_busy);
mysql_cond_destroy(&COND_xid_list);
mysql_cond_destroy(&COND_binlog_background_thread);
@@ -3248,7 +3302,8 @@ void MYSQL_BIN_LOG::init_pthread_objects()
mysql_mutex_setflags(&LOCK_index, MYF_NO_DEADLOCK_DETECTION);
mysql_mutex_init(key_BINLOG_LOCK_xid_list,
&LOCK_xid_list, MY_MUTEX_INIT_FAST);
- mysql_cond_init(m_key_update_cond, &update_cond, 0);
+ mysql_cond_init(m_key_relay_log_update, &COND_relay_log_updated, 0);
+ mysql_cond_init(m_key_bin_log_update, &COND_bin_log_updated, 0);
mysql_cond_init(m_key_COND_queue_busy, &COND_queue_busy, 0);
mysql_cond_init(key_BINLOG_COND_xid_list, &COND_xid_list, 0);
@@ -3569,9 +3624,9 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
Write the current binlog checkpoint into the log, so XA recovery will
know from where to start recovery.
*/
- uint off= dirname_length(log_file_name);
- uint len= strlen(log_file_name) - off;
- new_xid_list_entry= new xid_count_per_binlog(log_file_name+off, (int)len);
+ size_t off= dirname_length(log_file_name);
+ uint len= static_cast<uint>(strlen(log_file_name) - off);
+ new_xid_list_entry= new xid_count_per_binlog(log_file_name+off, len);
if (!new_xid_list_entry)
goto err;
@@ -3731,18 +3786,21 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
close_purge_index_file();
#endif
+ /* Notify the io thread that binlog is rotated to a new file */
+ if (is_relay_log)
+ signal_relay_log_update();
+ else
+ update_binlog_end_pos();
DBUG_RETURN(0);
err:
+ int tmp_errno= errno;
#ifdef HAVE_REPLICATION
if (is_inited_purge_index_file())
purge_index_entry(NULL, NULL, need_mutex);
close_purge_index_file();
#endif
- sql_print_error("Could not use %s for logging (error %d). \
-Turning logging off for the whole duration of the MySQL server process. \
-To turn it on again: fix the cause, \
-shutdown the MySQL server and restart it.", (name) ? name : log_name, errno);
+ sql_print_error(fatal_log_error, (name) ? name : log_name, tmp_errno);
if (new_xid_list_entry)
delete new_xid_list_entry;
if (file >= 0)
@@ -3883,7 +3941,7 @@ int MYSQL_BIN_LOG::find_log_pos(LOG_INFO *linfo, const char *log_name,
for (;;)
{
- uint length;
+ size_t length;
my_off_t offset= my_b_tell(&index_file);
DBUG_EXECUTE_IF("simulate_find_log_pos_error",
@@ -3953,7 +4011,7 @@ end:
int MYSQL_BIN_LOG::find_next_log(LOG_INFO* linfo, bool need_lock)
{
int error= 0;
- uint length;
+ size_t length;
char fname[FN_REFLEN];
char *full_fname= linfo->log_file_name;
@@ -4102,14 +4160,6 @@ bool MYSQL_BIN_LOG::reset_logs(THD *thd, bool create_new_log,
mysql_mutex_unlock(&LOCK_xid_list);
}
- /*
- The following mutex is needed to ensure that no threads call
- 'delete thd' as we would then risk missing a 'rollback' from this
- thread. If the transaction involved MyISAM tables, it should go
- into binlog even on rollback.
- */
- mysql_mutex_lock(&LOCK_thread_count);
-
/* Save variables so that we can reopen the log */
save_name=name;
name=0; // Protect against free
@@ -4136,7 +4186,7 @@ bool MYSQL_BIN_LOG::reset_logs(THD *thd, bool create_new_log,
for (;;)
{
- if ((error= my_delete(linfo.log_file_name, MYF(0))) != 0)
+ if (unlikely((error= my_delete(linfo.log_file_name, MYF(0)))))
{
if (my_errno == ENOENT)
{
@@ -4179,7 +4229,8 @@ bool MYSQL_BIN_LOG::reset_logs(THD *thd, bool create_new_log,
/* Start logging with a new file */
close(LOG_CLOSE_INDEX | LOG_CLOSE_TO_BE_OPENED);
- if ((error= my_delete(index_file_name, MYF(0)))) // Reset (open will update)
+ // Reset (open will update)
+ if (unlikely((error= my_delete(index_file_name, MYF(0)))))
{
if (my_errno == ENOENT)
{
@@ -4208,15 +4259,14 @@ 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 ((error= open(save_name, log_type, 0, next_log_number,
- io_cache_type, max_size, 0, FALSE)))
+ if (unlikely((error= open(save_name, log_type, 0, next_log_number,
+ io_cache_type, max_size, 0, FALSE))))
goto err;
my_free((void *) save_name);
err:
if (error == 1)
name= const_cast<char*>(save_name);
- mysql_mutex_unlock(&LOCK_thread_count);
if (!is_relay_log)
{
@@ -4358,8 +4408,9 @@ int MYSQL_BIN_LOG::purge_first_log(Relay_log_info* rli, bool included)
Read the next log file name from the index file and pass it back to
the caller.
*/
- if((error=find_log_pos(&rli->linfo, rli->event_relay_log_name, 0)) ||
- (error=find_next_log(&rli->linfo, 0)))
+ if (unlikely((error=find_log_pos(&rli->linfo, rli->event_relay_log_name,
+ 0))) ||
+ unlikely((error=find_next_log(&rli->linfo, 0))))
{
sql_print_error("next log error: %d offset: %llu log: %s included: %d",
error, rli->linfo.index_file_offset,
@@ -4474,14 +4525,14 @@ int MYSQL_BIN_LOG::purge_logs(const char *to_log,
if (need_mutex)
mysql_mutex_lock(&LOCK_index);
- if ((error=find_log_pos(&log_info, to_log, 0 /*no mutex*/)))
+ if (unlikely((error=find_log_pos(&log_info, to_log, 0 /*no mutex*/))) )
{
sql_print_error("MYSQL_BIN_LOG::purge_logs was called with file %s not "
"listed in the index.", to_log);
goto err;
}
- if ((error= open_purge_index_file(TRUE)))
+ if (unlikely((error= open_purge_index_file(TRUE))))
{
sql_print_error("MYSQL_BIN_LOG::purge_logs failed to sync the index file.");
goto err;
@@ -4491,12 +4542,12 @@ int MYSQL_BIN_LOG::purge_logs(const char *to_log,
File name exists in index file; delete until we find this file
or a file that is used.
*/
- if ((error=find_log_pos(&log_info, NullS, 0 /*no mutex*/)))
+ if (unlikely((error=find_log_pos(&log_info, NullS, 0 /*no mutex*/))))
goto err;
while ((strcmp(to_log,log_info.log_file_name) || (exit_loop=included)) &&
can_purge_log(log_info.log_file_name))
{
- if ((error= register_purge_index_entry(log_info.log_file_name)))
+ if (unlikely((error= register_purge_index_entry(log_info.log_file_name))))
{
sql_print_error("MYSQL_BIN_LOG::purge_logs failed to copy %s to register file.",
log_info.log_file_name);
@@ -4509,14 +4560,14 @@ int MYSQL_BIN_LOG::purge_logs(const char *to_log,
DBUG_EXECUTE_IF("crash_purge_before_update_index", DBUG_SUICIDE(););
- if ((error= sync_purge_index_file()))
+ if (unlikely((error= sync_purge_index_file())))
{
sql_print_error("MYSQL_BIN_LOG::purge_logs failed to flush register file.");
goto err;
}
/* We know how many files to delete. Update index file. */
- if ((error=update_log_index(&log_info, need_update_threads)))
+ if (unlikely((error=update_log_index(&log_info, need_update_threads))))
{
sql_print_error("MYSQL_BIN_LOG::purge_logs failed to update the index file");
goto err;
@@ -4607,8 +4658,9 @@ int MYSQL_BIN_LOG::sync_purge_index_file()
int error= 0;
DBUG_ENTER("MYSQL_BIN_LOG::sync_purge_index_file");
- if ((error= flush_io_cache(&purge_index_file)) ||
- (error= my_sync(purge_index_file.file, MYF(MY_WME|MY_SYNC_FILESIZE))))
+ if (unlikely((error= flush_io_cache(&purge_index_file))) ||
+ unlikely((error= my_sync(purge_index_file.file,
+ MYF(MY_WME | MY_SYNC_FILESIZE)))))
DBUG_RETURN(error);
DBUG_RETURN(error);
@@ -4619,8 +4671,9 @@ int MYSQL_BIN_LOG::register_purge_index_entry(const char *entry)
int error= 0;
DBUG_ENTER("MYSQL_BIN_LOG::register_purge_index_entry");
- if ((error=my_b_write(&purge_index_file, (const uchar*)entry, strlen(entry))) ||
- (error=my_b_write(&purge_index_file, (const uchar*)"\n", 1)))
+ if (unlikely((error=my_b_write(&purge_index_file, (const uchar*)entry,
+ strlen(entry)))) ||
+ unlikely((error=my_b_write(&purge_index_file, (const uchar*)"\n", 1))))
DBUG_RETURN (error);
DBUG_RETURN(error);
@@ -4643,7 +4696,8 @@ int MYSQL_BIN_LOG::purge_index_entry(THD *thd, ulonglong *reclaimed_space,
DBUG_ASSERT(my_b_inited(&purge_index_file));
- if ((error=reinit_io_cache(&purge_index_file, READ_CACHE, 0, 0, 0)))
+ if (unlikely((error= reinit_io_cache(&purge_index_file, READ_CACHE, 0, 0,
+ 0))))
{
sql_print_error("MYSQL_BIN_LOG::purge_index_entry failed to reinit register file "
"for read");
@@ -4652,7 +4706,7 @@ int MYSQL_BIN_LOG::purge_index_entry(THD *thd, ulonglong *reclaimed_space,
for (;;)
{
- uint length;
+ size_t length;
if ((length=my_b_gets(&purge_index_file, log_info.log_file_name,
FN_REFLEN)) <= 1)
@@ -4672,7 +4726,8 @@ int MYSQL_BIN_LOG::purge_index_entry(THD *thd, ulonglong *reclaimed_space,
/* Get rid of the trailing '\n' */
log_info.log_file_name[length-1]= 0;
- if (!mysql_file_stat(m_key_file_log, log_info.log_file_name, &s, MYF(0)))
+ if (unlikely(!mysql_file_stat(m_key_file_log, log_info.log_file_name, &s,
+ MYF(0))))
{
if (my_errno == ENOENT)
{
@@ -4719,7 +4774,8 @@ int MYSQL_BIN_LOG::purge_index_entry(THD *thd, ulonglong *reclaimed_space,
}
else
{
- if ((error= find_log_pos(&check_log_info, log_info.log_file_name, need_mutex)))
+ if (unlikely((error= find_log_pos(&check_log_info,
+ log_info.log_file_name, need_mutex))))
{
if (error != LOG_INFO_EOF)
{
@@ -4832,7 +4888,7 @@ int MYSQL_BIN_LOG::purge_logs_before_date(time_t purge_time)
mysql_mutex_lock(&LOCK_index);
to_log[0]= 0;
- if ((error=find_log_pos(&log_info, NullS, 0 /*no mutex*/)))
+ if (unlikely((error=find_log_pos(&log_info, NullS, 0 /*no mutex*/))))
goto err;
while (strcmp(log_file_name, log_info.log_file_name) &&
@@ -4999,7 +5055,7 @@ MYSQL_BIN_LOG::is_gtid_cached(THD *thd)
void MYSQL_BIN_LOG::make_log_name(char* buf, const char* log_ident)
{
- uint dir_len = dirname_length(log_file_name);
+ size_t dir_len = dirname_length(log_file_name);
if (dir_len >= FN_REFLEN)
dir_len=FN_REFLEN-1;
strnmov(buf, log_file_name, dir_len);
@@ -5099,8 +5155,13 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock)
We have to do this here and not in open as we want to store the
new file name in the current binary log file.
*/
- if ((error= generate_new_name(new_name, name, 0)))
+ if (unlikely((error= generate_new_name(new_name, name, 0))))
+ {
+#ifdef ENABLE_AND_FIX_HANG
+ close_on_error= TRUE;
+#endif
goto end;
+ }
new_name_ptr=new_name;
if (log_type == LOG_BIN)
@@ -5131,13 +5192,20 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock)
}
bytes_written += r.data_written;
}
- /*
- Update needs to be signalled even if there is no rotate event
- log rotation should give the waiting thread a signal to
- discover EOF and move on to the next log.
- */
- signal_update();
}
+
+ /*
+ Update needs to be signalled even if there is no rotate event
+ log rotation should give the waiting thread a signal to
+ discover EOF and move on to the next log.
+ */
+ if (unlikely((error= flush_io_cache(&log_file))))
+ {
+ close_on_error= TRUE;
+ goto end;
+ }
+ update_binlog_end_pos();
+
old_name=name;
name=0; // Don't free name
close_flag= LOG_CLOSE_TO_BE_OPENED | LOG_CLOSE_INDEX;
@@ -5177,7 +5245,7 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock)
/* reopen index binlog file, BUG#34582 */
file_to_open= index_file_name;
error= open_index_file(index_file_name, 0, FALSE);
- if (!error)
+ if (likely(!error))
{
/* reopen the binary log file. */
file_to_open= new_name_ptr;
@@ -5186,7 +5254,7 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock)
}
/* handle reopening errors */
- if (error)
+ if (unlikely(error))
{
my_error(ER_CANT_OPEN_FILE, MYF(ME_FATALERROR), file_to_open, error);
close_on_error= TRUE;
@@ -5202,7 +5270,7 @@ end:
mysql_file_close(old_file, MYF(MY_WME));
}
- if (error && close_on_error /* rotate or reopen failed */)
+ if (unlikely(error && close_on_error)) /* rotate or reopen failed */
{
/*
Close whatever was left opened.
@@ -5217,12 +5285,7 @@ end:
- ...
*/
close(LOG_CLOSE_INDEX);
- sql_print_error("Could not open %s for logging (error %d). "
- "Turning logging off for the whole duration "
- "of the MySQL server process. To turn it on "
- "again: fix the cause, shutdown the MySQL "
- "server and restart it.",
- new_name_ptr, errno);
+ sql_print_error(fatal_log_error, new_name_ptr, errno);
}
mysql_mutex_unlock(&LOCK_index);
@@ -5232,12 +5295,14 @@ end:
DBUG_RETURN(error);
}
-bool MYSQL_BIN_LOG::write_event(Log_event *ev, IO_CACHE *file)
+bool MYSQL_BIN_LOG::write_event(Log_event *ev, binlog_cache_data *cache_data,
+ IO_CACHE *file)
{
- Log_event_writer writer(file, &crypto);
+ Log_event_writer writer(file, 0, &crypto);
if (crypto.scheme && file == &log_file)
writer.ctx= alloca(crypto.ctx_size);
-
+ if (cache_data)
+ cache_data->add_status(ev->logged_status());
return writer.write(ev);
}
@@ -5271,7 +5336,7 @@ bool MYSQL_BIN_LOG::append_no_lock(Log_event* ev)
if (my_b_append_tell(&log_file) > max_size)
error= new_file_without_locking();
err:
- signal_update(); // Safe as we don't call close
+ update_binlog_end_pos();
DBUG_RETURN(error);
}
@@ -5331,8 +5396,8 @@ bool MYSQL_BIN_LOG::write_event_buffer(uchar* buf, uint len)
error= new_file_without_locking();
err:
my_safe_afree(ebuf, len);
- if (!error)
- signal_update();
+ if (likely(!error))
+ update_binlog_end_pos();
DBUG_RETURN(error);
}
@@ -5437,13 +5502,14 @@ stmt_has_updated_trans_table(const THD *thd)
*/
bool use_trans_cache(const THD* thd, bool is_transactional)
{
+ if (is_transactional)
+ return 1;
binlog_cache_mngr *const cache_mngr=
(binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
- return
- ((thd->is_current_stmt_binlog_format_row() ||
- thd->variables.binlog_direct_non_trans_update) ? is_transactional :
- (is_transactional || !cache_mngr->trx_cache.empty()));
+ return ((thd->is_current_stmt_binlog_format_row() ||
+ thd->variables.binlog_direct_non_trans_update) ? 0 :
+ !cache_mngr->trx_cache.empty());
}
/**
@@ -5600,10 +5666,9 @@ THD::binlog_start_trans_and_stmt()
{
binlog_cache_mngr *const cache_mngr=
(binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
-
- IO_CACHE *file=
- cache_mngr->get_binlog_cache_log(use_trans_cache(this, true));
- Log_event_writer writer(file);
+ 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,
@@ -5704,26 +5769,24 @@ int THD::binlog_write_table_map(TABLE *table, bool is_transactional,
binlog_cache_mngr *const cache_mngr=
(binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
-
- IO_CACHE *file=
- cache_mngr->get_binlog_cache_log(use_trans_cache(this, is_transactional));
- Log_event_writer writer(file);
- binlog_cache_data *cache_data=
- cache_mngr->get_binlog_cache_data(use_trans_cache(this, is_transactional));
+ binlog_cache_data *cache_data= (cache_mngr->
+ get_binlog_cache_data(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 ((error= writer.write(&anno)))
+ if (unlikely((error= writer.write(&anno))))
{
if (my_errno == EFBIG)
cache_data->set_incident();
DBUG_RETURN(error);
}
}
- if ((error= writer.write(&the_event)))
+ if (unlikely((error= writer.write(&the_event))))
DBUG_RETURN(error);
binlog_table_maps++;
@@ -5837,7 +5900,6 @@ MYSQL_BIN_LOG::flush_and_set_pending_rows_event(THD *thd,
DBUG_ASSERT(WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open());
DBUG_PRINT("enter", ("event: %p", event));
- int error= 0;
binlog_cache_mngr *const cache_mngr=
(binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
@@ -5850,7 +5912,7 @@ MYSQL_BIN_LOG::flush_and_set_pending_rows_event(THD *thd,
if (Rows_log_event* pending= cache_data->pending())
{
- Log_event_writer writer(&cache_data->cache_log);
+ Log_event_writer writer(&cache_data->cache_log, cache_data);
/*
Write pending event to the cache.
@@ -5875,7 +5937,7 @@ MYSQL_BIN_LOG::flush_and_set_pending_rows_event(THD *thd,
thd->binlog_set_pending_rows_event(event, is_transactional);
- DBUG_RETURN(error);
+ DBUG_RETURN(0);
}
@@ -5906,6 +5968,8 @@ MYSQL_BIN_LOG::write_gtid_event(THD *thd, bool standalone,
local_server_id= thd->variables.server_id;
seq_no= thd->variables.gtid_seq_no;
+ DBUG_ASSERT(local_server_id != 0);
+
if (thd->variables.option_bits & OPTION_GTID_BEGIN)
{
DBUG_PRINT("error", ("OPTION_GTID_BEGIN is set. "
@@ -6251,7 +6315,7 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
prev_binlog_id= current_binlog_id;
DBUG_EXECUTE_IF("binlog_force_commit_id",
{
- const LEX_STRING commit_name= { C_STRING_WITH_LEN("commit_id") };
+ const LEX_CSTRING commit_name= { STRING_WITH_LEN("commit_id") };
bool null_value;
user_var_entry *entry=
(user_var_entry*) my_hash_search(&thd->user_vars,
@@ -6269,8 +6333,8 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
goto err;
is_trans_cache= use_trans_cache(thd, using_trans);
- file= cache_mngr->get_binlog_cache_log(is_trans_cache);
cache_data= cache_mngr->get_binlog_cache_data(is_trans_cache);
+ file= &cache_data->cache_log;
if (thd->lex->stmt_accessed_non_trans_temp_table())
cache_data->set_changes_to_non_trans_temp_table();
@@ -6294,21 +6358,19 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
Annotate_rows_log_event anno(thd, using_trans, direct);
/* Annotate event should be written not more than once */
*with_annotate= 0;
- if (write_event(&anno, file))
+ if (write_event(&anno, cache_data, file))
goto err;
}
- if (thd)
{
if (!thd->is_current_stmt_binlog_format_row())
{
-
if (thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt)
{
Intvar_log_event e(thd,(uchar) LAST_INSERT_ID_EVENT,
thd->first_successful_insert_id_in_prev_stmt_for_binlog,
using_trans, direct);
- if (write_event(&e, file))
+ if (write_event(&e, cache_data, file))
goto err;
}
if (thd->auto_inc_intervals_in_cur_stmt_for_binlog.nb_elements() > 0)
@@ -6319,14 +6381,14 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
Intvar_log_event e(thd, (uchar) INSERT_ID_EVENT,
thd->auto_inc_intervals_in_cur_stmt_for_binlog.
minimum(), using_trans, direct);
- if (write_event(&e, file))
+ if (write_event(&e, cache_data, file))
goto err;
}
if (thd->rand_used)
{
Rand_log_event e(thd,thd->rand_saved_seed1,thd->rand_saved_seed2,
using_trans, direct);
- if (write_event(&e, file))
+ if (write_event(&e, cache_data, file))
goto err;
}
if (thd->user_var_events.elements)
@@ -6350,7 +6412,7 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
flags,
using_trans,
direct);
- if (write_event(&e, file))
+ if (write_event(&e, cache_data, file))
goto err;
}
}
@@ -6360,7 +6422,7 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
/*
Write the event.
*/
- if (write_event(event_info, file) ||
+ if (write_event(event_info, cache_data, file) ||
DBUG_EVALUATE_IF("injecting_fault_writing", 1, 0))
goto err;
@@ -6370,8 +6432,9 @@ err:
{
my_off_t offset= my_b_tell(file);
bool check_purge= false;
+ DBUG_ASSERT(!is_relay_log);
- if (!error)
+ if (likely(!error))
{
bool synced;
@@ -6384,26 +6447,23 @@ err:
mysql_mutex_assert_owner(&LOCK_log);
mysql_mutex_assert_not_owner(&LOCK_after_binlog_sync);
mysql_mutex_assert_not_owner(&LOCK_commit_ordered);
- bool first= true;
- bool last= true;
- if ((error= RUN_HOOK(binlog_storage, after_flush,
- (thd, log_file_name, file->pos_in_file,
- synced, first, last))))
+#ifdef HAVE_REPLICATION
+ if (repl_semisync_master.report_binlog_update(thd, log_file_name,
+ file->pos_in_file))
{
sql_print_error("Failed to run 'after_flush' hooks");
error= 1;
}
else
+#endif
{
- /* update binlog_end_pos so it can be read by dump thread
- *
- * note: must be _after_ the RUN_HOOK(after_flush) or else
- * semi-sync-plugin might not have put the transaction into
- * it's list before dump-thread tries to send it
- */
+ /*
+ update binlog_end_pos so it can be read by dump thread
+ note: must be _after_ the RUN_HOOK(after_flush) or else
+ semi-sync might not have put the transaction into
+ it's list before dump-thread tries to send it
+ */
update_binlog_end_pos(offset);
-
- signal_update();
/*
If a transaction with the LOAD DATA statement is divided
into logical mini-transactions (of the 10K rows) and binlog
@@ -6418,7 +6478,7 @@ err:
if (!thd->wsrep_split_flag)
{
#endif /* WITH_WSREP */
- if ((error= rotate(false, &check_purge)))
+ if (unlikely((error= rotate(false, &check_purge))))
check_purge= false;
#ifdef WITH_WSREP
}
@@ -6437,15 +6497,14 @@ err:
mysql_mutex_assert_not_owner(&LOCK_log);
mysql_mutex_assert_owner(&LOCK_after_binlog_sync);
mysql_mutex_assert_not_owner(&LOCK_commit_ordered);
- bool first= true;
- bool last= true;
- if (RUN_HOOK(binlog_storage, after_sync,
- (thd, log_file_name, file->pos_in_file,
- first, last)))
+#ifdef HAVE_REPLICATION
+ if (repl_semisync_master.wait_after_sync(log_file_name,
+ file->pos_in_file))
{
error=1;
/* error is already printed inside hook */
}
+#endif
/*
Take mutex to protect against a reader seeing partial writes of 64-bit
@@ -6460,7 +6519,7 @@ err:
checkpoint_and_purge(prev_binlog_id);
}
- if (error)
+ if (unlikely(error))
{
set_write_error(thd, is_trans_cache);
if (check_write_error(thd) && cache_data &&
@@ -6487,31 +6546,29 @@ bool slow_log_print(THD *thd, const char *query, uint query_length,
}
+/**
+ Decide if we should log the command to general log
+
+ @retval
+ FALSE No logging
+ TRUE Ok to log
+*/
+
bool LOGGER::log_command(THD *thd, enum enum_server_command command)
{
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- Security_context *sctx= thd->security_ctx;
-#endif
/*
Log command if we have at least one log event handler enabled and want
to log this king of commands
*/
- if (*general_log_handler_list && (what_to_log & (1L << (uint) command)))
- {
- if ((thd->variables.option_bits & OPTION_LOG_OFF)
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- && (sctx->master_access & SUPER_ACL)
-#endif
- )
- {
- /* No logging */
- return FALSE;
- }
-
- return TRUE;
- }
+ if (!(*general_log_handler_list && (what_to_log & (1L << (uint) command))))
+ return FALSE;
- return FALSE;
+ /*
+ If LOG_SLOW_DISABLE_SLAVE is set when slave thread starts, then
+ OPTION_LOG_OFF is set.
+ Only the super user can set this bit.
+ */
+ return !(thd->variables.option_bits & OPTION_LOG_OFF);
}
@@ -6521,7 +6578,7 @@ bool general_log_print(THD *thd, enum enum_server_command command,
va_list args;
uint error= 0;
- /* Print the message to the buffer if we want to log this king of commands */
+ /* Print the message to the buffer if we want to log this kind of commands */
if (! logger.log_command(thd, command))
return FALSE;
@@ -6533,7 +6590,7 @@ bool general_log_print(THD *thd, enum enum_server_command command,
}
bool general_log_write(THD *thd, enum enum_server_command command,
- const char *query, uint query_length)
+ const char *query, size_t query_length)
{
/* Write the message to the log if we want to log this king of commands */
if (logger.log_command(thd, command) || mysql_audit_general_enabled())
@@ -6659,7 +6716,7 @@ int MYSQL_BIN_LOG::rotate(bool force_rotate, bool* check_purge)
*/
mark_xids_active(binlog_id, 1);
- if ((error= new_file_without_locking()))
+ if (unlikely((error= new_file_without_locking())))
{
/**
Be conservative... There are possible lost events (eg,
@@ -6855,7 +6912,7 @@ int MYSQL_BIN_LOG::rotate_and_purge(bool force_rotate,
if (err_gtid < 0)
error= 1; // otherwise error is propagated the user
}
- else if ((error= rotate(force_rotate, &check_purge)))
+ else if (unlikely((error= rotate(force_rotate, &check_purge))))
check_purge= false;
/*
NOTE: Run purge_logs wo/ holding LOCK_log because it does not need
@@ -6881,11 +6938,12 @@ uint MYSQL_BIN_LOG::next_file_id()
class CacheWriter: public Log_event_writer
{
public:
- ulong remains;
+ size_t remains;
CacheWriter(THD *thd_arg, IO_CACHE *file_arg, bool do_checksum,
Binlog_crypt_data *cr)
- : Log_event_writer(file_arg, cr), remains(0), thd(thd_arg), first(true)
+ : Log_event_writer(file_arg, 0, cr), remains(0), thd(thd_arg),
+ first(true)
{ checksum_len= do_checksum ? BINLOG_CHECKSUM_LEN : 0; }
~CacheWriter()
@@ -6893,6 +6951,7 @@ public:
int write(uchar* pos, size_t len)
{
+ DBUG_ENTER("CacheWriter::write");
if (first)
write_header(pos, len);
else
@@ -6901,7 +6960,7 @@ public:
remains -= len;
if ((first= !remains))
write_footer();
- return 0;
+ DBUG_RETURN(0);
}
private:
THD *thd;
@@ -6932,9 +6991,9 @@ int MYSQL_BIN_LOG::write_cache(THD *thd, IO_CACHE *cache)
mysql_mutex_assert_owner(&LOCK_log);
if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0))
DBUG_RETURN(ER_ERROR_ON_WRITE);
- uint length= my_b_bytes_in_cache(cache), group, carry, hdr_offs;
- long val;
- ulong end_log_pos_inc= 0; // each event processed adds BINLOG_CHECKSUM_LEN 2 t
+ size_t length= my_b_bytes_in_cache(cache), group, carry, hdr_offs;
+ size_t val;
+ size_t end_log_pos_inc= 0; // each event processed adds BINLOG_CHECKSUM_LEN 2 t
uchar header[LOG_EVENT_HEADER_LEN];
CacheWriter writer(thd, &log_file, binlog_checksum_options, &crypto);
@@ -6959,7 +7018,7 @@ int MYSQL_BIN_LOG::write_cache(THD *thd, IO_CACHE *cache)
split.
*/
- group= (uint)my_b_tell(&log_file);
+ group= (size_t)my_b_tell(&log_file);
hdr_offs= carry= 0;
do
@@ -6971,12 +7030,12 @@ int MYSQL_BIN_LOG::write_cache(THD *thd, IO_CACHE *cache)
if (unlikely(carry > 0))
{
DBUG_ASSERT(carry < LOG_EVENT_HEADER_LEN);
- uint tail= LOG_EVENT_HEADER_LEN - carry;
+ size_t tail= LOG_EVENT_HEADER_LEN - carry;
/* assemble both halves */
memcpy(&header[carry], (char *)cache->read_pos, tail);
- ulong len= uint4korr(header + EVENT_LEN_OFFSET);
+ uint32 len= uint4korr(header + EVENT_LEN_OFFSET);
writer.remains= len;
/* fix end_log_pos */
@@ -7094,6 +7153,8 @@ int query_error_code(THD *thd, bool not_killed)
if (not_killed || (killed_mask_hard(thd->killed) == KILL_BAD_DATA))
{
error= thd->is_error() ? thd->get_stmt_da()->sql_errno() : 0;
+ if (!error)
+ return error;
/* thd->get_get_stmt_da()->sql_errno() might be ER_SERVER_SHUTDOWN or
ER_QUERY_INTERRUPTED, So here we need to make sure that error
@@ -7120,7 +7181,7 @@ bool MYSQL_BIN_LOG::write_incident_already_locked(THD *thd)
uint error= 0;
DBUG_ENTER("MYSQL_BIN_LOG::write_incident_already_locked");
Incident incident= INCIDENT_LOST_EVENTS;
- Incident_log_event ev(thd, incident, write_error_msg);
+ Incident_log_event ev(thd, incident, &write_error_msg);
if (likely(is_open()))
{
@@ -7144,10 +7205,10 @@ bool MYSQL_BIN_LOG::write_incident(THD *thd)
if (likely(is_open()))
{
prev_binlog_id= current_binlog_id;
- if (!(error= write_incident_already_locked(thd)) &&
- !(error= flush_and_sync(0)))
+ if (likely(!(error= write_incident_already_locked(thd))) &&
+ likely(!(error= flush_and_sync(0))))
{
- signal_update();
+ update_binlog_end_pos();
/*
If a transaction with the LOAD DATA statement is divided
into logical mini-transactions (of the 10K rows) and binlog
@@ -7162,7 +7223,7 @@ bool MYSQL_BIN_LOG::write_incident(THD *thd)
if (!thd->wsrep_split_flag)
{
#endif /* WITH_WSREP */
- if ((error= rotate(false, &check_purge)))
+ if (unlikely((error= rotate(false, &check_purge))))
check_purge= false;
#ifdef WITH_WSREP
}
@@ -7205,7 +7266,7 @@ MYSQL_BIN_LOG::write_binlog_checkpoint_event_already_locked(const char *name_arg
*/
if (!write_event(&ev) && !flush_and_sync(0))
{
- signal_update();
+ update_binlog_end_pos();
}
else
{
@@ -7305,7 +7366,7 @@ MYSQL_BIN_LOG::write_transaction_to_binlog(THD *thd,
if (cache_mngr->stmt_cache.has_incident() ||
cache_mngr->trx_cache.has_incident())
{
- Incident_log_event inc_ev(thd, INCIDENT_LOST_EVENTS, write_error_msg);
+ Incident_log_event inc_ev(thd, INCIDENT_LOST_EVENTS, &write_error_msg);
entry.incident_event= &inc_ev;
DBUG_RETURN(write_transaction_to_binlog_events(&entry));
}
@@ -7408,7 +7469,7 @@ MYSQL_BIN_LOG::queue_for_group_commit(group_commit_entry *orig_entry)
&wfc->LOCK_wait_commit,
&stage_waiting_for_prior_transaction_to_commit,
&old_stage);
- while ((loc_waitee= wfc->waitee) && !orig_entry->thd->check_killed())
+ while ((loc_waitee= wfc->waitee) && !orig_entry->thd->check_killed(1))
mysql_cond_wait(&wfc->COND_wait_commit, &wfc->LOCK_wait_commit);
wfc->opaque_pointer= NULL;
DBUG_PRINT("info", ("After waiting for prior commit, queued_by_other=%d",
@@ -7621,7 +7682,7 @@ MYSQL_BIN_LOG::queue_for_group_commit(group_commit_entry *orig_entry)
mysql_mutex_unlock(&LOCK_prepare_ordered);
DEBUG_SYNC(orig_entry->thd, "commit_after_release_LOCK_prepare_ordered");
- DBUG_PRINT("info", ("Queued for group commit as %s\n",
+ DBUG_PRINT("info", ("Queued for group commit as %s",
(orig_queue == NULL) ? "leader" : "participant"));
DBUG_RETURN(orig_queue == NULL);
}
@@ -7640,7 +7701,11 @@ MYSQL_BIN_LOG::write_transaction_to_binlog_events(group_commit_entry *entry)
else if (is_leader)
trx_group_commit_leader(entry);
else if (!entry->queued_by_other)
+ {
+ DEBUG_SYNC(entry->thd, "after_semisync_queue");
+
entry->thd->wait_for_wakeup_ready();
+ }
else
{
/*
@@ -7809,7 +7874,7 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
commit_id= (last_in_queue == leader ? 0 : (uint64)leader->thd->query_id);
DBUG_EXECUTE_IF("binlog_force_commit_id",
{
- const LEX_STRING commit_name= { C_STRING_WITH_LEN("commit_id") };
+ const LEX_CSTRING commit_name= { STRING_WITH_LEN("commit_id") };
bool null_value;
user_var_entry *entry=
(user_var_entry*) my_hash_search(&leader->thd->user_vars,
@@ -7829,6 +7894,7 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
*/
for (current= queue; current != NULL; current= current->next)
{
+ set_current_thd(current->thd);
binlog_cache_mngr *cache_mngr= current->cache_mngr;
/*
@@ -7837,7 +7903,8 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
*/
DBUG_ASSERT(!cache_mngr->stmt_cache.empty() || !cache_mngr->trx_cache.empty());
- if ((current->error= write_transaction_or_stmt(current, commit_id)))
+ if (unlikely((current->error= write_transaction_or_stmt(current,
+ commit_id))))
current->commit_errno= errno;
strmake_buf(cache_mngr->last_commit_pos_file, log_file_name);
@@ -7863,9 +7930,10 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
cache_mngr->delayed_error= false;
}
}
+ set_current_thd(leader->thd);
bool synced= 0;
- if (flush_and_sync(&synced))
+ if (unlikely(flush_and_sync(&synced)))
{
for (current= queue; current != NULL; current= current->next)
{
@@ -7880,45 +7948,41 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
else
{
bool any_error= false;
- bool all_error= true;
mysql_mutex_assert_not_owner(&LOCK_prepare_ordered);
mysql_mutex_assert_owner(&LOCK_log);
mysql_mutex_assert_not_owner(&LOCK_after_binlog_sync);
mysql_mutex_assert_not_owner(&LOCK_commit_ordered);
- bool first= true, last;
+
for (current= queue; current != NULL; current= current->next)
{
- last= current->next == NULL;
- if (!current->error &&
- RUN_HOOK(binlog_storage, after_flush,
- (current->thd,
- current->cache_mngr->last_commit_pos_file,
- current->cache_mngr->last_commit_pos_offset, synced,
- first, last)))
+#ifdef HAVE_REPLICATION
+ if (likely(!current->error) &&
+ unlikely(repl_semisync_master.
+ report_binlog_update(current->thd,
+ current->cache_mngr->
+ last_commit_pos_file,
+ current->cache_mngr->
+ last_commit_pos_offset)))
{
current->error= ER_ERROR_ON_WRITE;
current->commit_errno= -1;
current->error_cache= NULL;
any_error= true;
}
- else
- all_error= false;
- first= false;
+#endif
}
- /* update binlog_end_pos so it can be read by dump thread
- *
- * note: must be _after_ the RUN_HOOK(after_flush) or else
- * semi-sync-plugin might not have put the transaction into
- * it's list before dump-thread tries to send it
- */
+ /*
+ update binlog_end_pos so it can be read by dump thread
+ Note: must be _after_ the RUN_HOOK(after_flush) or else
+ semi-sync might not have put the transaction into
+ it's list before dump-thread tries to send it
+ */
update_binlog_end_pos(commit_offset);
- if (any_error)
+ if (unlikely(any_error))
sql_print_error("Failed to run 'after_flush' hooks");
- if (!all_error)
- signal_update();
}
/*
@@ -7993,18 +8057,19 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
mysql_mutex_assert_owner(&LOCK_after_binlog_sync);
mysql_mutex_assert_not_owner(&LOCK_commit_ordered);
- bool first= true, last;
+ bool first __attribute__((unused))= true;
+ bool last __attribute__((unused));
for (current= queue; current != NULL; current= current->next)
{
last= current->next == NULL;
- if (!current->error &&
- RUN_HOOK(binlog_storage, after_sync,
- (current->thd, current->cache_mngr->last_commit_pos_file,
- current->cache_mngr->last_commit_pos_offset,
- first, last)))
- {
- /* error is already printed inside hook */
- }
+#ifdef HAVE_REPLICATION
+ if (likely(!current->error))
+ current->error=
+ repl_semisync_master.wait_after_sync(current->cache_mngr->
+ last_commit_pos_file,
+ current->cache_mngr->
+ last_commit_pos_offset);
+#endif
first= false;
}
}
@@ -8060,7 +8125,7 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
DEBUG_SYNC(leader->thd, "commit_loop_entry_commit_ordered");
++num_commits;
- if (current->cache_mngr->using_xa && !current->error &&
+ if (current->cache_mngr->using_xa && likely(!current->error) &&
DBUG_EVALUATE_IF("skip_commit_ordered", 0, 1))
run_commit_ordered(current->thd, current->all);
current->thd->wakeup_subsequent_commits(current->error);
@@ -8152,12 +8217,12 @@ MYSQL_BIN_LOG::write_transaction_or_stmt(group_commit_entry *entry,
}
}
- if (mngr->get_binlog_cache_log(FALSE)->error) // Error on read
+ if (unlikely(mngr->get_binlog_cache_log(FALSE)->error))
{
entry->error_cache= &mngr->stmt_cache.cache_log;
DBUG_RETURN(ER_ERROR_ON_WRITE);
}
- if (mngr->get_binlog_cache_log(TRUE)->error) // Error on read
+ if (unlikely(mngr->get_binlog_cache_log(TRUE)->error)) // Error on read
{
entry->error_cache= &mngr->trx_cache.cache_log;
DBUG_RETURN(ER_ERROR_ON_WRITE);
@@ -8315,10 +8380,10 @@ void MYSQL_BIN_LOG::wait_for_update_relay_log(THD* thd)
DBUG_ENTER("wait_for_update_relay_log");
mysql_mutex_assert_owner(&LOCK_log);
- thd->ENTER_COND(&update_cond, &LOCK_log,
+ thd->ENTER_COND(&COND_relay_log_updated, &LOCK_log,
&stage_slave_has_read_all_relay_log,
&old_stage);
- mysql_cond_wait(&update_cond, &LOCK_log);
+ mysql_cond_wait(&COND_relay_log_updated, &LOCK_log);
thd->EXIT_COND(&old_stage);
DBUG_VOID_RETURN;
}
@@ -8339,23 +8404,6 @@ void MYSQL_BIN_LOG::wait_for_update_relay_log(THD* thd)
LOCK_log is released by the caller.
*/
-int MYSQL_BIN_LOG::wait_for_update_bin_log(THD* thd,
- const struct timespec *timeout)
-{
- int ret= 0;
- DBUG_ENTER("wait_for_update_bin_log");
-
- thd_wait_begin(thd, THD_WAIT_BINLOG);
- mysql_mutex_assert_owner(&LOCK_log);
- if (!timeout)
- mysql_cond_wait(&update_cond, &LOCK_log);
- else
- ret= mysql_cond_timedwait(&update_cond, &LOCK_log,
- const_cast<struct timespec *>(timeout));
- thd_wait_end(thd);
- DBUG_RETURN(ret);
-}
-
int MYSQL_BIN_LOG::wait_for_update_binlog_end_pos(THD* thd,
struct timespec *timeout)
{
@@ -8365,9 +8413,9 @@ int MYSQL_BIN_LOG::wait_for_update_binlog_end_pos(THD* thd,
thd_wait_begin(thd, THD_WAIT_BINLOG);
mysql_mutex_assert_owner(get_binlog_end_pos_lock());
if (!timeout)
- mysql_cond_wait(&update_cond, get_binlog_end_pos_lock());
+ mysql_cond_wait(&COND_bin_log_updated, get_binlog_end_pos_lock());
else
- ret= mysql_cond_timedwait(&update_cond, get_binlog_end_pos_lock(),
+ ret= mysql_cond_timedwait(&COND_bin_log_updated, get_binlog_end_pos_lock(),
timeout);
thd_wait_end(thd);
DBUG_RETURN(ret);
@@ -8412,7 +8460,8 @@ void MYSQL_BIN_LOG::close(uint exiting)
relay_log_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF);
write_event(&s);
bytes_written+= s.data_written;
- signal_update();
+ flush_io_cache(&log_file);
+ update_binlog_end_pos();
/*
When we shut down server, write out the binlog state to a separate
@@ -8465,7 +8514,8 @@ void MYSQL_BIN_LOG::close(uint exiting)
if ((exiting & LOG_CLOSE_INDEX) && my_b_inited(&index_file))
{
end_io_cache(&index_file);
- if (mysql_file_close(index_file.file, MYF(0)) < 0 && ! write_error)
+ if (unlikely(mysql_file_close(index_file.file, MYF(0)) < 0) &&
+ ! write_error)
{
write_error= 1;
sql_print_error(ER_DEFAULT(ER_ERROR_ON_WRITE), index_file_name, errno);
@@ -8585,14 +8635,14 @@ void sql_perror(const char *message)
redirect stdout and stderr to a file. The streams are reopened
only for appending (writing at end of file).
*/
-extern "C" my_bool reopen_fstreams(const char *filename,
- FILE *outstream, FILE *errstream)
+bool reopen_fstreams(const char *filename, FILE *outstream, FILE *errstream)
{
- if (outstream && !my_freopen(filename, "a", outstream))
- return TRUE;
-
- if (errstream && !my_freopen(filename, "a", errstream))
+ if ((outstream && !my_freopen(filename, "a", outstream)) ||
+ (errstream && !my_freopen(filename, "a", errstream)))
+ {
+ my_error(ER_CANT_CREATE_FILE, MYF(0), filename, errno);
return TRUE;
+ }
/* The error stream must be unbuffered. */
if (errstream)
@@ -8629,14 +8679,6 @@ bool flush_error_log()
return result;
}
-void MYSQL_BIN_LOG::signal_update()
-{
- DBUG_ENTER("MYSQL_BIN_LOG::signal_update");
- signal_cnt++;
- mysql_cond_broadcast(&update_cond);
- DBUG_VOID_RETURN;
-}
-
#ifdef _WIN32
static void print_buffer_to_nt_eventlog(enum loglevel level, char *buff,
size_t length, size_t buffLen)
@@ -8680,8 +8722,8 @@ static void print_buffer_to_file(enum loglevel level, const char *buffer,
time_t skr;
struct tm tm_tmp;
struct tm *start;
- THD *thd;
- int tag_length= 0;
+ THD *thd= 0;
+ size_t tag_length= 0;
char tag[NAME_LEN];
DBUG_ENTER("print_buffer_to_file");
DBUG_PRINT("enter",("buffer: %s", buffer));
@@ -8714,10 +8756,10 @@ static void print_buffer_to_file(enum loglevel level, const char *buffer,
start->tm_hour,
start->tm_min,
start->tm_sec,
- (unsigned long) pthread_self(),
+ (unsigned long) (thd ? thd->thread_id : 0),
(level == ERROR_LEVEL ? "ERROR" : level == WARNING_LEVEL ?
"Warning" : "Note"),
- tag_length, tag,
+ (int) tag_length, tag,
(int) length, buffer);
fflush(stderr);
@@ -10021,7 +10063,8 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name,
goto err1;
if (do_xa)
- init_alloc_root(&mem_root, TC_LOG_PAGE_SIZE, TC_LOG_PAGE_SIZE, MYF(0));
+ init_alloc_root(&mem_root, "TC_LOG_BINLOG", TC_LOG_PAGE_SIZE,
+ TC_LOG_PAGE_SIZE, MYF(0));
fdle->flags&= ~LOG_EVENT_BINLOG_IN_USE_F; // abort on the first error
@@ -10038,7 +10081,7 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name,
for (;;)
{
while ((ev= Log_event::read_log_event(first_round ? first_log : &log,
- 0, fdle, opt_master_verify_checksum))
+ fdle, opt_master_verify_checksum))
&& ev->is_valid())
{
enum Log_event_type typ= ev->get_type_code();
@@ -10059,7 +10102,7 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name,
case BINLOG_CHECKPOINT_EVENT:
if (first_round && do_xa)
{
- uint dir_len;
+ size_t dir_len;
Binlog_checkpoint_log_event *cev= (Binlog_checkpoint_log_event *)ev;
if (cev->binlog_file_len >= FN_REFLEN)
sql_print_warning("Incorrect binlog checkpoint event with too "
@@ -10234,7 +10277,7 @@ MYSQL_BIN_LOG::do_binlog_recovery(const char *opt_name, bool do_xa_recovery)
char log_name[FN_REFLEN];
int error;
- if ((error= find_log_pos(&log_info, NullS, 1)))
+ if (unlikely((error= find_log_pos(&log_info, NullS, 1))))
{
/*
If there are no binlog files (LOG_INFO_EOF), then we still try to read
@@ -10279,7 +10322,7 @@ MYSQL_BIN_LOG::do_binlog_recovery(const char *opt_name, bool do_xa_recovery)
return 1;
}
- if ((ev= Log_event::read_log_event(&log, 0, &fdle,
+ if ((ev= Log_event::read_log_event(&log, &fdle,
opt_master_verify_checksum)) &&
ev->get_type_code() == FORMAT_DESCRIPTION_EVENT)
{
@@ -10292,7 +10335,7 @@ MYSQL_BIN_LOG::do_binlog_recovery(const char *opt_name, bool do_xa_recovery)
else
{
error= read_state_from_file();
- if (error == 2)
+ if (unlikely(error == 2))
{
/*
The binlog exists, but the .state file is missing. This is normal if
@@ -10456,7 +10499,7 @@ static struct st_mysql_sys_var *binlog_sys_vars[]=
static void
set_binlog_snapshot_file(const char *src)
{
- int dir_len = dirname_length(src);
+ size_t dir_len = dirname_length(src);
strmake_buf(binlog_snapshot_file, src + dir_len);
}
@@ -10520,7 +10563,7 @@ get_gtid_list_event(IO_CACHE *cache, Gtid_list_log_event **out_gtid_list)
*out_gtid_list= NULL;
- if (!(ev= Log_event::read_log_event(cache, 0, &init_fdle,
+ if (!(ev= Log_event::read_log_event(cache, &init_fdle,
opt_master_verify_checksum)) ||
ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
{
@@ -10536,7 +10579,7 @@ get_gtid_list_event(IO_CACHE *cache, Gtid_list_log_event **out_gtid_list)
{
Log_event_type typ;
- ev= Log_event::read_log_event(cache, 0, fdle, opt_master_verify_checksum);
+ ev= Log_event::read_log_event(cache, fdle, opt_master_verify_checksum);
if (!ev)
{
errormsg= "Could not read GTID list event while looking for GTID "
@@ -10566,6 +10609,7 @@ get_gtid_list_event(IO_CACHE *cache, Gtid_list_log_event **out_gtid_list)
return errormsg;
}
+
struct st_mysql_storage_engine binlog_storage_engine=
{ MYSQL_HANDLERTON_INTERFACE_VERSION };
diff --git a/sql/log.h b/sql/log.h
index 0e6b2c895af..2ec7fb70da4 100644
--- a/sql/log.h
+++ b/sql/log.h
@@ -26,6 +26,7 @@ class Relay_log_info;
class Format_description_log_event;
+bool reopen_fstreams(const char *filename, FILE *outstream, FILE *errstream);
void setup_log_handling();
bool trans_has_updated_trans_table(const THD* thd);
bool stmt_has_updated_trans_table(const THD *thd);
@@ -349,19 +350,23 @@ public:
/* for documentation of mutexes held in various places in code */
};
+/* Tell the io thread if we can delay the master info sync. */
+#define SEMI_SYNC_SLAVE_DELAY_SYNC 1
+/* Tell the io thread if the current event needs a ack. */
+#define SEMI_SYNC_NEED_ACK 2
+
class MYSQL_QUERY_LOG: public MYSQL_LOG
{
public:
MYSQL_QUERY_LOG() : last_time(0) {}
void reopen_file();
- bool write(time_t event_time, const char *user_host,
- uint user_host_len, my_thread_id thread_id,
- const char *command_type, uint command_type_len,
- const char *sql_text, uint sql_text_len);
+ bool write(time_t event_time, const char *user_host, size_t user_host_len, my_thread_id thread_id,
+ const char *command_type, size_t command_type_len,
+ const char *sql_text, size_t sql_text_len);
bool write(THD *thd, time_t current_time,
- const char *user_host, uint user_host_len,
+ const char *user_host, size_t user_host_len,
ulonglong query_utime, ulonglong lock_utime, bool is_command,
- const char *sql_text, uint sql_text_len);
+ const char *sql_text, size_t sql_text_len);
bool open_slow_log(const char *log_name)
{
char buf[FN_REFLEN];
@@ -415,22 +420,28 @@ private:
( ((ulong)(c)>>1) == BINLOG_COOKIE_DUMMY_ID )
class binlog_cache_mngr;
+class binlog_cache_data;
struct rpl_gtid;
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 @ update_cond. */
- PSI_cond_key m_key_update_cond;
+ /** The instrumentation key to use for @ COND_relay_log_updated */
+ PSI_cond_key m_key_relay_log_update;
+ /** 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;
/** The instrumentation key to use for opening the log index file. */
PSI_file_key m_key_file_log_index;
PSI_file_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
@@ -486,7 +497,7 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
mysql_mutex_t LOCK_binlog_end_pos;
mysql_mutex_t LOCK_xid_list;
mysql_cond_t COND_xid_list;
- mysql_cond_t update_cond;
+ mysql_cond_t COND_relay_log_updated, COND_bin_log_updated;
ulonglong bytes_written;
IO_CACHE index_file;
char index_file_name[FN_REFLEN];
@@ -614,7 +625,7 @@ public:
/* This is relay log */
bool is_relay_log;
- ulong signal_cnt; // update of the counter is checked by heartbeat
+ ulong relay_signal_cnt; // update of the counter is checked by heartbeat
enum enum_binlog_checksum_alg checksum_alg_reset; // to contain a new value when binlog is rotated
/*
Holds the last seen in Relay-Log FD's checksum alg value.
@@ -677,16 +688,20 @@ public:
#ifdef HAVE_PSI_INTERFACE
void set_psi_keys(PSI_mutex_key key_LOCK_index,
- PSI_cond_key key_update_cond,
+ 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_index,
- PSI_file_key key_COND_queue_busy)
+ PSI_file_key key_COND_queue_busy,
+ PSI_mutex_key key_LOCK_binlog_end_pos)
{
m_key_LOCK_index= key_LOCK_index;
- m_key_update_cond= key_update_cond;
+ 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_index= key_file_log_index;
m_key_COND_queue_busy= key_COND_queue_busy;
+ m_key_LOCK_binlog_end_pos= key_LOCK_binlog_end_pos;
}
#endif
@@ -723,11 +738,56 @@ public:
DBUG_VOID_RETURN;
}
void set_max_size(ulong max_size_arg);
- void signal_update();
+
+ /* Handle signaling that relay has been updated */
+ void signal_relay_log_update()
+ {
+ mysql_mutex_assert_owner(&LOCK_log);
+ DBUG_ASSERT(is_relay_log);
+ DBUG_ENTER("MYSQL_BIN_LOG::signal_relay_log_update");
+ relay_signal_cnt++;
+ mysql_cond_broadcast(&COND_relay_log_updated);
+ DBUG_VOID_RETURN;
+ }
+ void signal_bin_log_update()
+ {
+ mysql_mutex_assert_owner(&LOCK_binlog_end_pos);
+ DBUG_ASSERT(!is_relay_log);
+ DBUG_ENTER("MYSQL_BIN_LOG::signal_bin_log_update");
+ mysql_cond_broadcast(&COND_bin_log_updated);
+ DBUG_VOID_RETURN;
+ }
+ void update_binlog_end_pos()
+ {
+ if (is_relay_log)
+ signal_relay_log_update();
+ else
+ {
+ lock_binlog_end_pos();
+ binlog_end_pos= my_b_safe_tell(&log_file);
+ signal_bin_log_update();
+ unlock_binlog_end_pos();
+ }
+ }
+ void update_binlog_end_pos(my_off_t pos)
+ {
+ mysql_mutex_assert_owner(&LOCK_log);
+ mysql_mutex_assert_not_owner(&LOCK_binlog_end_pos);
+ lock_binlog_end_pos();
+ /*
+ Note: it would make more sense to assert(pos > binlog_end_pos)
+ but there are two places triggered by mtr that has pos == binlog_end_pos
+ i didn't investigate but accepted as it should do no harm
+ */
+ DBUG_ASSERT(pos >= binlog_end_pos);
+ binlog_end_pos= pos;
+ signal_bin_log_update();
+ unlock_binlog_end_pos();
+ }
+
void wait_for_sufficient_commits();
void binlog_trigger_immediate_group_commit();
void wait_for_update_relay_log(THD* thd);
- int wait_for_update_bin_log(THD* thd, const struct timespec * timeout);
void init(ulong max_size);
void init_pthread_objects();
void cleanup();
@@ -761,8 +821,8 @@ public:
void stop_union_events(THD *thd);
bool is_query_in_union(THD *thd, query_id_t query_id_param);
- bool write_event(Log_event *ev, IO_CACHE *file);
- bool write_event(Log_event *ev) { return write_event(ev, &log_file); }
+ bool write_event(Log_event *ev, binlog_cache_data *data, IO_CACHE *file);
+ bool write_event(Log_event *ev) { return write_event(ev, 0, &log_file); }
bool write_event_buffer(uchar* buf,uint len);
bool append(Log_event* ev);
@@ -824,7 +884,7 @@ public:
inline char* get_log_fname() { return log_file_name; }
inline char* get_name() { return name; }
inline mysql_mutex_t* get_log_lock() { return &LOCK_log; }
- inline mysql_cond_t* get_log_cond() { return &update_cond; }
+ inline mysql_cond_t* get_bin_log_cond() { return &COND_bin_log_updated; }
inline IO_CACHE* get_log_file() { return &log_file; }
inline void lock_index() { mysql_mutex_lock(&LOCK_index);}
@@ -848,23 +908,6 @@ public:
bool check_strict_gtid_sequence(uint32 domain_id, uint32 server_id,
uint64 seq_no);
-
- void update_binlog_end_pos(my_off_t pos)
- {
- mysql_mutex_assert_owner(&LOCK_log);
- mysql_mutex_assert_not_owner(&LOCK_binlog_end_pos);
- lock_binlog_end_pos();
- /**
- * note: it would make more sense to assert(pos > binlog_end_pos)
- * but there are two places triggered by mtr that has pos == binlog_end_pos
- * i didn't investigate but accepted as it should do no harm
- */
- DBUG_ASSERT(pos >= binlog_end_pos);
- binlog_end_pos= pos;
- signal_update();
- unlock_binlog_end_pos();
- }
-
/**
* used when opening new file, and binlog_end_pos moves backwards
*/
@@ -875,7 +918,7 @@ public:
lock_binlog_end_pos();
binlog_end_pos= pos;
strcpy(binlog_end_pos_file, file_name);
- signal_update();
+ signal_bin_log_update();
unlock_binlog_end_pos();
}
@@ -918,16 +961,14 @@ public:
virtual void cleanup()= 0;
virtual bool log_slow(THD *thd, my_hrtime_t current_time,
- const char *user_host,
- uint user_host_len, ulonglong query_utime,
+ const char *user_host, size_t user_host_len, ulonglong query_utime,
ulonglong lock_utime, bool is_command,
- const char *sql_text, uint sql_text_len)= 0;
+ const char *sql_text, size_t sql_text_len)= 0;
virtual bool log_error(enum loglevel level, const char *format,
va_list args)= 0;
- virtual bool log_general(THD *thd, my_hrtime_t event_time, const char *user_host,
- uint user_host_len, my_thread_id thread_id,
- const char *command_type, uint command_type_len,
- const char *sql_text, uint sql_text_len,
+ virtual bool log_general(THD *thd, my_hrtime_t event_time, const char *user_host, size_t user_host_len, my_thread_id thread_id,
+ const char *command_type, size_t command_type_len,
+ const char *sql_text, size_t sql_text_len,
CHARSET_INFO *client_cs)= 0;
virtual ~Log_event_handler() {}
};
@@ -947,16 +988,14 @@ public:
virtual void cleanup();
virtual bool log_slow(THD *thd, my_hrtime_t current_time,
- const char *user_host,
- uint user_host_len, ulonglong query_utime,
+ const char *user_host, size_t user_host_len, ulonglong query_utime,
ulonglong lock_utime, bool is_command,
- const char *sql_text, uint sql_text_len);
+ const char *sql_text, size_t sql_text_len);
virtual bool log_error(enum loglevel level, const char *format,
va_list args);
- virtual bool log_general(THD *thd, my_hrtime_t event_time, const char *user_host,
- uint user_host_len, my_thread_id thread_id,
- const char *command_type, uint command_type_len,
- const char *sql_text, uint sql_text_len,
+ virtual bool log_general(THD *thd, my_hrtime_t event_time, const char *user_host, size_t user_host_len, my_thread_id thread_id,
+ const char *command_type, size_t command_type_len,
+ const char *sql_text, size_t sql_text_len,
CHARSET_INFO *client_cs);
int activate_log(THD *thd, uint log_type);
@@ -979,16 +1018,14 @@ public:
virtual void cleanup();
virtual bool log_slow(THD *thd, my_hrtime_t current_time,
- const char *user_host,
- uint user_host_len, ulonglong query_utime,
+ const char *user_host, size_t user_host_len, ulonglong query_utime,
ulonglong lock_utime, bool is_command,
- const char *sql_text, uint sql_text_len);
+ const char *sql_text, size_t sql_text_len);
virtual bool log_error(enum loglevel level, const char *format,
va_list args);
- virtual bool log_general(THD *thd, my_hrtime_t event_time, const char *user_host,
- uint user_host_len, my_thread_id thread_id,
- const char *command_type, uint command_type_len,
- const char *sql_text, uint sql_text_len,
+ virtual bool log_general(THD *thd, my_hrtime_t event_time, const char *user_host, size_t user_host_len, my_thread_id thread_id,
+ const char *command_type, size_t command_type_len,
+ const char *sql_text, size_t sql_text_len,
CHARSET_INFO *client_cs);
void flush();
void init_pthread_objects();
@@ -1042,12 +1079,12 @@ public:
void cleanup_end();
bool error_log_print(enum loglevel level, const char *format,
va_list args);
- bool slow_log_print(THD *thd, const char *query, uint query_length,
+ bool slow_log_print(THD *thd, const char *query, size_t query_length,
ulonglong current_utime);
bool general_log_print(THD *thd,enum enum_server_command command,
const char *format, va_list args);
bool general_log_write(THD *thd, enum enum_server_command command,
- const char *query, uint query_length);
+ const char *query, size_t query_length);
/* we use this function to setup all enabled log event handlers */
int set_handlers(ulonglong error_log_printer,
@@ -1100,7 +1137,7 @@ bool general_log_print(THD *thd, enum enum_server_command command,
const char *format,...);
bool general_log_write(THD *thd, enum enum_server_command command,
- const char *query, uint query_length);
+ const char *query, size_t query_length);
void binlog_report_wait_for(THD *thd, THD *other_thd);
void sql_perror(const char *message);
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 26c9cb6f9cb..4432a51b010 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -16,7 +16,7 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#ifndef MYSQL_CLIENT
@@ -44,6 +44,7 @@
#include <strfunc.h>
#include "compat56.h"
#include "wsrep_mysqld.h"
+#include "sql_insert.h"
#else
#include "mysqld_error.h"
#endif /* MYSQL_CLIENT */
@@ -53,6 +54,7 @@
#include "rpl_constants.h"
#include "sql_digest.h"
#include "zlib.h"
+#include "my_atomic.h"
#define my_b_write_string(A, B) my_b_write((A), (uchar*)(B), (uint) (sizeof(B) - 1))
@@ -216,12 +218,12 @@ is_parallel_retry_error(rpl_group_info *rgi, int err)
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, ulong pos)
+ 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);
- uint len;
+ size_t len;
Diagnostics_area::Sql_condition_iterator it=
thd->get_stmt_da()->sql_conditions();
Relay_log_info const *rli= rgi->rli;
@@ -250,14 +252,14 @@ static void inline slave_rows_error_report(enum loglevel level, int ha_error,
rli->report(level, errcode, rgi->gtid_info(),
"Could not execute %s event on table %s.%s;"
"%s handler error %s; "
- "the event's master log %s, end_log_pos %lu",
+ "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 %lu",
+ "%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);
}
@@ -268,7 +270,7 @@ 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_STRING new_db;
+ LEX_CSTRING new_db;
new_db.length= db_len;
if (lower_case_table_names == 1)
{
@@ -277,11 +279,11 @@ static void set_thd_db(THD *thd, Rpl_filter *rpl_filter,
new_db.str= lcase_db_buf;
}
else
- new_db.str= (char*) db;
+ 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= (char*) rpl_filter->get_rewrite_db(new_db.str, &new_db.length);
- thd->set_db(new_db.str, new_db.length);
+ new_db.str= rpl_filter->get_rewrite_db(new_db.str, &new_db.length);
+ thd->set_db(&new_db);
}
#endif
/*
@@ -311,10 +313,9 @@ public:
flags Flags for the cache
DESCRIPTION
-
- Class used to guarantee copy of cache to file before exiting the
- current block. On successful copy of the cache, the cache will
- be reinited as a WRITE_CACHE.
+ Cache common parameters and ensure common flush_data() code
+ on successful copy of the cache, the cache will be reinited as a
+ WRITE_CACHE.
Currently, a pointer to the cache is provided in the
constructor, but it would be possible to create a subclass
@@ -326,28 +327,36 @@ public:
reinit_io_cache(m_cache, WRITE_CACHE, 0L, FALSE, TRUE);
}
- ~Write_on_release_cache()
+ ~Write_on_release_cache() {}
+
+ bool flush_data()
{
#ifdef MYSQL_CLIENT
- if(m_ev == NULL)
+ if (m_ev == NULL)
{
- copy_event_cache_to_file_and_reinit(m_cache, m_file);
- if (m_flags & FLUSH_F)
- fflush(m_file);
+ if (copy_event_cache_to_file_and_reinit(m_cache, m_file))
+ return 1;
+ if ((m_flags & FLUSH_F) && fflush(m_file))
+ return 1;
}
else // if m_ev<>NULL, then storing the output in output_buf
{
LEX_STRING tmp_str;
+ bool res;
if (copy_event_cache_to_string_and_reinit(m_cache, &tmp_str))
- exit(1);
- m_ev->output_buf.append(tmp_str.str, tmp_str.length);
+ return 1;
+ /* use 2 argument append as tmp_str is not \0 terminated */
+ res= m_ev->output_buf.append(tmp_str.str, tmp_str.length);
my_free(tmp_str.str);
+ return res ? res : 0;
}
#else /* MySQL_SERVER */
- copy_event_cache_to_file_and_reinit(m_cache, m_file);
- if (m_flags & FLUSH_F)
- fflush(m_file);
+ if (copy_event_cache_to_file_and_reinit(m_cache, m_file))
+ return 1;
+ if ((m_flags & FLUSH_F) && fflush(m_file))
+ return 1;
#endif
+ return 0;
}
/*
@@ -385,27 +394,36 @@ private:
*/
#ifdef MYSQL_CLIENT
-static void pretty_print_str(IO_CACHE* cache, const char* str, int len)
+static bool pretty_print_str(IO_CACHE* cache, const char* str, int len)
{
const char* end = str + len;
- my_b_write_byte(cache, '\'');
+ if (my_b_write_byte(cache, '\''))
+ goto err;
+
while (str < end)
{
char c;
+ int error;
+
switch ((c=*str++)) {
- case '\n': my_b_write(cache, (uchar*)"\\n", 2); break;
- case '\r': my_b_write(cache, (uchar*)"\\r", 2); break;
- case '\\': my_b_write(cache, (uchar*)"\\\\", 2); break;
- case '\b': my_b_write(cache, (uchar*)"\\b", 2); break;
- case '\t': my_b_write(cache, (uchar*)"\\t", 2); break;
- case '\'': my_b_write(cache, (uchar*)"\\'", 2); break;
- case 0 : my_b_write(cache, (uchar*)"\\0", 2); break;
+ 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:
- my_b_write_byte(cache, c);
+ error= my_b_write_byte(cache, c);
break;
}
+ if (unlikely(error))
+ goto err;
}
- my_b_write_byte(cache, '\'');
+ return my_b_write_byte(cache, '\'');
+
+err:
+ return 1;
}
#endif /* MYSQL_CLIENT */
@@ -602,7 +620,7 @@ pretty_print_str(String *packet, const char *str, int len)
*/
static char *load_data_tmp_prefix(char *name,
- LEX_STRING *connection_name)
+ LEX_CSTRING *connection_name)
{
name= strmov(name, PREFIX_SQL_LOAD);
if (connection_name->length)
@@ -638,7 +656,7 @@ static char *load_data_tmp_prefix(char *name,
static char *slave_load_file_stem(char *buf, uint file_id,
int event_server_id, const char *ext,
- LEX_STRING *connection_name)
+ LEX_CSTRING *connection_name)
{
char *res;
res= buf+ unpack_dirname(buf, slave_load_tmpdir);
@@ -659,7 +677,7 @@ static char *slave_load_file_stem(char *buf, uint file_id,
Delete all temporary files used for SQL_LOAD.
*/
-static void cleanup_load_tmpdir(LEX_STRING *connection_name)
+static void cleanup_load_tmpdir(LEX_CSTRING *connection_name)
{
MY_DIR *dirp;
FILEINFO *file;
@@ -720,7 +738,7 @@ static inline int read_str(const char **buf, const char *buf_end,
Transforms a string into "" or its expression in X'HHHH' form.
*/
-char *str_to_hex(char *to, const char *from, uint len)
+char *str_to_hex(char *to, const char *from, size_t len)
{
if (len)
{
@@ -1170,19 +1188,25 @@ int append_query_string(CHARSET_INFO *csinfo, String *to,
#ifdef MYSQL_CLIENT
-static void print_set_option(IO_CACHE* file, uint32 bits_changed,
+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)
- my_b_write(file, (uchar*)", ", 2);
- my_b_printf(file, "%s=%d", name, MY_TEST(flags & option));
+ 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)
**************************************************************************/
@@ -1316,7 +1340,7 @@ Log_event::Log_event(const char* buf,
thd = 0;
#endif
when = uint4korr(buf);
- when_sec_part= 0;
+ when_sec_part= ~0UL;
server_id = uint4korr(buf + SERVER_ID_OFFSET);
data_written= uint4korr(buf + EVENT_LEN_OFFSET);
if (description_event->binlog_version==1)
@@ -1353,7 +1377,7 @@ Log_event::Log_event(const char* buf,
*/
log_pos+= data_written; /* purecov: inspected */
}
- DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos));
+ DBUG_PRINT("info", ("log_pos: %llu", log_pos));
flags= uint2korr(buf + FLAGS_OFFSET);
if (((uchar)buf[EVENT_TYPE_OFFSET] == FORMAT_DESCRIPTION_EVENT) ||
@@ -1482,7 +1506,7 @@ void Log_event::init_show_field_list(THD *thd, List<Item>* field_list)
mem_root);
field_list->push_back(new (mem_root)
Item_return_int(thd, "Pos",
- MY_INT32_NUM_DECIMAL_DIGITS,
+ MY_INT64_NUM_DECIMAL_DIGITS,
MYSQL_TYPE_LONGLONG),
mem_root);
field_list->push_back(new (mem_root)
@@ -1494,7 +1518,7 @@ void Log_event::init_show_field_list(THD *thd, List<Item>* field_list)
mem_root);
field_list->push_back(new (mem_root)
Item_return_int(thd, "End_log_pos",
- MY_INT32_NUM_DECIMAL_DIGITS,
+ MY_INT64_NUM_DECIMAL_DIGITS,
MYSQL_TYPE_LONGLONG),
mem_root);
field_list->push_back(new (mem_root) Item_empty_string(thd, "Info", 20),
@@ -1609,13 +1633,13 @@ int Log_event_writer::encrypt_and_write(const uchar *pos, size_t len)
if (ctx)
{
- dstsize= encryption_encrypted_length(len, ENCRYPTION_KEY_SYSTEM_DATA,
+ 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 (encryption_ctx_update(ctx, pos, len, dst, &dstlen))
+ if (encryption_ctx_update(ctx, pos, (uint)len, dst, &dstlen))
goto err;
if (maybe_write_event_len(dst, dstlen))
return 1;
@@ -1703,12 +1727,12 @@ int Log_event_writer::write_footer()
Log_event::write_header()
*/
-bool Log_event::write_header(ulong event_data_length)
+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: %lu type: %d",
+ DBUG_PRINT("enter", ("filepos: %lld length: %zu type: %d",
(longlong) writer->pos(), event_data_length,
(int) get_type_code()));
@@ -1882,7 +1906,7 @@ int Log_event::read_log_event(IO_CACHE* file, String* packet,
DBUG_RETURN(0);
}
-Log_event* Log_event::read_log_event(IO_CACHE* file, mysql_mutex_t* log_lock,
+Log_event* Log_event::read_log_event(IO_CACHE* file,
const Format_description_log_event *fdle,
my_bool crc_check)
{
@@ -1892,9 +1916,6 @@ Log_event* Log_event::read_log_event(IO_CACHE* file, mysql_mutex_t* log_lock,
const char *error= 0;
Log_event *res= 0;
- if (log_lock)
- mysql_mutex_lock(log_lock);
-
switch (read_log_event(file, &event, fdle, BINLOG_CHECKSUM_ALG_OFF))
{
case 0:
@@ -1931,9 +1952,7 @@ Log_event* Log_event::read_log_event(IO_CACHE* file, mysql_mutex_t* log_lock,
res->register_temp_buf(event.release(), true);
err:
- if (log_lock)
- mysql_mutex_unlock(log_lock);
- if (error)
+ if (unlikely(error))
{
DBUG_ASSERT(!res);
#ifdef MYSQL_CLIENT
@@ -2276,7 +2295,7 @@ exit:
#ifdef MYSQL_CLIENT
-static void hexdump_minimal_header_to_io_cache(IO_CACHE *file,
+static bool hexdump_minimal_header_to_io_cache(IO_CACHE *file,
my_off_t offset,
uchar *ptr)
{
@@ -2289,15 +2308,18 @@ static void hexdump_minimal_header_to_io_cache(IO_CACHE *file,
more headers (which must be printed by other methods, if desired).
*/
char emit_buf[120]; // Enough for storing one line
- my_b_printf(file,
- "# "
- "|Timestamp "
- "|Type "
- "|Master ID "
- "|Size "
- "|Master Pos "
- "|Flags\n");
- size_t const emit_buf_written=
+ 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 */
@@ -2315,8 +2337,13 @@ static void hexdump_minimal_header_to_io_cache(IO_CACHE *file,
ptr[17], ptr[18]); /* Flags */
DBUG_ASSERT(static_cast<size_t>(emit_buf_written) < sizeof(emit_buf));
- my_b_write(file, reinterpret_cast<uchar*>(emit_buf), emit_buf_written);
- my_b_write(file, (uchar*)"#\n", 2);
+ 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;
}
@@ -2341,7 +2368,7 @@ static void format_hex_line(char *emit_buff)
HEXDUMP_BYTES_PER_LINE + 2]= '\0';
}
-static void hexdump_data_to_io_cache(IO_CACHE *file,
+static bool hexdump_data_to_io_cache(IO_CACHE *file,
my_off_t offset,
uchar *ptr,
my_off_t size)
@@ -2363,7 +2390,7 @@ static void hexdump_data_to_io_cache(IO_CACHE *file,
my_off_t i;
if (size == 0)
- return;
+ return 0; // ok, nothing to do
format_hex_line(emit_buffer);
/*
@@ -2393,8 +2420,9 @@ static void hexdump_data_to_io_cache(IO_CACHE *file,
(ulonglong) starting_offset);
/* remove \0 left after printing address */
emit_buffer[2 + emit_buf_written]= ' ';
- my_b_write(file, reinterpret_cast<uchar*>(emit_buffer),
- sizeof(emit_buffer) - 1);
+ 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);
@@ -2429,17 +2457,23 @@ static void hexdump_data_to_io_cache(IO_CACHE *file,
/* pad unprinted area */
memset(h, ' ',
(HEXDUMP_BYTES_PER_LINE * 3 + 1) - (h - (emit_buffer + 2 + 8 + 2)));
- my_b_write(file, reinterpret_cast<uchar*>(emit_buffer),
- c - emit_buffer);
+ if (my_b_write(file, reinterpret_cast<uchar*>(emit_buffer),
+ c - emit_buffer))
+ goto err;
}
- my_b_write(file, (uchar*)"#\n", 2);
+ if (my_b_write(file, (uchar*)"#\n", 2))
+ goto err;
+
+ return 0;
+err:
+ return 1;
}
/*
Log_event::print_header()
*/
-void Log_event::print_header(IO_CACHE* file,
+bool Log_event::print_header(IO_CACHE* file,
PRINT_EVENT_INFO* print_event_info,
bool is_more __attribute__((unused)))
{
@@ -2447,10 +2481,11 @@ void Log_event::print_header(IO_CACHE* file,
my_off_t hexdump_from= print_event_info->hexdump_from;
DBUG_ENTER("Log_event::print_header");
- my_b_write_byte(file, '#');
- print_timestamp(file);
- my_b_printf(file, " server id %lu end_log_pos %s ", (ulong) server_id,
- llstr(log_pos,llbuff));
+ 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 */
@@ -2460,8 +2495,10 @@ void Log_event::print_header(IO_CACHE* file,
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);
- my_b_printf(file, "%s ", get_type(&binlog_checksum_typelib, checksum_alg));
- my_b_printf(file, checksum_buf, bytes_written);
+ 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 */
@@ -2474,24 +2511,32 @@ void Log_event::print_header(IO_CACHE* file,
size-= hdr_len;
- my_b_printf(file, "# Position\n");
+ if (my_b_printf(file, "# Position\n"))
+ goto err;
/* Write the header, nicely formatted by field. */
- hexdump_minimal_header_to_io_cache(file, hexdump_from, ptr);
+ 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. */
- hexdump_data_to_io_cache(file, hexdump_from, ptr, size);
+ 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.
*/
- my_b_write(file, (uchar*)"# Event: ", 9);
+ if (my_b_write(file, (uchar*)"# Event: ", 9))
+ goto err;
}
- DBUG_VOID_RETURN;
+
+ DBUG_RETURN(0);
+
+err:
+ DBUG_RETURN(1);
}
@@ -2499,7 +2544,7 @@ void Log_event::print_header(IO_CACHE* file,
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
@@ -2586,12 +2631,14 @@ my_b_write_quoted_with_length(IO_CACHE *file, const uchar *ptr, uint length)
@param[in] sl Signed number
@param[in] ul Unsigned number
*/
-static void
+static bool
my_b_write_sint32_and_uint32(IO_CACHE *file, int32 si, uint32 ui)
{
- my_b_printf(file, "%d", si);
+ bool res= my_b_printf(file, "%d", si);
if (si < 0)
- my_b_printf(file, " (%u)", ui);
+ if (my_b_printf(file, " (%u)", ui))
+ res= 1;
+ return res;
}
@@ -2610,8 +2657,8 @@ my_b_write_sint32_and_uint32(IO_CACHE *file, int32 si, uint32 ui)
*/
static size_t
-log_event_print_value(IO_CACHE *file, const uchar *ptr,
- uint type, uint meta,
+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;
@@ -2994,19 +3041,15 @@ log_event_print_value(IO_CACHE *file, const uchar *ptr,
return my_b_write_quoted_with_length(file, ptr, length);
case MYSQL_TYPE_DECIMAL:
- my_b_printf(file,
- "!! Old DECIMAL (mysql-4.1 or earlier). "
- "Not enough metadata to display the value. ");
+ 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;
-
default:
- {
- char tmp[5];
- my_snprintf(tmp, sizeof(tmp), "%04x", meta);
- my_b_printf(file,
- "!! Don't know how to handle column type=%d meta=%d (%s)",
- type, meta, tmp);
- }
+ 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;
@@ -3027,7 +3070,8 @@ return_null:
@param[in] value Pointer to packed row
@param[in] prefix Row's SQL clause ("SET", "WHERE", etc)
- @retval - number of bytes scanned.
+ @retval 0 error
+ # number of bytes scanned.
*/
@@ -3057,9 +3101,10 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td,
value+= (bitmap_bits_set(cols_bitmap) + 7) / 8;
if (!no_fill_output)
- my_b_printf(file, "%s", prefix);
+ if (my_b_printf(file, "%s", prefix))
+ goto err;
- for (size_t i= 0; i < td->size(); i ++)
+ for (uint i= 0; i < (uint)td->size(); i ++)
{
size_t size;
int is_null= (null_bits[null_bit_index / 8]
@@ -3069,7 +3114,8 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td,
continue;
if (!no_fill_output)
- my_b_printf(file, "### @%d=", static_cast<int>(i + 1));
+ if (my_b_printf(file, "### @%d=", static_cast<int>(i + 1)))
+ goto err;
if (!is_null)
{
@@ -3077,8 +3123,9 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td,
if (value + fsize > m_rows_end)
{
if (!no_fill_output)
- my_b_printf(file, "***Corrupted replication event was detected."
- " Not printing the value***\n");
+ if (my_b_printf(file, "***Corrupted replication event was detected."
+ " Not printing the value***\n"))
+ goto err;
value+= fsize;
return 0;
}
@@ -3086,7 +3133,7 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td,
if (!no_fill_output)
{
- size= log_event_print_value(file,is_null? NULL: value,
+ 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
@@ -3097,12 +3144,14 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td,
// 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, is_null? NULL: value,
+ size= log_event_print_value(&tmp_cache, print_event_info,
+ is_null ? NULL: value,
td->type(i), td->field_metadata(i),
typestr, sizeof(typestr));
- if (copy_event_cache_to_string_and_reinit(&tmp_cache, &review_str))
- exit(1);
+ 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
{
@@ -3120,12 +3169,14 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td,
exit(1);
}
octet2hex((char*) hex_str.ptr(), tmp_str.ptr(), tmp_str.length());
- my_b_printf(review_sql, ", UNHEX('%s')", hex_str.ptr());
+ if (my_b_printf(review_sql, ", UNHEX('%s')", hex_str.ptr()))
+ goto err;
break;
default:
tmp_str.free();
- tmp_str.append(review_str.str, review_str.length);
- my_b_printf(review_sql, ", %s", tmp_str.ptr());
+ 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);
@@ -3136,36 +3187,40 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td,
{
IO_CACHE tmp_cache;
open_cached_file(&tmp_cache, NULL, NULL, 0, MYF(MY_WME | MY_NABP));
- size= log_event_print_value(&tmp_cache,is_null? NULL: value,
+ 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)
- return 0;
+ goto err;
if (!is_null)
value+= size;
if (print_event_info->verbose > 1 && !no_fill_output)
{
- 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);
+ 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)
- my_b_write_byte(file, '\n');
+ if (my_b_write_byte(file, '\n'))
+ goto err;
null_bit_index++;
}
return value - value0;
+
+err:
+ return 0;
}
@@ -3204,10 +3259,10 @@ void Rows_log_event::change_to_flashback_event(PRINT_EVENT_INFO *print_event_inf
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);
- }
+ {
+ fprintf(stderr, "\nError row length: %zu\n", length1);
+ exit(1);
+ }
value+= length1;
swap_buff1= (uchar *) my_malloc(length1, MYF(0));
@@ -3279,18 +3334,241 @@ 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
*/
-void Rows_log_event::print_verbose(IO_CACHE *file,
+
+bool Rows_log_event::print_verbose(IO_CACHE *file,
PRINT_EVENT_INFO *print_event_info)
{
Table_map_log_event *map;
- table_def *td;
+ 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();
@@ -3304,9 +3582,10 @@ void Rows_log_event::print_verbose(IO_CACHE *file,
uint8 extra_payload_len= extra_data_len - EXTRA_ROW_INFO_HDR_BYTES;
assert(extra_data_len >= EXTRA_ROW_INFO_HDR_BYTES);
- my_b_printf(file, "### Extra row data format: %u, len: %u :",
- m_extra_row_data[EXTRA_ROW_INFO_FORMAT_OFFSET],
- extra_payload_len);
+ if (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)
{
/*
@@ -3317,9 +3596,11 @@ void Rows_log_event::print_verbose(IO_CACHE *file,
char buff[buff_len];
str_to_hex(buff, (const char*) &m_extra_row_data[EXTRA_ROW_INFO_HDR_BYTES],
extra_payload_len);
- my_b_printf(file, "%s", buff);
+ if (my_b_printf(file, "%s", buff))
+ goto err;
}
- my_b_printf(file, "\n");
+ if (my_b_printf(file, "\n"))
+ goto err;
}
switch (general_type_code) {
@@ -3346,41 +3627,45 @@ void Rows_log_event::print_verbose(IO_CACHE *file,
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()))
{
- my_b_printf(file, "### Row event for unknown table #%lu",
- (ulong) m_table_id);
- return;
+ 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)))
{
- my_b_printf(file, "### INSERT INTO %`s.%`s VALUES ()\n",
- map->get_db_name(), map->get_table_name());
+ 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;
- my_b_printf(file, "### %s %`s.%`s\n",
- sql_command,
- map->get_db_name(), map->get_table_name());
+ 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)
- my_b_printf(review_sql, "\nINSERT INTO `%s`.`%s` VALUES ('%s'",
- map->get_review_dbname(), map->get_review_tablename(), sql_command_short);
+ 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 end;
+ goto err;
value+= length;
/* Print the second image (for UPDATE only) */
@@ -3389,7 +3674,7 @@ void Rows_log_event::print_verbose(IO_CACHE *file,
if (!(length= print_verbose_one_row(file, td, print_event_info,
&m_cols_ai, value,
(const uchar*) sql_clause2)))
- goto end;
+ goto err;
value+= length;
}
#ifdef WHEN_FLASHBACK_REVIEW_READY
@@ -3397,16 +3682,22 @@ void Rows_log_event::print_verbose(IO_CACHE *file,
{
if (need_flashback_review)
for (size_t i= 0; i < td->size(); i ++)
- my_b_printf(review_sql, ", NULL");
+ if (my_b_printf(review_sql, ", NULL"))
+ goto err;
}
if (need_flashback_review)
- my_b_printf(review_sql, ")%s\n", print_event_info->delimiter);
+ 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)
@@ -3428,7 +3719,7 @@ void free_table_map_log_event(Table_map_log_event *event)
@param do_print_encoded whether to store base64-encoded event
into @file.
*/
-void Log_event::print_base64(IO_CACHE* file,
+bool Log_event::print_base64(IO_CACHE* file,
PRINT_EVENT_INFO* print_event_info,
bool do_print_encoded)
{
@@ -3436,14 +3727,6 @@ void Log_event::print_base64(IO_CACHE* file,
uint32 size= uint4korr(ptr + EVENT_LEN_OFFSET);
DBUG_ENTER("Log_event::print_base64");
- size_t const tmp_str_sz= my_base64_needed_encoded_length((int) size);
- char *const tmp_str= (char *) my_malloc(tmp_str_sz, MYF(MY_WME));
- if (!tmp_str) {
- fprintf(stderr, "\nError: Out of memory. "
- "Could not print correct binlog event.\n");
- DBUG_VOID_RETURN;
- }
-
if (is_flashback)
{
uint tmp_size= size;
@@ -3489,19 +3772,29 @@ void Log_event::print_base64(IO_CACHE* file,
delete ev;
}
- if (my_base64_encode(ptr, (size_t) size, tmp_str))
+ if (do_print_encoded)
{
- DBUG_ASSERT(0);
- }
+ 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);
+ }
- if (do_print_encoded)
my_b_printf(file, "%s\n", tmp_str);
+ my_free(tmp_str);
+ }
#ifdef WHEN_FLASHBACK_REVIEW_READY
- if (print_event_info->verbose || need_flashback_review)
+ 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 || is_flashback)
+ if (print_event_info->verbose || print_event_info->print_row_count ||
+ is_flashback)
#endif
{
Rows_log_event *ev= NULL;
@@ -3576,16 +3869,33 @@ void Log_event::print_base64(IO_CACHE* file,
if (ev)
{
+ bool error= 0;
+
#ifdef WHEN_FLASHBACK_REVIEW_READY
ev->need_flashback_review= need_flashback_review;
if (print_event_info->verbose)
- ev->print_verbose(file, print_event_info);
+ {
+ if (ev->print_verbose(file, print_event_info))
+ goto err;
+ }
else
{
IO_CACHE tmp_cache;
- open_cached_file(&tmp_cache, NULL, NULL, 0, MYF(MY_WME | MY_NABP));
- ev->print_verbose(&tmp_cache, print_event_info);
+
+ 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)
@@ -3599,15 +3909,22 @@ void Log_event::print_base64(IO_CACHE* file,
if (print_event_info->base64_output_mode !=
BASE64_OUTPUT_DECODE_ROWS)
my_b_printf(file, "'%s\n", print_event_info->delimiter);
- ev->print_verbose(file, print_event_info);
+ error= ev->print_verbose(file, print_event_info);
+ }
+ else
+ {
+ ev->count_row_events(print_event_info);
}
#endif
delete ev;
+ if (unlikely(error))
+ goto err;
}
}
+ DBUG_RETURN(0);
- my_free(tmp_str);
- DBUG_VOID_RETURN;
+err:
+ DBUG_RETURN(1);
}
@@ -3615,7 +3932,7 @@ void Log_event::print_base64(IO_CACHE* file,
Log_event::print_timestamp()
*/
-void Log_event::print_timestamp(IO_CACHE* file, time_t* ts)
+bool Log_event::print_timestamp(IO_CACHE* file, time_t* ts)
{
struct tm *res;
time_t my_when= when;
@@ -3624,14 +3941,13 @@ void Log_event::print_timestamp(IO_CACHE* file, time_t* ts)
ts = &my_when;
res=localtime(ts);
- 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);
- DBUG_VOID_RETURN;
+ 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 */
@@ -3857,8 +4173,8 @@ bool Query_log_event::write()
if (thd && thd->need_binlog_invoker())
{
- LEX_STRING user;
- LEX_STRING host;
+ LEX_CSTRING user;
+ LEX_CSTRING host;
memset(&user, 0, sizeof(user));
memset(&host, 0, sizeof(host));
@@ -3881,7 +4197,7 @@ bool Query_log_event::write()
else
{
user.str= ctx->priv_role;
- host= empty_lex_str;
+ host= empty_clex_str;
}
user.length= strlen(user.str);
}
@@ -3995,8 +4311,7 @@ Query_log_event::Query_log_event()
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,
- ulong query_length, bool using_trans,
+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,
@@ -4005,7 +4320,7 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg,
(suppress_use ? LOG_EVENT_SUPPRESS_USE_F : 0),
using_trans),
data_buf(0), query(query_arg), catalog(thd_arg->catalog),
- db(thd_arg->db), q_len((uint32) query_length),
+ 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),
@@ -4111,10 +4426,12 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg,
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
@@ -4220,7 +4537,7 @@ get_str_len_and_pointer(const Log_event::Byte **src,
static void copy_str_and_move(const char **src,
Log_event::Byte **dst,
- uint len)
+ size_t len)
{
memcpy(*dst, *src, len);
*src= (const char *)*dst;
@@ -4364,7 +4681,7 @@ Query_log_event::Query_log_event(const char* buf, uint event_len,
event from the relay log.
*/
DBUG_ASSERT(description_event->binlog_version < 4);
- master_data_written= data_written;
+ master_data_written= (uint32)data_written;
}
/*
We have parsed everything we know in the post header for QUERY_EVENT,
@@ -4544,22 +4861,20 @@ Query_log_event::Query_log_event(const char* buf, uint event_len,
if (user.length)
{
- copy_str_and_move((const char **)&(user.str), &start, user.length);
+ copy_str_and_move(&user.str, &start, user.length);
}
else
{
- user.str= (char *) start++;
- user.str[0]= '\0';
+ user.str= (char*) start;
+ *(start++)= 0;
}
if (host.length)
- {
- copy_str_and_move((const char **)&(host.str), &start, host.length);
- }
+ copy_str_and_move(&host.str, &start, host.length);
else
{
- host.str= (char *) start++;
- host.str[0]= '\0';
+ host.str= (char*) start;
+ *(start++)= 0;
}
/**
@@ -4702,7 +5017,7 @@ Query_log_event::dummy_event(String *packet, ulong ev_offset,
possibly just @`!`).
*/
static const char var_name[]= "!dummyvar";
- uint name_len= data_len - (min_user_var_event_len - 1);
+ size_t name_len= data_len - (min_user_var_event_len - 1);
p[EVENT_TYPE_OFFSET]= USER_VAR_EVENT;
int4store(p + LOG_EVENT_HEADER_LEN, name_len);
@@ -4821,7 +5136,7 @@ Query_log_event::begin_event(String *packet, ulong ev_offset,
@todo
print the catalog ??
*/
-void Query_log_event::print_query_header(IO_CACHE* file,
+bool Query_log_event::print_query_header(IO_CACHE* file,
PRINT_EVENT_INFO* print_event_info)
{
// TODO: print the catalog ??
@@ -4831,10 +5146,12 @@ void Query_log_event::print_query_header(IO_CACHE* file,
if (!print_event_info->short_form)
{
- 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);
+ 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))
@@ -4848,26 +5165,29 @@ void Query_log_event::print_query_header(IO_CACHE* file,
if (different_db)
memcpy(print_event_info->db, db, db_len + 1);
if (db[0] && different_db)
- my_b_printf(file, "use %`s%s\n", db, print_event_info->delimiter);
+ 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)
+ 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';
- my_b_write(file, (uchar*) buff, (uint) (end-buff));
+ 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.
- my_b_printf(file,"SET @@session.pseudo_thread_id=%lu%s\n",
- short_form ? 999999999 : (ulong)thread_id,
- print_event_info->delimiter);
+ 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;
}
@@ -4892,18 +5212,20 @@ void Query_log_event::print_query_header(IO_CACHE* file,
if (unlikely(tmp)) /* some bits have changed */
{
bool need_comma= 0;
- 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);
+ 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;
}
}
@@ -4926,17 +5248,19 @@ void Query_log_event::print_query_header(IO_CACHE* file,
!print_event_info->sql_mode_inited)))
{
char llbuff[22];
- my_b_printf(file,"SET @@session.sql_mode=%s%s\n",
- ullstr(sql_mode, llbuff), print_event_info->delimiter);
+ 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)
{
- 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);
+ 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;
}
@@ -4951,18 +5275,20 @@ void Query_log_event::print_query_header(IO_CACHE* file,
if (cs_info)
{
/* for mysql client */
- my_b_printf(file, "/*!\\C %s */%s\n",
- cs_info->csname, print_event_info->delimiter);
- }
- my_b_printf(file,"SET "
- "@@session.character_set_client=%d,"
- "@@session.collation_connection=%d,"
- "@@session.collation_server=%d"
- "%s\n",
- uint2korr(charset),
- uint2korr(charset+2),
- uint2korr(charset+4),
- print_event_info->delimiter);
+ 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;
}
@@ -4971,31 +5297,40 @@ void Query_log_event::print_query_header(IO_CACHE* file,
if (memcmp(print_event_info->time_zone_str,
time_zone_str, time_zone_len+1))
{
- my_b_printf(file,"SET @@session.time_zone='%s'%s\n",
- time_zone_str, print_event_info->delimiter);
+ 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)
{
- my_b_printf(file, "SET @@session.lc_time_names=%d%s\n",
- lc_time_names_number, print_event_info->delimiter);
+ 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)
- my_b_printf(file, "SET @@session.collation_database=%d%s\n",
- charset_database_number, print_event_info->delimiter);
- else
- my_b_printf(file, "SET @@session.collation_database=DEFAULT%s\n",
- print_event_info->delimiter);
+ {
+ 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;
}
-void Query_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
+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);
@@ -5005,25 +5340,31 @@ void Query_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
*/
DBUG_EXECUTE_IF ("simulate_file_write_error",
{(&cache)->write_pos= (&cache)->write_end- 500;});
- print_query_header(&cache, print_event_info);
+ if (print_query_header(&cache, print_event_info))
+ goto err;
if (!is_flashback)
{
- my_b_write(&cache, (uchar*) query, q_len);
- my_b_printf(&cache, "\n%s\n", print_event_info->delimiter);
+ 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)
{
- my_b_write(&cache, (uchar*) "COMMIT", 6);
- my_b_printf(&cache, "\n%s\n", print_event_info->delimiter);
+ 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)
{
- my_b_write(&cache, (uchar*) "BEGIN", 5);
- my_b_printf(&cache, "\n%s\n", print_event_info->delimiter);
+ 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 */
@@ -5098,6 +5439,7 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi,
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;
@@ -5130,7 +5472,7 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi,
/*
Setting the character set and collation of the current database thd->db.
*/
- load_db_opt_by_name(thd, thd->db, &db_options);
+ 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;
@@ -5154,7 +5496,7 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi,
::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))
+ 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,
@@ -5162,7 +5504,7 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi,
thd->variables.pseudo_thread_id= thread_id; // for temp tables
DBUG_PRINT("query",("%s", thd->query()));
- if (!(expected_error= error_code) ||
+ if (unlikely(!(expected_error= error_code)) ||
ignored_error_code(expected_error) ||
!unexpected_error_code(expected_error))
{
@@ -5189,7 +5531,7 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi,
if (sql_mode_inited)
thd->variables.sql_mode=
(sql_mode_t) ((thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE) |
- (sql_mode & ~(ulong) 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;
@@ -5294,8 +5636,10 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi,
rgi->gtid_pending= false;
gtid= rgi->current_gtid;
- if (rpl_global_gtid_slave_state->record_gtid(thd, &gtid, sub_id,
- rgi, false))
+ if (unlikely(rpl_global_gtid_slave_state->record_gtid(thd, &gtid,
+ sub_id,
+ rgi, false,
+ &hton)))
{
int errcode= thd->get_stmt_da()->sql_errno();
if (!is_parallel_retry_error(rgi, errcode))
@@ -5322,7 +5666,7 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi,
it is a concurrency issue or ignorable issue, effects
of the statement should be rolled back.
*/
- if (expected_error &&
+ if (unlikely(expected_error) &&
(ignored_error_code(expected_error) ||
concurrency_error_code(expected_error)))
{
@@ -5338,7 +5682,7 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi,
DBUG_ASSERT(thd->m_statement_psi == NULL);
thd->m_statement_psi= MYSQL_START_STATEMENT(&thd->m_statement_state,
stmt_info_rpl.m_key,
- thd->db, thd->db_length,
+ thd->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());
@@ -5348,13 +5692,13 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi,
if (thd->slave_thread)
{
/*
- The opt_log_slow_slave_statements variable can be changed
- dynamically, so we have to set the sql_log_slow respectively.
+ 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= opt_log_slow_slave_statements;
+ thd->variables.sql_log_slow= !MY_TEST(global_system_variables.log_slow_disabled_statements & LOG_SLOW_DISABLE_SLAVE);
}
- thd->enable_slow_log= true;
mysql_parse(thd, thd->query(), thd->query_length(), &parser_state,
FALSE, FALSE);
/* Finalize server status flags after executing a statement. */
@@ -5391,7 +5735,7 @@ START SLAVE; . Query: '%s'", expected_error, thd->query());
}
/* If the query was not ignored, it is printed to the general log */
- if (!thd->is_error() ||
+ 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
@@ -5416,7 +5760,8 @@ compare_errors:
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 &&
+ 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)
@@ -5429,7 +5774,7 @@ compare_errors:
DBUG_PRINT("info",("expected_error: %d sql_errno: %d",
expected_error, actual_error));
- if ((expected_error &&
+ if ((unlikely(expected_error) &&
!test_if_equal_repl_errors(expected_error, actual_error) &&
!concurrency_error_code(expected_error)) &&
!ignored_error_code(actual_error) &&
@@ -5464,14 +5809,14 @@ compare_errors:
/*
Other cases: mostly we expected no error and get one.
*/
- else if (thd->is_slave_error || thd->is_fatal_error)
+ 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"),
- print_slave_db_safe(thd->db), query_arg);
+ thd->get_db(), query_arg);
thd->is_slave_error= 1;
}
@@ -5514,8 +5859,8 @@ compare_errors:
}
end:
- if (sub_id && !thd->is_slave_error)
- rpl_global_gtid_slave_state->update_state_hash(sub_id, &gtid, rgi);
+ 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
@@ -5528,7 +5873,7 @@ end:
TABLE uses the db.table syntax.
*/
thd->catalog= 0;
- thd->set_db(NULL, 0); /* will free the current database */
+ thd->set_db(&null_clex_str); /* will free the current database */
thd->reset_query();
DBUG_PRINT("info", ("end: query= 0"));
@@ -5662,7 +6007,7 @@ void Start_log_event_v3::pack_info(Protocol *protocol)
*/
#ifdef MYSQL_CLIENT
-void Start_log_event_v3::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
+bool Start_log_event_v3::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
{
DBUG_ENTER("Start_log_event_v3::print");
@@ -5671,16 +6016,21 @@ void Start_log_event_v3::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
if (!print_event_info->short_form)
{
- print_header(&cache, print_event_info, FALSE);
- my_b_printf(&cache, "\tStart: binlog v %d, server v %s created ",
- binlog_version, server_version);
- print_timestamp(&cache);
+ 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)
- my_b_printf(&cache," at startup");
- my_b_printf(&cache, "\n");
+ 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)
- my_b_printf(&cache, "# Warning: this binlog is either in use or was not "
- "closed properly.\n");
+ 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)
{
@@ -5691,9 +6041,12 @@ void Start_log_event_v3::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
and rollback unfinished transaction.
Probably this can be done with RESET CONNECTION (syntax to be defined).
*/
- my_b_printf(&cache,"RESET CONNECTION%s\n", print_event_info->delimiter);
+ if (my_b_printf(&cache,"RESET CONNECTION%s\n",
+ print_event_info->delimiter))
+ goto err;
#else
- my_b_printf(&cache,"ROLLBACK%s\n", print_event_info->delimiter);
+ if (my_b_printf(&cache,"ROLLBACK%s\n", print_event_info->delimiter))
+ goto err;
#endif
}
if (temp_buf &&
@@ -5706,14 +6059,17 @@ void Start_log_event_v3::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
if (do_print_encoded)
my_b_printf(&cache, "BINLOG '\n");
- print_base64(&cache, print_event_info, do_print_encoded);
+ 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_VOID_RETURN;
+ DBUG_RETURN(cache.flush_data());
+err:
+ DBUG_RETURN(1);
}
#endif /* MYSQL_CLIENT */
@@ -6120,7 +6476,7 @@ bool Format_description_log_event::write()
FD_queue checksum_alg value.
*/
compile_time_assert(BINLOG_CHECKSUM_ALG_DESC_LEN == 1);
-#ifndef DBUG_OFF
+#ifdef DBUG_ASSERT_EXISTS
data_written= 0; // to prepare for need_checksum assert
#endif
uint8 checksum_byte= (uint8)
@@ -6402,7 +6758,7 @@ int Start_encryption_log_event::do_update_pos(rpl_group_info *rgi)
#endif
#ifndef MYSQL_SERVER
-void Start_encryption_log_event::print(FILE* file,
+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);
@@ -6414,7 +6770,9 @@ void Start_encryption_log_event::print(FILE* file,
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"));
- my_b_write(&cache, (uchar*)buf.ptr(), buf.length());
+ if (my_b_write(&cache, (uchar*)buf.ptr(), buf.length()))
+ return 1;
+ return (cache.flush_data());
}
#endif
/**************************************************************************
@@ -6439,7 +6797,7 @@ void Start_encryption_log_event::print(FILE* file,
*/
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-void Load_log_event::print_query(THD *thd, bool need_db, const char *cs,
+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)
{
@@ -6535,6 +6893,7 @@ void Load_log_event::print_query(THD *thd, bool need_db, const char *cs,
}
buf->append(STRING_WITH_LEN(")"));
}
+ return 0;
}
@@ -6593,7 +6952,7 @@ bool Load_log_event::write_data_body()
Load_log_event::Load_log_event()
*/
-Load_log_event::Load_log_event(THD *thd_arg, sql_exchange *ex,
+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,
@@ -6617,15 +6976,15 @@ Load_log_event::Load_log_event(THD *thd_arg, sql_exchange *ex,
db_len = (uint32) strlen(db);
table_name_len = (uint32) strlen(table_name);
fname_len = (fname) ? (uint) strlen(fname) : 0;
- sql_ex.field_term = (char*) ex->field_term->ptr();
+ sql_ex.field_term = ex->field_term->ptr();
sql_ex.field_term_len = (uint8) ex->field_term->length();
- sql_ex.enclosed = (char*) ex->enclosed->ptr();
+ sql_ex.enclosed = ex->enclosed->ptr();
sql_ex.enclosed_len = (uint8) ex->enclosed->length();
- sql_ex.line_term = (char*) ex->line_term->ptr();
+ sql_ex.line_term = ex->line_term->ptr();
sql_ex.line_term_len = (uint8) ex->line_term->length();
- sql_ex.line_start = (char*) ex->line_start->ptr();
+ sql_ex.line_start = ex->line_start->ptr();
sql_ex.line_start_len = (uint8) ex->line_start->length();
- sql_ex.escaped = (char*) ex->escaped->ptr();
+ 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;
@@ -6668,9 +7027,9 @@ Load_log_event::Load_log_event(THD *thd_arg, sql_exchange *ex,
while ((item = li++))
{
num_fields++;
- uchar len = (uchar) strlen(item->name);
+ uchar len= (uchar) item->name.length;
field_block_len += len + 1;
- fields_buf.append(item->name, len + 1);
+ fields_buf.append(item->name.str, len + 1);
field_lens_buf.append((char*)&len, 1);
}
@@ -6781,26 +7140,27 @@ err:
*/
#ifdef MYSQL_CLIENT
-void Load_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
+bool Load_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
{
- print(file, print_event_info, 0);
+ return print(file, print_event_info, 0);
}
-void Load_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info,
+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)
{
- print_header(&cache, print_event_info, FALSE);
- my_b_printf(&cache, "\tQuery\tthread_id=%ld\texec_time=%ld\n",
- thread_id, exec_time);
+ 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;
}
- bool different_db= 1;
if (db)
{
/*
@@ -6813,69 +7173,86 @@ void Load_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info,
!commented)
memcpy(print_event_info->db, db, db_len + 1);
}
-
+
if (db && db[0] && different_db)
- my_b_printf(&cache, "%suse %`s%s\n",
- commented ? "# " : "",
- db, print_event_info->delimiter);
+ if (my_b_printf(&cache, "%suse %`s%s\n",
+ commented ? "# " : "",
+ db, print_event_info->delimiter))
+ goto err;
if (flags & LOG_EVENT_THREAD_SPECIFIC_F)
- my_b_printf(&cache,"%sSET @@session.pseudo_thread_id=%lu%s\n",
- commented ? "# " : "", (ulong)thread_id,
- print_event_info->delimiter);
- my_b_printf(&cache, "%sLOAD DATA ",
- commented ? "# " : "");
+ 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())
- my_b_write_string(&cache, "LOCAL ");
- my_b_printf(&cache, "INFILE '%-*s' ", fname_len, fname);
+ 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)
- my_b_write_string(&cache, "REPLACE ");
+ {
+ if (my_b_write_string(&cache, "REPLACE "))
+ goto err;
+ }
else if (sql_ex.opt_flags & IGNORE_FLAG)
- my_b_write_string(&cache, "IGNORE ");
-
- 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);
+ if (my_b_write_string(&cache, "IGNORE "))
+ goto err;
- if (sql_ex.opt_flags & OPT_ENCLOSED_FLAG)
- my_b_write_string(&cache, " OPTIONALLY ");
- my_b_write_string(&cache, " ENCLOSED BY ");
- pretty_print_str(&cache, sql_ex.enclosed, sql_ex.enclosed_len);
-
- my_b_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);
+ 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)
{
- my_b_write_string(&cache," STARTING BY ");
- pretty_print_str(&cache, sql_ex.line_start, sql_ex.line_start_len);
+ 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)
- my_b_printf(&cache, " IGNORE %ld LINES", (long) skip_lines);
+ if (my_b_printf(&cache, " IGNORE %ld LINES", (long) skip_lines))
+ goto err;
if (num_fields)
{
uint i;
const char* field = fields;
- my_b_write_string(&cache, " (");
+ if (my_b_write_string(&cache, " ("))
+ goto err;
for (i = 0; i < num_fields; i++)
{
if (i)
- my_b_write_byte(&cache, ',');
- my_b_printf(&cache, "%`s", field);
-
+ if (my_b_write_byte(&cache, ','))
+ goto err;
+ if (my_b_printf(&cache, "%`s", field))
+ goto err;
field += field_lens[i] + 1;
}
- my_b_write_byte(&cache, ')');
+ if (my_b_write_byte(&cache, ')'))
+ goto err;
}
- my_b_printf(&cache, "%s\n", print_event_info->delimiter);
- DBUG_VOID_RETURN;
+ 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 */
@@ -6899,9 +7276,10 @@ void Load_log_event::set_fields(const char* affected_db,
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),
+ &field_name),
thd->mem_root);
field+= field_lens[i] + 1;
}
@@ -6983,23 +7361,22 @@ int Load_log_event::do_apply_event(NET* net, rpl_group_info *rgi,
::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))
+ 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);
- tables.init_one_table(thd->strmake(thd->db, thd->db_length),
- thd->db_length,
- table_name, strlen(table_name),
- table_name, TL_WRITE);
+ 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, &tables))
+ 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)
@@ -7085,7 +7462,7 @@ int Load_log_event::do_apply_event(NET* net, rpl_group_info *rgi,
ex.skip_lines = skip_lines;
List<Item> field_list;
thd->lex->select_lex.context.resolve_in_table_list_only(&tables);
- set_fields(tables.db, field_list, &thd->lex->select_lex.context);
+ set_fields(tables.db.str, field_list, &thd->lex->select_lex.context);
thd->variables.pseudo_thread_id= thread_id;
if (net)
{
@@ -7111,7 +7488,7 @@ int Load_log_event::do_apply_event(NET* net, rpl_group_info *rgi,
"warning(s). Default database: '%s'",
(char*) table_name, log_pos, RPL_LOG_NAME,
(ulong) thd->cuted_fields,
- print_slave_db_safe(thd->db));
+ thd->get_db());
}
if (net)
net->pkt_nr= thd->net.pkt_nr;
@@ -7130,9 +7507,9 @@ int Load_log_event::do_apply_event(NET* net, rpl_group_info *rgi,
error:
thd->net.vio = 0;
- const char *remember_db= thd->db;
+ const char *remember_db= thd->get_db();
thd->catalog= 0;
- thd->set_db(NULL, 0); /* will free the current database */
+ 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);
@@ -7164,7 +7541,7 @@ error:
DBUG_EXECUTE_IF("LOAD_DATA_INFILE_has_fatal_error",
thd->is_slave_error= 0; thd->is_fatal_error= 1;);
- if (thd->is_slave_error)
+ if (unlikely(thd->is_slave_error))
{
/* this err/sql_errno code is copy-paste from net_send_error() */
const char *err;
@@ -7181,20 +7558,20 @@ error:
}
rli->report(ERROR_LEVEL, sql_errno, rgi->gtid_info(), "\
Error '%s' running LOAD DATA INFILE on table '%s'. Default database: '%s'",
- err, (char*)table_name, print_slave_db_safe(remember_db));
+ 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 (thd->is_fatal_error)
+ 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,
- print_slave_db_safe(remember_db));
+ remember_db);
rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, rgi->gtid_info(),
ER_THD(thd, ER_SLAVE_FATAL_ERROR), buf);
@@ -7232,19 +7609,25 @@ void Rotate_log_event::pack_info(Protocol *protocol)
*/
#ifdef MYSQL_CLIENT
-void Rotate_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
+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_event_info->short_form)
- return;
- print_header(&cache, print_event_info, FALSE);
- my_b_write_string(&cache, "\tRotate to ");
+ if (print_header(&cache, print_event_info, FALSE) ||
+ my_b_write_string(&cache, "\tRotate to "))
+ goto err;
if (new_log_ident)
- my_b_write(&cache, (uchar*) new_log_ident, (uint)ident_len);
- my_b_printf(&cache, " pos: %s\n", llstr(pos, buf));
+ 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 */
@@ -7449,18 +7832,21 @@ Binlog_checkpoint_log_event::do_shall_skip(rpl_group_info *rgi)
#ifdef MYSQL_CLIENT
-void Binlog_checkpoint_log_event::print(FILE *file,
+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_event_info->short_form)
- return;
- print_header(&cache, print_event_info, FALSE);
- my_b_write_string(&cache, "\tBinlog checkpoint ");
- my_b_write(&cache, (uchar*)binlog_file_name, binlog_file_len);
- my_b_write_byte(&cache, '\n');
+ 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 */
@@ -7710,6 +8096,23 @@ Gtid_log_event::do_apply_event(rpl_group_info *rgi)
}
DBUG_ASSERT((bits & OPTION_GTID_BEGIN) == 0);
+
+ Master_info *mi=rgi->rli->mi;
+ switch (flags2 & (FL_DDL | FL_TRANSACTIONAL))
+ {
+ case FL_TRANSACTIONAL:
+ my_atomic_add64_explicit((volatile int64 *)&mi->total_trans_groups, 1,
+ MY_MEMORY_ORDER_RELAXED);
+ break;
+ case FL_DDL:
+ my_atomic_add64_explicit((volatile int64 *)&mi->total_ddl_groups, 1,
+ MY_MEMORY_ORDER_RELAXED);
+ break;
+ default:
+ my_atomic_add64_explicit((volatile int64 *)&mi->total_non_trans_groups, 1,
+ MY_MEMORY_ORDER_RELAXED);
+ }
+
if (flags2 & FL_STANDALONE)
return 0;
@@ -7779,7 +8182,7 @@ Gtid_log_event::do_shall_skip(rpl_group_info *rgi)
#else /* !MYSQL_SERVER */
-void
+bool
Gtid_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
{
Write_on_release_cache cache(&print_event_info->head_cache, file,
@@ -7791,26 +8194,34 @@ Gtid_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
{
print_header(&cache, print_event_info, FALSE);
longlong10_to_str(seq_no, buf, 10);
- my_b_printf(&cache, "\tGTID %u-%u-%s", domain_id, server_id, buf);
+ 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);
- my_b_printf(&cache, " cid=%s", buf2);
+ if (my_b_printf(&cache, " cid=%s", buf2))
+ goto err;
}
if (flags2 & FL_DDL)
- my_b_write_string(&cache, " ddl");
+ if (my_b_write_string(&cache, " ddl"))
+ goto err;
if (flags2 & FL_TRANSACTIONAL)
- my_b_write_string(&cache, " trans");
+ if (my_b_write_string(&cache, " trans"))
+ goto err;
if (flags2 & FL_WAITED)
- my_b_write_string(&cache, " waited");
- my_b_printf(&cache, "\n");
+ 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))
{
- my_b_printf(&cache,
+ if (my_b_printf(&cache,
"/*!100101 SET @@session.skip_parallel_replication=%u*/%s\n",
- !(flags2 & FL_ALLOW_PARALLEL), print_event_info->delimiter);
+ !(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;
}
@@ -7818,8 +8229,10 @@ Gtid_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
if (!print_event_info->domain_id_printed ||
print_event_info->domain_id != domain_id)
{
- my_b_printf(&cache, "/*!100001 SET @@session.gtid_domain_id=%u*/%s\n",
- domain_id, print_event_info->delimiter);
+ 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;
}
@@ -7827,18 +8240,26 @@ Gtid_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
if (!print_event_info->server_id_printed ||
print_event_info->server_id != server_id)
{
- my_b_printf(&cache, "/*!100001 SET @@session.server_id=%u*/%s\n",
- server_id, print_event_info->delimiter);
+ 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)
- my_b_printf(&cache, "/*!100001 SET @@session.gtid_seq_no=%s*/%s\n",
- buf, print_event_info->delimiter);
+ if (my_b_printf(&cache, "/*!100001 SET @@session.gtid_seq_no=%s*/%s\n",
+ buf, print_event_info->delimiter))
+ goto err;
}
if (!(flags2 & FL_STANDALONE))
- my_b_printf(&cache, is_flashback ? "COMMIT\n%s\n" : "BEGIN\n%s\n", print_event_info->delimiter);
+ 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 */
@@ -8015,15 +8436,17 @@ Gtid_list_log_event::do_apply_event(rpl_group_info *rgi)
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],
- NULL, false)))
+ sub_id_list[i],
+ NULL, false, &hton)))
return ret;
rpl_global_gtid_slave_state->update_state_hash(sub_id_list[i], &list[i],
- NULL);
+ hton, NULL);
}
}
ret= Log_event::do_apply_event(rgi);
@@ -8074,28 +8497,37 @@ Gtid_list_log_event::pack_info(Protocol *protocol)
#else /* !MYSQL_SERVER */
-void
+bool
Gtid_list_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
{
- if (!print_event_info->short_form)
- {
- Write_on_release_cache cache(&print_event_info->head_cache, file,
- Write_on_release_cache::FLUSH_F);
- char buf[21];
- uint32 i;
+ if (print_event_info->short_form)
+ return 0;
- print_header(&cache, print_event_info, FALSE);
- my_b_printf(&cache, "\tGtid list [");
- for (i= 0; i < count; ++i)
- {
- longlong10_to_str(list[i].seq_no, buf, 10);
- my_b_printf(&cache, "%u-%u-%s", list[i].domain_id,
- list[i].server_id, buf);
- if (i < count-1)
- my_b_printf(&cache, ",\n# ");
- }
- my_b_printf(&cache, "]\n");
+ 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 */
@@ -8106,7 +8538,7 @@ Gtid_list_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
fully construct the event object.
*/
bool
-Gtid_list_log_event::peek(const char *event_start, uint32 event_len,
+Gtid_list_log_event::peek(const char *event_start, size_t event_len,
enum enum_binlog_checksum_alg checksum_alg,
rpl_gtid **out_gtid_list, uint32 *out_list_len,
const Format_description_log_event *fdev)
@@ -8227,7 +8659,7 @@ bool Intvar_log_event::write()
*/
#ifdef MYSQL_CLIENT
-void Intvar_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
+bool Intvar_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
{
char llbuff[22];
const char *UNINIT_VAR(msg);
@@ -8236,11 +8668,13 @@ void Intvar_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
if (!print_event_info->short_form)
{
- print_header(&cache, print_event_info, FALSE);
- my_b_write_string(&cache, "\tIntvar\n");
+ if (print_header(&cache, print_event_info, FALSE) ||
+ my_b_write_string(&cache, "\tIntvar\n"))
+ goto err;
}
- my_b_printf(&cache, "SET ");
+ if (my_b_printf(&cache, "SET "))
+ goto err;
switch (type) {
case LAST_INSERT_ID_EVENT:
msg="LAST_INSERT_ID";
@@ -8253,8 +8687,13 @@ void Intvar_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
msg="INVALID_INT";
break;
}
- my_b_printf(&cache, "%s=%s%s\n",
- msg, llstr(val,llbuff), print_event_info->delimiter);
+ 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
@@ -8353,7 +8792,7 @@ bool Rand_log_event::write()
#ifdef MYSQL_CLIENT
-void Rand_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
+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);
@@ -8361,12 +8800,18 @@ void Rand_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
char llbuff[22],llbuff2[22];
if (!print_event_info->short_form)
{
- print_header(&cache, print_event_info, FALSE);
- my_b_write_string(&cache, "\tRand\n");
+ if (print_header(&cache, print_event_info, FALSE) ||
+ my_b_write_string(&cache, "\tRand\n"))
+ goto err;
}
- my_b_printf(&cache, "SET @@RAND_SEED1=%s, @@RAND_SEED2=%s%s\n",
- llstr(seed1, llbuff),llstr(seed2, llbuff2),
- print_event_info->delimiter);
+ 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 */
@@ -8478,7 +8923,7 @@ bool Xid_log_event::write()
#ifdef MYSQL_CLIENT
-void Xid_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
+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);
@@ -8488,10 +8933,17 @@ void Xid_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
char buf[64];
longlong10_to_str(xid, buf, 10);
- print_header(&cache, print_event_info, FALSE);
- my_b_printf(&cache, "\tXid = %s\n", buf);
+ if (print_header(&cache, print_event_info, FALSE) ||
+ my_b_printf(&cache, "\tXid = %s\n", buf))
+ goto err;
}
- my_b_printf(&cache, is_flashback ? "BEGIN%s\n" : "COMMIT%s\n", print_event_info->delimiter);
+ 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 */
@@ -8504,6 +8956,7 @@ int Xid_log_event::do_apply_event(rpl_group_info *rgi)
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
@@ -8528,8 +8981,8 @@ int Xid_log_event::do_apply_event(rpl_group_info *rgi)
gtid= rgi->current_gtid;
err= rpl_global_gtid_slave_state->record_gtid(thd, &gtid, sub_id, rgi,
- false);
- if (err)
+ false, &hton);
+ if (unlikely(err))
{
int ec= thd->get_stmt_da()->sql_errno();
/*
@@ -8564,9 +9017,9 @@ int Xid_log_event::do_apply_event(rpl_group_info *rgi)
if (WSREP(thd)) mysql_mutex_lock(&thd->LOCK_thd_data);
if ((!res || (WSREP(thd) && thd->wsrep_conflict_state == MUST_REPLAY)) && sub_id)
#else
- if (!res && sub_id)
+ if (likely(!res) && sub_id)
#endif /* WITH_WSREP */
- rpl_global_gtid_slave_state->update_state_hash(sub_id, &gtid, rgi);
+ 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 */
@@ -8816,7 +9269,7 @@ User_var_log_event(const char* buf, uint event_len,
}
err:
- if (error)
+ if (unlikely(error))
name= 0;
}
@@ -8830,7 +9283,7 @@ bool User_var_log_event::write()
uchar buf2[MY_MAX(8, DECIMAL_MAX_FIELD_SIZE + 2)], *pos= buf2;
uint unsigned_len= 0;
uint buf1_length;
- ulong event_length;
+ size_t event_length;
int4store(buf, name_len);
@@ -8893,23 +9346,26 @@ bool User_var_log_event::write()
*/
#ifdef MYSQL_CLIENT
-void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
+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)
{
- print_header(&cache, print_event_info, FALSE);
- my_b_write_string(&cache, "\tUser_var\n");
+ if (print_header(&cache, print_event_info, FALSE) ||
+ my_b_write_string(&cache, "\tUser_var\n"))
+ goto err;
}
- my_b_write_string(&cache, "SET @");
- my_b_write_backtick_quote(&cache, name, name_len);
+ if (my_b_write_string(&cache, "SET @") ||
+ my_b_write_backtick_quote(&cache, name, name_len))
+ goto err;
if (is_null)
{
- my_b_printf(&cache, ":=NULL%s\n", print_event_info->delimiter);
+ if (my_b_printf(&cache, ":=NULL%s\n", print_event_info->delimiter))
+ goto err;
}
else
{
@@ -8919,13 +9375,17 @@ void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
char real_buf[FMT_G_BUFSIZE(14)];
float8get(real_val, val);
sprintf(real_buf, "%.14g", real_val);
- my_b_printf(&cache, ":=%s%s\n", real_buf, print_event_info->delimiter);
+ 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));
- my_b_printf(&cache, ":=%s%s\n", int_buf, print_event_info->delimiter);
+ if (my_b_printf(&cache, ":=%s%s\n", int_buf,
+ print_event_info->delimiter))
+ goto err;
break;
case DECIMAL_RESULT:
{
@@ -8941,7 +9401,9 @@ void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
bin2decimal((uchar*) val+2, &dec, precision, scale);
decimal2string(&dec, str_buf, &str_len, 0, 0, 0);
str_buf[str_len]= 0;
- my_b_printf(&cache, ":=%s%s\n", str_buf, print_event_info->delimiter);
+ if (my_b_printf(&cache, ":=%s%s\n", str_buf,
+ print_event_info->delimiter))
+ goto err;
break;
}
case STRING_RESULT:
@@ -8962,11 +9424,12 @@ void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
*/
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)
- return;
+ goto err;
str_to_hex(hex_str, val, val_len);
/*
For proper behaviour when mysqlbinlog|mysql, we need to explicitly
@@ -8975,24 +9438,31 @@ void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
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.
*/
- my_b_printf(&cache, ":=???%s\n", print_event_info->delimiter);
+ error= my_b_printf(&cache, ":=???%s\n", print_event_info->delimiter);
+ }
else
- my_b_printf(&cache, ":=_%s %s COLLATE `%s`%s\n",
- cs->csname, hex_str, cs->name,
- print_event_info->delimiter);
+ 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);
- return;
+ break;
}
}
+
+ return cache.flush_data();
+err:
+ return 1;
}
#endif
@@ -9027,7 +9497,7 @@ int User_var_log_event::do_apply_event(rpl_group_info *rgi)
"Invalid character set for User var event");
DBUG_RETURN(1);
}
- LEX_STRING user_var_name;
+ LEX_CSTRING user_var_name;
user_var_name.str= name;
user_var_name.length= name_len;
double real_val;
@@ -9082,7 +9552,7 @@ int User_var_log_event::do_apply_event(rpl_group_info *rgi)
break;
}
case STRING_RESULT:
- it= new (thd->mem_root) Item_string(thd, val, val_len, charset);
+ it= new (thd->mem_root) Item_string(thd, val, (uint)val_len, charset);
break;
case ROW_RESULT:
default:
@@ -9091,7 +9561,7 @@ int User_var_log_event::do_apply_event(rpl_group_info *rgi)
}
}
- 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 *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)
@@ -9108,7 +9578,7 @@ int User_var_log_event::do_apply_event(rpl_group_info *rgi)
a single record and with a single column. Thus, like
a column value, it could always have IMPLICIT derivation.
*/
- e->update_hash(val, val_len, type, charset,
+ 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);
@@ -9141,19 +9611,25 @@ User_var_log_event::do_shall_skip(rpl_group_info *rgi)
#ifdef HAVE_REPLICATION
#ifdef MYSQL_CLIENT
-void Unknown_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info)
+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 (print_event_info->short_form)
- return;
if (what != ENCRYPTED)
{
- print_header(&cache, print_event_info, FALSE);
- my_b_printf(&cache, "\n# Unknown event\n");
+ if (print_header(&cache, print_event_info, FALSE) ||
+ my_b_printf(&cache, "\n# Unknown event\n"))
+ goto err;
}
- else
- my_b_printf(&cache, "# Encrypted event\n");
+ else if (my_b_printf(&cache, "# Encrypted event\n"))
+ goto err;
+
+ return cache.flush_data();
+err:
+ return 1;
}
#endif
@@ -9166,16 +9642,18 @@ void Unknown_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info
*/
#ifdef MYSQL_CLIENT
-void Stop_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
+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_event_info->short_form)
- return;
-
- print_header(&cache, print_event_info, FALSE);
- my_b_write_string(&cache, "\tStop\n");
+ if (print_header(&cache, print_event_info, FALSE) ||
+ my_b_write_string(&cache, "\tStop\n"))
+ return 1;
+ return cache.flush_data();
}
#endif /* MYSQL_CLIENT */
@@ -9357,22 +9835,25 @@ Create_file_log_event::Create_file_log_event(const char* buf, uint len,
*/
#ifdef MYSQL_CLIENT
-void Create_file_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info,
+bool Create_file_log_event::print(FILE* file,
+ PRINT_EVENT_INFO* print_event_info,
bool enable_local)
{
- Write_on_release_cache cache(&print_event_info->head_cache, file);
-
if (print_event_info->short_form)
{
if (enable_local && check_fname_outside_temp_buf())
- Load_log_event::print(file, print_event_info);
- return;
+ 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)
{
- Load_log_event::print(file, print_event_info,
- !check_fname_outside_temp_buf());
+ 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().
@@ -9384,16 +9865,24 @@ void Create_file_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info
That one is for "file_id: etc" below: in mysqlbinlog we want the #, in
SHOW BINLOG EVENTS we don't.
*/
- my_b_write_byte(&cache, '#');
+ if (my_b_write_byte(&cache, '#'))
+ goto err;
}
- my_b_printf(&cache, " file_id: %d block_len: %d\n", file_id, block_len);
+ 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;
+
}
-void Create_file_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
+bool Create_file_log_event::print(FILE* file,
+ PRINT_EVENT_INFO* print_event_info)
{
- print(file, print_event_info, 0);
+ return print(file, print_event_info, 0);
}
#endif /* MYSQL_CLIENT */
@@ -9437,7 +9926,7 @@ int Create_file_log_event::do_apply_event(rpl_group_info *rgi)
char *ext;
int fd = -1;
IO_CACHE file;
- Log_event_writer lew(&file);
+ Log_event_writer lew(&file, 0);
int error = 1;
Relay_log_info const *rli= rgi->rli;
@@ -9459,7 +9948,7 @@ int Create_file_log_event::do_apply_event(rpl_group_info *rgi)
fname_buf);
goto err;
}
-
+
// a trick to avoid allocating another buffer
fname= fname_buf;
fname_len= (uint) (strmov(ext, ".data") - fname);
@@ -9474,7 +9963,7 @@ int Create_file_log_event::do_apply_event(rpl_group_info *rgi)
}
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));
@@ -9498,9 +9987,9 @@ int Create_file_log_event::do_apply_event(rpl_group_info *rgi)
error=0; // Everything is ok
err:
- if (error)
+ if (unlikely(error))
end_io_cache(&file);
- if (fd >= 0)
+ if (likely(fd >= 0))
mysql_file_close(fd, MYF(0));
return error != 0;
}
@@ -9572,16 +10061,22 @@ bool Append_block_log_event::write()
*/
#ifdef MYSQL_CLIENT
-void Append_block_log_event::print(FILE* file,
+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_event_info->short_form)
- return;
- 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);
+ 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 */
@@ -9731,15 +10226,19 @@ bool Delete_file_log_event::write()
*/
#ifdef MYSQL_CLIENT
-void Delete_file_log_event::print(FILE* file,
+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_event_info->short_form)
- return;
- print_header(&cache, print_event_info, FALSE);
- my_b_printf(&cache, "\n#Delete_file: file_id=%u\n", file_id);
+ 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 */
@@ -9831,16 +10330,20 @@ bool Execute_load_log_event::write()
*/
#ifdef MYSQL_CLIENT
-void Execute_load_log_event::print(FILE* file,
+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_event_info->short_form)
- return;
- print_header(&cache, print_event_info, FALSE);
- my_b_printf(&cache, "\n#Exec_load: file_id=%d\n",
- file_id);
+ 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
@@ -9887,7 +10390,6 @@ int Execute_load_log_event::do_apply_event(rpl_group_info *rgi)
}
if (!(lev= (Load_log_event*)
Log_event::read_log_event(&file,
- (mysql_mutex_t*)0,
rli->relay_log.description_event_for_exec,
opt_slave_sql_verify_checksum)) ||
lev->get_type_code() != NEW_LOAD_EVENT)
@@ -10063,22 +10565,24 @@ Execute_load_query_log_event::write_post_header_for_derived()
#ifdef MYSQL_CLIENT
-void Execute_load_query_log_event::print(FILE* file,
+bool Execute_load_query_log_event::print(FILE* file,
PRINT_EVENT_INFO* print_event_info)
{
- print(file, print_event_info, 0);
+ return print(file, print_event_info, 0);
}
/**
Prints the query as LOAD DATA LOCAL and with rewritten filename.
*/
-void Execute_load_query_log_event::print(FILE* file,
+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);
- print_query_header(&cache, print_event_info);
+ 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().
@@ -10089,24 +10593,33 @@ void Execute_load_query_log_event::print(FILE* file,
if (local_fname)
{
- my_b_write(&cache, (uchar*) query, fn_pos_start);
- my_b_write_string(&cache, " LOCAL INFILE ");
- pretty_print_str(&cache, local_fname, strlen(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)
- my_b_write_string(&cache, " REPLACE");
- my_b_write_string(&cache, " INTO");
- my_b_write(&cache, (uchar*) query + fn_pos_end, q_len-fn_pos_end);
- my_b_printf(&cache, "\n%s\n", print_event_info->delimiter);
+ 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
{
- my_b_write(&cache, (uchar*) query, q_len);
- my_b_printf(&cache, "\n%s\n", print_event_info->delimiter);
+ 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
@@ -10187,7 +10700,7 @@ Execute_load_query_log_event::do_apply_event(rpl_group_info *rgi)
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 (!error)
+ if (unlikely(!error))
mysql_file_delete(key_file_log_event_data, fname, MYF(MY_WME));
my_free(buf);
@@ -10849,7 +11362,7 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
lex->query_tables_last= &tables->next_global;
}
}
- if (open_and_lock_tables(thd, rgi->tables_to_lock, FALSE, 0))
+ if (unlikely(open_and_lock_tables(thd, rgi->tables_to_lock, FALSE, 0)))
{
#ifdef WITH_WSREP
if (WSREP(thd))
@@ -11100,13 +11613,13 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
error= do_exec_row(rgi);
- if (error)
+ if (unlikely(error))
DBUG_PRINT("info", ("error: %s", HA_ERR(error)));
DBUG_ASSERT(error != HA_ERR_RECORD_DELETED);
table->in_use = old_thd;
- if (error)
+ if (unlikely(error))
{
int actual_error= convert_handler_error(error, thd, table);
bool idempotent_error= (idempotent_error_code(error) &&
@@ -11119,7 +11632,7 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
if (global_system_variables.log_warnings)
slave_rows_error_report(WARNING_LEVEL, error, rgi, thd, table,
get_type_str(),
- RPL_LOG_NAME, (ulong) log_pos);
+ RPL_LOG_NAME, log_pos);
thd->clear_error(1);
error= 0;
if (idempotent_error == 0)
@@ -11137,12 +11650,12 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
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 && !error)
+ if (!m_curr_row_end && likely(!error))
error= unpack_current_row(rgi);
m_curr_row= m_curr_row_end;
- if (error == 0 && !transactional_table)
+ if (likely(error == 0) && !transactional_table)
thd->transaction.all.modified_non_trans_table=
thd->transaction.stmt.modified_non_trans_table= TRUE;
} // row processing loop
@@ -11164,25 +11677,25 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
const_cast<Relay_log_info*>(rli)->abort_slave= 1;);
}
- if ((error= do_after_row_operations(rli, error)) &&
+ 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, (ulong) log_pos);
+ RPL_LOG_NAME, log_pos);
thd->clear_error(1);
error= 0;
}
} // if (table)
- if (error)
+ if (unlikely(error))
{
slave_rows_error_report(ERROR_LEVEL, error, rgi, thd, table,
get_type_str(),
- RPL_LOG_NAME, (ulong) log_pos);
+ RPL_LOG_NAME, log_pos);
/*
@todo We should probably not call
reset_current_stmt_binlog_format_row() from here.
@@ -11207,12 +11720,13 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
}
#endif /* WITH_WSREP && HAVE_QUERY_CACHE */
- if (get_flags(STMT_END_F) && (error= rows_event_stmt_cleanup(rgi, thd)))
+ 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, (ulong) log_pos);
+ RPL_LOG_NAME, log_pos);
DBUG_RETURN(error);
err:
@@ -11282,7 +11796,7 @@ static int rows_event_stmt_cleanup(rpl_group_info *rgi, THD * thd)
already. So there should be no need to rollback the transaction.
*/
DBUG_ASSERT(! thd->transaction_rollback_request);
- error|= (error ? trans_rollback_stmt(thd) : trans_commit_stmt(thd));
+ error|= (int)(error ? trans_rollback_stmt(thd) : trans_commit_stmt(thd));
/*
Now what if this is not a transactional engine? we still need to
@@ -11452,21 +11966,87 @@ void Rows_log_event::pack_info(Protocol *protocol)
#endif
#ifdef MYSQL_CLIENT
-class my_String : public String
+
+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)
{
-public:
- my_String() : String(), error(false) {};
- bool error;
+ const my_off_t cache_size= my_b_tell(body);
+
+ if (reinit_io_cache(body, READ_CACHE, 0L, FALSE, FALSE))
+ goto err;
- bool append(const LEX_STRING *ls)
+ if (!do_wrap)
{
- return error= error || String::append(ls);
+ my_b_copy_to_file(body, file, SIZE_T_MAX);
}
- bool append(IO_CACHE* file, uint32 arg_length)
+ else if (4 + sizeof(str_binlog) + cache_size + sizeof(fmt_delim) >
+ opt_binlog_rows_event_max_encoded_size)
{
- return error= error || String::append(file, arg_length);
+ /*
+ 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;
+ if (!is_verbose)
+ 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;
+ if (!is_verbose)
+ 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.
@@ -11480,40 +12060,36 @@ public:
The function signals on any error through setting @c body->error to -1.
*/
-void copy_cache_to_string_wrapped(IO_CACHE *cache,
+bool copy_cache_to_string_wrapped(IO_CACHE *cache,
LEX_STRING *to,
bool do_wrap,
const char *delimiter,
bool is_verbose)
{
- 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 my_off_t cache_size= my_b_tell(cache);
- my_String ret;
- /*
- substring to hold parts of encoded possibly defragramented event
- whose size is roughly estimated from the top.
- */
- char tmp[sizeof(str_binlog) + 2*(sizeof(fmt_frag) + 2 /* %d */) +
- sizeof(fmt_delim) + sizeof(fmt_n_delim) +
- PRINT_EVENT_INFO::max_delimiter_size];
- LEX_STRING str_tmp= { tmp, 0 };
+ // 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))))
{
- cache->error= -1;
- goto end;
+ perror("Out of memory: can't allocate memory in "
+ "copy_cache_to_string_wrapped().");
+ goto err;
}
if (!do_wrap)
{
- if (ret.append(cache, (uint32) cache->end_of_file))
- {
- cache->error= -1;
- goto end;
- }
+ 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)
@@ -11529,45 +12105,50 @@ void copy_cache_to_string_wrapped(IO_CACHE *cache,
limit. The estimate includes the maximum packet header
contribution of non-compressed packet.
*/
- str_tmp.length= sprintf(str_tmp.str, fmt_frag, 0);
- ret.append(&str_tmp);
- ret.append(cache, (uint32) cache_size/2 + 1);
- str_tmp.length= sprintf(str_tmp.str, fmt_n_delim, delimiter);
- ret.append(&str_tmp);
-
- str_tmp.length= sprintf(str_tmp.str, fmt_frag, 1);
- ret.append(&str_tmp);
- ret.append(cache, uint32(cache->end_of_file - (cache_size/2 + 1)));
+ 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;
if (!is_verbose)
{
- str_tmp.length= sprintf(str_tmp.str, fmt_delim, delimiter);
- ret.append(&str_tmp);
+ str += (add_to_len= sprintf(str , fmt_delim, delimiter));
+ to->length += add_to_len;
}
- str_tmp.length= sprintf(str_tmp.str, "BINLOG @binlog_fragment_0, @binlog_fragment_1%s\n",
- delimiter);
- ret.append(&str_tmp);
+ to->length += sprintf(str, fmt_binlog2, delimiter);
}
else
{
- str_tmp.length= sprintf(str_tmp.str, str_binlog);
- ret.append(&str_tmp);
- ret.append(cache, (uint32) cache->end_of_file);
+ 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;
if (!is_verbose)
- {
- str_tmp.length= sprintf(str_tmp.str, fmt_delim, delimiter);
- ret.append(&str_tmp);
- }
+ to->length += sprintf(str , fmt_delim, delimiter);
}
- to->length= ret.length();
- to->str= ret.release();
-
reinit_io_cache(cache, WRITE_CACHE, 0, FALSE, TRUE);
- if (ret.error)
- cache->error= -1;
-end:
- return;
+ return false;
+
+err:
+ cache->error= -1;
+ return true;
}
/**
@@ -11599,7 +12180,7 @@ end:
The function signals on any error of cache access through setting
that cache's @c error to -1.
*/
-void Rows_log_event::print_helper(FILE *file,
+bool Rows_log_event::print_helper(FILE *file,
PRINT_EVENT_INFO *print_event_info,
char const *const name)
{
@@ -11609,44 +12190,62 @@ void Rows_log_event::print_helper(FILE *file,
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)
{
-
- bool const last_stmt_event= get_flags(STMT_END_F);
char llbuff[22];
print_header(head, print_event_info, !last_stmt_event);
- my_b_printf(head, "\t%s: table id %s%s\n",
- name, ullstr(m_table_id, llbuff),
- last_stmt_event ? " flags: STMT_END_F" : "");
- print_base64(body, print_event_info, do_print_encoded);
+ 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 (get_flags(STMT_END_F))
+ if (last_stmt_event)
{
- LEX_STRING tmp_str;
-#ifdef WHEN_FLASHBACK_REVIEW_READY
- copy_event_cache_to_string_and_reinit(sql, &tmp_str);
- output_buf.append(&tmp_str);
- my_free(tmp_str.str);
-#endif
- if (copy_event_cache_to_string_and_reinit(head, &tmp_str))
+ if (!is_flashback)
{
- head->error= -1;
- return;
+ 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))
+ goto err;
}
- output_buf.append(&tmp_str);
+ 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);
- copy_cache_to_string_wrapped(body, &tmp_str, do_print_encoded,
- print_event_info->delimiter,
- print_event_info->verbose);
- output_buf.append(&tmp_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);
+#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
@@ -11660,7 +12259,9 @@ Annotate_rows_log_event::Annotate_rows_log_event(THD *thd,
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_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();
@@ -11674,7 +12275,9 @@ Annotate_rows_log_event::Annotate_rows_log_event(const char *buf,
const Format_description_log_event *desc)
: Log_event(buf, desc),
m_save_thd_query_txt(0),
- m_save_thd_query_len(0), m_saved_thd_query(false)
+ m_save_thd_query_len(0),
+ m_saved_thd_query(false),
+ m_used_query_txt(0)
{
m_query_len= event_len - desc->common_header_len;
m_query_txt= (char*) buf + desc->common_header_len;
@@ -11682,10 +12285,14 @@ Annotate_rows_log_event::Annotate_rows_log_event(const char *buf,
Annotate_rows_log_event::~Annotate_rows_log_event()
{
+ DBUG_ENTER("Annotate_rows_log_event::~Annotate_rows_log_event");
#ifndef MYSQL_CLIENT
if (m_saved_thd_query)
thd->set_query(m_save_thd_query_txt, m_save_thd_query_len);
+ else if (m_used_query_txt)
+ thd->reset_query();
#endif
+ DBUG_VOID_RETURN;
}
int Annotate_rows_log_event::get_data_size()
@@ -11726,25 +12333,28 @@ void Annotate_rows_log_event::pack_info(Protocol* protocol)
#endif
#ifdef MYSQL_CLIENT
-void Annotate_rows_log_event::print(FILE *file, PRINT_EVENT_INFO *pinfo)
+bool Annotate_rows_log_event::print(FILE *file, PRINT_EVENT_INFO *pinfo)
{
- if (pinfo->short_form)
- return;
-
- print_header(&pinfo->head_cache, pinfo, TRUE);
- my_b_printf(&pinfo->head_cache, "\tAnnotate_rows:\n");
-
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;
+ return 0;
if (*pbeg != '\r' && *pbeg != '\n')
break;
@@ -11757,10 +12367,15 @@ void Annotate_rows_log_event::print(FILE *file, PRINT_EVENT_INFO *pinfo)
;
// print next line
- 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);
+ 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
@@ -11771,6 +12386,7 @@ int Annotate_rows_log_event::do_apply_event(rpl_group_info *rgi)
m_save_thd_query_txt= thd->query();
m_save_thd_query_len= thd->query_length();
m_saved_thd_query= true;
+ m_used_query_txt= 1;
thd->set_query(m_query_txt, m_query_len);
return 0;
}
@@ -12121,7 +12737,7 @@ int Table_map_log_event::rewrite_db(const char* new_db, size_t new_len,
LOG_EVENT_MINIMAL_HEADER_LEN) + TABLE_MAP_HEADER_LEN;
int len_diff;
- if (!(len_diff= new_len - m_dblen))
+ 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);
@@ -12143,7 +12759,7 @@ int Table_map_log_event::rewrite_db(const char* new_db, size_t new_len,
// Rewrite temp_buf
char* ptr= new_temp_buf;
- ulong cnt= 0;
+ size_t cnt= 0;
// Copy header and change event length
memcpy(ptr, temp_buf, header_len);
@@ -12272,7 +12888,7 @@ check_table_map(rpl_group_info *rgi, RPL_TABLE_LIST *table_list)
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) ||
+ (!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
@@ -12284,8 +12900,8 @@ check_table_map(rpl_group_info *rgi, RPL_TABLE_LIST *table_list)
if (ptr->table_id == table_list->table_id)
{
- if (strcmp(ptr->db, table_list->db) ||
- strcmp(ptr->alias, table_list->table_name) ||
+ 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
@@ -12305,7 +12921,7 @@ 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;
+ size_t dummy_len, db_mem_length, tname_mem_length;
void *memory;
Rpl_filter *filter;
Relay_log_info const *rli= rgi->rli;
@@ -12321,8 +12937,8 @@ int Table_map_log_event::do_apply_event(rpl_group_info *rgi)
NullS)))
DBUG_RETURN(HA_ERR_OUT_OF_MEM);
- strmov(db_mem, m_dbnam);
- strmov(tname_mem, m_tblnam);
+ 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);
@@ -12334,17 +12950,18 @@ int Table_map_log_event::do_apply_event(rpl_group_info *rgi)
/* rewrite rules changed the database */
if (((ptr= (char*) filter->get_rewrite_db(db_mem, &dummy_len)) != db_mem))
- strmov(db_mem, ptr);
+ db_mem_length= strmov(db_mem, ptr) - db_mem;
- table_list->init_one_table(db_mem, strlen(db_mem),
- tname_mem, strlen(tname_mem),
- tname_mem, TL_WRITE);
+ 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= FRMTYPE_TABLE;
+ table_list->required_type= TABLE_TYPE_NORMAL;
- DBUG_PRINT("debug", ("table: %s is mapped to %llu", table_list->table_name,
+ 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",
@@ -12514,23 +13131,37 @@ void Table_map_log_event::pack_info(Protocol *protocol)
#ifdef MYSQL_CLIENT
-void Table_map_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
+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);
- 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)" : ""));
- print_base64(&print_event_info->body_cache, print_event_info,
- print_event_info->base64_output_mode !=
- BASE64_OUTPUT_DECODE_ROWS);
- copy_event_cache_to_file_and_reinit(&print_event_info->head_cache, file);
+ 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;
+ }
+
+ return 0;
+err:
+ return 1;
}
#endif
@@ -12700,7 +13331,7 @@ Write_rows_log_event::do_after_row_operations(const Slave_reporting_capability *
ultimately. Still todo: fix
*/
}
- if ((local_error= m_table->file->ha_end_bulk_insert()))
+ if (unlikely((local_error= m_table->file->ha_end_bulk_insert())))
{
m_table->file->print_error(local_error, MYF(0));
}
@@ -12818,7 +13449,7 @@ Rows_log_event::write_row(rpl_group_info *rgi,
prepare_record(table, m_width, true);
/* unpack row into table->record[0] */
- if ((error= unpack_current_row(rgi)))
+ if (unlikely((error= unpack_current_row(rgi))))
{
table->file->print_error(error, MYF(0));
DBUG_RETURN(error);
@@ -12858,9 +13489,20 @@ Rows_log_event::write_row(rpl_group_info *rgi,
DBUG_PRINT_BITSET("debug", "read_set: %s", table->read_set);
if (invoke_triggers &&
- process_triggers(TRG_EVENT_INSERT, TRG_ACTION_BEFORE, TRUE))
+ 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))
{
- DBUG_RETURN(HA_ERR_GENERIC); // in case if error is not set yet
+ 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();
}
/*
@@ -12871,7 +13513,9 @@ Rows_log_event::write_row(rpl_group_info *rgi,
TODO: Add safety measures against infinite looping.
*/
- while ((error= table->file->ha_write_row(table->record[0])))
+ 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 ||
@@ -12909,11 +13553,9 @@ Rows_log_event::write_row(rpl_group_info *rgi,
}
error= table->file->ha_rnd_pos(table->record[1], table->file->dup_ref);
- if (error)
+ if (unlikely(error))
{
DBUG_PRINT("info",("rnd_pos() returns error %d",error));
- if (error == HA_ERR_RECORD_DELETED)
- error= HA_ERR_KEY_NOT_FOUND;
table->file->print_error(error, MYF(0));
DBUG_RETURN(error);
}
@@ -12945,11 +13587,9 @@ Rows_log_event::write_row(rpl_group_info *rgi,
(const uchar*)key.get(),
HA_WHOLE_KEY,
HA_READ_KEY_EXACT);
- if (error)
+ if (unlikely(error))
{
DBUG_PRINT("info",("index_read_idx() returns %s", HA_ERR(error)));
- if (error == HA_ERR_RECORD_DELETED)
- error= HA_ERR_KEY_NOT_FOUND;
table->file->print_error(error, MYF(0));
DBUG_RETURN(error);
}
@@ -13020,18 +13660,20 @@ Rows_log_event::write_row(rpl_group_info *rgi,
{
DBUG_PRINT("info",("Deleting offending row and trying to write new one again"));
if (invoke_triggers &&
- process_triggers(TRG_EVENT_DELETE, TRG_ACTION_BEFORE, TRUE))
+ unlikely(process_triggers(TRG_EVENT_DELETE, TRG_ACTION_BEFORE,
+ TRUE)))
error= HA_ERR_GENERIC; // in case if error is not set yet
else
{
- if ((error= table->file->ha_delete_row(table->record[1])))
+ 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 &&
- process_triggers(TRG_EVENT_DELETE, TRG_ACTION_AFTER, TRUE))
+ 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. */
@@ -13039,12 +13681,39 @@ Rows_log_event::write_row(rpl_group_info *rgi,
}
if (invoke_triggers &&
- process_triggers(TRG_EVENT_INSERT, TRG_ACTION_AFTER, TRUE))
+ 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
int
@@ -13053,6 +13722,7 @@ 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,
@@ -13062,10 +13732,10 @@ Write_rows_log_event::do_exec_row(rpl_group_info *rgi)
#endif /* WSREP_PROC_INFO */
thd_proc_info(thd, message);
- int error= write_row(rgi, slave_exec_mode == SLAVE_EXEC_MODE_IDEMPOTENT);
+ error= write_row(rgi, slave_exec_mode == SLAVE_EXEC_MODE_IDEMPOTENT);
thd_proc_info(thd, tmp);
- if (error && !thd->is_error())
+ if (unlikely(error) && unlikely(!thd->is_error()))
{
DBUG_ASSERT(0);
my_error(ER_UNKNOWN_ERROR, MYF(0));
@@ -13077,14 +13747,14 @@ Write_rows_log_event::do_exec_row(rpl_group_info *rgi)
#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
#ifdef MYSQL_CLIENT
-void Write_rows_log_event::print(FILE *file, PRINT_EVENT_INFO* print_event_info)
+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");});
- Rows_log_event::print_helper(file, print_event_info, is_flashback ? "Delete_rows" : "Write_rows");
+ return Rows_log_event::print_helper(file, print_event_info, is_flashback ? "Delete_rows" : "Write_rows");
}
-void Write_rows_compressed_log_event::print(FILE *file,
+bool Write_rows_compressed_log_event::print(FILE *file,
PRINT_EVENT_INFO* print_event_info)
{
char *new_buf;
@@ -13096,14 +13766,20 @@ void Write_rows_compressed_log_event::print(FILE *file,
{
free_temp_buf();
register_temp_buf(new_buf, true);
- Rows_log_event::print_helper(file, print_event_info,
- "Write_compressed_rows");
+ if (Rows_log_event::print_helper(file, print_event_info,
+ "Write_compressed_rows"))
+ goto err;
}
else
{
- my_b_printf(&print_event_info->head_cache,
- "ERROR: uncompress write_compressed_rows failed\n");
+ if (my_b_printf(&print_event_info->head_cache,
+ "ERROR: uncompress write_compressed_rows failed\n"))
+ goto err;
}
+
+ return 0;
+err:
+ return 1;
}
#endif
@@ -13111,9 +13787,8 @@ void Write_rows_compressed_log_event::print(FILE *file,
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
uint8 Write_rows_log_event::get_trg_event_map()
{
- return (static_cast<uint8> (1 << static_cast<int>(TRG_EVENT_INSERT)) |
- static_cast<uint8> (1 << static_cast<int>(TRG_EVENT_UPDATE)) |
- static_cast<uint8> (1 << static_cast<int>(TRG_EVENT_DELETE)));
+ return trg2bit(TRG_EVENT_INSERT) | trg2bit(TRG_EVENT_UPDATE) |
+ trg2bit(TRG_EVENT_DELETE);
}
#endif
@@ -13160,7 +13835,10 @@ static bool record_compare(TABLE *table)
/* 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
@@ -13228,7 +13906,7 @@ int Rows_log_event::find_key()
*/
last_part= key->user_defined_key_parts - 1;
DBUG_PRINT("info", ("Index %s rec_per_key[%u]= %lu",
- key->name, last_part, key->rec_per_key[last_part]));
+ 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;
@@ -13279,13 +13957,13 @@ void issue_long_find_row_warning(Log_event_type type,
if ((global_system_variables.log_warnings > 1 &&
!rgi->is_long_find_row_note_printed()))
{
- time_t now= my_time(0);
- time_t stmt_ts= rgi->get_row_stmt_start_timestamp();
+ 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););
+ stmt_ts-=(LONG_FIND_ROW_THRESHOLD*2*HRTIME_RESOLUTION););
- long delta= (long) (now - stmt_ts);
+ longlong delta= (now - stmt_ts)/HRTIME_RESOLUTION;
if (delta > LONG_FIND_ROW_THRESHOLD)
{
@@ -13295,10 +13973,11 @@ void issue_long_find_row_warning(Log_event_type type,
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 (%ld seconds). This is due to the fact that it is %s "
+ "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, delta, scan_type);
+ "performance.",
+ evt_type, table_name, (long) delta, scan_type);
}
}
}
@@ -13365,6 +14044,25 @@ int Rows_log_event::find_row(rpl_group_info *rgi)
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);
@@ -13394,10 +14092,10 @@ int Rows_log_event::find_row(rpl_group_info *rgi)
DBUG_PRINT("info",("locating record using primary key (position)"));
error= table->file->ha_rnd_pos_by_record(table->record[0]);
- if (error)
+ if (unlikely(error))
{
DBUG_PRINT("info",("rnd_pos returns error %d",error));
- if (error == HA_ERR_RECORD_DELETED || error == HA_ERR_KEY_NOT_FOUND)
+ if (error == HA_ERR_KEY_NOT_FOUND)
error= row_not_found_error(rgi);
table->file->print_error(error, MYF(0));
}
@@ -13421,10 +14119,10 @@ int Rows_log_event::find_row(rpl_group_info *rgi)
if (m_key_info)
{
DBUG_PRINT("info",("locating record using key #%u [%s] (index_read)",
- m_key_nr, m_key_info->name));
+ 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,"expected_key")) abort(););
+ 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 &&
@@ -13458,12 +14156,13 @@ int Rows_log_event::find_row(rpl_group_info *rgi)
table->record[0][table->s->null_bytes - 1]|=
256U - (1U << table->s->last_null_bit_pos);
- if ((error= table->file->ha_index_read_map(table->record[0], m_key,
- HA_WHOLE_KEY,
- HA_READ_KEY_EXACT)))
+ 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_RECORD_DELETED || error == HA_ERR_KEY_NOT_FOUND)
+ 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();
@@ -13540,9 +14239,6 @@ int Rows_log_event::find_row(rpl_group_info *rgi)
{
while ((error= table->file->ha_index_next(table->record[0])))
{
- /* We just skip records that has already been deleted */
- if (error == HA_ERR_RECORD_DELETED)
- continue;
DBUG_PRINT("info",("no record matching the given row found"));
table->file->print_error(error, MYF(0));
table->file->ha_index_end();
@@ -13557,7 +14253,7 @@ int Rows_log_event::find_row(rpl_group_info *rgi)
DBUG_EXECUTE_IF("slave_crash_if_table_scan", abort(););
/* We don't have a key: search the table using rnd_next() */
- if ((error= table->file->ha_rnd_init_with_error(1)))
+ if (unlikely((error= table->file->ha_rnd_init_with_error(1))))
{
DBUG_PRINT("info",("error initializing table scan"
" (ha_rnd_init returns %d)",error));
@@ -13569,10 +14265,9 @@ int Rows_log_event::find_row(rpl_group_info *rgi)
/* Continue until we find the right record or have made a full loop */
do
{
- restart_rnd_next:
error= table->file->ha_rnd_next(table->record[0]);
- if (error)
+ if (unlikely(error))
DBUG_PRINT("info", ("error: %s", HA_ERR(error)));
switch (error) {
@@ -13585,13 +14280,6 @@ int Rows_log_event::find_row(rpl_group_info *rgi)
table->file->ha_rnd_end();
goto end;
- /*
- If the record was deleted, we pick the next one without doing
- any comparisons.
- */
- case HA_ERR_RECORD_DELETED:
- goto restart_rnd_next;
-
default:
DBUG_PRINT("info", ("Failed to get next record"
" (rnd_next returns %d)",error));
@@ -13696,7 +14384,6 @@ int
Delete_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);
m_key= NULL;
@@ -13722,7 +14409,7 @@ int Delete_rows_log_event::do_exec_row(rpl_group_info *rgi)
#endif /* WSREP_PROC_INFO */
thd_proc_info(thd, message);
- if (!(error= find_row(rgi)))
+ if (likely(!(error= find_row(rgi))))
{
/*
Delete the record found, located in record[0]
@@ -13737,16 +14424,28 @@ int Delete_rows_log_event::do_exec_row(rpl_group_info *rgi)
thd_proc_info(thd, message);
if (invoke_triggers &&
- process_triggers(TRG_EVENT_DELETE, TRG_ACTION_BEFORE, FALSE))
+ unlikely(process_triggers(TRG_EVENT_DELETE, TRG_ACTION_BEFORE, FALSE)))
error= HA_ERR_GENERIC; // in case if error is not set yet
- if (!error)
+ if (likely(!error))
{
m_table->mark_columns_per_binlog_row_image();
- error= m_table->file->ha_delete_row(m_table->record[0]);
+ 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 && !error &&
- process_triggers(TRG_EVENT_DELETE, TRG_ACTION_AFTER, FALSE))
+ 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();
}
@@ -13757,13 +14456,13 @@ int Delete_rows_log_event::do_exec_row(rpl_group_info *rgi)
#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
#ifdef MYSQL_CLIENT
-void Delete_rows_log_event::print(FILE *file,
+bool Delete_rows_log_event::print(FILE *file,
PRINT_EVENT_INFO* print_event_info)
{
- Rows_log_event::print_helper(file, print_event_info, is_flashback ? "Write_rows" : "Delete_rows");
+ return Rows_log_event::print_helper(file, print_event_info, is_flashback ? "Write_rows" : "Delete_rows");
}
-void Delete_rows_compressed_log_event::print(FILE *file,
+bool Delete_rows_compressed_log_event::print(FILE *file,
PRINT_EVENT_INFO* print_event_info)
{
char *new_buf;
@@ -13775,14 +14474,20 @@ void Delete_rows_compressed_log_event::print(FILE *file,
{
free_temp_buf();
register_temp_buf(new_buf, true);
- Rows_log_event::print_helper(file, print_event_info,
- "Delete_compressed_rows");
+ if (Rows_log_event::print_helper(file, print_event_info,
+ "Delete_compressed_rows"))
+ goto err;
}
else
{
- my_b_printf(&print_event_info->head_cache,
- "ERROR: uncompress delete_compressed_rows failed\n");
+ if (my_b_printf(&print_event_info->head_cache,
+ "ERROR: uncompress delete_compressed_rows failed\n"))
+ goto err;
}
+
+ return 0;
+err:
+ return 1;
}
#endif
@@ -13790,7 +14495,7 @@ void Delete_rows_compressed_log_event::print(FILE *file,
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
uint8 Delete_rows_log_event::get_trg_event_map()
{
- return static_cast<uint8> (1 << static_cast<int>(TRG_EVENT_DELETE));
+ return trg2bit(TRG_EVENT_DELETE);
}
#endif
@@ -13932,9 +14637,9 @@ Update_rows_log_event::do_exec_row(rpl_group_info *rgi)
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 (error)
+
+ int error= find_row(rgi);
+ if (unlikely(error))
{
/*
We need to read the second image in the event of error to be
@@ -13970,7 +14675,7 @@ Update_rows_log_event::do_exec_row(rpl_group_info *rgi)
/* this also updates m_curr_row_end */
thd_proc_info(thd, message);
- if ((error= unpack_current_row(rgi, &m_cols_ai)))
+ if (unlikely((error= unpack_current_row(rgi, &m_cols_ai))))
goto err;
/*
@@ -13997,19 +14702,27 @@ Update_rows_log_event::do_exec_row(rpl_group_info *rgi)
thd_proc_info(thd, message);
if (invoke_triggers &&
- process_triggers(TRG_EVENT_UPDATE, TRG_ACTION_BEFORE, TRUE))
+ 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 (error == HA_ERR_RECORD_IS_THE_SAME)
+ 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 && !error &&
- process_triggers(TRG_EVENT_UPDATE, TRG_ACTION_AFTER, TRUE))
+ 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);
@@ -14022,13 +14735,15 @@ err:
#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
#ifdef MYSQL_CLIENT
-void Update_rows_log_event::print(FILE *file,
+bool Update_rows_log_event::print(FILE *file,
PRINT_EVENT_INFO* print_event_info)
{
- Rows_log_event::print_helper(file, print_event_info, "Update_rows");
+ return Rows_log_event::print_helper(file, print_event_info, "Update_rows");
}
-void Update_rows_compressed_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
+bool
+Update_rows_compressed_log_event::print(FILE *file,
+ PRINT_EVENT_INFO *print_event_info)
{
char *new_buf;
ulong len;
@@ -14039,21 +14754,27 @@ void Update_rows_compressed_log_event::print(FILE *file, PRINT_EVENT_INFO *print
{
free_temp_buf();
register_temp_buf(new_buf, true);
- Rows_log_event::print_helper(file, print_event_info,
- "Update_compressed_rows");
+ if (Rows_log_event::print_helper(file, print_event_info,
+ "Update_compressed_rows"))
+ goto err;
}
else
{
- my_b_printf(&print_event_info->head_cache,
- "ERROR: uncompress update_compressed_rows failed\n");
+ 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 static_cast<uint8> (1 << static_cast<int>(TRG_EVENT_UPDATE));
+ return trg2bit(TRG_EVENT_UPDATE);
}
#endif
@@ -14142,7 +14863,7 @@ void Incident_log_event::pack_info(Protocol *protocol)
#endif /* MYSQL_CLIENT */
-#if WITH_WSREP && !defined(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
@@ -14185,16 +14906,18 @@ err:
#ifdef MYSQL_CLIENT
-void
-Incident_log_event::print(FILE *file,
- PRINT_EVENT_INFO *print_event_info)
+bool Incident_log_event::print(FILE *file,
+ PRINT_EVENT_INFO *print_event_info)
{
if (print_event_info->short_form)
- return;
+ return 0;
Write_on_release_cache cache(&print_event_info->head_cache, file);
- print_header(&cache, print_event_info, FALSE);
- my_b_printf(&cache, "\n# Incident: %s\nRELOAD DATABASE; # Shall generate syntax error\n", description());
+
+ 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
@@ -14270,19 +14993,20 @@ void Ignorable_log_event::pack_info(Protocol *protocol)
#ifdef MYSQL_CLIENT
/* Print for its unrecognized ignorable event */
-void
-Ignorable_log_event::print(FILE *file,
- PRINT_EVENT_INFO *print_event_info)
+bool Ignorable_log_event::print(FILE *file,
+ PRINT_EVENT_INFO *print_event_info)
{
if (print_event_info->short_form)
- return;
+ return 0;
- 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);
+ 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
@@ -14294,15 +15018,8 @@ Ignorable_log_event::print(FILE *file,
they will always be printed for the first event.
*/
st_print_event_info::st_print_event_info()
- :flags2_inited(0), sql_mode_inited(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), thread_id_printed(false), server_id(0),
- server_id_printed(false), domain_id(0), domain_id_printed(false),
- allow_parallel(true), allow_parallel_printed(false), skip_replication(0),
- base64_output_mode(BASE64_OUTPUT_UNSPEC), printed_fd_event(FALSE)
{
+ 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
@@ -14313,14 +15030,68 @@ st_print_event_info::st_print_event_info()
bzero(time_zone_str, sizeof(time_zone_str));
delimiter[0]= ';';
delimiter[1]= 0;
- myf const flags = MYF(MY_WME | MY_NABP);
+ 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);
#ifdef WHEN_FLASHBACK_REVIEW_READY
open_cached_file(&review_sql_cache, NULL, NULL, 0, flags);
#endif
}
-#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,
@@ -14335,43 +15106,6 @@ Heartbeat_log_event::Heartbeat_log_event(const char* buf, uint event_len,
#endif
#if defined(MYSQL_SERVER)
-/*
- Access to the current replication position.
-
- There is a dummy replacement for this in the embedded library that returns
- FALSE; this is used by XtraDB to allow it to access replication stuff while
- still being able to use the same plugin in both stand-alone and embedded.
-
- In this function it's ok to use active_mi, as this is only called for
- the main replication server.
-*/
-bool rpl_get_position_info(const char **log_file_name, ulonglong *log_pos,
- const char **group_relay_log_name,
- ulonglong *relay_log_pos)
-{
-#if defined(EMBEDDED_LIBRARY) || !defined(HAVE_REPLICATION)
- return FALSE;
-#else
- const Relay_log_info *rli= &(active_mi->rli);
- if (!rli->mi->using_parallel())
- {
- *log_file_name= rli->group_master_log_name;
- *log_pos= rli->group_master_log_pos +
- (rli->future_event_relay_log_pos - rli->group_relay_log_pos);
- *group_relay_log_name= rli->group_relay_log_name;
- *relay_log_pos= rli->future_event_relay_log_pos;
- }
- else
- {
- *log_file_name= "";
- *log_pos= 0;
- *group_relay_log_name= "";
- *relay_log_pos= 0;
- }
- return TRUE;
-#endif
-}
-
/**
Check if we should write event to the relay log
@@ -14395,4 +15129,4 @@ bool event_that_should_be_ignored(const char *buf)
return 1;
return 0;
}
-#endif
+#endif /* MYSQL_SERVER */
diff --git a/sql/log_event.h b/sql/log_event.h
index 1fae201057f..b9514e874e8 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -716,6 +716,21 @@ enum Log_event_type
ENUM_END_EVENT /* end marker */
};
+
+/*
+ Bit flags for what has been writting to cache. Used to
+ discard logs with table map events but not row events and
+ nothing else important. This is stored by cache.
+*/
+
+enum enum_logged_status
+{
+ LOGGED_TABLE_MAP= 1,
+ LOGGED_ROW_EVENT= 2,
+ LOGGED_NO_DATA= 4,
+ LOGGED_CRITICAL= 8
+};
+
static inline bool LOG_EVENT_IS_QUERY(enum Log_event_type type)
{
return type == QUERY_EVENT || type == QUERY_COMPRESSED_EVENT;
@@ -785,6 +800,9 @@ class THD;
class Format_description_log_event;
class Relay_log_info;
+class binlog_cache_data;
+
+bool copy_event_cache_to_file_and_reinit(IO_CACHE *cache, FILE *file);
#ifdef MYSQL_CLIENT
enum enum_base64_output_mode {
@@ -797,6 +815,8 @@ enum enum_base64_output_mode {
BASE64_OUTPUT_MODE_COUNT
};
+bool copy_event_cache_to_string_and_reinit(IO_CACHE *cache, LEX_STRING *to);
+
/*
A structure for mysqlbinlog to know how to print events
@@ -816,53 +836,39 @@ typedef struct st_print_event_info
that was printed. We cache these so that we don't have to print
them if they are unchanged.
*/
- // TODO: have the last catalog here ??
char db[FN_REFLEN+1]; // TODO: make this a LEX_STRING when thd->db is
- bool flags2_inited;
- uint32 flags2;
- bool sql_mode_inited;
- sql_mode_t sql_mode; /* must be same as THD.variables.sql_mode */
- ulong auto_increment_increment, auto_increment_offset;
- bool charset_inited;
char charset[6]; // 3 variables, each of them storable in 2 bytes
char time_zone_str[MAX_TIME_ZONE_NAME_LENGTH];
+ char delimiter[16];
+ sql_mode_t sql_mode; /* must be same as THD.variables.sql_mode */
+ my_thread_id thread_id;
+ ulonglong row_events;
+ ulong auto_increment_increment, auto_increment_offset;
uint lc_time_names_number;
uint charset_database_number;
- my_thread_id thread_id;
- bool thread_id_printed;
+ uint verbose;
+ uint32 flags2;
uint32 server_id;
- bool server_id_printed;
uint32 domain_id;
+ uint8 common_header_len;
+ enum_base64_output_mode base64_output_mode;
+ my_off_t hexdump_from;
+
+ table_mapping m_table_map;
+ table_mapping m_table_map_ignored;
+ bool flags2_inited;
+ bool sql_mode_inited;
+ bool charset_inited;
+ bool thread_id_printed;
+ bool server_id_printed;
bool domain_id_printed;
bool allow_parallel;
bool allow_parallel_printed;
+ bool found_row_event;
+ bool print_row_count;
static const uint max_delimiter_size= 16;
- /*
- Track when @@skip_replication changes so we need to output a SET
- statement for it.
- */
- int skip_replication;
-
- st_print_event_info();
-
- ~st_print_event_info() {
- close_cached_file(&head_cache);
- close_cached_file(&body_cache);
-#ifdef WHEN_FLASHBACK_REVIEW_READY
- close_cached_file(&review_sql_cache);
-#endif
- }
- bool init_ok() /* tells if construction was successful */
- { return my_b_inited(&head_cache) && my_b_inited(&body_cache)
-#ifdef WHEN_FLASHBACK_REVIEW_READY
- && my_b_inited(&review_sql_cache)
-#endif
- ; }
-
-
/* Settings on how to print the events */
bool short_form;
- enum_base64_output_mode base64_output_mode;
/*
This is set whenever a Format_description_event is printed.
Later, when an event is printed in base64, this flag is tested: if
@@ -870,13 +876,11 @@ typedef struct st_print_event_info
the base64 event, so an error message is generated.
*/
bool printed_fd_event;
- my_off_t hexdump_from;
- uint8 common_header_len;
- char delimiter[max_delimiter_size];
-
- uint verbose;
- table_mapping m_table_map;
- table_mapping m_table_map_ignored;
+ /*
+ Track when @@skip_replication changes so we need to output a SET
+ statement for it.
+ */
+ bool skip_replication;
/*
These two caches are used by the row-based replication events to
@@ -889,6 +893,28 @@ typedef struct st_print_event_info
/* Storing the SQL for reviewing */
IO_CACHE review_sql_cache;
#endif
+ FILE *file;
+ st_print_event_info();
+
+ ~st_print_event_info() {
+ close_cached_file(&head_cache);
+ close_cached_file(&body_cache);
+#ifdef WHEN_FLASHBACK_REVIEW_READY
+ close_cached_file(&review_sql_cache);
+#endif
+ }
+ bool init_ok() /* tells if construction was successful */
+ { return my_b_inited(&head_cache) && my_b_inited(&body_cache)
+#ifdef WHEN_FLASHBACK_REVIEW_READY
+ && my_b_inited(&review_sql_cache)
+#endif
+ ; }
+ void flush_for_error()
+ {
+ if (!copy_event_cache_to_file_and_reinit(&head_cache, file))
+ copy_event_cache_to_file_and_reinit(&body_cache, file);
+ fflush(file);
+ }
} PRINT_EVENT_INFO;
#endif
@@ -896,6 +922,7 @@ typedef struct st_print_event_info
This class encapsulates writing of Log_event objects to IO_CACHE.
Automatically calculates the checksum and encrypts the data, if necessary.
*/
+
class Log_event_writer
{
public:
@@ -907,13 +934,16 @@ public:
int write_data(const uchar *pos, size_t len);
int write_footer();
my_off_t pos() { return my_b_safe_tell(file); }
+ void add_status(enum_logged_status status);
-Log_event_writer(IO_CACHE *file_arg, Binlog_crypt_data *cr= 0)
+ Log_event_writer(IO_CACHE *file_arg, binlog_cache_data *cache_data_arg,
+ Binlog_crypt_data *cr= 0)
: bytes_written(0), ctx(0),
- file(file_arg), crypto(cr) { }
+ file(file_arg), cache_data(cache_data_arg), crypto(cr) { }
private:
IO_CACHE *file;
+ binlog_cache_data *cache_data;
/**
Placeholder for event checksum while writing to binlog.
*/
@@ -1174,7 +1204,7 @@ public:
/* The number of seconds the query took to run on the master. */
ulong exec_time;
/* Number of bytes written by write() function */
- ulong data_written;
+ size_t data_written;
/*
The master's server id (is preserved in the relay log; used to
@@ -1224,19 +1254,19 @@ public:
#endif /* HAVE_REPLICATION */
virtual const char* get_db()
{
- return thd ? thd->db : 0;
+ return thd ? thd->db.str : 0;
}
#else
Log_event() : temp_buf(0), when(0), flags(0) {}
ha_checksum crc;
/* print*() functions are used by mysqlbinlog */
- virtual void print(FILE* file, PRINT_EVENT_INFO* print_event_info) = 0;
- void print_timestamp(IO_CACHE* file, time_t *ts = 0);
- void print_header(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info,
+ virtual bool print(FILE* file, PRINT_EVENT_INFO* print_event_info) = 0;
+ bool print_timestamp(IO_CACHE* file, time_t *ts = 0);
+ bool print_header(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info,
bool is_more);
- void print_base64(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info,
+ bool print_base64(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info,
bool do_print_encoded);
-#endif
+#endif /* MYSQL_SERVER */
/* The following code used for Flashback */
#ifdef MYSQL_CLIENT
@@ -1280,7 +1310,6 @@ public:
constructor and pass description_event as an argument.
*/
static Log_event* read_log_event(IO_CACHE* file,
- mysql_mutex_t* log_lock,
const Format_description_log_event
*description_event,
my_bool crc_check);
@@ -1336,10 +1365,10 @@ public:
static void operator delete(void*, void*) { }
#ifdef MYSQL_SERVER
- bool write_header(ulong data_length);
- bool write_data(const uchar *buf, ulong data_length)
+ bool write_header(size_t event_data_length);
+ bool write_data(const uchar *buf, size_t data_length)
{ return writer->write_data(buf, data_length); }
- bool write_data(const char *buf, ulong data_length)
+ bool write_data(const char *buf, size_t data_length)
{ return write_data((uchar*)buf, data_length); }
bool write_footer()
{ return writer->write_footer(); }
@@ -1382,6 +1411,7 @@ public:
}
#endif
virtual Log_event_type get_type_code() = 0;
+ virtual enum_logged_status logged_status() { return LOGGED_CRITICAL; }
virtual bool is_valid() const = 0;
virtual my_off_t get_header_len(my_off_t len) { return len; }
void set_artificial_event() { flags |= LOG_EVENT_ARTIFICIAL_F; }
@@ -1992,8 +2022,8 @@ protected:
*/
class Query_log_event: public Log_event
{
- LEX_STRING user;
- LEX_STRING host;
+ LEX_CSTRING user;
+ LEX_CSTRING host;
protected:
Log_event::Byte* data_buf;
public:
@@ -2085,15 +2115,15 @@ public:
#ifdef MYSQL_SERVER
- Query_log_event(THD* thd_arg, const char* query_arg, ulong query_length,
+ Query_log_event(THD* thd_arg, const char* query_arg, size_t query_length,
bool using_trans, bool direct, bool suppress_use, int error);
const char* get_db() { return db; }
#ifdef HAVE_REPLICATION
void pack_info(Protocol* protocol);
#endif /* HAVE_REPLICATION */
#else
- void print_query_header(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info);
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
+ bool print_query_header(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info);
+ bool print(FILE* file, PRINT_EVENT_INFO* print_event_info);
#endif
Query_log_event();
@@ -2440,7 +2470,7 @@ protected:
const Format_description_log_event* description_event);
public:
- void print_query(THD *thd, bool need_db, const char *cs, String *buf,
+ bool 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);
my_thread_id thread_id;
@@ -2477,10 +2507,10 @@ public:
bool is_concurrent;
/* fname doesn't point to memory inside Log_event::temp_buf */
- void set_fname_outside_temp_buf(const char *afname, uint alen)
+ void set_fname_outside_temp_buf(const char *afname, size_t alen)
{
fname= afname;
- fname_len= alen;
+ fname_len= (uint)alen;
local_fname= TRUE;
}
/* fname doesn't point to memory inside Log_event::temp_buf */
@@ -2493,7 +2523,7 @@ public:
String field_lens_buf;
String fields_buf;
- Load_log_event(THD* thd, sql_exchange* ex, const char* db_arg,
+ Load_log_event(THD* thd, const sql_exchange* ex, const char* db_arg,
const char* table_name_arg,
List<Item>& fields_arg,
bool is_concurrent_arg,
@@ -2506,8 +2536,8 @@ public:
void pack_info(Protocol* protocol);
#endif /* HAVE_REPLICATION */
#else
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info, bool commented);
+ bool print(FILE* file, PRINT_EVENT_INFO* print_event_info);
+ bool print(FILE* file, PRINT_EVENT_INFO* print_event_info, bool commented);
#endif
/*
@@ -2604,7 +2634,7 @@ public:
#endif /* HAVE_REPLICATION */
#else
Start_log_event_v3() {}
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
+ bool print(FILE* file, PRINT_EVENT_INFO* print_event_info);
#endif
Start_log_event_v3(const char* buf, uint event_len,
@@ -2673,7 +2703,7 @@ public:
write_data(nonce, BINLOG_NONCE_LENGTH);
}
#else
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
+ bool print(FILE* file, PRINT_EVENT_INFO* print_event_info);
#endif
Start_encryption_log_event(
@@ -2861,7 +2891,7 @@ Intvar_log_event(THD* thd_arg,uchar type_arg, ulonglong val_arg,
void pack_info(Protocol* protocol);
#endif /* HAVE_REPLICATION */
#else
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
+ bool print(FILE* file, PRINT_EVENT_INFO* print_event_info);
#endif
Intvar_log_event(const char* buf,
@@ -2942,7 +2972,7 @@ class Rand_log_event: public Log_event
void pack_info(Protocol* protocol);
#endif /* HAVE_REPLICATION */
#else
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
+ bool print(FILE* file, PRINT_EVENT_INFO* print_event_info);
#endif
Rand_log_event(const char* buf,
@@ -2992,7 +3022,7 @@ class Xid_log_event: public Log_event
void pack_info(Protocol* protocol);
#endif /* HAVE_REPLICATION */
#else
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
+ bool print(FILE* file, PRINT_EVENT_INFO* print_event_info);
#endif
Xid_log_event(const char* buf,
@@ -3028,10 +3058,10 @@ public:
UNDEF_F= 0,
UNSIGNED_F= 1
};
- char *name;
- uint name_len;
- char *val;
- ulong val_len;
+ const char *name;
+ size_t name_len;
+ const char *val;
+ size_t val_len;
Item_result type;
uint charset_number;
bool is_null;
@@ -3039,8 +3069,8 @@ public:
#ifdef MYSQL_SERVER
bool deferred;
query_id_t query_id;
- User_var_log_event(THD* thd_arg, char *name_arg, uint name_len_arg,
- char *val_arg, ulong val_len_arg, Item_result type_arg,
+ User_var_log_event(THD* thd_arg, const char *name_arg, size_t name_len_arg,
+ const char *val_arg, size_t val_len_arg, Item_result type_arg,
uint charset_number_arg, uchar flags_arg,
bool using_trans, bool direct)
:Log_event(thd_arg, 0, using_trans),
@@ -3054,7 +3084,7 @@ public:
}
void pack_info(Protocol* protocol);
#else
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
+ bool print(FILE* file, PRINT_EVENT_INFO* print_event_info);
#endif
User_var_log_event(const char* buf, uint event_len,
@@ -3102,7 +3132,7 @@ public:
Stop_log_event() :Log_event()
{}
#else
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
+ bool print(FILE* file, PRINT_EVENT_INFO* print_event_info);
#endif
Stop_log_event(const char* buf,
@@ -3198,7 +3228,7 @@ public:
void pack_info(Protocol* protocol);
#endif /* HAVE_REPLICATION */
#else
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
+ bool print(FILE* file, PRINT_EVENT_INFO* print_event_info);
#endif
Rotate_log_event(const char* buf, uint event_len,
@@ -3238,7 +3268,7 @@ public:
void pack_info(Protocol *protocol);
#endif
#else
- void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
+ bool print(FILE *file, PRINT_EVENT_INFO *print_event_info);
#endif
Binlog_checkpoint_log_event(const char *buf, uint event_len,
const Format_description_log_event *description_event);
@@ -3363,12 +3393,13 @@ public:
virtual enum_skip_reason do_shall_skip(rpl_group_info *rgi);
#endif
#else
- void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
+ bool print(FILE *file, PRINT_EVENT_INFO *print_event_info);
#endif
Gtid_log_event(const char *buf, uint event_len,
const Format_description_log_event *description_event);
~Gtid_log_event() { }
Log_event_type get_type_code() { return GTID_EVENT; }
+ enum_logged_status logged_status() { return LOGGED_NO_DATA; }
int get_data_size()
{
return GTID_HEADER_LEN + ((flags2 & FL_GROUP_COMMIT_ID) ? 2 : 0);
@@ -3445,7 +3476,7 @@ public:
The three elements in the body repeat COUNT times to form the GTID list.
- At the time of writing, only one flag bit is in use.
+ At the time of writing, only two flag bit are in use.
Bit 28 of `count' is used for flag FLAG_UNTIL_REACHED, which is sent in a
Gtid_list event from the master to the slave to indicate that the START
@@ -3463,9 +3494,12 @@ public:
uint64 *sub_id_list;
static const uint element_size= 4+4+8;
- static const uint32 FLAG_UNTIL_REACHED= (1<<28);
- static const uint32 FLAG_IGN_GTIDS= (1<<29);
-
+ /* Upper bits stored in 'count'. See comment above */
+ enum gtid_flags
+ {
+ FLAG_UNTIL_REACHED= (1<<28),
+ FLAG_IGN_GTIDS= (1<<29),
+ };
#ifdef MYSQL_SERVER
Gtid_list_log_event(rpl_binlog_state *gtid_set, uint32 gl_flags);
Gtid_list_log_event(slave_connection_state *gtid_set, uint32 gl_flags);
@@ -3473,7 +3507,7 @@ public:
void pack_info(Protocol *protocol);
#endif
#else
- void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
+ bool print(FILE *file, PRINT_EVENT_INFO *print_event_info);
#endif
Gtid_list_log_event(const char *buf, uint event_len,
const Format_description_log_event *description_event);
@@ -3494,7 +3528,7 @@ public:
virtual int do_apply_event(rpl_group_info *rgi);
enum_skip_reason do_shall_skip(rpl_group_info *rgi);
#endif
- static bool peek(const char *event_start, uint32 event_len,
+ static bool peek(const char *event_start, size_t event_len,
enum enum_binlog_checksum_alg checksum_alg,
rpl_gtid **out_gtid_list, uint32 *out_list_len,
const Format_description_log_event *fdev);
@@ -3537,8 +3571,8 @@ public:
void pack_info(Protocol* protocol);
#endif /* HAVE_REPLICATION */
#else
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info,
+ bool print(FILE* file, PRINT_EVENT_INFO* print_event_info);
+ bool print(FILE* file, PRINT_EVENT_INFO* print_event_info,
bool enable_local);
#endif
@@ -3610,7 +3644,7 @@ public:
virtual int get_create_or_append() const;
#endif /* HAVE_REPLICATION */
#else
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
+ bool print(FILE* file, PRINT_EVENT_INFO* print_event_info);
#endif
Append_block_log_event(const char* buf, uint event_len,
@@ -3650,8 +3684,8 @@ public:
void pack_info(Protocol* protocol);
#endif /* HAVE_REPLICATION */
#else
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info,
+ bool print(FILE* file, PRINT_EVENT_INFO* print_event_info);
+ bool print(FILE* file, PRINT_EVENT_INFO* print_event_info,
bool enable_local);
#endif
@@ -3691,7 +3725,7 @@ public:
void pack_info(Protocol* protocol);
#endif /* HAVE_REPLICATION */
#else
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
+ bool print(FILE* file, PRINT_EVENT_INFO* print_event_info);
#endif
Execute_load_log_event(const char* buf, uint event_len,
@@ -3787,9 +3821,9 @@ public:
void pack_info(Protocol* protocol);
#endif /* HAVE_REPLICATION */
#else
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
+ bool print(FILE* file, PRINT_EVENT_INFO* print_event_info);
/* Prints the query as LOAD DATA LOCAL and with rewritten filename */
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info,
+ bool print(FILE* file, PRINT_EVENT_INFO* print_event_info,
const char *local_fname);
#endif
Execute_load_query_log_event(const char* buf, uint event_len,
@@ -3834,12 +3868,12 @@ public:
/* constructor for hopelessly corrupted events */
Unknown_log_event(): Log_event(), what(ENCRYPTED) {}
~Unknown_log_event() {}
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
+ bool print(FILE* file, PRINT_EVENT_INFO* print_event_info);
Log_event_type get_type_code() { return UNKNOWN_EVENT;}
bool is_valid() const { return 1; }
};
#endif
-char *str_to_hex(char *to, const char *from, uint len);
+char *str_to_hex(char *to, const char *from, size_t len);
/**
@class Annotate_rows_log_event
@@ -3865,6 +3899,7 @@ public:
virtual int get_data_size();
virtual Log_event_type get_type_code();
+ enum_logged_status logged_status() { return LOGGED_NO_DATA; }
virtual bool is_valid() const;
virtual bool is_part_of_group() { return 1; }
@@ -3878,7 +3913,7 @@ public:
#endif
#ifdef MYSQL_CLIENT
- virtual void print(FILE*, PRINT_EVENT_INFO*);
+ virtual bool print(FILE*, PRINT_EVENT_INFO*);
#endif
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
@@ -3894,6 +3929,7 @@ private:
char *m_save_thd_query_txt;
uint m_save_thd_query_len;
bool m_saved_thd_query;
+ bool m_used_query_txt;
};
/**
@@ -4280,6 +4316,7 @@ public:
const char *get_db_name() const { return m_dbnam; }
virtual Log_event_type get_type_code() { return TABLE_MAP_EVENT; }
+ virtual enum_logged_status logged_status() { return LOGGED_TABLE_MAP; }
virtual bool is_valid() const { return m_memory != NULL; /* we check malloc */ }
virtual bool is_part_of_group() { return 1; }
@@ -4296,7 +4333,7 @@ public:
#endif
#ifdef MYSQL_CLIENT
- virtual void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
+ virtual bool print(FILE *file, PRINT_EVENT_INFO *print_event_info);
#endif
@@ -4408,6 +4445,7 @@ public:
void update_flags() { int2store(temp_buf + m_flags_pos, m_flags); }
Log_event_type get_type_code() { return m_type; } /* Specific type (_V1 etc) */
+ enum_logged_status logged_status() { return LOGGED_ROW_EVENT; }
virtual Log_event_type get_general_type_code() = 0; /* General rows op type, no version */
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
@@ -4416,15 +4454,21 @@ public:
#ifdef MYSQL_CLIENT
/* not for direct call, each derived has its own ::print() */
- virtual void print(FILE *file, PRINT_EVENT_INFO *print_event_info)= 0;
+ virtual bool print(FILE *file, PRINT_EVENT_INFO *print_event_info)= 0;
void change_to_flashback_event(PRINT_EVENT_INFO *print_event_info, uchar *rows_buff, Log_event_type ev_type);
- void print_verbose(IO_CACHE *file,
+ bool print_verbose(IO_CACHE *file,
PRINT_EVENT_INFO *print_event_info);
size_t print_verbose_one_row(IO_CACHE *file, table_def *td,
PRINT_EVENT_INFO *print_event_info,
MY_BITMAP *cols_bitmap,
const uchar *ptr, const uchar *prefix,
const my_bool no_fill_output= 0); // if no_fill_output=1, then print result is unnecessary
+ size_t calc_row_event_length(table_def *td,
+ PRINT_EVENT_INFO *print_event_info,
+ MY_BITMAP *cols_bitmap,
+ const uchar *value);
+ void count_row_events(PRINT_EVENT_INFO *print_event_info);
+
#endif
#ifdef MYSQL_SERVER
@@ -4531,7 +4575,7 @@ protected:
void uncompress_buf();
#ifdef MYSQL_CLIENT
- void print_helper(FILE *, PRINT_EVENT_INFO *, char const *const name);
+ bool print_helper(FILE *, PRINT_EVENT_INFO *, char const *const name);
#endif
#ifdef MYSQL_SERVER
@@ -4573,6 +4617,8 @@ protected:
uchar *m_extra_row_data; /* Pointer to extra row data if any */
/* If non null, first byte is length */
+ bool m_vers_from_plain;
+
/* helper functions */
@@ -4587,6 +4633,7 @@ protected:
int find_key(); // Find a best key to use in find_row()
int find_row(rpl_group_info *);
int write_row(rpl_group_info *, const bool);
+ int update_sequence();
// Unpack the current row into m_table->record[0], but with
// a different columns bitmap.
@@ -4722,6 +4769,7 @@ public:
__attribute__((unused)),
const uchar *after_record)
{
+ DBUG_ASSERT(!table->versioned(VERS_TRX_ID));
return thd->binlog_write_row(table, is_transactional, after_record);
}
#endif
@@ -4734,7 +4782,7 @@ private:
virtual Log_event_type get_general_type_code() { return (Log_event_type)TYPE_CODE; }
#ifdef MYSQL_CLIENT
- void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
+ bool print(FILE *file, PRINT_EVENT_INFO *print_event_info);
#endif
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
@@ -4758,7 +4806,7 @@ public:
#endif
private:
#if defined(MYSQL_CLIENT)
- void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
+ bool print(FILE *file, PRINT_EVENT_INFO *print_event_info);
#endif
};
@@ -4803,6 +4851,7 @@ public:
const uchar *before_record,
const uchar *after_record)
{
+ DBUG_ASSERT(!table->versioned(VERS_TRX_ID));
return thd->binlog_update_row(table, is_transactional,
before_record, after_record);
}
@@ -4821,7 +4870,7 @@ protected:
virtual Log_event_type get_general_type_code() { return (Log_event_type)TYPE_CODE; }
#ifdef MYSQL_CLIENT
- void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
+ bool print(FILE *file, PRINT_EVENT_INFO *print_event_info);
#endif
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
@@ -4845,7 +4894,7 @@ public:
#endif
private:
#if defined(MYSQL_CLIENT)
- void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
+ bool print(FILE *file, PRINT_EVENT_INFO *print_event_info);
#endif
};
@@ -4892,6 +4941,7 @@ public:
const uchar *after_record
__attribute__((unused)))
{
+ DBUG_ASSERT(!table->versioned(VERS_TRX_ID));
return thd->binlog_delete_row(table, is_transactional,
before_record);
}
@@ -4905,7 +4955,7 @@ protected:
virtual Log_event_type get_general_type_code() { return (Log_event_type)TYPE_CODE; }
#ifdef MYSQL_CLIENT
- void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
+ bool print(FILE *file, PRINT_EVENT_INFO *print_event_info);
#endif
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
@@ -4928,7 +4978,7 @@ public:
#endif
private:
#if defined(MYSQL_CLIENT)
- void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
+ bool print(FILE *file, PRINT_EVENT_INFO *print_event_info);
#endif
};
@@ -4987,21 +5037,21 @@ public:
DBUG_VOID_RETURN;
}
- Incident_log_event(THD *thd_arg, Incident incident, LEX_STRING const msg)
+ Incident_log_event(THD *thd_arg, Incident incident, const LEX_CSTRING *msg)
: Log_event(thd_arg, 0, FALSE), m_incident(incident)
{
DBUG_ENTER("Incident_log_event::Incident_log_event");
DBUG_PRINT("enter", ("m_incident: %d", m_incident));
- m_message.str= NULL;
m_message.length= 0;
- if (!(m_message.str= (char*) my_malloc(msg.length+1, MYF(MY_WME))))
+ if (unlikely(!(m_message.str= (char*) my_malloc(msg->length+1,
+ MYF(MY_WME)))))
{
/* Mark this event invalid */
m_incident= INCIDENT_NONE;
DBUG_VOID_RETURN;
}
- strmake(m_message.str, msg.str, msg.length);
- m_message.length= msg.length;
+ strmake(m_message.str, msg->str, msg->length);
+ m_message.length= msg->length;
set_direct_logging();
/* Replicate the incident regardless of @@skip_replication. */
flags&= ~LOG_EVENT_SKIP_REPLICATION_F;
@@ -5022,7 +5072,7 @@ public:
virtual ~Incident_log_event();
#ifdef MYSQL_CLIENT
- virtual void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
+ virtual bool print(FILE *file, PRINT_EVENT_INFO *print_event_info);
#endif
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
@@ -5089,7 +5139,7 @@ public:
#endif
#ifdef MYSQL_CLIENT
- virtual void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
+ virtual bool print(FILE *file, PRINT_EVENT_INFO *print_event_info);
#endif
virtual Log_event_type get_type_code() { return IGNORABLE_LOG_EVENT; }
@@ -5100,45 +5150,18 @@ public:
};
#ifdef MYSQL_CLIENT
-void copy_cache_to_string_wrapped(IO_CACHE *body,
+bool copy_cache_to_string_wrapped(IO_CACHE *body,
LEX_STRING *to,
bool do_wrap,
const char *delimiter,
bool is_verbose);
+bool copy_cache_to_file_wrapped(IO_CACHE *body,
+ FILE *file,
+ bool do_wrap,
+ const char *delimiter,
+ bool is_verbose);
#endif
-static inline bool copy_event_cache_to_string_and_reinit(IO_CACHE *cache, LEX_STRING *to)
-{
- String tmp;
-
- reinit_io_cache(cache, READ_CACHE, 0L, FALSE, FALSE);
- if (tmp.append(cache, (uint32)cache->end_of_file))
- goto err;
- reinit_io_cache(cache, WRITE_CACHE, 0, FALSE, TRUE);
-
- /*
- Can't change the order, because the String::release() will clear the
- length.
- */
- to->length= tmp.length();
- to->str= tmp.release();
-
- return false;
-
-err:
- perror("Out of memory: can't allocate memory in copy_event_cache_to_string_and_reinit().");
- return true;
-}
-
-static inline 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);
-}
-
-
#ifdef MYSQL_SERVER
/*****************************************************************************
@@ -5179,6 +5202,7 @@ inline int Log_event_writer::write(Log_event *ev)
ev->writer= this;
int res= ev->write();
IF_DBUG(ev->writer= 0,); // writer must be set before every Log_event::write
+ add_status(ev->logged_status());
return res;
}
@@ -5191,10 +5215,6 @@ inline int Log_event_writer::write(Log_event *ev)
bool slave_execute_deferred_events(THD *thd);
#endif
-bool rpl_get_position_info(const char **log_file_name, ulonglong *log_pos,
- const char **group_relay_log_name,
- ulonglong *relay_log_pos);
-
bool event_that_should_be_ignored(const char *buf);
bool event_checksum_test(uchar *buf, ulong event_len, enum_binlog_checksum_alg alg);
enum enum_binlog_checksum_alg get_checksum_alg(const char* buf, ulong len);
diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc
index b655d510bd5..8ec823d3d64 100644
--- a/sql/log_event_old.cc
+++ b/sql/log_event_old.cc
@@ -14,7 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#ifndef MYSQL_CLIENT
#include "unireg.h"
@@ -99,7 +99,7 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, rpl_group_info *rgi)
*/
ev_thd->lex->set_stmt_row_injection();
- if (open_and_lock_tables(ev_thd, rgi->tables_to_lock, FALSE, 0))
+ if (unlikely(open_and_lock_tables(ev_thd, rgi->tables_to_lock, FALSE, 0)))
{
if (ev_thd->is_error())
{
@@ -227,7 +227,8 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, rpl_group_info *rgi)
while (error == 0 && row_start < ev->m_rows_end)
{
uchar const *row_end= NULL;
- if ((error= do_prepare_row(ev_thd, rgi, table, row_start, &row_end)))
+ if (unlikely((error= do_prepare_row(ev_thd, rgi, table, row_start,
+ &row_end))))
break; // We should perform the after-row operation even in
// the case of error
@@ -266,7 +267,7 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, rpl_group_info *rgi)
error= do_after_row_operations(table, error);
}
- if (error)
+ if (unlikely(error))
{ /* error has occurred during the transaction */
rli->report(ERROR_LEVEL, ev_thd->get_stmt_da()->sql_errno(), NULL,
"Error in %s event: error during transaction execution "
@@ -477,14 +478,14 @@ replace_record(THD *thd, TABLE *table,
DBUG_PRINT_BITSET("debug", "read_set = %s", table->read_set);
#endif
- while ((error= table->file->ha_write_row(table->record[0])))
+ while (unlikely(error= table->file->ha_write_row(table->record[0])))
{
if (error == HA_ERR_LOCK_DEADLOCK || error == HA_ERR_LOCK_WAIT_TIMEOUT)
{
table->file->print_error(error, MYF(0)); /* to check at exec_relay_log_event */
DBUG_RETURN(error);
}
- if ((keynum= table->file->get_dup_key(error)) < 0)
+ if (unlikely((keynum= table->file->get_dup_key(error)) < 0))
{
table->file->print_error(error, MYF(0));
/*
@@ -508,18 +509,16 @@ replace_record(THD *thd, TABLE *table,
if (table->file->ha_table_flags() & HA_DUPLICATE_POS)
{
error= table->file->ha_rnd_pos(table->record[1], table->file->dup_ref);
- if (error)
+ if (unlikely(error))
{
DBUG_PRINT("info",("rnd_pos() returns error %d",error));
- if (error == HA_ERR_RECORD_DELETED)
- error= HA_ERR_KEY_NOT_FOUND;
table->file->print_error(error, MYF(0));
DBUG_RETURN(error);
}
}
else
{
- if (table->file->extra(HA_EXTRA_FLUSH_CACHE))
+ if (unlikely(table->file->extra(HA_EXTRA_FLUSH_CACHE)))
{
DBUG_RETURN(my_errno);
}
@@ -527,7 +526,7 @@ replace_record(THD *thd, TABLE *table,
if (key.get() == NULL)
{
key.assign(static_cast<char*>(my_alloca(table->s->max_unique_length)));
- if (key.get() == NULL)
+ if (unlikely(key.get() == NULL))
DBUG_RETURN(ENOMEM);
}
@@ -537,11 +536,9 @@ replace_record(THD *thd, TABLE *table,
(const uchar*)key.get(),
HA_WHOLE_KEY,
HA_READ_KEY_EXACT);
- if (error)
+ if (unlikely(error))
{
DBUG_PRINT("info", ("index_read_idx() returns error %d", error));
- if (error == HA_ERR_RECORD_DELETED)
- error= HA_ERR_KEY_NOT_FOUND;
table->file->print_error(error, MYF(0));
DBUG_RETURN(error);
}
@@ -577,7 +574,7 @@ replace_record(THD *thd, TABLE *table,
{
error=table->file->ha_update_row(table->record[1],
table->record[0]);
- if (error && error != HA_ERR_RECORD_IS_THE_SAME)
+ if (unlikely(error) && error != HA_ERR_RECORD_IS_THE_SAME)
table->file->print_error(error, MYF(0));
else
error= 0;
@@ -585,7 +582,7 @@ replace_record(THD *thd, TABLE *table,
}
else
{
- if ((error= table->file->ha_delete_row(table->record[1])))
+ if (unlikely((error= table->file->ha_delete_row(table->record[1]))))
{
table->file->print_error(error, MYF(0));
DBUG_RETURN(error);
@@ -671,7 +668,8 @@ static int find_and_fetch_row(TABLE *table, uchar *key)
{
int error;
/* We have a key: search the table using the index */
- if (!table->file->inited && (error= table->file->ha_index_init(0, FALSE)))
+ if (!table->file->inited &&
+ unlikely(error= table->file->ha_index_init(0, FALSE)))
{
table->file->print_error(error, MYF(0));
DBUG_RETURN(error);
@@ -695,9 +693,9 @@ static int find_and_fetch_row(TABLE *table, uchar *key)
my_ptrdiff_t const pos=
table->s->null_bytes > 0 ? table->s->null_bytes - 1 : 0;
table->record[1][pos]= 0xFF;
- if ((error= table->file->ha_index_read_map(table->record[1], key,
- HA_WHOLE_KEY,
- HA_READ_KEY_EXACT)))
+ if (unlikely((error= table->file->ha_index_read_map(table->record[1], key,
+ HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT))))
{
table->file->print_error(error, MYF(0));
table->file->ha_index_end();
@@ -738,9 +736,6 @@ static int find_and_fetch_row(TABLE *table, uchar *key)
while ((error= table->file->ha_index_next(table->record[1])))
{
- /* We just skip records that has already been deleted */
- if (error == HA_ERR_RECORD_DELETED)
- continue;
table->file->print_error(error, MYF(0));
table->file->ha_index_end();
DBUG_RETURN(error);
@@ -758,13 +753,12 @@ static int find_and_fetch_row(TABLE *table, uchar *key)
int error;
/* We don't have a key: search the table using rnd_next() */
- if ((error= table->file->ha_rnd_init_with_error(1)))
+ if (unlikely((error= table->file->ha_rnd_init_with_error(1))))
return error;
/* Continue until we find the right record or have made a full loop */
do
{
- restart_rnd_next:
error= table->file->ha_rnd_next(table->record[1]);
DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
@@ -774,18 +768,11 @@ static int find_and_fetch_row(TABLE *table, uchar *key)
case 0:
break;
- /*
- If the record was deleted, we pick the next one without doing
- any comparisons.
- */
- case HA_ERR_RECORD_DELETED:
- goto restart_rnd_next;
-
case HA_ERR_END_OF_FILE:
if (++restart_count < 2)
{
int error2;
- if ((error2= table->file->ha_rnd_init_with_error(1)))
+ if (unlikely((error2= table->file->ha_rnd_init_with_error(1))))
DBUG_RETURN(error2);
}
break;
@@ -853,7 +840,7 @@ int Write_rows_log_event_old::do_after_row_operations(TABLE *table, int error)
fires bug#27077
todo: explain or fix
*/
- if ((local_error= table->file->ha_end_bulk_insert()))
+ if (unlikely((local_error= table->file->ha_end_bulk_insert())))
{
table->file->print_error(local_error, MYF(0));
}
@@ -985,7 +972,7 @@ int Delete_rows_log_event_old::do_exec_row(TABLE *table)
int error;
DBUG_ASSERT(table != NULL);
- if (!(error= ::find_and_fetch_row(table, m_key)))
+ if (likely(!(error= ::find_and_fetch_row(table, m_key))))
{
/*
Now we should have the right row to delete. We are using
@@ -1094,7 +1081,7 @@ int Update_rows_log_event_old::do_exec_row(TABLE *table)
DBUG_ASSERT(table != NULL);
int error= ::find_and_fetch_row(table, m_key);
- if (error)
+ if (unlikely(error))
return error;
/*
@@ -1120,7 +1107,7 @@ int Update_rows_log_event_old::do_exec_row(TABLE *table)
database into the after image delivered from the master.
*/
error= table->file->ha_update_row(table->record[1], table->record[0]);
- if (error == HA_ERR_RECORD_IS_THE_SAME)
+ if (unlikely(error == HA_ERR_RECORD_IS_THE_SAME))
error= 0;
return error;
@@ -1416,8 +1403,8 @@ int Old_rows_log_event::do_apply_event(rpl_group_info *rgi)
*/
lex_start(thd);
- if ((error= lock_tables(thd, rgi->tables_to_lock,
- rgi->tables_to_lock_count, 0)))
+ if (unlikely((error= lock_tables(thd, rgi->tables_to_lock,
+ rgi->tables_to_lock_count, 0))))
{
if (thd->is_slave_error || thd->is_fatal_error)
{
@@ -1609,7 +1596,7 @@ int Old_rows_log_event::do_apply_event(rpl_group_info *rgi)
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 && !error)
+ if (!m_curr_row_end && likely(!error))
unpack_current_row(rgi);
// at this moment m_curr_row_end should be set
@@ -1626,7 +1613,7 @@ int Old_rows_log_event::do_apply_event(rpl_group_info *rgi)
error= do_after_row_operations(rli, error);
} // if (table)
- if (error)
+ if (unlikely(error))
{ /* error has occurred during the transaction */
rli->report(ERROR_LEVEL, thd->net.last_errno, NULL,
"Error in %s event: error during transaction execution "
@@ -1710,7 +1697,9 @@ int Old_rows_log_event::do_apply_event(rpl_group_info *rgi)
already. So there should be no need to rollback the transaction.
*/
DBUG_ASSERT(! thd->transaction_rollback_request);
- if ((error= (binlog_error ? trans_rollback_stmt(thd) : trans_commit_stmt(thd))))
+ if (unlikely((error= (binlog_error ?
+ trans_rollback_stmt(thd) :
+ trans_commit_stmt(thd)))))
rli->report(ERROR_LEVEL, error, NULL,
"Error in %s event: commit of row events failed, "
"table `%s`.`%s`",
@@ -1853,7 +1842,7 @@ void Old_rows_log_event::pack_info(Protocol *protocol)
#ifdef MYSQL_CLIENT
/* Method duplicates Rows_log_event's one */
-void Old_rows_log_event::print_helper(FILE *file,
+bool Old_rows_log_event::print_helper(FILE *file,
PRINT_EVENT_INFO *print_event_info,
char const *const name)
{
@@ -1861,36 +1850,30 @@ void Old_rows_log_event::print_helper(FILE *file,
IO_CACHE *const body= &print_event_info->body_cache;
bool do_print_encoded=
print_event_info->base64_output_mode != BASE64_OUTPUT_DECODE_ROWS &&
+ print_event_info->base64_output_mode != BASE64_OUTPUT_NEVER &&
!print_event_info->short_form;
if (!print_event_info->short_form)
{
- bool const last_stmt_event= get_flags(STMT_END_F);
- print_header(head, print_event_info, !last_stmt_event);
- my_b_printf(head, "\t%s: table id %lu%s\n",
- name, m_table_id,
- last_stmt_event ? " flags: STMT_END_F" : "");
- print_base64(body, print_event_info, do_print_encoded);
+ if (print_header(head, print_event_info, !do_print_encoded) ||
+ my_b_printf(head, "\t%s: table id %lu%s\n",
+ name, m_table_id,
+ do_print_encoded ? " flags: STMT_END_F" : "") ||
+ print_base64(body, print_event_info, do_print_encoded))
+ goto err;
}
if (get_flags(STMT_END_F))
{
- LEX_STRING tmp_str;
-
- if (copy_event_cache_to_string_and_reinit(head, &tmp_str))
- {
- head->error= -1;
- return;
- }
- output_buf.append(&tmp_str);
- my_free(tmp_str.str);
-
- copy_cache_to_string_wrapped(body, &tmp_str, do_print_encoded,
- print_event_info->delimiter,
- print_event_info->verbose);
- output_buf.append(&tmp_str);
- my_free(tmp_str.str);
+ 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))
+ goto err;
}
+ return 0;
+err:
+ return 1;
}
#endif
@@ -1944,8 +1927,9 @@ Old_rows_log_event::write_row(rpl_group_info *rgi, const bool overwrite)
/* fill table->record[0] with default values */
- if ((error= prepare_record(table, m_width,
- TRUE /* check if columns have def. values */)))
+ if (unlikely((error=
+ prepare_record(table, m_width,
+ TRUE /* check if columns have def. values */))))
DBUG_RETURN(error);
/* unpack row into table->record[0] */
@@ -1966,14 +1950,14 @@ Old_rows_log_event::write_row(rpl_group_info *rgi, const bool overwrite)
TODO: Add safety measures against infinite looping.
*/
- while ((error= table->file->ha_write_row(table->record[0])))
+ while (unlikely(error= table->file->ha_write_row(table->record[0])))
{
if (error == HA_ERR_LOCK_DEADLOCK || error == HA_ERR_LOCK_WAIT_TIMEOUT)
{
table->file->print_error(error, MYF(0)); /* to check at exec_relay_log_event */
DBUG_RETURN(error);
}
- if ((keynum= table->file->get_dup_key(error)) < 0)
+ if (unlikely((keynum= table->file->get_dup_key(error)) < 0))
{
DBUG_PRINT("info",("Can't locate duplicate key (get_dup_key returns %d)",keynum));
table->file->print_error(error, MYF(0));
@@ -1999,11 +1983,9 @@ Old_rows_log_event::write_row(rpl_group_info *rgi, const bool overwrite)
{
DBUG_PRINT("info",("Locating offending record using rnd_pos()"));
error= table->file->ha_rnd_pos(table->record[1], table->file->dup_ref);
- if (error)
+ if (unlikely(error))
{
DBUG_PRINT("info",("rnd_pos() returns error %d",error));
- if (error == HA_ERR_RECORD_DELETED)
- error= HA_ERR_KEY_NOT_FOUND;
table->file->print_error(error, MYF(0));
DBUG_RETURN(error);
}
@@ -2021,7 +2003,7 @@ Old_rows_log_event::write_row(rpl_group_info *rgi, const bool overwrite)
if (key.get() == NULL)
{
key.assign(static_cast<char*>(my_alloca(table->s->max_unique_length)));
- if (key.get() == NULL)
+ if (unlikely(key.get() == NULL))
{
DBUG_PRINT("info",("Can't allocate key buffer"));
DBUG_RETURN(ENOMEM);
@@ -2034,11 +2016,9 @@ Old_rows_log_event::write_row(rpl_group_info *rgi, const bool overwrite)
(const uchar*)key.get(),
HA_WHOLE_KEY,
HA_READ_KEY_EXACT);
- if (error)
+ if (unlikely(error))
{
DBUG_PRINT("info",("index_read_idx() returns error %d", error));
- if (error == HA_ERR_RECORD_DELETED)
- error= HA_ERR_KEY_NOT_FOUND;
table->file->print_error(error, MYF(0));
DBUG_RETURN(error);
}
@@ -2107,7 +2087,7 @@ Old_rows_log_event::write_row(rpl_group_info *rgi, const bool overwrite)
else
{
DBUG_PRINT("info",("Deleting offending row and trying to write new one again"));
- if ((error= table->file->ha_delete_row(table->record[1])))
+ if (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));
@@ -2195,11 +2175,9 @@ int Old_rows_log_event::find_row(rpl_group_info *rgi)
*/
DBUG_PRINT("info",("locating record using primary key (position)"));
int error= table->file->ha_rnd_pos_by_record(table->record[0]);
- if (error)
+ if (unlikely(error))
{
DBUG_PRINT("info",("rnd_pos returns error %d",error));
- if (error == HA_ERR_RECORD_DELETED)
- error= HA_ERR_KEY_NOT_FOUND;
table->file->print_error(error, MYF(0));
}
DBUG_RETURN(error);
@@ -2224,7 +2202,8 @@ int Old_rows_log_event::find_row(rpl_group_info *rgi)
DBUG_PRINT("info",("locating record using primary key (index_read)"));
/* We have a key: search the table using the index */
- if (!table->file->inited && (error= table->file->ha_index_init(0, FALSE)))
+ if (!table->file->inited &&
+ unlikely(error= table->file->ha_index_init(0, FALSE)))
{
DBUG_PRINT("info",("ha_index_init returns error %d",error));
table->file->print_error(error, MYF(0));
@@ -2254,13 +2233,12 @@ int Old_rows_log_event::find_row(rpl_group_info *rgi)
table->s->null_bytes > 0 ? table->s->null_bytes - 1 : 0;
table->record[0][pos]= 0xFF;
- if ((error= table->file->ha_index_read_map(table->record[0], m_key,
- HA_WHOLE_KEY,
- HA_READ_KEY_EXACT)))
+ 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_RECORD_DELETED)
- error= HA_ERR_KEY_NOT_FOUND;
table->file->print_error(error, MYF(0));
table->file->ha_index_end();
DBUG_RETURN(error);
@@ -2328,11 +2306,8 @@ int Old_rows_log_event::find_row(rpl_group_info *rgi)
while (record_compare(table))
{
- while ((error= table->file->ha_index_next(table->record[0])))
+ while (unlikely(error= table->file->ha_index_next(table->record[0])))
{
- /* We just skip records that has already been deleted */
- if (error == HA_ERR_RECORD_DELETED)
- continue;
DBUG_PRINT("info",("no record matching the given row found"));
table->file->print_error(error, MYF(0));
(void) table->file->ha_index_end();
@@ -2347,7 +2322,7 @@ int Old_rows_log_event::find_row(rpl_group_info *rgi)
int restart_count= 0; // Number of times scanning has restarted from top
/* We don't have a key: search the table using rnd_next() */
- if ((error= table->file->ha_rnd_init_with_error(1)))
+ if (unlikely((error= table->file->ha_rnd_init_with_error(1))))
{
DBUG_PRINT("info",("error initializing table scan"
" (ha_rnd_init returns %d)",error));
@@ -2365,15 +2340,12 @@ int Old_rows_log_event::find_row(rpl_group_info *rgi)
case 0:
break;
- case HA_ERR_RECORD_DELETED:
- goto restart_rnd_next;
-
case HA_ERR_END_OF_FILE:
if (++restart_count < 2)
{
int error2;
table->file->ha_rnd_end();
- if ((error2= table->file->ha_rnd_init_with_error(1)))
+ if (unlikely((error2= table->file->ha_rnd_init_with_error(1))))
DBUG_RETURN(error2);
goto restart_rnd_next;
}
@@ -2487,12 +2459,12 @@ Write_rows_log_event_old::do_after_row_operations(const Slave_reporting_capabili
m_table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
m_table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
/*
- reseting the extra with
+ resetting the extra with
table->file->extra(HA_EXTRA_NO_IGNORE_NO_KEY);
fires bug#27077
todo: explain or fix
*/
- if ((local_error= m_table->file->ha_end_bulk_insert()))
+ if (unlikely((local_error= m_table->file->ha_end_bulk_insert())))
{
m_table->file->print_error(local_error, MYF(0));
}
@@ -2506,7 +2478,7 @@ Write_rows_log_event_old::do_exec_row(rpl_group_info *rgi)
DBUG_ASSERT(m_table != NULL);
int error= write_row(rgi, TRUE /* overwrite */);
- if (error && !thd->net.last_errno)
+ if (unlikely(error) && !thd->net.last_errno)
thd->net.last_errno= error;
return error;
@@ -2516,10 +2488,11 @@ Write_rows_log_event_old::do_exec_row(rpl_group_info *rgi)
#ifdef MYSQL_CLIENT
-void Write_rows_log_event_old::print(FILE *file,
+bool Write_rows_log_event_old::print(FILE *file,
PRINT_EVENT_INFO* print_event_info)
{
- Old_rows_log_event::print_helper(file, print_event_info, "Write_rows_old");
+ return Old_rows_log_event::print_helper(file, print_event_info,
+ "Write_rows_old");
}
#endif
@@ -2608,7 +2581,7 @@ int Delete_rows_log_event_old::do_exec_row(rpl_group_info *rgi)
int error;
DBUG_ASSERT(m_table != NULL);
- if (!(error= find_row(rgi)))
+ if (likely(!(error= find_row(rgi))) )
{
/*
Delete the record found, located in record[0]
@@ -2623,10 +2596,11 @@ int Delete_rows_log_event_old::do_exec_row(rpl_group_info *rgi)
#ifdef MYSQL_CLIENT
-void Delete_rows_log_event_old::print(FILE *file,
+bool Delete_rows_log_event_old::print(FILE *file,
PRINT_EVENT_INFO* print_event_info)
{
- Old_rows_log_event::print_helper(file, print_event_info, "Delete_rows_old");
+ return Old_rows_log_event::print_helper(file, print_event_info,
+ "Delete_rows_old");
}
#endif
@@ -2707,7 +2681,7 @@ Update_rows_log_event_old::do_exec_row(rpl_group_info *rgi)
DBUG_ASSERT(m_table != NULL);
int error= find_row(rgi);
- if (error)
+ if (unlikely(error))
{
/*
We need to read the second image in the event of error to be
@@ -2751,7 +2725,7 @@ Update_rows_log_event_old::do_exec_row(rpl_group_info *rgi)
error= m_table->file->ha_update_row(m_table->record[1], m_table->record[0]);
m_table->file->ha_index_or_rnd_end();
- if (error == HA_ERR_RECORD_IS_THE_SAME)
+ if (unlikely(error == HA_ERR_RECORD_IS_THE_SAME))
error= 0;
return error;
@@ -2761,9 +2735,10 @@ Update_rows_log_event_old::do_exec_row(rpl_group_info *rgi)
#ifdef MYSQL_CLIENT
-void Update_rows_log_event_old::print(FILE *file,
+bool Update_rows_log_event_old::print(FILE *file,
PRINT_EVENT_INFO* print_event_info)
{
- Old_rows_log_event::print_helper(file, print_event_info, "Update_rows_old");
+ return Old_rows_log_event::print_helper(file, print_event_info,
+ "Update_rows_old");
}
#endif
diff --git a/sql/log_event_old.h b/sql/log_event_old.h
index cccb0674e82..3a11313a31f 100644
--- a/sql/log_event_old.h
+++ b/sql/log_event_old.h
@@ -116,7 +116,7 @@ public:
#ifdef MYSQL_CLIENT
/* not for direct call, each derived has its own ::print() */
- virtual void print(FILE *file, PRINT_EVENT_INFO *print_event_info)= 0;
+ virtual bool print(FILE *file, PRINT_EVENT_INFO *print_event_info)= 0;
#endif
#ifndef MYSQL_CLIENT
@@ -166,7 +166,7 @@ protected:
const Format_description_log_event *description_event);
#ifdef MYSQL_CLIENT
- void print_helper(FILE *, PRINT_EVENT_INFO *, char const *const name);
+ bool print_helper(FILE *, PRINT_EVENT_INFO *, char const *const name);
#endif
#ifndef MYSQL_CLIENT
@@ -379,7 +379,7 @@ public:
private:
#ifdef MYSQL_CLIENT
- void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
+ bool print(FILE *file, PRINT_EVENT_INFO *print_event_info);
#endif
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
@@ -455,7 +455,7 @@ public:
protected:
#ifdef MYSQL_CLIENT
- void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
+ bool print(FILE *file, PRINT_EVENT_INFO *print_event_info);
#endif
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
@@ -529,7 +529,7 @@ public:
protected:
#ifdef MYSQL_CLIENT
- void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
+ bool print(FILE *file, PRINT_EVENT_INFO *print_event_info);
#endif
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
diff --git a/sql/log_slow.h b/sql/log_slow.h
index 349e366974d..c6b3407a811 100644
--- a/sql/log_slow.h
+++ b/sql/log_slow.h
@@ -15,6 +15,9 @@
/* Defining what to log to slow log */
+#ifndef LOG_SLOW_INCLUDED
+#define LOG_SLOW_INCLUDED
+
#define LOG_SLOW_VERBOSITY_INIT 0
#define LOG_SLOW_VERBOSITY_INNODB (1U << 0)
#define LOG_SLOW_VERBOSITY_QUERY_PLAN (1U << 1)
@@ -25,14 +28,27 @@
#define QPLAN_ADMIN (1U << 0)
#define QPLAN_FILESORT (1U << 1)
#define QPLAN_FILESORT_DISK (1U << 2)
-#define QPLAN_FULL_JOIN (1U << 3)
-#define QPLAN_FULL_SCAN (1U << 4)
-#define QPLAN_QC (1U << 5)
-#define QPLAN_QC_NO (1U << 6)
-#define QPLAN_TMP_DISK (1U << 7)
-#define QPLAN_TMP_TABLE (1U << 8)
-#define QPLAN_FILESORT_PRIORITY_QUEUE (1U << 9)
+#define QPLAN_FILESORT_PRIORITY_QUEUE (1U << 3)
+#define QPLAN_FULL_JOIN (1U << 4)
+#define QPLAN_FULL_SCAN (1U << 5)
+#define QPLAN_NOT_USING_INDEX (1U << 6)
+#define QPLAN_QC (1U << 7)
+#define QPLAN_QC_NO (1U << 8)
+#define QPLAN_TMP_TABLE (1U << 9)
+#define QPLAN_TMP_DISK (1U << 10)
/* ... */
#define QPLAN_STATUS (1UL << 31) /* not in the slow_log_filter */
#define QPLAN_MAX (1UL << 31) /* reserved as placeholder */
+
+/* Bits for log_slow_disabled_statements */
+#define LOG_SLOW_DISABLE_ADMIN (1 << 0)
+#define LOG_SLOW_DISABLE_CALL (1 << 1)
+#define LOG_SLOW_DISABLE_SLAVE (1 << 2)
+#define LOG_SLOW_DISABLE_SP (1 << 3)
+
+/* Bits for log_disabled_statements */
+#define LOG_DISABLE_SLAVE (1 << 0)
+#define LOG_DISABLE_SP (1 << 1)
+
+#endif /* LOG_SLOW_INCLUDED */
diff --git a/sql/mariadb.h b/sql/mariadb.h
new file mode 100644
index 00000000000..00cf2ed1d9c
--- /dev/null
+++ b/sql/mariadb.h
@@ -0,0 +1,30 @@
+/* Copyright (c) 2010, 2017, 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-1301 USA */
+
+/*
+ Include file that should always be included first in all file in the sql
+ directory. Used to ensure that some files, like my_global.h and my_config.h
+ are always included first.
+ It can also be used to speed up compilation by using precompiled headers.
+
+ This file should include a minum set of header files used by all files
+ and header files that are very seldom changed.
+ It can also include some defines that all files should be aware of.
+*/
+
+#ifndef MARIADB_INCLUDED
+#define MARIADB_INCLUDED
+#include <my_global.h>
+#endif /* MARIADB_INCLUDED */
diff --git a/sql/mdl.cc b/sql/mdl.cc
index 9eeb82eeffd..8d6780671d1 100644
--- a/sql/mdl.cc
+++ b/sql/mdl.cc
@@ -14,6 +14,7 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
+#include "mariadb.h"
#include "sql_class.h"
#include "debug_sync.h"
#include "sql_array.h"
@@ -84,6 +85,7 @@ PSI_stage_info MDL_key::m_namespace_to_wait_state_name[NAMESPACE_END]=
{0, "Waiting for table metadata lock", 0},
{0, "Waiting for stored function metadata lock", 0},
{0, "Waiting for stored procedure metadata lock", 0},
+ {0, "Waiting for stored package body metadata lock", 0},
{0, "Waiting for trigger metadata lock", 0},
{0, "Waiting for event metadata lock", 0},
{0, "Waiting for commit lock", 0},
@@ -2086,6 +2088,14 @@ MDL_context::acquire_lock(MDL_request *mdl_request, double lock_wait_timeout)
*/
lock= ticket->m_lock;
+ if (lock_wait_timeout == 0)
+ {
+ mysql_prlock_unlock(&lock->m_rwlock);
+ MDL_ticket::destroy(ticket);
+ my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+
lock->m_waiting.add_ticket(ticket);
/*
@@ -2110,11 +2120,11 @@ MDL_context::acquire_lock(MDL_request *mdl_request, double lock_wait_timeout)
the parallel replication scheduler should never schedule a DDL while
DML's are still running.
*/
- DBUG_ASSERT((mdl_request->type != MDL_INTENTION_EXCLUSIVE &&
- mdl_request->type != MDL_EXCLUSIVE) ||
- !(get_thd()->rgi_slave &&
- get_thd()->rgi_slave->is_parallel_exec &&
- lock->check_if_conflicting_replication_locks(this)));
+ DBUG_SLOW_ASSERT((mdl_request->type != MDL_INTENTION_EXCLUSIVE &&
+ mdl_request->type != MDL_EXCLUSIVE) ||
+ !(get_thd()->rgi_slave &&
+ get_thd()->rgi_slave->is_parallel_exec &&
+ lock->check_if_conflicting_replication_locks(this)));
mysql_prlock_unlock(&lock->m_rwlock);
@@ -2320,13 +2330,6 @@ MDL_context::upgrade_shared_lock(MDL_ticket *mdl_ticket,
if (mdl_ticket->has_stronger_or_equal_type(new_type))
DBUG_RETURN(FALSE);
- /* Only allow upgrades from UPGRADABLE/NO_WRITE/NO_READ_WRITE/READ/WRITE */
- DBUG_ASSERT(mdl_ticket->m_type == MDL_SHARED_UPGRADABLE ||
- mdl_ticket->m_type == MDL_SHARED_NO_WRITE ||
- mdl_ticket->m_type == MDL_SHARED_NO_READ_WRITE ||
- mdl_ticket->m_type == MDL_SHARED_READ ||
- mdl_ticket->m_type == MDL_SHARED_WRITE);
-
mdl_xlock_request.init(&mdl_ticket->m_lock->key, new_type,
MDL_TRANSACTION);
@@ -2642,7 +2645,7 @@ void MDL_context::release_lock(enum_mdl_duration duration, MDL_ticket *ticket)
void MDL_context::release_lock(MDL_ticket *ticket)
{
- DBUG_ASSERT(ticket->m_duration == MDL_EXPLICIT);
+ DBUG_SLOW_ASSERT(ticket->m_duration == MDL_EXPLICIT);
release_lock(MDL_EXPLICIT, ticket);
}
@@ -2902,8 +2905,8 @@ bool MDL_context::has_lock(const MDL_savepoint &mdl_savepoint,
void MDL_context::set_lock_duration(MDL_ticket *mdl_ticket,
enum_mdl_duration duration)
{
- DBUG_ASSERT(mdl_ticket->m_duration == MDL_TRANSACTION &&
- duration != MDL_TRANSACTION);
+ DBUG_SLOW_ASSERT(mdl_ticket->m_duration == MDL_TRANSACTION &&
+ duration != MDL_TRANSACTION);
m_tickets[MDL_TRANSACTION].remove(mdl_ticket);
m_tickets[duration].push_front(mdl_ticket);
@@ -3041,6 +3044,7 @@ const char *wsrep_get_mdl_namespace_name(MDL_key::enum_mdl_namespace ns)
case MDL_key::TABLE : return "TABLE";
case MDL_key::FUNCTION : return "FUNCTION";
case MDL_key::PROCEDURE : return "PROCEDURE";
+ case MDL_key::PACKAGE_BODY: return "PACKAGE BODY";
case MDL_key::TRIGGER : return "TRIGGER";
case MDL_key::EVENT : return "EVENT";
case MDL_key::COMMIT : return "COMMIT";
diff --git a/sql/mdl.h b/sql/mdl.h
index 65c7c98c31b..93da280dda6 100644
--- a/sql/mdl.h
+++ b/sql/mdl.h
@@ -299,6 +299,7 @@ public:
TABLE,
FUNCTION,
PROCEDURE,
+ PACKAGE_BODY,
TRIGGER,
EVENT,
COMMIT,
@@ -347,7 +348,7 @@ public:
NAME_LEN) - m_ptr + 1);
m_hash_value= my_hash_sort(&my_charset_bin, (uchar*) m_ptr + 1,
m_length - 1);
- DBUG_ASSERT(mdl_namespace_arg == USER_LOCK || ok_for_lower_case_names(db));
+ DBUG_SLOW_ASSERT(mdl_namespace_arg == USER_LOCK || ok_for_lower_case_names(db));
}
void mdl_key_init(const MDL_key *rhs)
{
@@ -455,7 +456,7 @@ public:
static void *operator new(size_t size, MEM_ROOT *mem_root) throw ()
{ return alloc_root(mem_root, size); }
- static void operator delete(void *ptr, MEM_ROOT *mem_root) {}
+ static void operator delete(void *, MEM_ROOT *) {}
void init(MDL_key::enum_mdl_namespace namespace_arg,
const char *db_arg, const char *name_arg,
@@ -506,7 +507,7 @@ public:
is mandatory. Can only be used before the request has been
granted.
*/
- MDL_request& operator=(const MDL_request &rhs)
+ MDL_request& operator=(const MDL_request &)
{
ticket= NULL;
/* Do nothing, in particular, don't try to copy the key. */
diff --git a/sql/mf_iocache.cc b/sql/mf_iocache.cc
index 8533c9037aa..c9cff6ad930 100644
--- a/sql/mf_iocache.cc
+++ b/sql/mf_iocache.cc
@@ -32,7 +32,7 @@
flush_io_cache().
*/
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "sql_class.h" // THD
#ifdef HAVE_REPLICATION
@@ -58,12 +58,12 @@ int _my_b_net_read(IO_CACHE *info, uchar *Buffer, size_t)
if (!info->end_of_file)
DBUG_RETURN(1); /* because my_b_get (no _) takes 1 byte at a time */
read_length= my_net_read_packet(net, 0);
- if (read_length == packet_error)
+ if (unlikely(read_length == packet_error))
{
info->error= -1;
DBUG_RETURN(1);
}
- if (read_length == 0)
+ if (unlikely(read_length == 0))
{
info->end_of_file= 0; /* End of file from client */
DBUG_RETURN(1);
diff --git a/sql/mf_iocache_encr.cc b/sql/mf_iocache_encr.cc
index d741cf8b837..b27f10c7d72 100644
--- a/sql/mf_iocache_encr.cc
+++ b/sql/mf_iocache_encr.cc
@@ -177,9 +177,9 @@ static int my_b_encr_write(IO_CACHE *info, const uchar *Buffer, size_t Count)
crypt_data->inbuf_counter= crypt_data->counter;
set_iv(iv, info->pos_in_file, crypt_data->inbuf_counter);
- if (encryption_crypt(Buffer, length, ebuffer, &elength,
- crypt_data->key, sizeof(crypt_data->key),
- iv, sizeof(iv), ENCRYPTION_FLAG_ENCRYPT,
+ if (encryption_crypt(Buffer, (uint)length, ebuffer, &elength,
+ crypt_data->key, (uint) sizeof(crypt_data->key),
+ iv, (uint) sizeof(iv), ENCRYPTION_FLAG_ENCRYPT,
keyid, keyver))
{
my_errno= 1;
@@ -194,7 +194,7 @@ static int my_b_encr_write(IO_CACHE *info, const uchar *Buffer, size_t Count)
buffer_length bytes should *always* produce block_length bytes
*/
DBUG_ASSERT(crypt_data->block_length == 0 || crypt_data->block_length == wlength);
- DBUG_ASSERT(elength <= encryption_encrypted_length(length, keyid, keyver));
+ DBUG_ASSERT(elength <= encryption_encrypted_length((uint)length, keyid, keyver));
crypt_data->block_length= wlength;
}
else
diff --git a/sql/multi_range_read.cc b/sql/multi_range_read.cc
index 2151b6d41cc..5e0afd6edb2 100644
--- a/sql/multi_range_read.cc
+++ b/sql/multi_range_read.cc
@@ -13,6 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
+#include "mariadb.h"
#include "sql_parse.h"
#include <my_bit.h>
#include "sql_select.h"
@@ -769,12 +770,6 @@ int Mrr_ordered_rndpos_reader::get_next(range_id_t *range_info)
res= file->ha_rnd_pos(file->get_table()->record[0],
rowid_buffer->read_ptr1);
- if (res == HA_ERR_RECORD_DELETED)
- {
- /* not likely to get this code with current storage engines, but still */
- continue;
- }
-
if (res)
return res; /* Some fatal error */
@@ -1594,11 +1589,10 @@ bool DsMrr_impl::choose_mrr_impl(uint keyno, ha_rows rows, uint *flags,
}
uint add_len= share->key_info[keyno].key_length + primary_file->ref_length;
- *bufsz -= add_len;
- if (get_disk_sweep_mrr_cost(keyno, rows, *flags, bufsz, &dsmrr_cost))
+ if (get_disk_sweep_mrr_cost(keyno, rows, *flags, bufsz, add_len,
+ &dsmrr_cost))
return TRUE;
- *bufsz += add_len;
-
+
bool force_dsmrr;
/*
If mrr_cost_based flag is not set, then set cost of DS-MRR to be minimum of
@@ -1668,10 +1662,10 @@ int DsMrr_impl::dsmrr_explain_info(uint mrr_mode, char *str, size_t size)
else if (mrr_mode & DSMRR_IMPL_SORT_ROWIDS)
used_str= rowid_ordered;
- uint used_str_len= strlen(used_str);
- uint copy_len= MY_MIN(used_str_len, size);
+ size_t used_str_len= strlen(used_str);
+ size_t copy_len= MY_MIN(used_str_len, size);
memcpy(str, used_str, copy_len);
- return copy_len;
+ return (int)copy_len;
}
return 0;
}
@@ -1687,6 +1681,11 @@ static void get_sort_and_sweep_cost(TABLE *table, ha_rows nrows, Cost_estimate *
@param rows E(Number of rows to be scanned)
@param flags Scan parameters (HA_MRR_* flags)
@param buffer_size INOUT Buffer size
+ IN: Buffer of size 0 means the function
+ will determine the best size and return it.
+ @param extra_mem_overhead Extra memory overhead of the MRR implementation
+ (the function assumes this many bytes of buffer
+ space will not be usable by DS-MRR)
@param cost OUT The cost
@retval FALSE OK
@@ -1695,7 +1694,9 @@ static void get_sort_and_sweep_cost(TABLE *table, ha_rows nrows, Cost_estimate *
*/
bool DsMrr_impl::get_disk_sweep_mrr_cost(uint keynr, ha_rows rows, uint flags,
- uint *buffer_size, Cost_estimate *cost)
+ uint *buffer_size,
+ uint extra_mem_overhead,
+ Cost_estimate *cost)
{
ulong max_buff_entries, elem_size;
ha_rows rows_in_full_step;
@@ -1705,11 +1706,24 @@ bool DsMrr_impl::get_disk_sweep_mrr_cost(uint keynr, ha_rows rows, uint flags,
elem_size= primary_file->ref_length +
sizeof(void*) * (!MY_TEST(flags & HA_MRR_NO_ASSOCIATION));
- max_buff_entries = *buffer_size / elem_size;
- if (!max_buff_entries)
+ if (!*buffer_size)
+ {
+ /*
+ We are requested to determine how much memory we need.
+ Request memory to finish the scan in one pass but do not request
+ more than @@mrr_buff_size.
+ */
+ *buffer_size= (uint) MY_MIN(extra_mem_overhead + elem_size*(ulong)rows,
+ MY_MAX(table->in_use->variables.mrr_buff_size,
+ extra_mem_overhead));
+ }
+
+ if (elem_size + extra_mem_overhead > *buffer_size)
return TRUE; /* Buffer has not enough space for even 1 rowid */
+ max_buff_entries = (*buffer_size - extra_mem_overhead) / elem_size;
+
/* Number of iterations we'll make with full buffer */
n_full_steps= (uint)floor(rows2double(rows) / max_buff_entries);
@@ -1730,7 +1744,7 @@ bool DsMrr_impl::get_disk_sweep_mrr_cost(uint keynr, ha_rows rows, uint flags,
else
{
cost->reset();
- *buffer_size= MY_MAX(*buffer_size,
+ *buffer_size= (uint)MY_MAX(*buffer_size,
(size_t)(1.2*rows_in_last_step) * elem_size +
primary_file->ref_length + table->key_info[keynr].key_length);
}
diff --git a/sql/multi_range_read.h b/sql/multi_range_read.h
index 524e60cb593..0473fef04ae 100644
--- a/sql/multi_range_read.h
+++ b/sql/multi_range_read.h
@@ -57,7 +57,7 @@
Storage engine internals
- Currently DS-MRR is used by MyISAM, InnoDB/XtraDB and Maria storage engines.
+ Currently DS-MRR is used by MyISAM, InnoDB and Maria storage engines.
Potentially it can be used with any table handler that has disk-based data
storage and has better performance when reading data in rowid order.
*/
@@ -631,8 +631,9 @@ private:
bool choose_mrr_impl(uint keyno, ha_rows rows, uint *flags, uint *bufsz,
Cost_estimate *cost);
- bool get_disk_sweep_mrr_cost(uint keynr, ha_rows rows, uint flags,
- uint *buffer_size, Cost_estimate *cost);
+ bool get_disk_sweep_mrr_cost(uint keynr, ha_rows rows, uint flags,
+ uint *buffer_size, uint extra_mem_overhead,
+ Cost_estimate *cost);
bool check_cpk_scan(THD *thd, TABLE_SHARE *share, uint keyno, uint mrr_flags);
bool setup_buffer_sharing(uint key_size_in_keybuf, key_part_map key_tuple_map);
diff --git a/sql/my_apc.cc b/sql/my_apc.cc
index f54e2ad3a8f..e0feabab8e2 100644
--- a/sql/my_apc.cc
+++ b/sql/my_apc.cc
@@ -17,6 +17,7 @@
#ifndef MY_APC_STANDALONE
+#include "mariadb.h"
#include "sql_class.h"
#endif
@@ -34,7 +35,7 @@
void Apc_target::init(mysql_mutex_t *target_mutex)
{
DBUG_ASSERT(!enabled);
- LOCK_thd_data_ptr= target_mutex;
+ LOCK_thd_kill_ptr= target_mutex;
#ifndef DBUG_OFF
n_calls_processed= 0;
#endif
@@ -45,7 +46,7 @@ void Apc_target::init(mysql_mutex_t *target_mutex)
void Apc_target::enqueue_request(Call_request *qe)
{
- mysql_mutex_assert_owner(LOCK_thd_data_ptr);
+ mysql_mutex_assert_owner(LOCK_thd_kill_ptr);
if (apc_calls)
{
Call_request *after= apc_calls->prev;
@@ -71,7 +72,7 @@ void Apc_target::enqueue_request(Call_request *qe)
void Apc_target::dequeue_request(Call_request *qe)
{
- mysql_mutex_assert_owner(LOCK_thd_data_ptr);
+ mysql_mutex_assert_owner(LOCK_thd_kill_ptr);
if (apc_calls == qe)
{
if ((apc_calls= apc_calls->next) == qe)
@@ -145,14 +146,14 @@ bool Apc_target::make_apc_call(THD *caller_thd, Apc_call *call,
int wait_res= 0;
PSI_stage_info old_stage;
- caller_thd->ENTER_COND(&apc_request.COND_request, LOCK_thd_data_ptr,
+ caller_thd->ENTER_COND(&apc_request.COND_request, LOCK_thd_kill_ptr,
&stage_show_explain, &old_stage);
/* todo: how about processing other errors here? */
while (!apc_request.processed && (wait_res != ETIMEDOUT))
{
- /* We own LOCK_thd_data_ptr */
+ /* We own LOCK_thd_kill_ptr */
wait_res= mysql_cond_timedwait(&apc_request.COND_request,
- LOCK_thd_data_ptr, &abstime);
+ LOCK_thd_kill_ptr, &abstime);
// &apc_request.LOCK_request, &abstime);
if (caller_thd->killed)
break;
@@ -163,7 +164,7 @@ bool Apc_target::make_apc_call(THD *caller_thd, Apc_call *call,
/*
The wait has timed out, or this thread was KILLed.
Remove the request from the queue (ok to do because we own
- LOCK_thd_data_ptr)
+ LOCK_thd_kill_ptr)
*/
apc_request.processed= TRUE;
dequeue_request(&apc_request);
@@ -176,7 +177,7 @@ bool Apc_target::make_apc_call(THD *caller_thd, Apc_call *call,
res= FALSE;
}
/*
- exit_cond() will call mysql_mutex_unlock(LOCK_thd_data_ptr) for us:
+ exit_cond() will call mysql_mutex_unlock(LOCK_thd_kill_ptr) for us:
*/
caller_thd->EXIT_COND(&old_stage);
@@ -185,7 +186,7 @@ bool Apc_target::make_apc_call(THD *caller_thd, Apc_call *call,
}
else
{
- mysql_mutex_unlock(LOCK_thd_data_ptr);
+ mysql_mutex_unlock(LOCK_thd_kill_ptr);
}
return res;
}
@@ -202,11 +203,11 @@ void Apc_target::process_apc_requests()
{
Call_request *request;
- mysql_mutex_lock(LOCK_thd_data_ptr);
+ mysql_mutex_lock(LOCK_thd_kill_ptr);
if (!(request= get_first_in_queue()))
{
/* No requests in the queue */
- mysql_mutex_unlock(LOCK_thd_data_ptr);
+ mysql_mutex_unlock(LOCK_thd_kill_ptr);
break;
}
@@ -225,7 +226,7 @@ void Apc_target::process_apc_requests()
n_calls_processed++;
#endif
mysql_cond_signal(&request->COND_request);
- mysql_mutex_unlock(LOCK_thd_data_ptr);
+ mysql_mutex_unlock(LOCK_thd_kill_ptr);
}
}
diff --git a/sql/my_apc.h b/sql/my_apc.h
index 0ac840b784e..cc98e36bbe4 100644
--- a/sql/my_apc.h
+++ b/sql/my_apc.h
@@ -44,7 +44,7 @@ class THD;
*/
class Apc_target
{
- mysql_mutex_t *LOCK_thd_data_ptr;
+ mysql_mutex_t *LOCK_thd_kill_ptr;
public:
Apc_target() : enabled(0), apc_calls(NULL) {}
~Apc_target() { DBUG_ASSERT(!enabled && !apc_calls);}
@@ -66,9 +66,9 @@ public:
void disable()
{
DBUG_ASSERT(enabled);
- mysql_mutex_lock(LOCK_thd_data_ptr);
+ mysql_mutex_lock(LOCK_thd_kill_ptr);
bool process= !--enabled && have_apc_requests();
- mysql_mutex_unlock(LOCK_thd_data_ptr);
+ mysql_mutex_unlock(LOCK_thd_kill_ptr);
if (unlikely(process))
process_apc_requests();
}
diff --git a/sql/my_decimal.cc b/sql/my_decimal.cc
index eec2676766c..8acb46b9ef2 100644
--- a/sql/my_decimal.cc
+++ b/sql/my_decimal.cc
@@ -14,7 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include <time.h>
@@ -238,7 +238,7 @@ int my_decimal2binary(uint mask, const my_decimal *d, uchar *bin, int prec,
E_DEC_OOM
*/
-int str2my_decimal(uint mask, const char *from, uint length,
+int str2my_decimal(uint mask, const char *from, size_t length,
CHARSET_INFO *charset, my_decimal *decimal_value,
const char **end_ptr)
{
@@ -345,12 +345,12 @@ void my_decimal_trim(ulonglong *precision, uint *scale)
*/
int my_decimal2int(uint mask, const decimal_t *d, bool unsigned_flag,
- longlong *l)
+ longlong *l, decimal_round_mode round_type)
{
int res;
my_decimal rounded;
/* decimal_round can return only E_DEC_TRUNCATED */
- decimal_round(d, &rounded, 0, HALF_UP);
+ decimal_round(d, &rounded, 0, round_type);
res= (unsigned_flag ?
decimal2ulonglong(&rounded, (ulonglong *) l) :
decimal2longlong(&rounded, l));
diff --git a/sql/my_decimal.h b/sql/my_decimal.h
index f318bcd1cd3..b409a87bdd4 100644
--- a/sql/my_decimal.h
+++ b/sql/my_decimal.h
@@ -132,8 +132,8 @@ public:
void sanity_check()
{
- DBUG_ASSERT(foo1 == test_value);
- DBUG_ASSERT(foo2 == test_value);
+ DBUG_SLOW_ASSERT(foo1 == test_value);
+ DBUG_SLOW_ASSERT(foo2 == test_value);
}
void fix_buffer_pointer() { buf= buffer; }
@@ -288,7 +288,7 @@ int my_decimal_set_zero(my_decimal *d)
{
/*
We need the up-cast here, since my_decimal has sign() member functions,
- which conflicts with decimal_t::size
+ which conflicts with decimal_t::sign
(and decimal_make_zero is a macro, rather than a funcion).
*/
decimal_make_zero(static_cast<decimal_t*>(d));
@@ -348,7 +348,7 @@ my_decimal *seconds2my_decimal(bool sign, ulonglong sec, ulong microsec,
(TIME)->second_part, (DECIMAL))
int my_decimal2int(uint mask, const decimal_t *d, bool unsigned_flag,
- longlong *l);
+ longlong *l, decimal_round_mode round_type= HALF_UP);
inline
int my_decimal2double(uint, const decimal_t *d, double *result)
@@ -365,11 +365,11 @@ int str2my_decimal(uint mask, const char *str, my_decimal *d, char **end)
}
-int str2my_decimal(uint mask, const char *from, uint length,
+int str2my_decimal(uint mask, const char *from, size_t length,
CHARSET_INFO *charset, my_decimal *decimal_value,
const char **end);
-inline int str2my_decimal(uint mask, const char *from, uint length,
+inline int str2my_decimal(uint mask, const char *from, size_t length,
CHARSET_INFO *charset, my_decimal *decimal_value)
{
const char *end;
diff --git a/sql/my_json_writer.cc b/sql/my_json_writer.cc
index 22c66ba7a34..656023d4033 100644
--- a/sql/my_json_writer.cc
+++ b/sql/my_json_writer.cc
@@ -13,7 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "sql_string.h"
@@ -228,7 +228,7 @@ bool Single_line_formatting_helper::on_add_member(const char *name)
buf_ptr+=len;
*(buf_ptr++)= 0;
- line_len= owner->indent_level + len + 1;
+ line_len= owner->indent_level + (uint)len + 1;
state= ADD_MEMBER;
return true; // handled
}
@@ -293,7 +293,7 @@ bool Single_line_formatting_helper::on_add_str(const char *str)
memcpy(buf_ptr, str, len);
buf_ptr+=len;
*(buf_ptr++)= 0;
- line_len += len + 4;
+ line_len += (uint)len + 4;
return true; // handled
}
diff --git a/sql/my_json_writer.h b/sql/my_json_writer.h
index ffee6db4c03..4c7ca46ef7c 100644
--- a/sql/my_json_writer.h
+++ b/sql/my_json_writer.h
@@ -174,6 +174,10 @@ public:
class Json_writer_nesting_guard
{
+#ifdef DBUG_OFF
+public:
+ Json_writer_nesting_guard(Json_writer *) {}
+#else
Json_writer* writer;
int indent_level;
public:
@@ -186,6 +190,7 @@ public:
{
DBUG_ASSERT(indent_level == writer->indent_level);
}
+#endif
};
diff --git a/sql/mysql_install_db.cc b/sql/mysql_install_db.cc
index 1352dfb2e45..9639051e93c 100644
--- a/sql/mysql_install_db.cc
+++ b/sql/mysql_install_db.cc
@@ -18,9 +18,8 @@
on Windows.
*/
#define DONT_DEFINE_VOID
-#include <my_global.h>
+#include "mariadb.h"
#include <my_getopt.h>
-#include <my_sys.h>
#include <m_string.h>
#include <windows.h>
@@ -96,9 +95,7 @@ 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(int optid, const struct my_option *, char *)
{
DBUG_ENTER("get_one_option");
switch (optid) {
@@ -112,7 +109,7 @@ get_one_option(int optid,
}
-static void die(const char *fmt, ...)
+ATTRIBUTE_NORETURN static void die(const char *fmt, ...)
{
va_list args;
DBUG_ENTER("die");
@@ -293,7 +290,7 @@ static int create_myini()
FILE *myini= fopen("my.ini","wt");
if (!myini)
{
- die("Cannot create my.ini in data directory");
+ die("Can't create my.ini in data directory");
}
/* Write out server settings. */
@@ -311,7 +308,7 @@ static int create_myini()
if (enable_named_pipe)
{
- fprintf(myini,"enable-named-pipe\n");
+ fprintf(myini,"named-pipe=ON\n");
}
if (opt_socket && opt_socket[0])
@@ -445,7 +442,6 @@ static int set_directory_permissions(const char *dir, const char *os_user)
ACL* pOldDACL;
SECURITY_DESCRIPTOR* pSD= NULL;
EXPLICIT_ACCESS ea={0};
- BOOL isWellKnownSID= FALSE;
WELL_KNOWN_SID_TYPE wellKnownSidType = WinNullSid;
PSID pSid= NULL;
@@ -512,7 +508,7 @@ static int set_directory_permissions(const char *dir, const char *os_user)
ea.grfInheritance= CONTAINER_INHERIT_ACE|OBJECT_INHERIT_ACE;
ea.Trustee.TrusteeType= TRUSTEE_IS_UNKNOWN;
ACL* pNewDACL= 0;
- DWORD err= SetEntriesInAcl(1,&ea,pOldDACL,&pNewDACL);
+ SetEntriesInAcl(1,&ea,pOldDACL,&pNewDACL);
if (pNewDACL)
{
SetSecurityInfo(hDir,SE_FILE_OBJECT,DACL_SECURITY_INFORMATION,NULL, NULL,
@@ -527,27 +523,6 @@ static int set_directory_permissions(const char *dir, const char *os_user)
}
-/*
- Give directory permissions for special service user NT SERVICE\servicename
- this user is available only on Win7 and later.
-*/
-
-void grant_directory_permissions_to_service()
-{
- char service_user[MAX_PATH+ 12];
- OSVERSIONINFO info;
- info.dwOSVersionInfoSize= sizeof(info);
- GetVersionEx(&info);
- if (info.dwMajorVersion >6 ||
- (info.dwMajorVersion== 6 && info.dwMinorVersion > 0)
- && opt_service)
- {
- my_snprintf(service_user,sizeof(service_user), "NT SERVICE\\%s",
- opt_service);
- set_directory_permissions(opt_datadir, service_user);
- }
-}
-
/* Create database instance (including registering as service etc) .*/
@@ -656,11 +631,11 @@ static int create_db_instance()
if (setvbuf(in, NULL, _IONBF, 0))
{
- verbose("WARNING: Cannot disable buffering on mysqld's stdin");
+ verbose("WARNING: Can't disable buffering on mysqld's stdin");
}
if (fwrite("use mysql;\n",11,1, in) != 1)
{
- verbose("ERROR: Cannot write to mysqld's stdin");
+ verbose("ERROR: Can't write to mysqld's stdin");
ret= 1;
goto end;
}
@@ -671,7 +646,7 @@ static int create_db_instance()
/* Write the bootstrap script to stdin. */
if (fwrite(mysql_bootstrap_sql[i], strlen(mysql_bootstrap_sql[i]), 1, in) != 1)
{
- verbose("ERROR: Cannot write to mysqld's stdin");
+ verbose("ERROR: Can't write to mysqld's stdin");
ret= 1;
goto end;
}
@@ -735,7 +710,6 @@ static int create_db_instance()
if (opt_service && opt_service[0])
{
ret= register_service();
- grant_directory_permissions_to_service();
if (ret)
goto end;
}
diff --git a/sql/mysql_upgrade_service.cc b/sql/mysql_upgrade_service.cc
index 37dae648563..b3683618e6e 100644
--- a/sql/mysql_upgrade_service.cc
+++ b/sql/mysql_upgrade_service.cc
@@ -20,8 +20,8 @@
*/
#define DONT_DEFINE_VOID
+#include "mariadb.h"
#include <process.h>
-#include <my_global.h>
#include <my_getopt.h>
#include <my_sys.h>
#include <m_string.h>
@@ -29,6 +29,9 @@
#include <winservice.h>
#include <windows.h>
+#include <string>
+
+extern int upgrade_config_file(const char *myini_path);
/* We're using version APIs */
#pragma comment(lib, "version")
@@ -47,11 +50,13 @@ static char mysqlupgrade_path[MAX_PATH];
static char defaults_file_param[MAX_PATH + 16]; /*--defaults-file=<path> */
static char logfile_path[MAX_PATH];
+char my_ini_bck[MAX_PATH];
+mysqld_service_properties service_properties;
static char *opt_service;
static SC_HANDLE service;
static SC_HANDLE scm;
HANDLE mysqld_process; // mysqld.exe started for upgrade
-DWORD initial_service_state= -1; // initial state of the service
+DWORD initial_service_state= UINT_MAX; // initial state of the service
HANDLE logfile_handle;
/*
@@ -59,7 +64,7 @@ HANDLE logfile_handle;
Maybe,they can be made parameters
*/
static unsigned int startup_timeout= 60;
-static unsigned int shutdown_timeout= 60;
+static unsigned int shutdown_timeout= 60*60;
static struct my_option my_long_options[]=
{
@@ -112,6 +117,7 @@ static void die(const char *fmt, ...)
fprintf(stderr, "FATAL ERROR: ");
vfprintf(stderr, fmt, args);
+ fputc('\n', stderr);
if (logfile_path[0])
{
fprintf(stderr, "Additional information can be found in the log file %s",
@@ -122,11 +128,16 @@ static void die(const char *fmt, ...)
fflush(stdout);
/* Cleanup */
+ if (my_ini_bck[0])
+ {
+ MoveFileEx(my_ini_bck, service_properties.inifile,MOVEFILE_REPLACE_EXISTING);
+ }
+
/*
- Stop service that we started, if it was not initally running at
+ Stop service that we started, if it was not initially running at
program start.
*/
- if (initial_service_state != -1 && initial_service_state != SERVICE_RUNNING)
+ if (initial_service_state != UINT_MAX && initial_service_state != SERVICE_RUNNING)
{
SERVICE_STATUS service_status;
ControlService(service, SERVICE_CONTROL_STOP, &service_status);
@@ -150,7 +161,7 @@ static void die(const char *fmt, ...)
char log_buf[1024]; \
DWORD nbytes; \
snprintf(log_buf,sizeof(log_buf), fmt, __VA_ARGS__);\
- WriteFile(logfile_handle,log_buf, strlen(log_buf), &nbytes , 0);\
+ WriteFile(logfile_handle,log_buf, (DWORD)strlen(log_buf), &nbytes , 0);\
}
/*
@@ -263,7 +274,7 @@ void stop_mysqld_service()
Remeber initial state of the service, we will restore it on
exit.
*/
- if(initial_service_state == -1)
+ if(initial_service_state == UINT_MAX)
initial_service_state= ssp.dwCurrentState;
switch(ssp.dwCurrentState)
@@ -310,77 +321,76 @@ void initiate_mysqld_shutdown()
}
}
-
-/*
- Change service configuration (binPath) to point to mysqld from
- this installation.
-*/
-static void change_service_config()
+static void get_service_config()
{
-
- char defaults_file[MAX_PATH];
- char default_character_set[64];
- char buf[MAX_PATH];
- char commandline[3*MAX_PATH + 19];
- int i;
-
- scm= OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
- if(!scm)
+ scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
+ if (!scm)
die("OpenSCManager failed with %u", GetLastError());
- service= OpenService(scm, opt_service, SERVICE_ALL_ACCESS);
+ service = OpenService(scm, opt_service, SERVICE_ALL_ACCESS);
if (!service)
die("OpenService failed with %u", GetLastError());
- BYTE config_buffer[8*1024];
- LPQUERY_SERVICE_CONFIGW config= (LPQUERY_SERVICE_CONFIGW)config_buffer;
- DWORD size= sizeof(config_buffer);
+ BYTE config_buffer[8 * 1024];
+ LPQUERY_SERVICE_CONFIGW config = (LPQUERY_SERVICE_CONFIGW)config_buffer;
+ DWORD size = sizeof(config_buffer);
DWORD needed;
if (!QueryServiceConfigW(service, config, size, &needed))
die("QueryServiceConfig failed with %u", GetLastError());
- mysqld_service_properties props;
- if (get_mysql_service_properties(config->lpBinaryPathName, &props))
+ if (get_mysql_service_properties(config->lpBinaryPathName, &service_properties))
{
die("Not a valid MySQL service");
}
- int my_major= MYSQL_VERSION_ID/10000;
- int my_minor= (MYSQL_VERSION_ID %10000)/100;
- int my_patch= MYSQL_VERSION_ID%100;
+ int my_major = MYSQL_VERSION_ID / 10000;
+ int my_minor = (MYSQL_VERSION_ID % 10000) / 100;
+ int my_patch = MYSQL_VERSION_ID % 100;
- if(my_major < props.version_major ||
- (my_major == props.version_major && my_minor < props.version_minor))
+ if (my_major < service_properties.version_major ||
+ (my_major == service_properties.version_major && my_minor < service_properties.version_minor))
{
die("Can not downgrade, the service is currently running as version %d.%d.%d"
- ", my version is %d.%d.%d", props.version_major, props.version_minor,
- props.version_patch, my_major, my_minor, my_patch);
+ ", my version is %d.%d.%d", service_properties.version_major, service_properties.version_minor,
+ service_properties.version_patch, my_major, my_minor, my_patch);
}
-
- if(props.inifile[0] == 0)
+ if (service_properties.inifile[0] == 0)
{
/*
Weird case, no --defaults-file in service definition, need to create one.
*/
- sprintf_s(props.inifile, MAX_PATH, "%s\\my.ini", props.datadir);
+ sprintf_s(service_properties.inifile, MAX_PATH, "%s\\my.ini", service_properties.datadir);
}
+ sprintf(defaults_file_param, "--defaults-file=%s", service_properties.inifile);
+}
+/*
+ Change service configuration (binPath) to point to mysqld from
+ this installation.
+*/
+static void change_service_config()
+{
+ char defaults_file[MAX_PATH];
+ char default_character_set[64];
+ char buf[MAX_PATH];
+ char commandline[3 * MAX_PATH + 19];
+ int i;
/*
Write datadir to my.ini, after converting backslashes to
unix style slashes.
*/
- strcpy_s(buf, MAX_PATH, props.datadir);
+ strcpy_s(buf, MAX_PATH, service_properties.datadir);
for(i= 0; buf[i]; i++)
{
if (buf[i] == '\\')
buf[i]= '/';
}
- WritePrivateProfileString("mysqld", "datadir",buf, props.inifile);
+ WritePrivateProfileString("mysqld", "datadir",buf, service_properties.inifile);
/*
Remove basedir from defaults file, otherwise the service wont come up in
the new version, and will complain about mismatched message file.
*/
- WritePrivateProfileString("mysqld", "basedir",NULL, props.inifile);
+ WritePrivateProfileString("mysqld", "basedir",NULL, service_properties.inifile);
/*
Replace default-character-set with character-set-server, to avoid
@@ -398,7 +408,7 @@ static void change_service_config()
default_character_set, defaults_file);
}
- sprintf(defaults_file_param,"--defaults-file=%s", props.inifile);
+ sprintf(defaults_file_param,"--defaults-file=%s", service_properties.inifile);
sprintf_s(commandline, "\"%s\" \"%s\" \"%s\"", mysqld_path,
defaults_file_param, opt_service);
if (!ChangeServiceConfig(service, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE,
@@ -450,23 +460,97 @@ int main(int argc, char **argv)
reads them from pipe and uses as progress indicator.
*/
setvbuf(stdout, NULL, _IONBF, 0);
+ int phase = 0;
+ int max_phases=10;
+ get_service_config();
- log("Phase 1/8: Changing service configuration");
- change_service_config();
+ bool my_ini_exists;
+ bool old_mysqld_exe_exists;
- log("Phase 2/8: Stopping service");
+ log("Phase %d/%d: Stopping service", ++phase,max_phases);
stop_mysqld_service();
+ my_ini_exists = (GetFileAttributes(service_properties.inifile) != INVALID_FILE_ATTRIBUTES);
+ if (!my_ini_exists)
+ {
+ HANDLE h = CreateFile(service_properties.inifile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
+ 0, CREATE_NEW, 0 ,0);
+ if (h != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(h);
+ }
+ else if (GetLastError() != ERROR_FILE_EXISTS)
+ {
+ die("Can't create ini file %s, last error %u", service_properties.inifile, GetLastError());
+ }
+ }
+
+ old_mysqld_exe_exists = (GetFileAttributes(service_properties.mysqld_exe) != INVALID_FILE_ATTRIBUTES);
+ log("Phase %d/%d: Fixing server config file%s", ++phase, max_phases, my_ini_exists ? "" : "(skipped)");
+
+ snprintf(my_ini_bck, sizeof(my_ini_bck), "%s.BCK", service_properties.inifile);
+ CopyFile(service_properties.inifile, my_ini_bck, FALSE);
+ upgrade_config_file(service_properties.inifile);
+
+ log("Phase %d/%d: Ensuring innodb slow shutdown%s", ++phase, max_phases,
+ old_mysqld_exe_exists?",this can take some time":"(skipped)");
+
+ char socket_param[FN_REFLEN];
+ sprintf_s(socket_param, "--socket=mysql_upgrade_service_%d",
+ GetCurrentProcessId());
+
+ DWORD start_duration_ms = 0;
+
+ if (old_mysqld_exe_exists)
+ {
+ /* Start/stop server with --loose-innodb-fast-shutdown=0 */
+ mysqld_process = (HANDLE)run_tool(P_NOWAIT, service_properties.mysqld_exe,
+ defaults_file_param, "--loose-innodb-fast-shutdown=0", "--skip-networking",
+ "--enable-named-pipe", socket_param, "--skip-slave-start", NULL);
+
+ if (mysqld_process == INVALID_HANDLE_VALUE)
+ {
+ die("Cannot start mysqld.exe process, last error =%u", GetLastError());
+ }
+ char pipe_name[64];
+ snprintf(pipe_name, sizeof(pipe_name), "\\\\.\\pipe\\mysql_upgrade_service_%u",
+ GetCurrentProcessId());
+ for (;;)
+ {
+ if (WaitForSingleObject(mysqld_process, 0) != WAIT_TIMEOUT)
+ die("mysqld.exe did not start");
+
+ if (WaitNamedPipe(pipe_name, 0))
+ {
+ // Server started, shut it down.
+ initiate_mysqld_shutdown();
+ if (WaitForSingleObject((HANDLE)mysqld_process, shutdown_timeout * 1000) != WAIT_OBJECT_0)
+ {
+ die("Could not shutdown server started with '--innodb-fast-shutdown=0'");
+ }
+ DWORD exit_code;
+ if (!GetExitCodeProcess((HANDLE)mysqld_process, &exit_code))
+ {
+ die("Could not get mysqld's exit code");
+ }
+ if (exit_code)
+ {
+ die("Could not get successfully shutdown mysqld");
+ }
+ CloseHandle(mysqld_process);
+ break;
+ }
+ Sleep(500);
+ start_duration_ms += 500;
+ }
+ }
/*
Start mysqld.exe as non-service skipping privileges (so we do not
care about the password). But disable networking and enable pipe
for communication, for security reasons.
*/
- char socket_param[FN_REFLEN];
- sprintf_s(socket_param,"--socket=mysql_upgrade_service_%d",
- GetCurrentProcessId());
- log("Phase 3/8: Starting mysqld for upgrade");
+ log("Phase %d/%d: Starting mysqld for upgrade",++phase,max_phases);
mysqld_process= (HANDLE)run_tool(P_NOWAIT, mysqld_path,
defaults_file_param, "--skip-networking", "--skip-grant-tables",
"--enable-named-pipe", socket_param,"--skip-slave-start", NULL);
@@ -476,8 +560,8 @@ int main(int argc, char **argv)
die("Cannot start mysqld.exe process, errno=%d", errno);
}
- log("Phase 4/8: Waiting for startup to complete");
- DWORD start_duration_ms= 0;
+ log("Phase %d/%d: Waiting for startup to complete",++phase,max_phases);
+ start_duration_ms= 0;
for(;;)
{
if (WaitForSingleObject(mysqld_process, 0) != WAIT_TIMEOUT)
@@ -494,7 +578,7 @@ int main(int argc, char **argv)
start_duration_ms+= 500;
}
- log("Phase 5/8: Running mysql_upgrade");
+ log("Phase %d/%d: Running mysql_upgrade",++phase,max_phases);
int upgrade_err= (int) run_tool(P_WAIT, mysqlupgrade_path,
"--protocol=pipe", "--force", socket_param,
NULL);
@@ -502,10 +586,13 @@ int main(int argc, char **argv)
if (upgrade_err)
die("mysql_upgrade failed with error code %d\n", upgrade_err);
- log("Phase 6/8: Initiating server shutdown");
+ log("Phase %d/%d: Changing service configuration", ++phase, max_phases);
+ change_service_config();
+
+ log("Phase %d/%d: Initiating server shutdown",++phase, max_phases);
initiate_mysqld_shutdown();
- log("Phase 7/8: Waiting for shutdown to complete");
+ log("Phase %d/%d: Waiting for shutdown to complete",++phase, max_phases);
if (WaitForSingleObject(mysqld_process, shutdown_timeout*1000)
!= WAIT_OBJECT_0)
{
@@ -515,7 +602,7 @@ int main(int argc, char **argv)
CloseHandle(mysqld_process);
mysqld_process= NULL;
- log("Phase 8/8: Starting service%s",
+ log("Phase %d/%d: Starting service%s",++phase,max_phases,
(initial_service_state == SERVICE_RUNNING)?"":" (skipped)");
if (initial_service_state == SERVICE_RUNNING)
{
@@ -528,6 +615,10 @@ int main(int argc, char **argv)
CloseServiceHandle(scm);
if (logfile_handle)
CloseHandle(logfile_handle);
+ if(my_ini_bck[0])
+ {
+ DeleteFile(my_ini_bck);
+ }
my_end(0);
exit(0);
}
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 44f8558e474..34d9145ea3d 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -14,7 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
-#include "sql_plugin.h" // Includes my_global.h
+#include "sql_plugin.h" // Includes mariadb.h
#include "sql_priv.h"
#include "unireg.h"
#include <signal.h>
@@ -46,7 +46,6 @@
#include "sql_servers.h" // servers_free, servers_init
#include "init.h" // unireg_init
#include "derror.h" // init_errmessage
-#include "derror.h" // init_errmessage
#include "des_key_file.h" // load_des_key_file
#include "sql_manager.h" // stop_handle_manager, start_handle_manager
#include "sql_expression_cache.h" // subquery_cache_miss, subquery_cache_hit
@@ -75,6 +74,7 @@
#include "wsrep_var.h"
#include "wsrep_thd.h"
#include "wsrep_sst.h"
+#include "proxy_protocol.h"
#include "sql_callback.h"
#include "threadpool.h"
@@ -96,8 +96,10 @@
#include "set_var.h"
#include "rpl_injector.h"
+#include "semisync_master.h"
+#include "semisync_slave.h"
-#include "rpl_handler.h"
+#include "transaction.h"
#ifdef HAVE_SYS_PRCTL_H
#include <sys/prctl.h>
@@ -155,16 +157,11 @@ extern "C" { // Because of SCO 3.2V4.2
#include <my_libwrap.h>
-#ifdef HAVE_SYS_MMAN_H
-#include <sys/mman.h>
-#endif
-
#ifdef __WIN__
#include <crtdbg.h>
#endif
#ifdef HAVE_SOLARIS_LARGE_PAGES
-#include <sys/mman.h>
#if defined(__sun__) && defined(__GNUC__) && defined(__cplusplus) \
&& defined(_XOPEN_SOURCE)
extern int getpagesizes(size_t *, int);
@@ -207,9 +204,6 @@ typedef fp_except fp_except_t;
#define fcntl(X,Y,Z) 0
#endif
-extern "C" my_bool reopen_fstreams(const char *filename,
- FILE *outstream, FILE *errstream);
-
inline void setup_fpu()
{
#if defined(__FreeBSD__) && defined(HAVE_IEEEFP_H) && !defined(HAVE_FEDISABLEEXCEPT)
@@ -300,7 +294,7 @@ static TYPELIB tc_heuristic_recover_typelib=
tc_heuristic_recover_names, NULL
};
-const char *first_keyword= "first", *binary_keyword= "BINARY";
+const char *first_keyword= "first";
const char *my_localhost= "localhost", *delayed_user= "DELAYED";
bool opt_large_files= sizeof(my_off_t) > 4;
@@ -311,14 +305,6 @@ static my_bool opt_autocommit; ///< for --autocommit command-line option
*/
static my_bool opt_verbose= 0;
-arg_cmp_func Arg_comparator::comparator_matrix[6][2] =
-{{&Arg_comparator::compare_string, &Arg_comparator::compare_e_string},
- {&Arg_comparator::compare_real, &Arg_comparator::compare_e_real},
- {&Arg_comparator::compare_int_signed, &Arg_comparator::compare_e_int},
- {&Arg_comparator::compare_row, &Arg_comparator::compare_e_row},
- {&Arg_comparator::compare_decimal, &Arg_comparator::compare_e_decimal},
- {&Arg_comparator::compare_datetime, &Arg_comparator::compare_e_datetime}};
-
/* Timer info to be used by the SQL layer */
MY_TIMER_INFO sys_timer_info;
@@ -379,6 +365,8 @@ char *my_bind_addr_str;
static char *default_collation_name;
char *default_storage_engine, *default_tmp_storage_engine;
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;
static bool binlog_format_used= false;
@@ -387,6 +375,7 @@ 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;
/* Global variables */
@@ -399,7 +388,6 @@ my_bool disable_log_notes, opt_support_flashback= 0;
static my_bool opt_abort;
ulonglong log_output_options;
my_bool opt_userstat_running;
-my_bool opt_log_queries_not_using_indexes= 0;
bool opt_error_log= IF_WIN(1,0);
bool opt_disable_networking=0, opt_skip_show_db=0;
bool opt_skip_name_resolve=0;
@@ -447,6 +435,7 @@ my_bool opt_replicate_annotate_row_events= 0;
my_bool opt_mysql56_temporal_format=0, strict_password_validation= 1;
my_bool opt_explicit_defaults_for_timestamp= 0;
char *opt_slave_skip_errors;
+char *opt_slave_transaction_retry_errors;
/*
Legacy global handlerton. These will be removed (please do not add more).
@@ -461,8 +450,6 @@ my_bool relay_log_recovery;
my_bool opt_sync_frm, opt_allow_suspicious_udfs;
my_bool opt_secure_auth= 0;
char* opt_secure_file_priv;
-my_bool opt_log_slow_admin_statements= 0;
-my_bool opt_log_slow_slave_statements= 0;
my_bool lower_case_file_system= 0;
my_bool opt_large_pages= 0;
my_bool opt_super_large_pages= 0;
@@ -496,7 +483,6 @@ uint protocol_version;
uint lower_case_table_names;
ulong tc_heuristic_recover= 0;
int32 thread_count, service_thread_count;
-int32 thread_running;
int32 slave_open_temp_tables;
ulong thread_created;
ulong back_log, connect_timeout, concurrency, server_id;
@@ -504,6 +490,7 @@ ulong what_to_log;
ulong slow_launch_time;
ulong open_files_limit, max_binlog_size;
ulong slave_trans_retries;
+ulong slave_trans_retry_interval;
uint slave_net_timeout;
ulong slave_exec_mode_options;
ulong slave_run_triggers_for_rbr= 0;
@@ -511,6 +498,7 @@ ulong slave_ddl_exec_mode_options= SLAVE_EXEC_MODE_IDEMPOTENT;
ulonglong slave_type_conversions_options;
ulong thread_cache_size=0;
ulonglong binlog_cache_size=0;
+ulonglong binlog_file_cache_size=0;
ulonglong max_binlog_cache_size=0;
ulong slave_max_allowed_packet= 0;
ulonglong binlog_stmt_cache_size=0;
@@ -531,10 +519,14 @@ ulong max_connections, max_connect_errors;
ulong extra_max_connections;
uint max_digest_length= 0;
ulong slave_retried_transactions;
+ulong transactions_multi_engine;
+ulong rpl_transactions_multi_engine;
+ulong transactions_gtid_foreign_engine;
ulonglong slave_skipped_errors;
ulong feature_files_opened_with_delayed_keys= 0, feature_check_constraint= 0;
ulonglong denied_connections;
my_decimal decimal_zero;
+long opt_secure_timestamp;
/*
Maximum length of parameter value which can be set through
@@ -625,7 +617,7 @@ char mysql_real_data_home[FN_REFLEN],
*opt_init_file, *opt_tc_log_file;
char *lc_messages_dir_ptr= lc_messages_dir, *log_error_file_ptr;
char mysql_unpacked_real_data_home[FN_REFLEN];
-int mysql_unpacked_real_data_home_len;
+size_t mysql_unpacked_real_data_home_len;
uint mysql_real_data_home_len, mysql_data_home_len= 1;
uint reg_ext_length;
const key_map key_map_empty(0);
@@ -644,10 +636,10 @@ my_bool encrypt_binlog;
my_bool encrypt_tmp_disk_tables, encrypt_tmp_files;
/** name of reference on left expression in rewritten IN subquery */
-const char *in_left_expr_name= "<left expr>";
+const LEX_CSTRING in_left_expr_name= {STRING_WITH_LEN("<left expr>") };
/** name of additional condition */
-const char *in_additional_cond= "<IN COND>";
-const char *in_having_cond= "<IN HAVING>";
+const LEX_CSTRING in_having_cond= {STRING_WITH_LEN("<IN HAVING>") };
+const LEX_CSTRING in_additional_cond= {STRING_WITH_LEN("<IN COND>") };
/** Number of connection errors when selecting on the listening port */
ulong connection_errors_select= 0;
@@ -914,7 +906,7 @@ PSI_mutex_key key_LOCK_des_key_file;
PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list,
key_BINLOG_LOCK_binlog_background_thread,
- m_key_LOCK_binlog_end_pos,
+ 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_delayed_insert, key_LOCK_delayed_status, key_LOCK_error_log,
@@ -936,9 +928,12 @@ PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list,
key_LOCK_thread_count, key_LOCK_thread_cache,
key_PARTITION_LOCK_auto_inc;
PSI_mutex_key key_RELAYLOG_LOCK_index;
+PSI_mutex_key key_LOCK_relaylog_end_pos;
PSI_mutex_key key_LOCK_thread_id;
PSI_mutex_key key_LOCK_slave_state, key_LOCK_binlog_state,
key_LOCK_rpl_thread, key_LOCK_rpl_thread_pool, key_LOCK_parallel_entry;
+PSI_mutex_key key_LOCK_rpl_semi_sync_master_enabled;
+PSI_mutex_key key_LOCK_binlog;
PSI_mutex_key key_LOCK_stats,
key_LOCK_global_user_client_stats, key_LOCK_global_table_stats,
@@ -950,6 +945,10 @@ PSI_mutex_key key_LOCK_after_binlog_sync;
PSI_mutex_key key_LOCK_prepare_ordered, key_LOCK_commit_ordered,
key_LOCK_slave_background;
PSI_mutex_key key_TABLE_SHARE_LOCK_share;
+PSI_mutex_key key_LOCK_ack_receiver;
+
+PSI_mutex_key key_TABLE_SHARE_LOCK_rotation;
+PSI_cond_key key_TABLE_SHARE_COND_rotation;
static PSI_mutex_info all_server_mutexes[]=
{
@@ -968,8 +967,9 @@ static PSI_mutex_info all_server_mutexes[]=
{ &key_BINLOG_LOCK_index, "MYSQL_BIN_LOG::LOCK_index", 0},
{ &key_BINLOG_LOCK_xid_list, "MYSQL_BIN_LOG::LOCK_xid_list", 0},
{ &key_BINLOG_LOCK_binlog_background_thread, "MYSQL_BIN_LOG::LOCK_binlog_background_thread", 0},
- { &m_key_LOCK_binlog_end_pos, "MYSQL_BIN_LOG::LOCK_binlog_end_pos", 0 },
+ { &key_LOCK_binlog_end_pos, "MYSQL_BIN_LOG::LOCK_binlog_end_pos", 0 },
{ &key_RELAYLOG_LOCK_index, "MYSQL_RELAY_LOG::LOCK_index", 0},
+ { &key_LOCK_relaylog_end_pos, "MYSQL_RELAY_LOG::LOCK_binlog_end_pos", 0},
{ &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},
@@ -1013,6 +1013,7 @@ static PSI_mutex_info all_server_mutexes[]=
{ &key_structure_guard_mutex, "Query_cache::structure_guard_mutex", 0},
{ &key_TABLE_SHARE_LOCK_ha_data, "TABLE_SHARE::LOCK_ha_data", 0},
{ &key_TABLE_SHARE_LOCK_share, "TABLE_SHARE::LOCK_share", 0},
+ { &key_TABLE_SHARE_LOCK_rotation, "TABLE_SHARE::LOCK_rotation", 0},
{ &key_LOCK_error_messages, "LOCK_error_messages", PSI_FLAG_GLOBAL},
{ &key_LOCK_prepare_ordered, "LOCK_prepare_ordered", PSI_FLAG_GLOBAL},
{ &key_LOCK_after_binlog_sync, "LOCK_after_binlog_sync", PSI_FLAG_GLOBAL},
@@ -1027,12 +1028,17 @@ static PSI_mutex_info all_server_mutexes[]=
{ &key_LOCK_binlog_state, "LOCK_binlog_state", 0},
{ &key_LOCK_rpl_thread, "LOCK_rpl_thread", 0},
{ &key_LOCK_rpl_thread_pool, "LOCK_rpl_thread_pool", 0},
- { &key_LOCK_parallel_entry, "LOCK_parallel_entry", 0}
+ { &key_LOCK_parallel_entry, "LOCK_parallel_entry", 0},
+ { &key_LOCK_ack_receiver, "Ack_receiver::mutex", 0},
+ { &key_LOCK_rpl_semi_sync_master_enabled, "LOCK_rpl_semi_sync_master_enabled", 0},
+ { &key_LOCK_binlog, "LOCK_binlog", 0}
};
PSI_rwlock_key key_rwlock_LOCK_grant, key_rwlock_LOCK_logger,
key_rwlock_LOCK_sys_init_connect, key_rwlock_LOCK_sys_init_slave,
- key_rwlock_LOCK_system_variables_hash, key_rwlock_query_cache_query_lock;
+ key_rwlock_LOCK_system_variables_hash, key_rwlock_query_cache_query_lock,
+ key_LOCK_SEQUENCE,
+ key_rwlock_LOCK_vers_stats, key_rwlock_LOCK_stat_serial;
static PSI_rwlock_info all_server_rwlocks[]=
{
@@ -1043,15 +1049,19 @@ static PSI_rwlock_info all_server_rwlocks[]=
{ &key_rwlock_LOCK_logger, "LOGGER::LOCK_logger", 0},
{ &key_rwlock_LOCK_sys_init_connect, "LOCK_sys_init_connect", PSI_FLAG_GLOBAL},
{ &key_rwlock_LOCK_sys_init_slave, "LOCK_sys_init_slave", PSI_FLAG_GLOBAL},
+ { &key_LOCK_SEQUENCE, "LOCK_SEQUENCE", 0},
{ &key_rwlock_LOCK_system_variables_hash, "LOCK_system_variables_hash", PSI_FLAG_GLOBAL},
- { &key_rwlock_query_cache_query_lock, "Query_cache_query::lock", 0}
+ { &key_rwlock_query_cache_query_lock, "Query_cache_query::lock", 0},
+ { &key_rwlock_LOCK_vers_stats, "Vers_field_stats::lock", 0},
+ { &key_rwlock_LOCK_stat_serial, "TABLE_SHARE::LOCK_stat_serial", 0}
};
#ifdef HAVE_MMAP
PSI_cond_key key_PAGE_cond, key_COND_active, key_COND_pool;
#endif /* HAVE_MMAP */
-PSI_cond_key key_BINLOG_COND_xid_list, key_BINLOG_update_cond,
+PSI_cond_key key_BINLOG_COND_xid_list,
+ key_BINLOG_COND_bin_log_updated, key_BINLOG_COND_relay_log_updated,
key_BINLOG_COND_binlog_background_thread,
key_BINLOG_COND_binlog_background_thread_end,
key_COND_cache_status_changed, key_COND_manager,
@@ -1065,9 +1075,10 @@ PSI_cond_key key_BINLOG_COND_xid_list, key_BINLOG_update_cond,
key_rpl_group_info_sleep_cond,
key_TABLE_SHARE_cond, key_user_level_lock_cond,
key_COND_thread_count, key_COND_thread_cache, key_COND_flush_thread_cache,
- key_COND_start_thread,
+ key_COND_start_thread, key_COND_binlog_send,
key_BINLOG_COND_queue_busy;
-PSI_cond_key key_RELAYLOG_update_cond, key_COND_wakeup_ready,
+PSI_cond_key key_RELAYLOG_COND_relay_log_updated,
+ key_RELAYLOG_COND_bin_log_updated, key_COND_wakeup_ready,
key_COND_wait_commit;
PSI_cond_key key_RELAYLOG_COND_queue_busy;
PSI_cond_key key_TC_LOG_MMAP_COND_queue_busy;
@@ -1076,6 +1087,7 @@ PSI_cond_key key_COND_rpl_thread_queue, key_COND_rpl_thread,
key_COND_parallel_entry, key_COND_group_commit_orderer,
key_COND_prepare_ordered, key_COND_slave_background;
PSI_cond_key key_COND_wait_gtid, key_COND_gtid_ignore_duplicates;
+PSI_cond_key key_COND_ack_receiver;
static PSI_cond_info all_server_conds[]=
{
@@ -1088,12 +1100,13 @@ static PSI_cond_info all_server_conds[]=
{ &key_COND_pool, "TC_LOG_MMAP::COND_pool", 0},
{ &key_TC_LOG_MMAP_COND_queue_busy, "TC_LOG_MMAP::COND_queue_busy", 0},
#endif /* HAVE_MMAP */
+ { &key_BINLOG_COND_bin_log_updated, "MYSQL_BIN_LOG::COND_bin_log_updated", 0}, { &key_BINLOG_COND_relay_log_updated, "MYSQL_BIN_LOG::COND_relay_log_updated", 0},
{ &key_BINLOG_COND_xid_list, "MYSQL_BIN_LOG::COND_xid_list", 0},
- { &key_BINLOG_update_cond, "MYSQL_BIN_LOG::update_cond", 0},
{ &key_BINLOG_COND_binlog_background_thread, "MYSQL_BIN_LOG::COND_binlog_background_thread", 0},
{ &key_BINLOG_COND_binlog_background_thread_end, "MYSQL_BIN_LOG::COND_binlog_background_thread_end", 0},
{ &key_BINLOG_COND_queue_busy, "MYSQL_BIN_LOG::COND_queue_busy", 0},
- { &key_RELAYLOG_update_cond, "MYSQL_RELAY_LOG::update_cond", 0},
+ { &key_RELAYLOG_COND_relay_log_updated, "MYSQL_RELAY_LOG::COND_relay_log_updated", 0},
+ { &key_RELAYLOG_COND_bin_log_updated, "MYSQL_RELAY_LOG::COND_bin_log_updated", 0},
{ &key_RELAYLOG_COND_queue_busy, "MYSQL_RELAY_LOG::COND_queue_busy", 0},
{ &key_COND_wakeup_ready, "THD::COND_wakeup_ready", 0},
{ &key_COND_wait_commit, "wait_for_commit::COND_wait_commit", 0},
@@ -1127,13 +1140,17 @@ static PSI_cond_info all_server_conds[]=
{ &key_COND_slave_background, "COND_slave_background", 0},
{ &key_COND_start_thread, "COND_start_thread", PSI_FLAG_GLOBAL},
{ &key_COND_wait_gtid, "COND_wait_gtid", 0},
- { &key_COND_gtid_ignore_duplicates, "COND_gtid_ignore_duplicates", 0}
+ { &key_COND_gtid_ignore_duplicates, "COND_gtid_ignore_duplicates", 0},
+ { &key_COND_ack_receiver, "Ack_receiver::cond", 0},
+ { &key_COND_binlog_send, "COND_binlog_send", 0},
+ { &key_TABLE_SHARE_COND_rotation, "TABLE_SHARE::COND_rotation", 0}
};
PSI_thread_key key_thread_bootstrap, key_thread_delayed_insert,
key_thread_handle_manager, key_thread_main,
key_thread_one_connection, key_thread_signal_hand,
key_thread_slave_background, key_rpl_parallel_thread;
+PSI_thread_key key_thread_ack_receiver;
static PSI_thread_info all_server_threads[]=
{
@@ -1160,6 +1177,7 @@ static PSI_thread_info all_server_threads[]=
{ &key_thread_one_connection, "one_connection", 0},
{ &key_thread_signal_hand, "signal_handler", PSI_FLAG_GLOBAL},
{ &key_thread_slave_background, "slave_background", PSI_FLAG_GLOBAL},
+ { &key_thread_ack_receiver, "Ack_receiver", PSI_FLAG_GLOBAL},
{ &key_rpl_parallel_thread, "rpl_parallel_thread", 0}
};
@@ -1224,7 +1242,7 @@ 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->db, thd->db_length,
+ thd->get_db(), thd->db.length,
thd->charset());
THD_STAGE_INFO(thd, stage_init);
@@ -1353,7 +1371,7 @@ private:
void Buffered_logs::init()
{
- init_alloc_root(&m_root, 1024, 0, MYF(0));
+ init_alloc_root(&m_root, "Buffered_logs", 1024, 0, MYF(0));
}
void Buffered_logs::cleanup()
@@ -1635,13 +1653,11 @@ static void close_connections(void)
{
if (mysql_socket_getfd(base_ip_sock) != INVALID_SOCKET)
{
- (void) mysql_socket_shutdown(base_ip_sock, SHUT_RDWR);
(void) mysql_socket_close(base_ip_sock);
base_ip_sock= MYSQL_INVALID_SOCKET;
}
if (mysql_socket_getfd(extra_ip_sock) != INVALID_SOCKET)
{
- (void) mysql_socket_shutdown(extra_ip_sock, SHUT_RDWR);
(void) mysql_socket_close(extra_ip_sock);
extra_ip_sock= MYSQL_INVALID_SOCKET;
}
@@ -1673,7 +1689,6 @@ static void close_connections(void)
#ifdef HAVE_SYS_UN_H
if (mysql_socket_getfd(unix_sock) != INVALID_SOCKET)
{
- (void) mysql_socket_shutdown(unix_sock, SHUT_RDWR);
(void) mysql_socket_close(unix_sock);
(void) unlink(mysqld_unix_port);
unix_sock= MYSQL_INVALID_SOCKET;
@@ -1710,7 +1725,7 @@ static void close_connections(void)
#endif
tmp->set_killed(KILL_SERVER_HARD);
MYSQL_CALLBACK(thread_scheduler, post_kill_notification, (tmp));
- mysql_mutex_lock(&tmp->LOCK_thd_data);
+ mysql_mutex_lock(&tmp->LOCK_thd_kill);
if (tmp->mysys_var)
{
tmp->mysys_var->abort=1;
@@ -1733,13 +1748,14 @@ static void close_connections(void)
}
mysql_mutex_unlock(&tmp->mysys_var->mutex);
}
- mysql_mutex_unlock(&tmp->LOCK_thd_data);
+ mysql_mutex_unlock(&tmp->LOCK_thd_kill);
}
mysql_mutex_unlock(&LOCK_thread_count); // For unlink from list
Events::deinit();
slave_prepare_for_shutdown();
mysql_bin_log.stop_background_thread();
+ ack_receiver.stop();
/*
Give threads time to die.
@@ -1940,10 +1956,11 @@ void kill_mysql(THD *thd)
pthread_t tmp;
int error;
abort_loop=1;
- if ((error= mysql_thread_create(0, /* Not instrumented */
- &tmp, &connection_attrib,
- kill_server_thread, (void*) 0)))
- sql_print_error("Can't create thread to kill server (errno= %d).", error);
+ if (unlikely((error= mysql_thread_create(0, /* Not instrumented */
+ &tmp, &connection_attrib,
+ kill_server_thread, (void*) 0))))
+ sql_print_error("Can't create thread to kill server (errno= %d).",
+ error);
}
#endif
DBUG_VOID_RETURN;
@@ -2220,12 +2237,14 @@ void clean_up(bool print_message)
lex_free(); /* Free some memory */
item_create_cleanup();
tdc_start_shutdown();
+#ifdef HAVE_REPLICATION
+ semi_sync_master_deinit();
+#endif
plugin_shutdown();
udf_free();
ha_end();
if (tc_log)
tc_log->close();
- delegates_destroy();
xid_cache_free();
tdc_deinit();
mdl_destroy();
@@ -2256,6 +2275,7 @@ void clean_up(bool print_message)
#endif
wsrep_thr_deinit();
my_uuid_end();
+ delete type_handler_data;
delete binlog_filter;
delete global_rpl_filter;
end_ssl();
@@ -2299,6 +2319,7 @@ void clean_up(bool print_message)
my_free(const_cast<char*>(relay_log_index));
#endif
free_list(opt_plugin_load_list_ptr);
+ destroy_proxy_protocol_networks();
/*
The following lines may never be executed as the main thread may have
@@ -2488,10 +2509,9 @@ static void set_user(const char *user, struct passwd *user_info_arg)
allow_coredumps();
}
-
+#if !defined(__WIN__)
static void set_effective_user(struct passwd *user_info_arg)
{
-#if !defined(__WIN__)
DBUG_ASSERT(user_info_arg != 0);
if (setregid((gid_t)-1, user_info_arg->pw_gid) == -1)
{
@@ -2504,9 +2524,8 @@ static void set_effective_user(struct passwd *user_info_arg)
unireg_abort(1);
}
allow_coredumps();
-#endif
}
-
+#endif
/** Change root user if started with @c --chroot . */
static void set_root(const char *path)
@@ -2549,7 +2568,7 @@ static MYSQL_SOCKET activate_tcp_port(uint port)
my_snprintf(port_buf, NI_MAXSERV, "%d", port);
error= getaddrinfo(real_bind_addr_str, port_buf, &hints, &ai);
- if (error != 0)
+ if (unlikely(error != 0))
{
DBUG_PRINT("error",("Got error: %d from getaddrinfo()", error));
@@ -2698,6 +2717,9 @@ static void network_init(void)
if (MYSQL_CALLBACK_ELSE(thread_scheduler, init, (), 0))
unireg_abort(1); /* purecov: inspected */
+ if (init_proxy_protocol_networks(my_proxy_protocol_networks))
+ unireg_abort(1);
+
set_ports();
if (report_port == 0)
@@ -2718,7 +2740,7 @@ static void network_init(void)
#ifdef _WIN32
/* create named pipe */
- if (Service.IsNT() && mysqld_unix_port[0] && !opt_bootstrap &&
+ if (mysqld_unix_port[0] && !opt_bootstrap &&
opt_enable_named_pipe)
{
@@ -2907,16 +2929,17 @@ void unlink_thd(THD *thd)
DBUG_ENTER("unlink_thd");
DBUG_PRINT("enter", ("thd: %p", thd));
+ thd->cleanup();
+ thd->add_status_to_global();
+ unlink_not_visible_thd(thd);
+
/*
Do not decrement when its wsrep system thread. wsrep_applier is set for
applier as well as rollbacker threads.
*/
if (IF_WSREP(!thd->wsrep_applier, 1))
dec_connection_count(thd->scheduler);
- thd->cleanup();
- thd->add_status_to_global();
- unlink_not_visible_thd(thd);
thd->free_connection();
DBUG_VOID_RETURN;
@@ -2954,13 +2977,11 @@ static bool cache_thread(THD *thd)
DBUG_PRINT("info", ("Adding thread to cache"));
cached_thread_count++;
-#ifdef HAVE_PSI_THREAD_INTERFACE
/*
Delete the instrumentation for the job that just completed,
before parking this pthread in the cache (blocked on COND_thread_cache).
*/
- PSI_THREAD_CALL(delete_current_thread)();
-#endif
+ PSI_CALL_delete_current_thread();
#ifndef DBUG_OFF
while (_db_is_pushed_())
@@ -3007,15 +3028,12 @@ static bool cache_thread(THD *thd)
*/
thd->store_globals();
-#ifdef HAVE_PSI_THREAD_INTERFACE
/*
Create new instrumentation for the new THD job,
and attach it to this running pthread.
*/
- PSI_thread *psi= PSI_THREAD_CALL(new_thread)(key_thread_one_connection,
- thd, thd->thread_id);
- PSI_THREAD_CALL(set_thread)(psi);
-#endif
+ 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;
@@ -3332,6 +3350,20 @@ static size_t my_setstacksize(pthread_attr_t *attr, size_t stacksize)
}
#endif
+#ifdef DBUG_ASSERT_AS_PRINTF
+extern "C" void
+mariadb_dbug_assert_failed(const char *assert_expr, const char *file,
+ unsigned long line)
+{
+ fprintf(stderr, "Warning: assertion failed: %s at %s line %lu\n",
+ assert_expr, file, line);
+ if (opt_stack_trace)
+ {
+ fprintf(stderr, "Attempting backtrace to find out the reason for the assert:\n");
+ my_print_stacktrace(NULL, (ulong) my_thread_stack_size, 1);
+ }
+}
+#endif /* DBUG_ASSERT_AS_PRINT */
#if !defined(__WIN__)
#ifndef SA_RESETHAND
@@ -3433,8 +3465,9 @@ static void start_signal_handler(void)
(void) my_setstacksize(&thr_attr,my_thread_stack_size);
mysql_mutex_lock(&LOCK_start_thread);
- if ((error= mysql_thread_create(key_thread_signal_hand,
- &signal_thread, &thr_attr, signal_hand, 0)))
+ if (unlikely((error= mysql_thread_create(key_thread_signal_hand,
+ &signal_thread, &thr_attr,
+ signal_hand, 0))))
{
sql_print_error("Can't create interrupt-thread (error %d, errno: %d)",
error,errno);
@@ -3532,16 +3565,14 @@ pthread_handler_t signal_hand(void *arg __attribute__((unused)))
if (!abort_loop)
{
abort_loop=1; // mark abort for threads
-#ifdef HAVE_PSI_THREAD_INTERFACE
/* Delete the instrumentation for the signal thread */
- PSI_THREAD_CALL(delete_current_thread)();
-#endif
+ PSI_CALL_delete_current_thread();
#ifdef USE_ONE_SIGNAL_HAND
pthread_t tmp;
- if ((error= mysql_thread_create(0, /* Not instrumented */
- &tmp, &connection_attrib,
- kill_server_thread,
- (void*) &sig)))
+ if (unlikely((error= mysql_thread_create(0, /* Not instrumented */
+ &tmp, &connection_attrib,
+ kill_server_thread,
+ (void*) &sig))))
sql_print_error("Can't create thread to kill server (errno= %d)",
error);
#else
@@ -3633,9 +3664,9 @@ void my_message_sql(uint error, const char *str, myf MyFlags)
func= sql_print_error;
}
- if (thd)
+ if (likely(thd))
{
- if (MyFlags & ME_FATALERROR)
+ if (unlikely(MyFlags & ME_FATALERROR))
thd->is_fatal_error= 1;
(void) thd->raise_condition(error, NULL, level, str);
}
@@ -3645,7 +3676,7 @@ void my_message_sql(uint error, const char *str, myf MyFlags)
/* When simulating OOM, skip writing to error log to avoid mtr errors */
DBUG_EXECUTE_IF("simulate_out_of_memory", DBUG_VOID_RETURN;);
- if (!thd || thd->log_all_errors || (MyFlags & ME_NOREFRESH))
+ if (unlikely(!thd) || thd->log_all_errors || (MyFlags & ME_NOREFRESH))
(*func)("%s: %s", my_progname_short, str); /* purecov: inspected */
DBUG_VOID_RETURN;
}
@@ -3777,6 +3808,7 @@ SHOW_VAR com_status_vars[]= {
{"alter_function", STMT_STATUS(SQLCOM_ALTER_FUNCTION)},
{"alter_procedure", STMT_STATUS(SQLCOM_ALTER_PROCEDURE)},
{"alter_server", STMT_STATUS(SQLCOM_ALTER_SERVER)},
+ {"alter_sequence", STMT_STATUS(SQLCOM_ALTER_SEQUENCE)},
{"alter_table", STMT_STATUS(SQLCOM_ALTER_TABLE)},
{"alter_tablespace", STMT_STATUS(SQLCOM_ALTER_TABLESPACE)},
{"alter_user", STMT_STATUS(SQLCOM_ALTER_USER)},
@@ -3795,8 +3827,11 @@ SHOW_VAR com_status_vars[]= {
{"create_event", STMT_STATUS(SQLCOM_CREATE_EVENT)},
{"create_function", STMT_STATUS(SQLCOM_CREATE_SPFUNCTION)},
{"create_index", STMT_STATUS(SQLCOM_CREATE_INDEX)},
+ {"create_package", STMT_STATUS(SQLCOM_CREATE_PACKAGE)},
+ {"create_package_body", STMT_STATUS(SQLCOM_CREATE_PACKAGE_BODY)},
{"create_procedure", STMT_STATUS(SQLCOM_CREATE_PROCEDURE)},
{"create_role", STMT_STATUS(SQLCOM_CREATE_ROLE)},
+ {"create_sequence", STMT_STATUS(SQLCOM_CREATE_SEQUENCE)},
{"create_server", STMT_STATUS(SQLCOM_CREATE_SERVER)},
{"create_table", STMT_STATUS(SQLCOM_CREATE_TABLE)},
{"create_temporary_table", COM_STATUS(com_create_tmp_table)},
@@ -3813,8 +3848,11 @@ SHOW_VAR com_status_vars[]= {
{"drop_function", STMT_STATUS(SQLCOM_DROP_FUNCTION)},
{"drop_index", STMT_STATUS(SQLCOM_DROP_INDEX)},
{"drop_procedure", STMT_STATUS(SQLCOM_DROP_PROCEDURE)},
+ {"drop_package", STMT_STATUS(SQLCOM_DROP_PACKAGE)},
+ {"drop_package_body", STMT_STATUS(SQLCOM_DROP_PACKAGE_BODY)},
{"drop_role", STMT_STATUS(SQLCOM_DROP_ROLE)},
{"drop_server", STMT_STATUS(SQLCOM_DROP_SERVER)},
+ {"drop_sequence", STMT_STATUS(SQLCOM_DROP_SEQUENCE)},
{"drop_table", STMT_STATUS(SQLCOM_DROP_TABLE)},
{"drop_temporary_table", COM_STATUS(com_drop_tmp_table)},
{"drop_trigger", STMT_STATUS(SQLCOM_DROP_TRIGGER)},
@@ -3868,6 +3906,8 @@ SHOW_VAR com_status_vars[]= {
{"show_create_db", STMT_STATUS(SQLCOM_SHOW_CREATE_DB)},
{"show_create_event", STMT_STATUS(SQLCOM_SHOW_CREATE_EVENT)},
{"show_create_func", STMT_STATUS(SQLCOM_SHOW_CREATE_FUNC)},
+ {"show_create_package", STMT_STATUS(SQLCOM_SHOW_CREATE_PACKAGE)},
+ {"show_create_package_body",STMT_STATUS(SQLCOM_SHOW_CREATE_PACKAGE_BODY)},
{"show_create_proc", STMT_STATUS(SQLCOM_SHOW_CREATE_PROC)},
{"show_create_table", STMT_STATUS(SQLCOM_SHOW_CREATE)},
{"show_create_trigger", STMT_STATUS(SQLCOM_SHOW_CREATE_TRIGGER)},
@@ -3889,6 +3929,11 @@ SHOW_VAR com_status_vars[]= {
{"show_keys", STMT_STATUS(SQLCOM_SHOW_KEYS)},
{"show_master_status", STMT_STATUS(SQLCOM_SHOW_MASTER_STAT)},
{"show_open_tables", STMT_STATUS(SQLCOM_SHOW_OPEN_TABLES)},
+ {"show_package_status", STMT_STATUS(SQLCOM_SHOW_STATUS_PACKAGE)},
+#ifndef DBUG_OFF
+ {"show_package_body_code", STMT_STATUS(SQLCOM_SHOW_PACKAGE_BODY_CODE)},
+#endif
+ {"show_package_body_status", STMT_STATUS(SQLCOM_SHOW_STATUS_PACKAGE_BODY)},
{"show_plugins", STMT_STATUS(SQLCOM_SHOW_PLUGINS)},
{"show_privileges", STMT_STATUS(SQLCOM_SHOW_PRIVILEGES)},
#ifndef DBUG_OFF
@@ -4035,9 +4080,11 @@ static void my_malloc_size_cb_func(long long size, my_bool is_thread_specific)
(longlong) thd->status_var.local_memory_used,
size));
thd->status_var.local_memory_used+= size;
+ set_if_bigger(thd->status_var.max_local_memory_used,
+ thd->status_var.local_memory_used);
if (size > 0 &&
thd->status_var.local_memory_used > (int64)thd->variables.max_mem_used &&
- !thd->killed && !thd->get_stmt_da()->is_set())
+ likely(!thd->killed) && !thd->get_stmt_da()->is_set())
{
/* Ensure we don't get called here again */
char buf[50], *buf2;
@@ -4130,7 +4177,7 @@ static void get_win_tzname(char* buf, size_t size)
{0,0}
};
DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
- if (GetDynamicTimeZoneInformation(&tzinfo) == TIME_ZONE_ID_UNKNOWN)
+ if (GetDynamicTimeZoneInformation(&tzinfo) == TIME_ZONE_ID_INVALID)
{
strncpy(buf, "unknown", size);
return;
@@ -4166,7 +4213,17 @@ static int init_common_variables()
#ifdef SAFEMALLOC
sf_malloc_dbug_id= mariadb_dbug_id;
-#endif
+#endif /* SAFEMALLOC */
+#ifdef DBUG_ASSERT_AS_PRINTF
+ my_dbug_assert_failed= mariadb_dbug_assert_failed;
+#endif /* DBUG_ASSERT_AS_PRINTF */
+
+ if (!(type_handler_data= new Type_handler_data) ||
+ type_handler_data->init())
+ {
+ sql_perror("Could not allocate type_handler_data");
+ return 1;
+ }
max_system_variables.pseudo_thread_id= ~(my_thread_id) 0;
server_start_time= flush_status_time= my_time(0);
@@ -4219,10 +4276,12 @@ static int init_common_variables()
constructor (called before main()).
*/
mysql_bin_log.set_psi_keys(key_BINLOG_LOCK_index,
- key_BINLOG_update_cond,
+ key_BINLOG_COND_relay_log_updated,
+ key_BINLOG_COND_bin_log_updated,
key_file_binlog,
key_file_binlog_index,
- key_BINLOG_COND_queue_busy);
+ key_BINLOG_COND_queue_busy,
+ key_LOCK_binlog_end_pos);
#endif
/*
@@ -4272,12 +4331,13 @@ static int init_common_variables()
(except in the embedded server, where the default continues to
be MyISAM)
*/
-#if defined(WITH_INNOBASE_STORAGE_ENGINE) || defined(WITH_XTRADB_STORAGE_ENGINE)
+#if defined(WITH_INNOBASE_STORAGE_ENGINE)
default_storage_engine= const_cast<char *>("InnoDB");
#else
default_storage_engine= const_cast<char *>("MyISAM");
#endif
default_tmp_storage_engine= NULL;
+ gtid_pos_auto_engines= const_cast<char *>("");
/*
Add server status variables to the dynamic list of
@@ -4724,6 +4784,7 @@ static int init_common_variables()
return 1;
}
+
#ifdef WITH_WSREP
/*
We need to initialize auxiliary variables, that will be
@@ -4982,7 +5043,7 @@ static int init_default_storage_engine_impl(const char *opt_name,
return 0;
}
- LEX_STRING name= { engine_name, strlen(engine_name) };
+ LEX_CSTRING name= { engine_name, strlen(engine_name) };
plugin_ref plugin;
handlerton *hton;
if ((plugin= ha_resolve_by_name(0, &name, false)))
@@ -5016,6 +5077,34 @@ static int init_default_storage_engine_impl(const char *opt_name,
return 0;
}
+
+static int
+init_gtid_pos_auto_engines(void)
+{
+ plugin_ref *plugins;
+
+ /*
+ For the command-line option --gtid_pos_auto_engines, we allow (and ignore)
+ engines that are unknown. This is convenient, since it allows to set
+ default auto-create engines that might not be used by particular users.
+ The option sets a list of storage engines that will have gtid position
+ table auto-created for them if needed. And if the engine is not available,
+ then it will certainly not be needed.
+ */
+ if (gtid_pos_auto_engines)
+ plugins= resolve_engine_list(NULL, gtid_pos_auto_engines,
+ strlen(gtid_pos_auto_engines), false, false);
+ else
+ plugins= resolve_engine_list(NULL, "", 0, false, false);
+ if (!plugins)
+ return 1;
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ opt_gtid_pos_auto_plugins= plugins;
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ return 0;
+}
+
+
static int init_server_components()
{
DBUG_ENTER("init_server_components");
@@ -5023,6 +5112,7 @@ static int init_server_components()
We need to call each of these following functions to ensure that
all things are initialized so that unireg_abort() doesn't fail
*/
+ my_cpu_init();
mdl_init();
if (tdc_init() || hostname_cache_init())
unireg_abort(1);
@@ -5133,13 +5223,6 @@ static int init_server_components()
xid_cache_init();
- /*
- initialize delegates for extension observers, errors have already
- been reported in the function
- */
- if (delegates_init())
- unireg_abort(1);
-
/* need to configure logging before initializing storage engines */
if (!opt_bin_log_used && !WSREP_ON)
{
@@ -5287,10 +5370,7 @@ static int init_server_components()
{
unireg_abort(1);
}
- }
- if (opt_bin_log)
- {
log_bin_basename=
rpl_make_log_name(opt_bin_logname, pidfile_name,
opt_bin_logname ? "" : "-bin");
@@ -5335,7 +5415,7 @@ static int init_server_components()
init_global_index_stats();
/* Allow storage engine to give real error messages */
- if (ha_init_errors())
+ if (unlikely(ha_init_errors()))
DBUG_RETURN(1);
tc_log= 0; // ha_initialize_handlerton() needs that
@@ -5349,15 +5429,22 @@ static int init_server_components()
}
plugins_are_initialized= TRUE; /* Don't separate from init function */
-#ifndef EMBEDDED_LIBRARY
+#ifdef HAVE_REPLICATION
+ /*
+ Semisync is not required by other components, which justifies its
+ initialization at this point when thread specific memory is also available.
+ */
+ if (repl_semisync_master.init_object() ||
+ repl_semisync_slave.init_object())
{
- if (Session_tracker::server_boot_verify(system_charset_info))
- {
- sql_print_error("The variable session_track_system_variables has "
- "invalid values.");
- unireg_abort(1);
- }
+ sql_print_error("Could not initialize semisync.");
+ unireg_abort(1);
}
+#endif
+
+#ifndef EMBEDDED_LIBRARY
+ if (session_tracker_init())
+ return 1;
#endif //EMBEDDED_LIBRARY
/* we do want to exit if there are any other unknown options */
@@ -5427,7 +5514,7 @@ static int init_server_components()
else
{
/* fall back to the log files if tables are not present */
- LEX_STRING csv_name={C_STRING_WITH_LEN("csv")};
+ LEX_CSTRING csv_name={STRING_WITH_LEN("csv")};
if (!plugin_is_ready(&csv_name, MYSQL_STORAGE_ENGINE_PLUGIN))
{
/* purecov: begin inspected */
@@ -5459,6 +5546,9 @@ static int init_server_components()
if (init_default_storage_engine(enforced_storage_engine, enforced_table_plugin))
unireg_abort(1);
+ if (init_gtid_pos_auto_engines())
+ unireg_abort(1);
+
#ifdef USE_ARIA_FOR_TMP_TABLES
if (!ha_storage_engine_is_enabled(maria_hton) && !opt_bootstrap)
{
@@ -5474,7 +5564,8 @@ static int init_server_components()
initialized. This initialization was not possible before, as plugins
(and thus some global system variables) are initialized after wsrep
startup threads are created.
- Note: This only needs to be done for rsync, xtrabackup based SST methods.
+ Note: This only needs to be done for rsync and mariabackup based SST
+ methods.
*/
if (wsrep_before_SE())
wsrep_plugins_post_init();
@@ -5506,7 +5597,7 @@ static int init_server_components()
error= mysql_bin_log.open(opt_bin_logname, LOG_BIN, 0, 0,
WRITE_CACHE, max_binlog_size, 0, TRUE);
mysql_mutex_unlock(log_lock);
- if (error)
+ if (unlikely(error))
unireg_abort(1);
}
@@ -5540,7 +5631,7 @@ static int init_server_components()
else
error= mlockall(MCL_CURRENT);
- if (error)
+ if (unlikely(error))
{
if (global_system_variables.log_warnings)
sql_print_warning("Failed to lock memory. Errno: %d\n",errno);
@@ -5572,9 +5663,9 @@ static void create_shutdown_thread()
hEventShutdown=CreateEvent(0, FALSE, FALSE, shutdown_event_name);
pthread_t hThread;
int error;
- if ((error= mysql_thread_create(key_thread_handle_shutdown,
- &hThread, &connection_attrib,
- handle_shutdown, 0)))
+ if (unlikely((error= mysql_thread_create(key_thread_handle_shutdown,
+ &hThread, &connection_attrib,
+ handle_shutdown, 0))))
sql_print_warning("Can't create thread to handle shutdown requests"
" (errno= %d)", error);
@@ -5673,8 +5764,8 @@ static void test_lc_time_sz()
DBUG_ENTER("test_lc_time_sz");
for (MY_LOCALE **loc= my_locales; *loc; loc++)
{
- uint max_month_len= 0;
- uint max_day_len = 0;
+ size_t max_month_len= 0;
+ size_t max_day_len= 0;
for (const char **month= (*loc)->month_names->type_names; *month; month++)
{
set_if_bigger(max_month_len,
@@ -5825,8 +5916,8 @@ int mysqld_main(int argc, char **argv)
*/
init_server_psi_keys();
/* Instrument the main thread */
- PSI_thread *psi= PSI_THREAD_CALL(new_thread)(key_thread_main, NULL, 0);
- PSI_THREAD_CALL(set_thread)(psi);
+ PSI_thread *psi= PSI_CALL_new_thread(key_thread_main, NULL, 0);
+ PSI_CALL_set_thread(psi);
/*
Now that some instrumentation is in place,
@@ -6131,6 +6222,9 @@ int mysqld_main(int argc, char **argv)
MYSQL_SET_STAGE(0 ,__FILE__, __LINE__);
+ /* Memory used when everything is setup */
+ start_memory_used= global_status_var.global_memory_used;
+
#if defined(_WIN32) || defined(HAVE_SMEM)
handle_connections_methods();
#else
@@ -6148,13 +6242,11 @@ int mysqld_main(int argc, char **argv)
mysql_mutex_unlock(&LOCK_start_thread);
#endif /* __WIN__ */
-#ifdef HAVE_PSI_THREAD_INTERFACE
/*
Disable the main thread instrumentation,
to avoid recording events during the shutdown.
*/
- PSI_THREAD_CALL(delete_current_thread)();
-#endif
+ PSI_CALL_delete_current_thread();
/* Wait until cleanup is done */
mysql_mutex_lock(&LOCK_thread_count);
@@ -6163,7 +6255,7 @@ int mysqld_main(int argc, char **argv)
mysql_mutex_unlock(&LOCK_thread_count);
#if defined(__WIN__) && !defined(EMBEDDED_LIBRARY)
- if (Service.IsNT() && start_mode)
+ if (start_mode)
Service.Stop();
else
{
@@ -6188,10 +6280,10 @@ int mysqld_main(int argc, char **argv)
****************************************************************************/
#if defined(__WIN__) && !defined(EMBEDDED_LIBRARY)
-int mysql_service(void *p)
+void mysql_service(void *p)
{
if (my_thread_init())
- return 1;
+ abort();
if (use_opt_args)
win_main(opt_argc, opt_argv);
@@ -6199,7 +6291,6 @@ int mysql_service(void *p)
win_main(Service.my_argc, Service.my_argv);
my_thread_end();
- return 0;
}
@@ -6250,7 +6341,7 @@ default_service_handling(char **argv,
the option name) should be quoted if it contains a string.
*/
*pos++= ' ';
- if (opt_delim= strchr(extra_opt, '='))
+ if ((opt_delim= strchr(extra_opt, '=')))
{
size_t length= ++opt_delim - extra_opt;
pos= strnmov(pos, extra_opt, length);
@@ -6306,87 +6397,86 @@ int mysqld_main(int argc, char **argv)
return 1;
}
- if (Service.GetOS()) /* true NT family */
+
+ char file_path[FN_REFLEN];
+ 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)
{
- char file_path[FN_REFLEN];
- 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 (!default_service_handling(argv, MYSQL_SERVICENAME, MYSQL_SERVICENAME,
+ file_path, "", NULL))
+ return 0;
- if (argc == 2)
- {
- if (!default_service_handling(argv, MYSQL_SERVICENAME, MYSQL_SERVICENAME,
- file_path, "", NULL))
- return 0;
- if (Service.IsService(argv[1])) /* Start an optional service */
- {
- /*
- Only add the service name to the groups read from the config file
- if it's not "MySQL". (The default service name should be 'mysqld'
- but we started a bad tradition by calling it MySQL from the start
- and we are now stuck with it.
- */
- if (my_strcasecmp(system_charset_info, argv[1],"mysql"))
- load_default_groups[load_default_groups_sz-2]= argv[1];
- start_mode= 1;
- Service.Init(argv[1], mysql_service);
- return 0;
- }
- }
- else if (argc == 3) /* install or remove any optional service */
- {
- if (!default_service_handling(argv, argv[2], argv[2], file_path, "",
- NULL))
- return 0;
- if (Service.IsService(argv[2]))
- {
- /*
- 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(argv[2], mysql_service);
- return 0;
- }
- }
- else if (argc == 4 || argc == 5)
+ if (Service.IsService(argv[1])) /* Start an optional service */
{
/*
- This may seem strange, because we handle --local-service while
- preserving 4.1's behavior of allowing any one other argument that is
- passed to the service on startup. (The assumption is that this is
- --defaults-file=file, but that was not enforced in 4.1, so we don't
- enforce it here.)
+ Only add the service name to the groups read from the config file
+ if it's not "MySQL". (The default service name should be 'mysqld'
+ but we started a bad tradition by calling it MySQL from the start
+ and we are now stuck with it.
*/
- const char *extra_opt= NullS;
- const char *account_name = NullS;
- int index;
- for (index = 3; index < argc; index++)
- {
- if (!strcmp(argv[index], "--local-service"))
- account_name= "NT AUTHORITY\\LocalService";
- else
- extra_opt= argv[index];
- }
-
- if (argc == 4 || account_name)
- if (!default_service_handling(argv, argv[2], argv[2], file_path,
- extra_opt, account_name))
- return 0;
+ if (my_strcasecmp(system_charset_info, argv[1],"mysql"))
+ load_default_groups[load_default_groups_sz-2]= argv[1];
+ start_mode= 1;
+ Service.Init(argv[1], mysql_service);
+ return 0;
}
- else if (argc == 1 && Service.IsService(MYSQL_SERVICENAME))
+ }
+ else if (argc == 3) /* install or remove any optional service */
+ {
+ if (!default_service_handling(argv, argv[2], argv[2], file_path, "",
+ NULL))
+ return 0;
+ if (Service.IsService(argv[2]))
{
- /* start the default service */
+ /*
+ 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;
- Service.Init(MYSQL_SERVICENAME, mysql_service);
+ if (my_strcasecmp(system_charset_info, argv[2],"mysql"))
+ load_default_groups[load_default_groups_sz-2]= argv[2];
+ Service.Init(argv[2], mysql_service);
return 0;
}
}
+ else if (argc == 4 || argc == 5)
+ {
+ /*
+ This may seem strange, because we handle --local-service while
+ preserving 4.1's behavior of allowing any one other argument that is
+ passed to the service on startup. (The assumption is that this is
+ --defaults-file=file, but that was not enforced in 4.1, so we don't
+ enforce it here.)
+ */
+ const char *extra_opt= NullS;
+ const char *account_name = NullS;
+ int index;
+ for (index = 3; index < argc; index++)
+ {
+ if (!strcmp(argv[index], "--local-service"))
+ account_name= "NT AUTHORITY\\LocalService";
+ else
+ extra_opt= argv[index];
+ }
+
+ if (argc == 4 || account_name)
+ if (!default_service_handling(argv, argv[2], argv[2], file_path,
+ extra_opt, account_name))
+ return 0;
+ }
+ else if (argc == 1 && Service.IsService(MYSQL_SERVICENAME))
+ {
+ /* start the default service */
+ start_mode= 1;
+ Service.Init(MYSQL_SERVICENAME, mysql_service);
+ return 0;
+ }
+
/* Start as standalone server */
Service.my_argc=argc;
Service.my_argv=argv;
@@ -6427,6 +6517,7 @@ static void bootstrap(MYSQL_FILE *file)
sql_print_warning("Can't create thread to handle bootstrap (errno= %d)",
error);
bootstrap_error=-1;
+ delete thd;
DBUG_VOID_RETURN;
}
/* Wait for thread to die */
@@ -7307,9 +7398,6 @@ struct my_option my_long_options[]=
{"autocommit", 0, "Set default value for autocommit (0 or 1)",
&opt_autocommit, &opt_autocommit, 0,
GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, NULL},
- {"bind-address", 0, "IP address to bind to.",
- &my_bind_addr_str, &my_bind_addr_str, 0, GET_STR,
- REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"binlog-do-db", OPT_BINLOG_DO_DB,
"Tells the master it should log updates for the specified database, "
"and exclude all others not explicitly mentioned.",
@@ -7323,7 +7411,7 @@ struct my_option my_long_options[]=
"The value has to be a multiple of 256.",
&opt_binlog_rows_event_max_size, &opt_binlog_rows_event_max_size,
0, GET_ULONG, REQUIRED_ARG,
- /* def_value */ 8192, /* min_value */ 256, /* max_value */ ULONG_MAX,
+ /* def_value */ 8192, /* min_value */ 256, /* max_value */ UINT_MAX32-1,
/* sub_size */ 0, /* block_size */ 256,
/* app_type */ 0
},
@@ -7367,6 +7455,10 @@ struct my_option my_long_options[]=
0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
#endif /* HAVE_REPLICATION */
#ifndef DBUG_OFF
+ {"debug-assert", 0,
+ "Allow DBUG_ASSERT() to invoke assert()",
+ &my_assert, &my_assert,
+ 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0},
{"debug-assert-on-error", 0,
"Do an assert in various functions if we get a fatal error",
&my_assert_on_error, &my_assert_on_error,
@@ -7394,13 +7486,6 @@ struct my_option my_long_options[]=
&max_binlog_dump_events, &max_binlog_dump_events, 0,
GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
#endif /* HAVE_REPLICATION */
-#ifdef SAFE_MUTEX
- {"debug-mutex-deadlock-detector", 0,
- "Enable checking of wrong mutex usage.",
- &safe_mutex_deadlock_detector,
- &safe_mutex_deadlock_detector,
- 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
-#endif
{"debug-no-sync", 0,
"Disables system sync calls. Only for running tests or debugging!",
&my_disable_sync, &my_disable_sync, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
@@ -7467,6 +7552,14 @@ struct my_option my_long_options[]=
"Set up signals usable for debugging. Deprecated, use --debug-gdb instead.",
&opt_debugging, &opt_debugging,
0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"gtid-pos-auto-engines", 0,
+ "List of engines for which to automatically create a "
+ "mysql.gtid_slave_pos_ENGINE table, if a transaction using that engine "
+ "is replicated. This can be used to avoid introducing cross-engine "
+ "transactions, if engines are used different from that used by table "
+ "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
{"super-large-pages", 0, "Enable support for super large pages.",
&opt_super_large_pages, &opt_super_large_pages, 0,
@@ -7763,6 +7856,7 @@ struct my_option my_long_options[]=
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"),
@@ -7863,7 +7957,7 @@ static int show_slaves_running(THD *thd, SHOW_VAR *var, char *buff)
var->type= SHOW_LONGLONG;
var->value= buff;
- *((longlong *)buff)= any_slave_sql_running();
+ *((longlong *)buff)= any_slave_sql_running(false);
return 0;
}
@@ -8235,6 +8329,27 @@ static int show_ssl_get_cipher_list(THD *thd, SHOW_VAR *var, char *buff,
return 0;
}
+#define SHOW_FNAME(name) \
+ rpl_semi_sync_master_show_##name
+
+#define DEF_SHOW_FUNC(name, show_type) \
+ static int SHOW_FNAME(name)(MYSQL_THD thd, SHOW_VAR *var, char *buff) \
+ { \
+ repl_semisync_master.set_export_stats(); \
+ var->type= show_type; \
+ var->value= (char *)&rpl_semi_sync_master_##name; \
+ return 0; \
+ }
+
+DEF_SHOW_FUNC(status, SHOW_BOOL)
+DEF_SHOW_FUNC(clients, SHOW_LONG)
+DEF_SHOW_FUNC(wait_sessions, SHOW_LONG)
+DEF_SHOW_FUNC(trx_wait_time, SHOW_LONGLONG)
+DEF_SHOW_FUNC(trx_wait_num, SHOW_LONGLONG)
+DEF_SHOW_FUNC(net_wait_time, SHOW_LONGLONG)
+DEF_SHOW_FUNC(net_wait_num, SHOW_LONGLONG)
+DEF_SHOW_FUNC(avg_net_wait_time, SHOW_LONG)
+DEF_SHOW_FUNC(avg_trx_wait_time, SHOW_LONG)
#ifdef HAVE_YASSL
@@ -8465,6 +8580,8 @@ SHOW_VAR status_vars[]= {
{"Busy_time", (char*) offsetof(STATUS_VAR, busy_time), SHOW_DOUBLE_STATUS},
{"Bytes_received", (char*) offsetof(STATUS_VAR, bytes_received), SHOW_LONGLONG_STATUS},
{"Bytes_sent", (char*) offsetof(STATUS_VAR, bytes_sent), SHOW_LONGLONG_STATUS},
+ {"Column_compressions", (char*) offsetof(STATUS_VAR, column_compressions), SHOW_LONG_STATUS},
+ {"Column_decompressions", (char*) offsetof(STATUS_VAR, column_decompressions), SHOW_LONG_STATUS},
{"Com", (char*) com_status_vars, SHOW_ARRAY},
{"Compression", (char*) &show_net_compression, SHOW_SIMPLE_FUNC},
{"Connections", (char*) &global_thread_id, SHOW_LONG_NOFLUSH},
@@ -8489,12 +8606,16 @@ SHOW_VAR status_vars[]= {
{"Executed_events", (char*) &executed_events, SHOW_LONG_NOFLUSH },
{"Executed_triggers", (char*) offsetof(STATUS_VAR, executed_triggers), SHOW_LONG_STATUS},
{"Feature_check_constraint", (char*) &feature_check_constraint, SHOW_LONG },
+ {"Feature_custom_aggregate_functions", (char*) offsetof(STATUS_VAR, feature_custom_aggregate_functions), SHOW_LONG_STATUS},
{"Feature_delay_key_write", (char*) &feature_files_opened_with_delayed_keys, SHOW_LONG },
{"Feature_dynamic_columns", (char*) offsetof(STATUS_VAR, feature_dynamic_columns), SHOW_LONG_STATUS},
{"Feature_fulltext", (char*) offsetof(STATUS_VAR, feature_fulltext), SHOW_LONG_STATUS},
{"Feature_gis", (char*) offsetof(STATUS_VAR, feature_gis), SHOW_LONG_STATUS},
+ {"Feature_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},
{"Feature_subquery", (char*) offsetof(STATUS_VAR, feature_subquery), SHOW_LONG_STATUS},
+ {"Feature_system_versioning", (char*) offsetof(STATUS_VAR, feature_system_versioning), SHOW_LONG_STATUS},
{"Feature_timezone", (char*) offsetof(STATUS_VAR, feature_timezone), SHOW_LONG_STATUS},
{"Feature_trigger", (char*) offsetof(STATUS_VAR, feature_trigger), SHOW_LONG_STATUS},
{"Feature_window_functions", (char*) offsetof(STATUS_VAR, feature_window_functions), SHOW_LONG_STATUS},
@@ -8503,7 +8624,7 @@ SHOW_VAR status_vars[]= {
{"Handler_commit", (char*) offsetof(STATUS_VAR, ha_commit_count), SHOW_LONG_STATUS},
{"Handler_delete", (char*) offsetof(STATUS_VAR, ha_delete_count), SHOW_LONG_STATUS},
{"Handler_discover", (char*) offsetof(STATUS_VAR, ha_discover_count), SHOW_LONG_STATUS},
- {"Handler_external_lock", (char*) offsetof(STATUS_VAR, ha_external_lock_count), SHOW_LONGLONG_STATUS},
+ {"Handler_external_lock", (char*) offsetof(STATUS_VAR, ha_external_lock_count), SHOW_LONG_STATUS},
{"Handler_icp_attempts", (char*) offsetof(STATUS_VAR, ha_icp_attempts), SHOW_LONG_STATUS},
{"Handler_icp_match", (char*) offsetof(STATUS_VAR, ha_icp_match), SHOW_LONG_STATUS},
{"Handler_mrr_init", (char*) offsetof(STATUS_VAR, ha_mrr_init_count), SHOW_LONG_STATUS},
@@ -8522,6 +8643,7 @@ SHOW_VAR status_vars[]= {
{"Handler_rollback", (char*) offsetof(STATUS_VAR, ha_rollback_count), SHOW_LONG_STATUS},
{"Handler_savepoint", (char*) offsetof(STATUS_VAR, ha_savepoint_count), SHOW_LONG_STATUS},
{"Handler_savepoint_rollback",(char*) offsetof(STATUS_VAR, ha_savepoint_rollback_count), SHOW_LONG_STATUS},
+ {"Handler_tmp_delete", (char*) offsetof(STATUS_VAR, ha_tmp_delete_count), SHOW_LONG_STATUS},
{"Handler_tmp_update", (char*) offsetof(STATUS_VAR, ha_tmp_update_count), SHOW_LONG_STATUS},
{"Handler_tmp_write", (char*) offsetof(STATUS_VAR, ha_tmp_write_count), SHOW_LONG_STATUS},
{"Handler_update", (char*) offsetof(STATUS_VAR, ha_update_count), SHOW_LONG_STATUS},
@@ -8534,8 +8656,9 @@ SHOW_VAR status_vars[]= {
{"Master_gtid_wait_time", (char*) offsetof(STATUS_VAR, master_gtid_wait_time), SHOW_LONG_STATUS},
{"Max_used_connections", (char*) &max_used_connections, SHOW_LONG},
{"Memory_used", (char*) &show_memory_used, SHOW_SIMPLE_FUNC},
+ {"Memory_used_initial", (char*) &start_memory_used, SHOW_LONGLONG},
{"Not_flushed_delayed_rows", (char*) &delayed_rows_in_use, SHOW_LONG_NOFLUSH},
- {"Open_files", (char*) &my_file_opened, SHOW_LONG_NOFLUSH},
+ {"Open_files", (char*) &my_file_opened, SHOW_SINT},
{"Open_streams", (char*) &my_stream_opened, SHOW_LONG_NOFLUSH},
{"Open_table_definitions", (char*) &show_table_definitions, SHOW_SIMPLE_FUNC},
{"Open_tables", (char*) &show_open_tables, SHOW_SIMPLE_FUNC},
@@ -8548,6 +8671,26 @@ SHOW_VAR status_vars[]= {
{"Rows_sent", (char*) offsetof(STATUS_VAR, rows_sent), SHOW_LONGLONG_STATUS},
{"Rows_read", (char*) offsetof(STATUS_VAR, rows_read), SHOW_LONGLONG_STATUS},
{"Rows_tmp_read", (char*) offsetof(STATUS_VAR, rows_tmp_read), SHOW_LONGLONG_STATUS},
+#ifdef HAVE_REPLICATION
+ {"Rpl_semi_sync_master_status", (char*) &SHOW_FNAME(status), SHOW_FUNC},
+ {"Rpl_semi_sync_master_clients", (char*) &SHOW_FNAME(clients), SHOW_FUNC},
+ {"Rpl_semi_sync_master_yes_tx", (char*) &rpl_semi_sync_master_yes_transactions, SHOW_LONG},
+ {"Rpl_semi_sync_master_no_tx", (char*) &rpl_semi_sync_master_no_transactions, SHOW_LONG},
+ {"Rpl_semi_sync_master_wait_sessions", (char*) &SHOW_FNAME(wait_sessions), SHOW_FUNC},
+ {"Rpl_semi_sync_master_no_times", (char*) &rpl_semi_sync_master_off_times, SHOW_LONG},
+ {"Rpl_semi_sync_master_timefunc_failures", (char*) &rpl_semi_sync_master_timefunc_fails, SHOW_LONG},
+ {"Rpl_semi_sync_master_wait_pos_backtraverse", (char*) &rpl_semi_sync_master_wait_pos_backtraverse, SHOW_LONG},
+ {"Rpl_semi_sync_master_tx_wait_time", (char*) &SHOW_FNAME(trx_wait_time), SHOW_FUNC},
+ {"Rpl_semi_sync_master_tx_waits", (char*) &SHOW_FNAME(trx_wait_num), SHOW_FUNC},
+ {"Rpl_semi_sync_master_tx_avg_wait_time", (char*) &SHOW_FNAME(avg_trx_wait_time), SHOW_FUNC},
+ {"Rpl_semi_sync_master_net_wait_time", (char*) &SHOW_FNAME(net_wait_time), SHOW_FUNC},
+ {"Rpl_semi_sync_master_net_waits", (char*) &SHOW_FNAME(net_wait_num), SHOW_FUNC},
+ {"Rpl_semi_sync_master_net_avg_wait_time", (char*) &SHOW_FNAME(avg_net_wait_time), SHOW_FUNC},
+ {"Rpl_semi_sync_master_request_ack", (char*) &rpl_semi_sync_master_request_ack, SHOW_LONGLONG},
+ {"Rpl_semi_sync_master_get_ack", (char*)&rpl_semi_sync_master_get_ack, SHOW_LONGLONG},
+ {"Rpl_semi_sync_slave_status", (char*) &rpl_semi_sync_slave_status, SHOW_BOOL},
+ {"Rpl_semi_sync_slave_send_ack", (char*) &rpl_semi_sync_slave_send_ack, SHOW_LONGLONG},
+#endif /* HAVE_REPLICATION */
#ifdef HAVE_QUERY_CACHE
{"Qcache_free_blocks", (char*) &query_cache.free_memory_blocks, SHOW_LONG_NOFLUSH},
{"Qcache_free_memory", (char*) &query_cache.free_memory, SHOW_LONG_NOFLUSH},
@@ -8624,6 +8767,10 @@ SHOW_VAR status_vars[]= {
{"Subquery_cache_miss", (char*) &subquery_cache_miss, SHOW_LONG},
{"Table_locks_immediate", (char*) &locks_immediate, SHOW_LONG},
{"Table_locks_waited", (char*) &locks_waited, SHOW_LONG},
+ {"Table_open_cache_active_instances", (char*) &tc_active_instances, SHOW_UINT},
+ {"Table_open_cache_hits", (char*) offsetof(STATUS_VAR, table_open_cache_hits), SHOW_LONGLONG_STATUS},
+ {"Table_open_cache_misses", (char*) offsetof(STATUS_VAR, table_open_cache_misses), SHOW_LONGLONG_STATUS},
+ {"Table_open_cache_overflows", (char*) offsetof(STATUS_VAR, table_open_cache_overflows), SHOW_LONGLONG_STATUS},
#ifdef HAVE_MMAP
{"Tc_log_max_pages_used", (char*) &tc_log_max_pages_used, SHOW_LONG},
{"Tc_log_page_size", (char*) &tc_log_page_size, SHOW_LONG_NOFLUSH},
@@ -8636,7 +8783,10 @@ SHOW_VAR status_vars[]= {
{"Threads_cached", (char*) &cached_thread_count, SHOW_LONG_NOFLUSH},
{"Threads_connected", (char*) &connection_count, SHOW_INT},
{"Threads_created", (char*) &thread_created, SHOW_LONG_NOFLUSH},
- {"Threads_running", (char*) &thread_running, SHOW_INT},
+ {"Threads_running", (char*) offsetof(STATUS_VAR, threads_running), SHOW_UINT32_STATUS},
+ {"Transactions_multi_engine", (char*) &transactions_multi_engine, SHOW_LONG},
+ {"Rpl_transactions_multi_engine", (char*) &rpl_transactions_multi_engine, SHOW_LONG},
+ {"Transactions_gtid_foreign_engine", (char*) &transactions_gtid_foreign_engine, SHOW_LONG},
{"Update_scan", (char*) offsetof(STATUS_VAR, update_scan_count), SHOW_LONG_STATUS},
{"Uptime", (char*) &show_starttime, SHOW_SIMPLE_FUNC},
#ifdef ENABLED_PROFILING
@@ -8701,7 +8851,7 @@ static int option_cmp(my_option *a, my_option *b)
static void print_help()
{
MEM_ROOT mem_root;
- init_alloc_root(&mem_root, 4096, 4096, MYF(0));
+ init_alloc_root(&mem_root, "help", 4096, 4096, MYF(0));
pop_dynamic(&all_options);
add_many_options(&all_options, pfs_early_options,
@@ -8810,7 +8960,7 @@ static int mysql_init_variables(void)
kill_in_progress= 0;
cleanup_done= 0;
test_flags= select_errors= dropping_tables= ha_open_options=0;
- thread_count= thread_running= kill_cached_threads= wake_thread= 0;
+ thread_count= kill_cached_threads= wake_thread= 0;
service_thread_count= 0;
slave_open_temp_tables= 0;
cached_thread_count= 0;
@@ -8880,6 +9030,9 @@ static int mysql_init_variables(void)
report_user= report_password = report_host= 0; /* TO BE DELETED */
opt_relay_logname= opt_relaylog_index_name= 0;
slave_retried_transactions= 0;
+ transactions_multi_engine= 0;
+ rpl_transactions_multi_engine= 0;
+ transactions_gtid_foreign_engine= 0;
log_bin_basename= NULL;
log_bin_index= NULL;
@@ -8906,7 +9059,7 @@ static int mysql_init_variables(void)
#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
have_ssl=SHOW_OPTION_YES;
-#if HAVE_YASSL
+#if defined(HAVE_YASSL)
have_openssl= SHOW_OPTION_NO;
#else
have_openssl= SHOW_OPTION_YES;
@@ -9034,12 +9187,12 @@ mysqld_get_one_option(int optid, const struct my_option *opt, char *argument)
opt->name);
break;
case OPT_MYSQL_COMPATIBILITY:
- sql_print_warning("'%s' is MySQL 5.6 compatible option. Not used or needed "
- "in MariaDB.", opt->name);
+ sql_print_warning("'%s' is MySQL 5.6 / 5.7 compatible option. Not used or "
+ "needed in MariaDB.", opt->name);
break;
case OPT_MYSQL_TO_BE_IMPLEMENTED:
- sql_print_warning("'%s' is MySQL 5.6 compatible option. To be implemented "
- "in later versions.", opt->name);
+ sql_print_warning("'%s' is MySQL 5.6 / 5.7 compatible option. To be "
+ "implemented in later versions.", opt->name);
break;
case 'a':
SYSVAR_AUTOSIZE(global_system_variables.sql_mode, MODE_ANSI);
@@ -9462,7 +9615,7 @@ mysql_getopt_value(const char *name, uint length,
case OPT_KEY_CACHE_CHANGED_BLOCKS_HASH_SIZE:
{
KEY_CACHE *key_cache;
- if (!(key_cache= get_or_create_key_cache(name, length)))
+ if (unlikely(!(key_cache= get_or_create_key_cache(name, length))))
{
if (error)
*error= EXIT_OUT_OF_MEMORY;
@@ -9592,17 +9745,6 @@ static int get_options(int *argc_ptr, char ***argv_ptr)
global_system_variables.max_allowed_packet);
}
-#if MYSQL_VERSION_ID > 101001
- /*
- TIMESTAMP columns get implicit DEFAULT values when
- --explicit_defaults_for_timestamp is not set.
- */
- if (!opt_help && !opt_explicit_defaults_for_timestamp)
- sql_print_warning("TIMESTAMP with implicit DEFAULT value is deprecated. "
- "Please use --explicit_defaults_for_timestamp server "
- "option (see documentation for more details).");
-#endif
-
if (log_error_file_ptr != disabled_my_option)
opt_error_log= 1;
else
@@ -9631,8 +9773,10 @@ static int get_options(int *argc_ptr, char ***argv_ptr)
flush_time= 0;
#ifdef HAVE_REPLICATION
- if (opt_slave_skip_errors)
- init_slave_skip_errors(opt_slave_skip_errors);
+ if (init_slave_skip_errors(opt_slave_skip_errors))
+ return 1;
+ if (init_slave_transaction_retry_errors(opt_slave_transaction_retry_errors))
+ return 1;
#endif
if (global_system_variables.max_join_size == HA_POS_ERROR)
@@ -9799,7 +9943,7 @@ static int get_options(int *argc_ptr, char ***argv_ptr)
/* Ensure that some variables are not set higher than needed */
if (thread_cache_size > max_connections)
SYSVAR_AUTOSIZE(thread_cache_size, max_connections);
-
+
return 0;
}
@@ -9947,7 +10091,7 @@ static int fix_paths(void)
my_realpath(mysql_unpacked_real_data_home, mysql_real_data_home, MYF(0));
mysql_unpacked_real_data_home_len=
- (int) strlen(mysql_unpacked_real_data_home);
+ strlen(mysql_unpacked_real_data_home);
if (mysql_unpacked_real_data_home[mysql_unpacked_real_data_home_len-1] == FN_LIBCHAR)
--mysql_unpacked_real_data_home_len;
@@ -10169,108 +10313,118 @@ static PSI_file_info all_server_files[]=
};
#endif /* HAVE_PSI_INTERFACE */
-PSI_stage_info stage_after_apply_event= { 0, "after apply log event", 0};
+PSI_stage_info stage_after_apply_event= { 0, "After apply log event", 0};
PSI_stage_info stage_after_create= { 0, "After create", 0};
PSI_stage_info stage_after_opening_tables= { 0, "After opening tables", 0};
PSI_stage_info stage_after_table_lock= { 0, "After table lock", 0};
-PSI_stage_info stage_allocating_local_table= { 0, "allocating local table", 0};
-PSI_stage_info stage_alter_inplace_prepare= { 0, "preparing for alter table", 0};
-PSI_stage_info stage_alter_inplace= { 0, "altering table", 0};
-PSI_stage_info stage_alter_inplace_commit= { 0, "committing alter table to storage engine", 0};
-PSI_stage_info stage_apply_event= { 0, "apply log event", 0};
+PSI_stage_info stage_allocating_local_table= { 0, "Allocating local table", 0};
+PSI_stage_info stage_alter_inplace_prepare= { 0, "Preparing for alter table", 0};
+PSI_stage_info stage_alter_inplace= { 0, "Altering table", 0};
+PSI_stage_info stage_alter_inplace_commit= { 0, "Committing alter table to storage engine", 0};
+PSI_stage_info stage_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_query_cache_for_query= { 0, "checking query cache for query", 0};
-PSI_stage_info stage_cleaning_up= { 0, "cleaning up", 0};
-PSI_stage_info stage_closing_tables= { 0, "closing tables", 0};
+PSI_stage_info stage_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_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_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", 0};
PSI_stage_info stage_creating_delayed_handler= { 0, "Creating delayed handler", 0};
PSI_stage_info stage_creating_sort_index= { 0, "Creating sort index", 0};
-PSI_stage_info stage_creating_table= { 0, "creating table", 0};
+PSI_stage_info stage_creating_table= { 0, "Creating table", 0};
PSI_stage_info stage_creating_tmp_table= { 0, "Creating tmp table", 0};
-PSI_stage_info stage_deleting_from_main_table= { 0, "deleting from main table", 0};
-PSI_stage_info stage_deleting_from_reference_tables= { 0, "deleting from reference tables", 0};
-PSI_stage_info stage_discard_or_import_tablespace= { 0, "discard_or_import_tablespace", 0};
-PSI_stage_info stage_enabling_keys= { 0, "enabling keys", 0};
-PSI_stage_info stage_end= { 0, "end", 0};
-PSI_stage_info stage_executing= { 0, "executing", 0};
+PSI_stage_info stage_deleting_from_main_table= { 0, "Deleting from main table", 0};
+PSI_stage_info stage_deleting_from_reference_tables= { 0, "Deleting from reference tables", 0};
+PSI_stage_info stage_discard_or_import_tablespace= { 0, "Discard_or_import_tablespace", 0};
+PSI_stage_info stage_enabling_keys= { 0, "Enabling keys", 0};
+PSI_stage_info stage_end= { 0, "End of update loop", 0};
+PSI_stage_info stage_executing= { 0, "Executing", 0};
PSI_stage_info stage_execution_of_init_command= { 0, "Execution of init_command", 0};
-PSI_stage_info stage_explaining= { 0, "explaining", 0};
+PSI_stage_info stage_explaining= { 0, "Explaining", 0};
PSI_stage_info stage_finding_key_cache= { 0, "Finding key cache", 0};
PSI_stage_info stage_finished_reading_one_binlog_switching_to_next_binlog= { 0, "Finished reading one binlog; switching to next binlog", 0};
PSI_stage_info stage_flushing_relay_log_and_master_info_repository= { 0, "Flushing relay log and master info repository.", 0};
PSI_stage_info stage_flushing_relay_log_info_file= { 0, "Flushing relay-log info file.", 0};
-PSI_stage_info stage_freeing_items= { 0, "freeing items", 0};
-PSI_stage_info stage_fulltext_initialization= { 0, "FULLTEXT initialization", 0};
-PSI_stage_info stage_got_handler_lock= { 0, "got handler lock", 0};
-PSI_stage_info stage_got_old_table= { 0, "got old table", 0};
-PSI_stage_info stage_init= { 0, "init", 0};
-PSI_stage_info stage_insert= { 0, "insert", 0};
-PSI_stage_info stage_invalidating_query_cache_entries_table= { 0, "invalidating query cache entries (table)", 0};
-PSI_stage_info stage_invalidating_query_cache_entries_table_list= { 0, "invalidating query cache entries (table list)", 0};
+PSI_stage_info stage_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_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_logging_slow_query= { 0, "Logging slow query", 0};
PSI_stage_info stage_making_temp_file_append_before_load_data= { 0, "Making temporary file (append) before replaying LOAD DATA INFILE.", 0};
PSI_stage_info stage_making_temp_file_create_before_load_data= { 0, "Making temporary file (create) before replaying LOAD DATA INFILE.", 0};
-PSI_stage_info stage_manage_keys= { 0, "manage keys", 0};
+PSI_stage_info stage_manage_keys= { 0, "Manage keys", 0};
PSI_stage_info stage_master_has_sent_all_binlog_to_slave= { 0, "Master has sent all binlog to slave; waiting for binlog to be updated", 0};
PSI_stage_info stage_opening_tables= { 0, "Opening tables", 0};
-PSI_stage_info stage_optimizing= { 0, "optimizing", 0};
-PSI_stage_info stage_preparing= { 0, "preparing", 0};
+PSI_stage_info stage_optimizing= { 0, "Optimizing", 0};
+PSI_stage_info stage_preparing= { 0, "Preparing", 0};
PSI_stage_info stage_purging_old_relay_logs= { 0, "Purging old relay logs", 0};
-PSI_stage_info stage_query_end= { 0, "query end", 0};
+PSI_stage_info stage_query_end= { 0, "Query end", 0};
+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_queueing_master_event_to_the_relay_log= { 0, "Queueing master event to the relay log", 0};
PSI_stage_info stage_reading_event_from_the_relay_log= { 0, "Reading event from the relay log", 0};
-PSI_stage_info stage_recreating_table= { 0, "recreating table", 0};
+PSI_stage_info stage_recreating_table= { 0, "Recreating table", 0};
PSI_stage_info stage_registering_slave_on_master= { 0, "Registering slave on master", 0};
PSI_stage_info stage_removing_duplicates= { 0, "Removing duplicates", 0};
-PSI_stage_info stage_removing_tmp_table= { 0, "removing tmp table", 0};
-PSI_stage_info stage_rename= { 0, "rename", 0};
-PSI_stage_info stage_rename_result_table= { 0, "rename result table", 0};
+PSI_stage_info stage_removing_tmp_table= { 0, "Removing tmp table", 0};
+PSI_stage_info stage_rename= { 0, "Rename", 0};
+PSI_stage_info stage_rename_result_table= { 0, "Rename result table", 0};
PSI_stage_info stage_requesting_binlog_dump= { 0, "Requesting binlog dump", 0};
-PSI_stage_info stage_reschedule= { 0, "reschedule", 0};
+PSI_stage_info stage_reschedule= { 0, "Reschedule", 0};
PSI_stage_info stage_searching_rows_for_update= { 0, "Searching rows for update", 0};
PSI_stage_info stage_sending_binlog_event_to_slave= { 0, "Sending binlog event to slave", 0};
-PSI_stage_info stage_sending_cached_result_to_client= { 0, "sending cached result to client", 0};
+PSI_stage_info stage_sending_cached_result_to_client= { 0, "Sending cached result to client", 0};
PSI_stage_info stage_sending_data= { 0, "Sending data", 0};
-PSI_stage_info stage_setup= { 0, "setup", 0};
-PSI_stage_info stage_show_explain= { 0, "show explain", 0};
+PSI_stage_info stage_setup= { 0, "Setup", 0};
+PSI_stage_info stage_show_explain= { 0, "Show explain", 0};
PSI_stage_info stage_slave_has_read_all_relay_log= { 0, "Slave has read all relay log; waiting for the slave I/O thread to update it", 0};
PSI_stage_info stage_sorting= { 0, "Sorting", 0};
PSI_stage_info stage_sorting_for_group= { 0, "Sorting for group", 0};
PSI_stage_info stage_sorting_for_order= { 0, "Sorting for order", 0};
PSI_stage_info stage_sorting_result= { 0, "Sorting result", 0};
-PSI_stage_info stage_statistics= { 0, "statistics", 0};
+PSI_stage_info stage_statistics= { 0, "Statistics", 0};
PSI_stage_info stage_sql_thd_waiting_until_delay= { 0, "Waiting until MASTER_DELAY seconds after master executed event", 0 };
-PSI_stage_info stage_storing_result_in_query_cache= { 0, "storing result in query cache", 0};
-PSI_stage_info stage_storing_row_into_queue= { 0, "storing row into queue", 0};
+PSI_stage_info stage_storing_result_in_query_cache= { 0, "Storing result in query cache", 0};
+PSI_stage_info stage_storing_row_into_queue= { 0, "Storing row into queue", 0};
PSI_stage_info stage_system_lock= { 0, "System lock", 0};
PSI_stage_info stage_unlocking_tables= { 0, "Unlocking tables", 0};
PSI_stage_info stage_table_lock= { 0, "Table lock", 0};
PSI_stage_info stage_filling_schema_table= { 0, "Filling schema table", 0};
-PSI_stage_info stage_update= { 0, "update", 0};
-PSI_stage_info stage_updating= { 0, "updating", 0};
-PSI_stage_info stage_updating_main_table= { 0, "updating main table", 0};
-PSI_stage_info stage_updating_reference_tables= { 0, "updating reference tables", 0};
-PSI_stage_info stage_upgrading_lock= { 0, "upgrading lock", 0};
+PSI_stage_info stage_update= { 0, "Update", 0};
+PSI_stage_info stage_updating= { 0, "Updating", 0};
+PSI_stage_info stage_updating_main_table= { 0, "Updating main table", 0};
+PSI_stage_info stage_updating_reference_tables= { 0, "Updating reference tables", 0};
+PSI_stage_info stage_upgrading_lock= { 0, "Upgrading lock", 0};
PSI_stage_info stage_user_lock= { 0, "User lock", 0};
PSI_stage_info stage_user_sleep= { 0, "User sleep", 0};
-PSI_stage_info stage_verifying_table= { 0, "verifying table", 0};
-PSI_stage_info stage_waiting_for_delay_list= { 0, "waiting for delay_list", 0};
-PSI_stage_info stage_waiting_for_gtid_to_be_written_to_binary_log= { 0, "waiting for GTID to be written to binary log", 0};
-PSI_stage_info stage_waiting_for_handler_insert= { 0, "waiting for handler insert", 0};
-PSI_stage_info stage_waiting_for_handler_lock= { 0, "waiting for handler lock", 0};
-PSI_stage_info stage_waiting_for_handler_open= { 0, "waiting for handler open", 0};
+PSI_stage_info stage_verifying_table= { 0, "Verifying table", 0};
+PSI_stage_info stage_waiting_for_delay_list= { 0, "Waiting for delay_list", 0};
+PSI_stage_info stage_waiting_for_gtid_to_be_written_to_binary_log= { 0, "Waiting for GTID to be written to binary log", 0};
+PSI_stage_info stage_waiting_for_handler_insert= { 0, "Waiting for handler insert", 0};
+PSI_stage_info stage_waiting_for_handler_lock= { 0, "Waiting for handler lock", 0};
+PSI_stage_info stage_waiting_for_handler_open= { 0, "Waiting for handler open", 0};
PSI_stage_info stage_waiting_for_insert= { 0, "Waiting for INSERT", 0};
PSI_stage_info stage_waiting_for_master_to_send_event= { 0, "Waiting for master to send event", 0};
PSI_stage_info stage_waiting_for_master_update= { 0, "Waiting for master update", 0};
PSI_stage_info stage_waiting_for_relay_log_space= { 0, "Waiting for the slave SQL thread to free enough relay log space", 0};
+PSI_stage_info stage_waiting_for_semi_sync_ack_from_slave=
+{ 0, "Waiting for semi-sync ACK from slave", 0};
+PSI_stage_info stage_waiting_for_semi_sync_slave={ 0, "Waiting for semi-sync slave connection", 0};
+PSI_stage_info stage_reading_semi_sync_ack={ 0, "Reading semi-sync ACK from slave", 0};
PSI_stage_info stage_waiting_for_slave_mutex_on_exit= { 0, "Waiting for slave mutex on exit", 0};
PSI_stage_info stage_waiting_for_slave_thread_to_start= { 0, "Waiting for slave thread to start", 0};
PSI_stage_info stage_waiting_for_table_flush= { 0, "Waiting for table flush", 0};
@@ -10280,6 +10434,7 @@ PSI_stage_info stage_waiting_for_the_slave_thread_to_advance_position= { 0, "Wai
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};
@@ -10310,6 +10465,7 @@ PSI_stage_info *all_server_stages[]=
& stage_alter_inplace_commit,
& stage_alter_inplace_prepare,
& stage_apply_event,
+ & stage_binlog_write,
& stage_binlog_processing_checkpoint_notify,
& stage_binlog_stopping_background_thread,
& stage_binlog_waiting_background_tasks,
@@ -10320,6 +10476,8 @@ PSI_stage_info *all_server_stages[]=
& stage_checking_query_cache_for_query,
& stage_cleaning_up,
& stage_closing_tables,
+ & stage_commit,
+ & stage_commit_implicit,
& stage_connecting_to_master,
& stage_converting_heap_to_myisam,
& stage_copy_to_tmp_table,
@@ -10346,6 +10504,7 @@ PSI_stage_info *all_server_stages[]=
& stage_got_handler_lock,
& stage_got_old_table,
& stage_init,
+ & stage_init_update,
& stage_insert,
& stage_invalidating_query_cache_entries_table,
& stage_invalidating_query_cache_entries_table_list,
@@ -10359,6 +10518,7 @@ PSI_stage_info *all_server_stages[]=
& stage_optimizing,
& stage_preparing,
& stage_purging_old_relay_logs,
+ & stage_starting_cleanup,
& stage_query_end,
& stage_queueing_master_event_to_the_relay_log,
& stage_reading_event_from_the_relay_log,
@@ -10370,6 +10530,8 @@ PSI_stage_info *all_server_stages[]=
& stage_rename_result_table,
& stage_requesting_binlog_dump,
& stage_reschedule,
+ & stage_rollback,
+ & stage_rollback_implicit,
& stage_searching_rows_for_update,
& stage_sending_binlog_event_to_slave,
& stage_sending_cached_result_to_client,
@@ -10423,6 +10585,9 @@ PSI_stage_info *all_server_stages[]=
& stage_gtid_wait_other_connection,
& stage_slave_background_process_request,
& stage_slave_background_wait_request,
+ & stage_waiting_for_semi_sync_ack_from_slave,
+ & stage_waiting_for_semi_sync_slave,
+ & stage_reading_semi_sync_ack,
& stage_waiting_for_deadlock_kill
};
diff --git a/sql/mysqld.h b/sql/mysqld.h
index 2947734901c..dc0641502ce 100644
--- a/sql/mysqld.h
+++ b/sql/mysqld.h
@@ -17,9 +17,9 @@
#ifndef MYSQLD_INCLUDED
#define MYSQLD_INCLUDED
-#include <my_global.h> /* MYSQL_PLUGIN_IMPORT, FN_REFLEN, FN_EXTLEN */
#include "sql_basic_types.h" /* query_id_t */
#include "sql_mode.h" /* Sql_mode_dependency */
+#include "sql_plugin.h"
#include "sql_bitmap.h" /* Bitmap */
#include "my_decimal.h" /* my_decimal */
#include "mysql_com.h" /* SERVER_VERSION_LENGTH */
@@ -38,7 +38,6 @@ class Time_zone;
struct scheduler_functions;
-typedef struct st_mysql_const_lex_string LEX_CSTRING;
typedef struct st_mysql_show_var SHOW_VAR;
#if MAX_INDEXES <= 64
@@ -117,7 +116,6 @@ extern my_bool opt_backup_progress_log;
extern my_bool opt_support_flashback;
extern ulonglong log_output_options;
extern ulong log_backup_output_options;
-extern my_bool opt_log_queries_not_using_indexes;
extern bool opt_disable_networking, opt_skip_show_db;
extern bool opt_skip_name_resolve;
extern bool opt_ignore_builtin_innodb;
@@ -131,6 +129,9 @@ extern my_bool opt_safe_show_db, opt_local_infile, opt_myisam_use_mmap;
extern my_bool opt_slave_compressed_protocol, use_temp_pool;
extern ulong slave_exec_mode_options, slave_ddl_exec_mode_options;
extern ulong slave_retried_transactions;
+extern ulong transactions_multi_engine;
+extern ulong rpl_transactions_multi_engine;
+extern ulong transactions_gtid_foreign_engine;
extern ulong slave_run_triggers_for_rbr;
extern ulonglong slave_type_conversions_options;
extern my_bool read_only, opt_readonly;
@@ -141,7 +142,6 @@ extern const char *current_dbug_option;
extern char* opt_secure_file_priv;
extern char* opt_secure_backup_file_priv;
extern size_t opt_secure_backup_file_priv_len;
-extern my_bool opt_log_slow_admin_statements, opt_log_slow_slave_statements;
extern my_bool sp_automatic_privileges, opt_noacl;
extern ulong use_stat_tables;
extern my_bool opt_old_style_user_limits, trust_function_creators;
@@ -152,8 +152,11 @@ extern my_bool opt_enable_shared_memory;
extern ulong opt_replicate_events_marked_for_skip;
extern char *default_tz_name;
extern Time_zone *default_tz;
+extern char *my_bind_addr_str;
extern char *default_storage_engine, *default_tmp_storage_engine;
extern char *enforced_storage_engine;
+extern char *gtid_pos_auto_engines;
+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;
@@ -178,6 +181,32 @@ extern char *opt_backup_history_logname, *opt_backup_progress_logname,
*opt_backup_settings_name;
extern const char *log_output_str;
extern const char *log_backup_output_str;
+
+/* System Versioning begin */
+enum vers_system_time_t
+{
+ SYSTEM_TIME_UNSPECIFIED = 0,
+ SYSTEM_TIME_AS_OF,
+ SYSTEM_TIME_FROM_TO,
+ SYSTEM_TIME_BETWEEN,
+ SYSTEM_TIME_BEFORE, // used for DELETE HISTORY ... BEFORE
+ SYSTEM_TIME_HISTORY, // used for DELETE HISTORY
+ SYSTEM_TIME_ALL
+};
+
+struct vers_asof_timestamp_t
+{
+ ulong type;
+ MYSQL_TIME ltime;
+};
+
+enum vers_alter_history_enum
+{
+ VERS_ALTER_HISTORY_ERROR= 0,
+ VERS_ALTER_HISTORY_KEEP
+};
+/* System Versioning end */
+
extern char *mysql_home_ptr, *pidfile_name_ptr;
extern MYSQL_PLUGIN_IMPORT char glob_hostname[FN_REFLEN];
extern char mysql_home[FN_REFLEN];
@@ -207,13 +236,14 @@ extern my_bool slave_allow_batching;
extern my_bool allow_slave_start;
extern LEX_CSTRING reason_slave_blocked;
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;
-extern ulonglong binlog_cache_size, binlog_stmt_cache_size;
+extern ulonglong binlog_cache_size, binlog_stmt_cache_size, binlog_file_cache_size;
extern ulonglong max_binlog_cache_size, max_binlog_stmt_cache_size;
extern ulong max_binlog_size;
extern ulong slave_max_allowed_packet;
@@ -234,13 +264,13 @@ extern "C" MYSQL_PLUGIN_IMPORT ulong server_id;
extern ulong concurrency;
extern time_t server_start_time, flush_status_time;
extern char *opt_mysql_tmpdir, mysql_charsets_dir[];
-extern int mysql_unpacked_real_data_home_len;
+extern size_t mysql_unpacked_real_data_home_len;
extern MYSQL_PLUGIN_IMPORT MY_TMPDIR mysql_tmpdir_list;
-extern const char *first_keyword, *delayed_user, *binary_keyword;
+extern const char *first_keyword, *delayed_user;
extern MYSQL_PLUGIN_IMPORT const char *my_localhost;
extern MYSQL_PLUGIN_IMPORT const char **errmesg; /* Error messages */
extern const char *myisam_recover_options_str;
-extern const char *in_left_expr_name, *in_additional_cond, *in_having_cond;
+extern const LEX_CSTRING in_left_expr_name, in_additional_cond, in_having_cond;
extern SHOW_VAR status_vars[];
extern struct system_variables max_system_variables;
extern struct system_status_var global_status_var;
@@ -273,6 +303,9 @@ extern my_bool encrypt_binlog;
extern my_bool encrypt_tmp_disk_tables, encrypt_tmp_files;
extern ulong encryption_algorithm;
extern const char *encryption_algorithm_names[];
+extern long opt_secure_timestamp;
+
+enum secure_timestamp { SECTIME_NO, SECTIME_SUPER, SECTIME_REPL, SECTIME_YES };
#ifdef HAVE_PSI_INTERFACE
#ifdef HAVE_MMAP
@@ -286,7 +319,7 @@ extern PSI_mutex_key key_LOCK_des_key_file;
extern PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list,
key_BINLOG_LOCK_binlog_background_thread,
- m_key_LOCK_binlog_end_pos,
+ 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_delayed_insert, key_LOCK_delayed_status, key_LOCK_error_log,
@@ -306,17 +339,21 @@ extern PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list,
key_LOCK_start_thread,
key_LOCK_error_messages, key_LOCK_thread_count, key_PARTITION_LOCK_auto_inc;
extern PSI_mutex_key key_RELAYLOG_LOCK_index;
+extern PSI_mutex_key key_LOCK_relaylog_end_pos;
extern PSI_mutex_key key_LOCK_slave_state, key_LOCK_binlog_state,
key_LOCK_rpl_thread, key_LOCK_rpl_thread_pool, key_LOCK_parallel_entry;
extern PSI_mutex_key key_TABLE_SHARE_LOCK_share, key_LOCK_stats,
key_LOCK_global_user_client_stats, key_LOCK_global_table_stats,
- key_LOCK_global_index_stats, key_LOCK_wakeup_ready, key_LOCK_wait_commit;
+ key_LOCK_global_index_stats, key_LOCK_wakeup_ready, key_LOCK_wait_commit,
+ key_TABLE_SHARE_LOCK_rotation;
extern PSI_mutex_key key_LOCK_gtid_waiting;
extern PSI_rwlock_key key_rwlock_LOCK_grant, key_rwlock_LOCK_logger,
key_rwlock_LOCK_sys_init_connect, key_rwlock_LOCK_sys_init_slave,
- key_rwlock_LOCK_system_variables_hash, key_rwlock_query_cache_query_lock;
+ key_rwlock_LOCK_system_variables_hash, key_rwlock_query_cache_query_lock,
+ key_LOCK_SEQUENCE,
+ key_rwlock_LOCK_vers_stats, key_rwlock_LOCK_stat_serial;
#ifdef HAVE_MMAP
extern PSI_cond_key key_PAGE_cond, key_COND_active, key_COND_pool;
@@ -337,7 +374,8 @@ extern PSI_cond_key key_BINLOG_COND_xid_list, key_BINLOG_update_cond,
key_TABLE_SHARE_cond, key_user_level_lock_cond,
key_COND_start_thread,
key_COND_thread_count, key_COND_thread_cache, key_COND_flush_thread_cache;
-extern PSI_cond_key key_RELAYLOG_update_cond, key_COND_wakeup_ready,
+extern PSI_cond_key key_RELAYLOG_COND_relay_log_updated,
+ key_RELAYLOG_COND_bin_log_updated, key_COND_wakeup_ready,
key_COND_wait_commit;
extern PSI_cond_key key_RELAYLOG_COND_queue_busy;
extern PSI_cond_key key_TC_LOG_MMAP_COND_queue_busy;
@@ -345,6 +383,7 @@ extern PSI_cond_key key_COND_rpl_thread, key_COND_rpl_thread_queue,
key_COND_rpl_thread_stop, key_COND_rpl_thread_pool,
key_COND_parallel_entry, key_COND_group_commit_orderer;
extern PSI_cond_key key_COND_wait_gtid, key_COND_gtid_ignore_duplicates;
+extern PSI_cond_key key_TABLE_SHARE_COND_rotation;
extern PSI_thread_key key_thread_bootstrap, key_thread_delayed_insert,
key_thread_handle_manager, key_thread_kill_server, key_thread_main,
@@ -413,6 +452,7 @@ extern PSI_stage_info stage_fulltext_initialization;
extern PSI_stage_info stage_got_handler_lock;
extern PSI_stage_info stage_got_old_table;
extern PSI_stage_info stage_init;
+extern PSI_stage_info stage_init_update;
extern PSI_stage_info stage_insert;
extern PSI_stage_info stage_invalidating_query_cache_entries_table;
extern PSI_stage_info stage_invalidating_query_cache_entries_table_list;
@@ -427,6 +467,11 @@ extern PSI_stage_info stage_optimizing;
extern PSI_stage_info stage_preparing;
extern PSI_stage_info stage_purging_old_relay_logs;
extern PSI_stage_info stage_query_end;
+extern PSI_stage_info stage_starting_cleanup;
+extern PSI_stage_info stage_rollback;
+extern PSI_stage_info stage_rollback_implicit;
+extern PSI_stage_info stage_commit;
+extern PSI_stage_info stage_commit_implicit;
extern PSI_stage_info stage_queueing_master_event_to_the_relay_log;
extern PSI_stage_info stage_reading_event_from_the_relay_log;
extern PSI_stage_info stage_recreating_table;
@@ -482,6 +527,7 @@ 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;
extern PSI_stage_info stage_binlog_stopping_background_thread;
extern PSI_stage_info stage_waiting_for_work_from_sql_thread;
@@ -554,6 +600,7 @@ extern MYSQL_PLUGIN_IMPORT char mysql_real_data_home[];
extern char mysql_unpacked_real_data_home[];
extern MYSQL_PLUGIN_IMPORT struct system_variables global_system_variables;
extern char default_logfile_name[FN_REFLEN];
+extern char *my_proxy_protocol_networks;
#define mysql_tmpdir (my_tmpdir(&mysql_tmpdir_list))
@@ -584,7 +631,6 @@ extern mysql_prlock_t LOCK_system_variables_hash;
extern mysql_cond_t COND_thread_count, COND_start_thread;
extern mysql_cond_t COND_manager;
extern mysql_cond_t COND_slave_background;
-extern int32 thread_running;
extern int32 thread_count, service_thread_count;
extern char *opt_ssl_ca, *opt_ssl_capath, *opt_ssl_cert, *opt_ssl_cipher,
@@ -695,11 +741,13 @@ enum enum_query_type
QT_ITEM_CACHE_WRAPPER_SKIP_DETAILS |
QT_ITEM_SUBSELECT_ID_ONLY,
- /// This is used for EXPLAIN EXTENDED extra warnings
+ QT_SHOW_SELECT_NUMBER= (1<<10),
+ /// 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
/// here, as it would give better readable results
- QT_EXPLAIN_EXTENDED= QT_TO_SYSTEM_CHARSET,
+ QT_EXPLAIN_EXTENDED= QT_TO_SYSTEM_CHARSET|
+ QT_SHOW_SELECT_NUMBER,
// If an expression is constant, print the expression, not the value
// it evaluates to. Should be used for error messages, so that they
@@ -744,36 +792,6 @@ inline void table_case_convert(char * name, uint length)
name, length, name, length);
}
-inline void thread_safe_increment32(int32 *value)
-{
- (void) my_atomic_add32_explicit(value, 1, MY_MEMORY_ORDER_RELAXED);
-}
-
-inline void thread_safe_decrement32(int32 *value)
-{
- (void) my_atomic_add32_explicit(value, -1, MY_MEMORY_ORDER_RELAXED);
-}
-
-inline void thread_safe_increment64(int64 *value)
-{
- (void) my_atomic_add64_explicit(value, 1, MY_MEMORY_ORDER_RELAXED);
-}
-
-inline void thread_safe_decrement64(int64 *value)
-{
- (void) my_atomic_add64_explicit(value, -1, MY_MEMORY_ORDER_RELAXED);
-}
-
-inline void inc_thread_running()
-{
- thread_safe_increment32(&thread_running);
-}
-
-inline void dec_thread_running()
-{
- thread_safe_decrement32(&thread_running);
-}
-
extern void set_server_version(char *buf, size_t size);
#define current_thd _current_thd()
diff --git a/sql/net_serv.cc b/sql/net_serv.cc
index dc24360851e..5d2ad6d17a6 100644
--- a/sql/net_serv.cc
+++ b/sql/net_serv.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2016, Oracle and/or its affiliates.
- Copyright (c) 2012, 2018, MariaDB Corporation.
+ 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
@@ -35,7 +35,7 @@
embedded library
*/
-#include <my_global.h>
+#include "mariadb.h"
#include <mysql.h>
#include <mysql_com.h>
#include <mysqld_error.h>
@@ -46,6 +46,7 @@
#include <signal.h>
#include "probes_mysql.h"
#include <debug_sync.h>
+#include "proxy_protocol.h"
#ifdef EMBEDDED_LIBRARY
#undef MYSQL_SERVER
@@ -65,8 +66,11 @@ static void inline EXTRA_DEBUG_fprintf(...) {}
#ifndef MYSQL_SERVER
static int inline EXTRA_DEBUG_fflush(...) { return 0; }
#endif
-#endif
+#endif /* EXTRA_DEBUG */
+
#ifdef MYSQL_SERVER
+#include <sql_class.h>
+#include <sql_connect.h>
#define MYSQL_SERVER_my_error my_error
#else
static void inline MYSQL_SERVER_my_error(...) {}
@@ -107,25 +111,24 @@ void sql_print_error(const char *format,...);
extern, but as it's hard to include sql_priv.h here, we have to
live with this for a while.
*/
-extern uint test_flags;
+extern ulonglong test_flags;
extern ulong bytes_sent, bytes_received, net_big_packet_count;
#ifdef HAVE_QUERY_CACHE
#define USE_QUERY_CACHE
-extern void query_cache_insert(void *thd, const char *packet, ulong length,
+extern void query_cache_insert(void *thd, const char *packet, size_t length,
unsigned pkt_nr);
#endif // HAVE_QUERY_CACHE
#define update_statistics(A) A
-extern my_bool thd_net_is_killed();
+extern my_bool thd_net_is_killed(THD *thd);
/* Additional instrumentation hooks for the server */
#include "mysql_com_server.h"
#else
#define update_statistics(A)
-#define thd_net_is_killed() 0
+#define thd_net_is_killed(A) 0
#endif
-#define TEST_BLOCKING 8
-static my_bool net_write_buff(NET *, const uchar *, ulong);
+static my_bool net_write_buff(NET *, const uchar *, size_t len);
my_bool net_allocate_new_packet(NET *net, void *thd, uint my_flags);
@@ -561,13 +564,13 @@ net_write_command(NET *net,uchar command,
*/
static my_bool
-net_write_buff(NET *net, const uchar *packet, ulong len)
+net_write_buff(NET *net, const uchar *packet, size_t len)
{
- ulong left_length;
+ size_t left_length;
if (net->compress && net->max_packet > MAX_PACKET_LENGTH)
- left_length= (ulong) (MAX_PACKET_LENGTH - (net->write_pos - net->buff));
+ left_length= (MAX_PACKET_LENGTH - (net->write_pos - net->buff));
else
- left_length= (ulong) (net->buff_end - net->write_pos);
+ left_length= (net->buff_end - net->write_pos);
#ifdef DEBUG_DATA_PACKETS
DBUG_DUMP("data_written", packet, len);
@@ -635,7 +638,7 @@ net_real_write(NET *net,const uchar *packet, size_t len)
query_cache_insert(net->thd, (char*) packet, len, net->pkt_nr);
#endif
- if (net->error == 2)
+ if (unlikely(net->error == 2))
DBUG_RETURN(-1); /* socket can't be used */
net->reading_or_writing=2;
@@ -845,6 +848,70 @@ static my_bool my_net_skip_rest(NET *net, uint32 remain, thr_alarm_t *alarmed,
/**
+ Try to parse and process proxy protocol header.
+
+ This function is called in case MySQL packet header cannot be parsed.
+ It checks if proxy header was sent, and that it was send from allowed remote
+ host, as defined by proxy-protocol-networks parameter.
+
+ If proxy header is parsed, then THD and ACL structures and changed to indicate
+ the new peer address and port.
+
+ Note, that proxy header can only be sent either when the connection is established,
+ or as the client reply packet to
+*/
+#undef IGNORE /* for Windows */
+typedef enum { RETRY, ABORT, IGNORE} handle_proxy_header_result;
+static handle_proxy_header_result handle_proxy_header(NET *net)
+{
+#if !defined(MYSQL_SERVER) || defined(EMBEDDED_LIBRARY)
+ return IGNORE;
+#else
+ THD *thd= (THD *)net->thd;
+
+ if (!has_proxy_protocol_header(net) || !thd ||
+ thd->get_command() != COM_CONNECT)
+ return IGNORE;
+
+ /*
+ Proxy information found in the first 4 bytes received so far.
+ Read and parse proxy header , change peer ip address and port in THD.
+ */
+ proxy_peer_info peer_info;
+
+ if (!thd->net.vio)
+ {
+ DBUG_ASSERT(0);
+ return ABORT;
+ }
+
+ if (!is_proxy_protocol_allowed((sockaddr *)&(thd->net.vio->remote)))
+ {
+ /* proxy-protocol-networks variable needs to be set to allow this remote address */
+ my_printf_error(ER_HOST_NOT_PRIVILEGED, "Proxy header is not accepted from %s",
+ MYF(0), thd->main_security_ctx.ip);
+ return ABORT;
+ }
+
+ if (parse_proxy_protocol_header(net, &peer_info))
+ {
+ /* Failed to parse proxy header*/
+ my_printf_error(ER_UNKNOWN_ERROR, "Failed to parse proxy header", MYF(0));
+ return ABORT;
+ }
+
+ if (peer_info.is_local_command)
+ /* proxy header indicates LOCAL connection, no action necessary */
+ return RETRY;
+ /* Change peer address in THD and ACL structures.*/
+ uint host_errors;
+ return (handle_proxy_header_result)thd_set_peer_addr(thd,
+ &(peer_info.peer_addr), NULL, peer_info.port,
+ false, &host_errors);
+#endif
+}
+
+/**
Reads one packet to net->buff + net->where_b.
Long packets are handled by my_net_read().
This function reallocates the net->buff buffer if necessary.
@@ -866,6 +933,9 @@ my_real_read(NET *net, size_t *complen,
#ifndef NO_ALARM
ALARM alarm_buff;
#endif
+
+retry:
+
my_bool net_blocking=vio_is_blocking(net->vio);
uint32 remain= (net->compress ? NET_HEADER_SIZE+COMP_HEADER_SIZE :
NET_HEADER_SIZE);
@@ -908,7 +978,7 @@ my_real_read(NET *net, size_t *complen,
DBUG_PRINT("info",("vio_read returned %ld errno: %d",
(long) length, vio_errno(net->vio)));
- if (i== 0 && thd_net_is_killed())
+ if (i== 0 && unlikely(thd_net_is_killed((THD*) net->thd)))
{
DBUG_PRINT("info", ("thd is killed"));
len= packet_error;
@@ -986,7 +1056,7 @@ my_real_read(NET *net, size_t *complen,
#endif
if (i == 0)
{ /* First parts is packet length */
- ulong helping;
+ size_t helping;
#ifndef DEBUG_DATA_PACKETS
DBUG_DUMP("packet_header", net->buff+net->where_b,
NET_HEADER_SIZE);
@@ -1097,6 +1167,17 @@ end:
packets_out_of_order:
{
+ switch (handle_proxy_header(net)) {
+ case ABORT:
+ /* error happened, message is already written. */
+ len= packet_error;
+ goto end;
+ case RETRY:
+ goto retry;
+ case IGNORE:
+ break;
+ }
+
DBUG_PRINT("error",
("Packets out of order (Found: %d, expected %u)",
(int) net->buff[net->where_b + 3],
@@ -1179,22 +1260,23 @@ my_net_read_packet_reallen(NET *net, my_bool read_from_server, ulong* reallen)
size_t total_length= 0;
do
{
- net->where_b += len;
+ net->where_b += (ulong)len;
total_length += len;
len = my_real_read(net,&complen, 0);
} while (len == MAX_PACKET_LENGTH);
- if (len != packet_error)
+ if (likely(len != packet_error))
len+= total_length;
net->where_b = save_pos;
}
+
net->read_pos = net->buff + net->where_b;
- if (len != packet_error)
+ if (likely(len != packet_error))
{
net->read_pos[len]=0; /* Safeguard for mysql_use_result */
- *reallen = len;
+ *reallen = (ulong)len;
}
MYSQL_NET_READ_DONE(0, len);
- return len;
+ return (ulong)len;
#ifdef HAVE_COMPRESS
}
else
@@ -1292,7 +1374,7 @@ my_net_read_packet_reallen(NET *net, my_bool read_from_server, ulong* reallen)
MYSQL_NET_READ_DONE(1, 0);
return packet_error;
}
- buf_length+= complen;
+ buf_length+= (ulong)complen;
*reallen += packet_len;
}
@@ -1306,7 +1388,7 @@ my_net_read_packet_reallen(NET *net, my_bool read_from_server, ulong* reallen)
}
#endif /* HAVE_COMPRESS */
MYSQL_NET_READ_DONE(0, len);
- return len;
+ return (ulong)len;
}
diff --git a/sql/nt_servc.cc b/sql/nt_servc.cc
index e05e43a0a59..9c754763aab 100644
--- a/sql/nt_servc.cc
+++ b/sql/nt_servc.cc
@@ -64,19 +64,6 @@ NTService::~NTService()
-------------------------------------------------------------------------- */
-BOOL NTService::GetOS()
-{
- bOsNT = FALSE;
- memset(&osVer, 0, sizeof(OSVERSIONINFO));
- osVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
- if (GetVersionEx(&osVer))
- {
- if (osVer.dwPlatformId == VER_PLATFORM_WIN32_NT)
- bOsNT = TRUE;
- }
- return bOsNT;
-}
-
/**
Registers the main service thread with the service manager.
@@ -86,18 +73,18 @@ BOOL NTService::GetOS()
*/
-long NTService::Init(LPCSTR szInternName,void *ServiceThread)
+long NTService::Init(LPCSTR szInternName, THREAD_FC ServiceThread)
{
pService = this;
- fpServiceThread = (THREAD_FC)ServiceThread;
+ fpServiceThread = ServiceThread;
ServiceName = new char[lstrlen(szInternName)+1];
lstrcpy(ServiceName,szInternName);
SERVICE_TABLE_ENTRY stb[] =
{
- { (char *)szInternName,(LPSERVICE_MAIN_FUNCTION) ServiceMain} ,
+ { (char *)szInternName, ServiceMain} ,
{ NULL, NULL }
};
@@ -232,7 +219,6 @@ void NTService::ServiceMain(DWORD argc, LPTSTR *argv)
// registration function
if (!(pService->hServiceStatusHandle =
RegisterServiceCtrlHandler(pService->ServiceName,
- (LPHANDLER_FUNCTION)
NTService::ServiceCtrlHandler)))
goto error;
@@ -293,7 +279,7 @@ void NTService::SetSlowStarting(unsigned long timeout)
BOOL NTService::StartService()
{
// Start the real service's thread (application)
- if (!(hThreadHandle = (HANDLE) _beginthread((THREAD_FC)fpServiceThread,0,
+ if (!(hThreadHandle = (HANDLE) _beginthread(fpServiceThread,0,
(void *) this)))
return FALSE;
bRunning = TRUE;
diff --git a/sql/nt_servc.h b/sql/nt_servc.h
index 6781fe0ddfa..8ba29519c8f 100644
--- a/sql/nt_servc.h
+++ b/sql/nt_servc.h
@@ -45,10 +45,8 @@ class NTService
int nError;
DWORD dwState;
- BOOL GetOS(); // returns TRUE if WinNT
- BOOL IsNT() { return bOsNT;}
//init service entry point
- long Init(LPCSTR szInternName,void *ServiceThread);
+ long Init(LPCSTR szInternName,THREAD_FC ServiceThread);
//application shutdown event
void SetShutdownEvent(HANDLE hEvent){ hShutdownEvent=hEvent; }
@@ -101,8 +99,8 @@ class NTService
void StopService();
BOOL StartService();
- static void ServiceMain(DWORD argc, LPTSTR *argv);
- static void ServiceCtrlHandler (DWORD ctrlCode);
+ static void WINAPI ServiceMain(DWORD argc, LPTSTR *argv);
+ static void WINAPI ServiceCtrlHandler (DWORD ctrlCode);
void Exit(DWORD error);
BOOL SetStatus (DWORD dwCurrentState,DWORD dwWin32ExitCode,
diff --git a/sql/opt_index_cond_pushdown.cc b/sql/opt_index_cond_pushdown.cc
index cbaa6f4fe7a..af6a75cdef2 100644
--- a/sql/opt_index_cond_pushdown.cc
+++ b/sql/opt_index_cond_pushdown.cc
@@ -14,6 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
+#include "mariadb.h"
#include "sql_select.h"
#include "sql_test.h"
@@ -392,8 +393,23 @@ void push_index_cond(JOIN_TAB *tab, uint keyno)
~(tab->table->map | tab->join->const_table_map)))
tab->cache_idx_cond= idx_cond;
else
+ {
idx_remainder_cond= tab->table->file->idx_cond_push(keyno, idx_cond);
+ /*
+ If (1) there is an index condition that we couldn't push using ICP,
+ (2) we are using Join Buffering
+ (3) and we are using BKA
+ then use BKA's Index Condition Pushdown mechanism to check it.
+ */
+ if (idx_remainder_cond && tab->use_join_cache && // (1) && (2)
+ tab->icp_other_tables_ok) // (3)
+ {
+ tab->cache_idx_cond= idx_remainder_cond;
+ idx_remainder_cond= NULL;
+ }
+ }
+
/*
Disable eq_ref's "lookup cache" if we've pushed down an index
condition.
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index 6a5f1c9f750..0edc1665ac9 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -108,7 +108,7 @@
#pragma implementation // gcc: Class implementation
#endif
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "key.h" // is_key_used, key_copy, key_cmp, key_restore
#include "sql_parse.h" // check_stack_overrun
@@ -1236,7 +1236,8 @@ QUICK_SELECT_I::QUICK_SELECT_I()
QUICK_RANGE_SELECT::QUICK_RANGE_SELECT(THD *thd, TABLE *table, uint key_nr,
bool no_alloc, MEM_ROOT *parent_alloc,
bool *create_error)
- :free_file(0),cur_range(NULL),last_range(0),dont_free(0)
+ :thd(thd), no_alloc(no_alloc), parent_alloc(parent_alloc),
+ free_file(0),cur_range(NULL),last_range(0),dont_free(0)
{
my_bitmap_map *bitmap;
DBUG_ENTER("QUICK_RANGE_SELECT::QUICK_RANGE_SELECT");
@@ -1253,7 +1254,8 @@ QUICK_RANGE_SELECT::QUICK_RANGE_SELECT(THD *thd, TABLE *table, uint key_nr,
if (!no_alloc && !parent_alloc)
{
// Allocates everything through the internal memroot
- init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0,
+ init_sql_alloc(&alloc, "QUICK_RANGE_SELECT",
+ thd->variables.range_alloc_block_size, 0,
MYF(MY_THREAD_SPECIFIC));
thd->mem_root= &alloc;
}
@@ -1348,7 +1350,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, thd->variables.range_alloc_block_size, 0,
+ init_sql_alloc(&alloc, "QUICK_INDEX_SORT_SELECT",
+ thd->variables.range_alloc_block_size, 0,
MYF(MY_THREAD_SPECIFIC));
DBUG_VOID_RETURN;
}
@@ -1419,7 +1422,8 @@ QUICK_ROR_INTERSECT_SELECT::QUICK_ROR_INTERSECT_SELECT(THD *thd_param,
head= table;
record= head->record[0];
if (!parent_alloc)
- init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0,
+ init_sql_alloc(&alloc, "QUICK_ROR_INTERSECT_SELECT",
+ thd->variables.range_alloc_block_size, 0,
MYF(MY_THREAD_SPECIFIC));
else
bzero(&alloc, sizeof(MEM_ROOT));
@@ -1590,7 +1594,7 @@ int QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan(bool reuse_handler,
selects.
*/
int error= quick->init_ror_merged_scan(TRUE, local_alloc);
- if (error)
+ if (unlikely(error))
DBUG_RETURN(error);
quick->file->extra(HA_EXTRA_KEYREAD_PRESERVE_FIELDS);
}
@@ -1614,7 +1618,8 @@ int QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan(bool reuse_handler,
quick->record= head->record[0];
}
- if (need_to_fetch_row && head->file->ha_rnd_init_with_error(false))
+ if (need_to_fetch_row &&
+ unlikely(head->file->ha_rnd_init_with_error(false)))
{
DBUG_PRINT("error", ("ROR index_merge rnd_init call failed"));
DBUG_RETURN(1);
@@ -1696,7 +1701,8 @@ QUICK_ROR_UNION_SELECT::QUICK_ROR_UNION_SELECT(THD *thd_param,
head= table;
rowid_length= table->file->ref_length;
record= head->record[0];
- init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0,
+ init_sql_alloc(&alloc, "QUICK_ROR_UNION_SELECT",
+ thd->variables.range_alloc_block_size, 0,
MYF(MY_THREAD_SPECIFIC));
thd_param->mem_root= &alloc;
}
@@ -1787,9 +1793,9 @@ int QUICK_ROR_UNION_SELECT::reset()
List_iterator_fast<QUICK_SELECT_I> it(quick_selects);
while ((quick= it++))
{
- if ((error= quick->reset()))
+ if (unlikely((error= quick->reset())))
DBUG_RETURN(error);
- if ((error= quick->get_next()))
+ if (unlikely((error= quick->get_next())))
{
if (error == HA_ERR_END_OF_FILE)
continue;
@@ -1799,12 +1805,12 @@ int QUICK_ROR_UNION_SELECT::reset()
queue_insert(&queue, (uchar*)quick);
}
/* Prepare for ha_rnd_pos calls. */
- if (head->file->inited && (error= head->file->ha_rnd_end()))
+ if (head->file->inited && unlikely((error= head->file->ha_rnd_end())))
{
DBUG_PRINT("error", ("ROR index_merge rnd_end call failed"));
DBUG_RETURN(error);
}
- if ((error= head->file->ha_rnd_init(false)))
+ if (unlikely((error= head->file->ha_rnd_init(false))))
{
DBUG_PRINT("error", ("ROR index_merge rnd_init call failed"));
DBUG_RETURN(error);
@@ -2380,7 +2386,7 @@ static int fill_used_fields_bitmap(PARAM *param)
force_quick_range is really needed.
RETURN
- -1 if impossible select (i.e. certainly no rows will be selected)
+ -1 if error or impossible select (i.e. certainly no rows will be selected)
0 if can't use quick_select
1 if found usable ranges and quick select has been successfully created.
*/
@@ -2451,12 +2457,14 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
param.imerge_cost_buff_size= 0;
param.using_real_indexes= TRUE;
param.remove_jump_scans= TRUE;
+ param.is_ror_scan= 0;
param.remove_false_where_parts= remove_false_parts_of_where;
param.force_default_mrr= ordered_output;
param.possible_keys.clear_all();
thd->no_errors=1; // Don't warn about NULL
- init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0,
+ init_sql_alloc(&alloc, "test_quick_select",
+ thd->variables.range_alloc_block_size, 0,
MYF(MY_THREAD_SPECIFIC));
if (!(param.key_parts=
(KEY_PART*) alloc_root(&alloc,
@@ -2466,7 +2474,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
{
thd->no_errors=0;
free_root(&alloc,MYF(0)); // Return memory & allocator
- DBUG_RETURN(0); // Can't use range
+ DBUG_RETURN(-1); // Error
}
key_parts= param.key_parts;
@@ -2517,7 +2525,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
{
thd->no_errors=0;
free_root(&alloc,MYF(0)); // Return memory & allocator
- DBUG_RETURN(0); // Can't use range
+ DBUG_RETURN(-1); // Error
}
thd->mem_root= &alloc;
@@ -2554,6 +2562,13 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
if (tree->type != SEL_TREE::KEY && tree->type != SEL_TREE::KEY_SMALLER)
tree= NULL;
}
+ else if (thd->is_error())
+ {
+ thd->no_errors=0;
+ thd->mem_root= param.old_root;
+ free_root(&alloc, MYF(0));
+ DBUG_RETURN(-1);
+ }
}
/*
@@ -3056,7 +3071,8 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
SEL_TREE *tree;
double rows;
- init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0,
+ init_sql_alloc(&alloc, "calculate_cond_selectivity_for_table",
+ thd->variables.range_alloc_block_size, 0,
MYF(MY_THREAD_SPECIFIC));
param.thd= thd;
param.mem_root= &alloc;
@@ -3479,7 +3495,8 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond)
my_bitmap_map *old_sets[2];
prune_param.part_info= part_info;
- init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0,
+ init_sql_alloc(&alloc, "prune_partitions",
+ thd->variables.range_alloc_block_size, 0,
MYF(MY_THREAD_SPECIFIC));
range_par->mem_root= &alloc;
range_par->old_root= thd->mem_root;
@@ -3490,7 +3507,7 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond)
free_root(&alloc,MYF(0)); // Return memory & allocator
DBUG_RETURN(FALSE);
}
-
+
dbug_tmp_use_all_columns(table, old_sets,
table->read_set, table->write_set);
range_par->thd= thd;
@@ -4016,7 +4033,7 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree)
simply set res= -1 as if the mapper had returned that.
TODO: What to do here is defined in WL#4065.
*/
- if (ppar->arg_stack[0]->part == 0)
+ if (ppar->arg_stack[0]->part == 0 || ppar->part_info->part_type == VERSIONING_PARTITION)
{
uint32 i;
uint32 store_length_array[MAX_KEY];
@@ -4434,7 +4451,7 @@ static void print_partitioning_index(KEY_PART *parts, KEY_PART *parts_end)
fprintf(DBUG_FILE, "partitioning INDEX(");
for (KEY_PART *p=parts; p != parts_end; p++)
{
- fprintf(DBUG_FILE, "%s%s", p==parts?"":" ,", p->field->field_name);
+ fprintf(DBUG_FILE, "%s%s", p==parts?"":" ,", p->field->field_name.str);
}
fputs(");\n", DBUG_FILE);
DBUG_UNLOCK_FILE;
@@ -4473,7 +4490,7 @@ static void dbug_print_segment_range(SEL_ARG *arg, KEY_PART *part)
fputs(" <= ", DBUG_FILE);
}
- fprintf(DBUG_FILE, "%s", part->field->field_name);
+ fprintf(DBUG_FILE, "%s", part->field->field_name.str);
if (!(arg->max_flag & NO_MAX_RANGE))
{
@@ -4512,7 +4529,7 @@ static void dbug_print_singlepoint_range(SEL_ARG **start, uint num)
for (SEL_ARG **arg= start; arg != end; arg++)
{
Field *field= (*arg)->field;
- fprintf(DBUG_FILE, "%s%s=", (arg==start)?"":", ", field->field_name);
+ fprintf(DBUG_FILE, "%s%s=", (arg==start)?"":", ", field->field_name.str);
dbug_print_field(field);
}
fputs("\n", DBUG_FILE);
@@ -6248,7 +6265,7 @@ static double ror_scan_selectivity(const ROR_INTERSECT_INFO *info,
&key_ptr, 0);
keypart_map= (keypart_map << 1) | 1;
}
- min_range.length= max_range.length= (size_t) (key_ptr - key_val);
+ 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));
@@ -6324,7 +6341,7 @@ static bool ror_intersect_add(ROR_INTERSECT_INFO *info,
DBUG_ENTER("ror_intersect_add");
DBUG_PRINT("info", ("Current out_rows= %g", info->out_rows));
DBUG_PRINT("info", ("Adding scan on %s",
- info->param->table->key_info[ror_scan->keynr].name));
+ info->param->table->key_info[ror_scan->keynr].name.str));
DBUG_PRINT("info", ("is_cpk_scan: %d",is_cpk_scan));
selectivity_mult = ror_scan_selectivity(info, ror_scan);
@@ -6709,7 +6726,7 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param,
total_cost += (*ror_scan_mark)->index_read_cost;
records += (*ror_scan_mark)->records;
DBUG_PRINT("info", ("Adding scan on %s",
- param->table->key_info[(*ror_scan_mark)->keynr].name));
+ param->table->key_info[(*ror_scan_mark)->keynr].name.str));
if (total_cost > read_time)
DBUG_RETURN(NULL);
/* F=F-covered by first(I) */
@@ -6880,7 +6897,7 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree,
read_plan->mrr_buf_size= best_buf_size;
DBUG_PRINT("info",
("Returning range plan for key %s, cost %g, records %lu",
- param->table->key_info[param->real_keynr[best_idx]].name,
+ param->table->key_info[param->real_keynr[best_idx]].name.str,
read_plan->read_cost, (ulong) read_plan->records));
}
}
@@ -7109,7 +7126,7 @@ SEL_TREE *Item_func_in::get_func_mm_tree(RANGE_OPT_PARAM *param,
if (negated)
{
- if (array && array->result_type() != ROW_RESULT)
+ if (array && array->type_handler()->result_type() != ROW_RESULT)
{
/*
We get here for conditions in form "t.key NOT IN (c1, c2, ...)",
@@ -7341,9 +7358,19 @@ SEL_TREE *Item_func_in::get_func_row_mm_tree(RANGE_OPT_PARAM *param,
param->current_table);
uint row_cols= key_row->cols();
Dynamic_array <Key_col_info> key_cols_info(row_cols);
- cmp_item_row *row_cmp_item= (cmp_item_row *)
- (array ? ((in_row *) array)->get_cmp_item() :
- cmp_items[(uint) ROW_RESULT]);
+ cmp_item_row *row_cmp_item;
+
+ if (array)
+ {
+ in_row *row= static_cast<in_row*>(array);
+ row_cmp_item= static_cast<cmp_item_row*>(row->get_cmp_item());
+ }
+ else
+ {
+ DBUG_ASSERT(get_comparator_type_handler(0) == &type_handler_row);
+ row_cmp_item= static_cast<cmp_item_row*>(get_comparator_cmp_item(0));
+ }
+ DBUG_ASSERT(row_cmp_item);
Item **key_col_ptr= key_row->addr(0);
for(uint i= 0; i < row_cols; i++, key_col_ptr++)
@@ -7390,6 +7417,7 @@ SEL_TREE *Item_func_in::get_func_row_mm_tree(RANGE_OPT_PARAM *param,
key_col->bring_value();
key_col_info.comparator= row_cmp_item->get_comparator(i);
+ DBUG_ASSERT(key_col_info.comparator);
key_col_info.comparator->store_value(key_col);
col_comparators++;
@@ -7982,7 +8010,9 @@ Item_func_like::get_mm_leaf(RANGE_OPT_PARAM *param,
if (!(res= value->val_str(&tmp)))
DBUG_RETURN(&null_element);
- if (field->cmp_type() != STRING_RESULT)
+ if (field->cmp_type() != STRING_RESULT ||
+ field->type_handler() == &type_handler_enum ||
+ field->type_handler() == &type_handler_set)
DBUG_RETURN(0);
/*
@@ -7997,7 +8027,7 @@ Item_func_like::get_mm_leaf(RANGE_OPT_PARAM *param,
}
uint maybe_null= (uint) field->real_maybe_null();
- uint field_length= field->pack_length() + maybe_null;
+ size_t field_length= field->pack_length() + maybe_null;
size_t offset= maybe_null;
size_t length= key_part->store_length;
@@ -8078,27 +8108,59 @@ Item_bool_func::get_mm_leaf(RANGE_OPT_PARAM *param,
goto end;
err= value->save_in_field_no_warnings(field, 1);
- if (err == 2 && field->cmp_type() == STRING_RESULT)
+ if (err > 0)
{
- if (type == EQ_FUNC || type == EQUAL_FUNC)
+ if (field->type_handler() == &type_handler_enum ||
+ field->type_handler() == &type_handler_set)
{
- tree= new (alloc) SEL_ARG(field, 0, 0);
- tree->type= SEL_ARG::IMPOSSIBLE;
+ if (type == EQ_FUNC || type == EQUAL_FUNC)
+ tree= new (alloc) SEL_ARG_IMPOSSIBLE(field);
+ goto end;
}
- else
- tree= NULL; /* Cannot infer anything */
- goto end;
- }
- if (err > 0)
- {
- if (field->cmp_type() != value->result_type())
+
+ if (err == 2 && field->cmp_type() == STRING_RESULT)
+ {
+ if (type == EQ_FUNC || type == EQUAL_FUNC)
+ tree= new (alloc) SEL_ARG_IMPOSSIBLE(field);
+ else
+ tree= NULL; /* Cannot infer anything */
+ goto end;
+ }
+
+ if (err == 3 && field->type() == FIELD_TYPE_DATE)
+ {
+ /*
+ We were saving DATETIME into a DATE column, the conversion went ok
+ but a non-zero time part was cut off.
+
+ In MySQL's SQL dialect, DATE and DATETIME are compared as datetime
+ values. Index over a DATE column uses DATE comparison. Changing
+ from one comparison to the other is possible:
+
+ datetime(date_col)< '2007-12-10 12:34:55' -> date_col<='2007-12-10'
+ datetime(date_col)<='2007-12-10 12:34:55' -> date_col<='2007-12-10'
+
+ datetime(date_col)> '2007-12-10 12:34:55' -> date_col>='2007-12-10'
+ datetime(date_col)>='2007-12-10 12:34:55' -> date_col>='2007-12-10'
+
+ but we'll need to convert '>' to '>=' and '<' to '<='. This will
+ be done together with other types at the end of this function
+ (grep for stored_field_cmp_to_item)
+ */
+ if (type == EQ_FUNC || type == EQUAL_FUNC)
+ {
+ tree= new (alloc) SEL_ARG_IMPOSSIBLE(field);
+ goto end;
+ }
+ // Continue with processing non-equality ranges
+ }
+ else if (field->cmp_type() != value->result_type())
{
if ((type == EQ_FUNC || type == EQUAL_FUNC) &&
value->result_type() == item_cmp_type(field->result_type(),
value->result_type()))
{
- tree= new (alloc) SEL_ARG(field, 0, 0);
- tree->type= SEL_ARG::IMPOSSIBLE;
+ tree= new (alloc) SEL_ARG_IMPOSSIBLE(field);
goto end;
}
else
@@ -8108,31 +8170,7 @@ Item_bool_func::get_mm_leaf(RANGE_OPT_PARAM *param,
for the cases like int_field > 999999999999999999999999 as well.
*/
tree= 0;
- if (err == 3 && field->type() == FIELD_TYPE_DATE &&
- (type == GT_FUNC || type == GE_FUNC ||
- type == LT_FUNC || type == LE_FUNC) )
- {
- /*
- We were saving DATETIME into a DATE column, the conversion went ok
- but a non-zero time part was cut off.
-
- In MySQL's SQL dialect, DATE and DATETIME are compared as datetime
- values. Index over a DATE column uses DATE comparison. Changing
- from one comparison to the other is possible:
-
- datetime(date_col)< '2007-12-10 12:34:55' -> date_col<='2007-12-10'
- datetime(date_col)<='2007-12-10 12:34:55' -> date_col<='2007-12-10'
-
- datetime(date_col)> '2007-12-10 12:34:55' -> date_col>='2007-12-10'
- datetime(date_col)>='2007-12-10 12:34:55' -> date_col>='2007-12-10'
-
- but we'll need to convert '>' to '>=' and '<' to '<='. This will
- be done together with other types at the end of this function
- (grep for stored_field_cmp_to_item)
- */
- }
- else
- goto end;
+ goto end;
}
}
@@ -8983,6 +9021,8 @@ and_all_keys(RANGE_OPT_PARAM *param, SEL_ARG *key1, SEL_ARG *key2,
}
if (key1->type == SEL_ARG::MAYBE_KEY)
{
+ if (key2->type == SEL_ARG::KEY_RANGE)
+ return key2;
key1->right= key1->left= &null_element;
key1->next= key1->prev= 0;
}
@@ -10876,8 +10916,9 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
goto err;
quick->records= records;
- if ((cp_buffer_from_ref(thd, table, ref) && thd->is_fatal_error) ||
- !(range= new(alloc) QUICK_RANGE()))
+ if ((cp_buffer_from_ref(thd, table, ref) &&
+ unlikely(thd->is_fatal_error)) ||
+ unlikely(!(range= new(alloc) QUICK_RANGE())))
goto err; // out of memory
range->min_key= range->max_key= ref->key_buff;
@@ -10886,8 +10927,8 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
make_prev_keypart_map(ref->key_parts);
range->flag= EQ_RANGE;
- if (!(quick->key_parts=key_part=(KEY_PART *)
- alloc_root(&quick->alloc,sizeof(KEY_PART)*ref->key_parts)))
+ if (unlikely(!(quick->key_parts=key_part=(KEY_PART *)
+ alloc_root(&quick->alloc,sizeof(KEY_PART)*ref->key_parts))))
goto err;
max_used_key_len=0;
@@ -11002,7 +11043,7 @@ int read_keys_and_merge_scans(THD *thd,
if (unique == NULL)
{
- DBUG_EXECUTE_IF("index_merge_may_not_create_a_Unique", DBUG_ABORT(); );
+ DBUG_EXECUTE_IF("index_merge_may_not_create_a_Unique", DBUG_SUICIDE(); );
DBUG_EXECUTE_IF("only_one_Unique_may_be_created",
DBUG_SET("+d,index_merge_may_not_create_a_Unique"); );
@@ -11116,7 +11157,7 @@ int QUICK_INDEX_MERGE_SELECT::get_next()
if (doing_pk_scan)
DBUG_RETURN(pk_quick_select->get_next());
- if ((result= read_record.read_record(&read_record)) == -1)
+ if ((result= read_record.read_record()) == -1)
{
result= HA_ERR_END_OF_FILE;
end_read_record(&read_record);
@@ -11152,7 +11193,7 @@ int QUICK_INDEX_INTERSECT_SELECT::get_next()
int result;
DBUG_ENTER("QUICK_INDEX_INTERSECT_SELECT::get_next");
- if ((result= read_record.read_record(&read_record)) == -1)
+ if ((result= read_record.read_record()) == -1)
{
result= HA_ERR_END_OF_FILE;
end_read_record(&read_record);
@@ -11205,103 +11246,100 @@ int QUICK_ROR_INTERSECT_SELECT::get_next()
uint last_rowid_count=0;
DBUG_ENTER("QUICK_ROR_INTERSECT_SELECT::get_next");
- do
+ /* Get a rowid for first quick and save it as a 'candidate' */
+ qr= quick_it++;
+ quick= qr->quick;
+ error= quick->get_next();
+ if (cpk_quick)
{
- /* Get a rowid for first quick and save it as a 'candidate' */
- qr= quick_it++;
- quick= qr->quick;
- error= quick->get_next();
- if (cpk_quick)
+ while (!error && !cpk_quick->row_in_ranges())
{
- while (!error && !cpk_quick->row_in_ranges())
- {
- quick->file->unlock_row(); /* row not in range; unlock */
- error= quick->get_next();
- }
+ quick->file->unlock_row(); /* row not in range; unlock */
+ error= quick->get_next();
}
- if (error)
- DBUG_RETURN(error);
+ }
+ if (unlikely(error))
+ DBUG_RETURN(error);
- /* Save the read key tuple */
- key_copy(qr->key_tuple, record, head->key_info + quick->index,
- quick->max_used_key_length);
+ /* Save the read key tuple */
+ key_copy(qr->key_tuple, record, head->key_info + quick->index,
+ quick->max_used_key_length);
- quick->file->position(quick->record);
- memcpy(last_rowid, quick->file->ref, head->file->ref_length);
- last_rowid_count= 1;
- quick_with_last_rowid= quick;
+ quick->file->position(quick->record);
+ memcpy(last_rowid, quick->file->ref, head->file->ref_length);
+ last_rowid_count= 1;
+ quick_with_last_rowid= quick;
- while (last_rowid_count < quick_selects.elements)
+ while (last_rowid_count < quick_selects.elements)
+ {
+ if (!(qr= quick_it++))
+ {
+ quick_it.rewind();
+ qr= quick_it++;
+ }
+ quick= qr->quick;
+
+ do
{
- if (!(qr= quick_it++))
+ DBUG_EXECUTE_IF("innodb_quick_report_deadlock",
+ DBUG_SET("+d,innodb_report_deadlock"););
+ if (unlikely((error= quick->get_next())))
{
- quick_it.rewind();
- qr= quick_it++;
+ /* On certain errors like deadlock, trx might be rolled back.*/
+ if (!thd->transaction_rollback_request)
+ quick_with_last_rowid->file->unlock_row();
+ DBUG_RETURN(error);
}
- quick= qr->quick;
-
- do
+ quick->file->position(quick->record);
+ cmp= head->file->cmp_ref(quick->file->ref, last_rowid);
+ if (cmp < 0)
{
- DBUG_EXECUTE_IF("innodb_quick_report_deadlock",
- DBUG_SET("+d,innodb_report_deadlock"););
- if ((error= quick->get_next()))
- {
- /* On certain errors like deadlock, trx might be rolled back.*/
- if (!thd->transaction_rollback_request)
- quick_with_last_rowid->file->unlock_row();
- DBUG_RETURN(error);
- }
- quick->file->position(quick->record);
- cmp= head->file->cmp_ref(quick->file->ref, last_rowid);
- if (cmp < 0)
- {
- /* This row is being skipped. Release lock on it. */
- quick->file->unlock_row();
- }
- } while (cmp < 0);
+ /* This row is being skipped. Release lock on it. */
+ quick->file->unlock_row();
+ }
+ } while (cmp < 0);
- key_copy(qr->key_tuple, record, head->key_info + quick->index,
- quick->max_used_key_length);
+ key_copy(qr->key_tuple, record, head->key_info + quick->index,
+ quick->max_used_key_length);
- /* Ok, current select 'caught up' and returned ref >= cur_ref */
- if (cmp > 0)
+ /* Ok, current select 'caught up' and returned ref >= cur_ref */
+ if (cmp > 0)
+ {
+ /* Found a row with ref > cur_ref. Make it a new 'candidate' */
+ if (cpk_quick)
{
- /* Found a row with ref > cur_ref. Make it a new 'candidate' */
- if (cpk_quick)
+ while (!cpk_quick->row_in_ranges())
{
- while (!cpk_quick->row_in_ranges())
+ quick->file->unlock_row(); /* row not in range; unlock */
+ if (unlikely((error= quick->get_next())))
{
- quick->file->unlock_row(); /* row not in range; unlock */
- if ((error= quick->get_next()))
- {
- /* On certain errors like deadlock, trx might be rolled back.*/
- if (!thd->transaction_rollback_request)
- quick_with_last_rowid->file->unlock_row();
- DBUG_RETURN(error);
- }
+ /* On certain errors like deadlock, trx might be rolled back.*/
+ if (!thd->transaction_rollback_request)
+ quick_with_last_rowid->file->unlock_row();
+ DBUG_RETURN(error);
}
- quick->file->position(quick->record);
}
- memcpy(last_rowid, quick->file->ref, head->file->ref_length);
- quick_with_last_rowid->file->unlock_row();
- last_rowid_count= 1;
- quick_with_last_rowid= quick;
-
- //save the fields here
- key_copy(qr->key_tuple, record, head->key_info + quick->index,
- quick->max_used_key_length);
- }
- else
- {
- /* current 'candidate' row confirmed by this select */
- last_rowid_count++;
+ quick->file->position(quick->record);
}
+ memcpy(last_rowid, quick->file->ref, head->file->ref_length);
+ quick_with_last_rowid->file->unlock_row();
+ last_rowid_count= 1;
+ quick_with_last_rowid= quick;
+
+ //save the fields here
+ key_copy(qr->key_tuple, record, head->key_info + quick->index,
+ quick->max_used_key_length);
}
+ else
+ {
+ /* current 'candidate' row confirmed by this select */
+ last_rowid_count++;
+ }
+ }
- /* We get here if we got the same row ref in all scans. */
- if (need_to_fetch_row)
- error= head->file->ha_rnd_pos(head->record[0], last_rowid);
- } while (error == HA_ERR_RECORD_DELETED);
+ /* We get here if we got the same row ref in all scans. */
+ if (need_to_fetch_row)
+ error= head->file->ha_rnd_pos(head->record[0], last_rowid);
if (!need_to_fetch_row)
{
@@ -11345,44 +11383,41 @@ int QUICK_ROR_UNION_SELECT::get_next()
do
{
- do
- {
- if (!queue.elements)
- DBUG_RETURN(HA_ERR_END_OF_FILE);
- /* Ok, we have a queue with >= 1 scans */
+ if (!queue.elements)
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+ /* Ok, we have a queue with >= 1 scans */
- quick= (QUICK_SELECT_I*)queue_top(&queue);
- memcpy(cur_rowid, quick->last_rowid, rowid_length);
+ quick= (QUICK_SELECT_I*)queue_top(&queue);
+ memcpy(cur_rowid, quick->last_rowid, rowid_length);
- /* put into queue rowid from the same stream as top element */
- if ((error= quick->get_next()))
- {
- if (error != HA_ERR_END_OF_FILE)
- DBUG_RETURN(error);
- queue_remove_top(&queue);
- }
- else
- {
- quick->save_last_pos();
- queue_replace_top(&queue);
- }
+ /* put into queue rowid from the same stream as top element */
+ if ((error= quick->get_next()))
+ {
+ if (error != HA_ERR_END_OF_FILE)
+ DBUG_RETURN(error);
+ queue_remove_top(&queue);
+ }
+ else
+ {
+ quick->save_last_pos();
+ queue_replace_top(&queue);
+ }
- if (!have_prev_rowid)
- {
- /* No rows have been returned yet */
- dup_row= FALSE;
- have_prev_rowid= TRUE;
- }
- else
- dup_row= !head->file->cmp_ref(cur_rowid, prev_rowid);
- } while (dup_row);
+ if (!have_prev_rowid)
+ {
+ /* No rows have been returned yet */
+ dup_row= FALSE;
+ have_prev_rowid= TRUE;
+ }
+ else
+ dup_row= !head->file->cmp_ref(cur_rowid, prev_rowid);
+ } while (dup_row);
- tmp= cur_rowid;
- cur_rowid= prev_rowid;
- prev_rowid= tmp;
+ tmp= cur_rowid;
+ cur_rowid= prev_rowid;
+ prev_rowid= tmp;
- error= head->file->ha_rnd_pos(quick->record, prev_rowid);
- } while (error == HA_ERR_RECORD_DELETED);
+ error= head->file->ha_rnd_pos(quick->record, prev_rowid);
DBUG_RETURN(error);
}
@@ -11404,7 +11439,7 @@ int QUICK_RANGE_SELECT::reset()
if (file->inited == handler::RND)
{
/* Handler could be left in this state by MRR */
- if ((error= file->ha_rnd_end()))
+ if (unlikely((error= file->ha_rnd_end())))
DBUG_RETURN(error);
}
@@ -11416,7 +11451,7 @@ int QUICK_RANGE_SELECT::reset()
{
DBUG_EXECUTE_IF("bug14365043_2",
DBUG_SET("+d,ha_index_init_fail"););
- if ((error= file->ha_index_init(index,1)))
+ if (unlikely((error= file->ha_index_init(index,1))))
{
file->print_error(error, MYF(0));
goto err;
@@ -11774,7 +11809,7 @@ int QUICK_SELECT_DESC::get_next()
if (last_range->flag & NO_MAX_RANGE) // Read last record
{
int local_error;
- if ((local_error= file->ha_index_last(record)))
+ if (unlikely((local_error= file->ha_index_last(record))))
DBUG_RETURN(local_error); // Empty table
if (cmp_prev(last_range) == 0)
DBUG_RETURN(0);
@@ -11922,7 +11957,7 @@ void QUICK_SELECT_I::add_key_name(String *str, bool *first)
*first= FALSE;
else
str->append(',');
- str->append(key_info->name);
+ str->append(&key_info->name);
}
@@ -12077,7 +12112,7 @@ void QUICK_SELECT_I::add_key_and_length(String *key_names,
key_names->append(',');
used_lengths->append(',');
}
- key_names->append(key_info->name);
+ key_names->append(&key_info->name);
length= longlong10_to_str(max_used_key_length, buf, 10) - buf;
used_lengths->append(buf, length);
}
@@ -13676,7 +13711,8 @@ QUICK_GROUP_MIN_MAX_SELECT(TABLE *table, JOIN *join_arg, bool have_min_arg,
DBUG_ASSERT(!parent_alloc);
if (!parent_alloc)
{
- init_sql_alloc(&alloc, join->thd->variables.range_alloc_block_size, 0,
+ init_sql_alloc(&alloc, "QUICK_GROUP_MIN_MAX_SELECT",
+ join->thd->variables.range_alloc_block_size, 0,
MYF(MY_THREAD_SPECIFIC));
join->thd->mem_root= &alloc;
}
@@ -14746,7 +14782,7 @@ static void print_sel_tree(PARAM *param, SEL_TREE *tree, key_map *tree_map,
uint keynr= param->real_keynr[idx];
if (tmp.length())
tmp.append(',');
- tmp.append(param->table->key_info[keynr].name);
+ tmp.append(&param->table->key_info[keynr].name);
}
}
if (!tmp.length())
@@ -14772,7 +14808,7 @@ static void print_ror_scans_arr(TABLE *table, const char *msg,
{
if (tmp.length())
tmp.append(',');
- tmp.append(table->key_info[(*start)->keynr].name);
+ tmp.append(&table->key_info[(*start)->keynr].name);
}
if (!tmp.length())
tmp.append(STRING_WITH_LEN("(empty)"));
@@ -14854,7 +14890,7 @@ void QUICK_RANGE_SELECT::dbug_dump(int indent, bool verbose)
{
/* purecov: begin inspected */
fprintf(DBUG_FILE, "%*squick range select, key %s, length: %d\n",
- indent, "", head->key_info[index].name, max_used_key_length);
+ indent, "", head->key_info[index].name.str, max_used_key_length);
if (verbose)
{
@@ -14958,7 +14994,7 @@ void QUICK_GROUP_MIN_MAX_SELECT::dbug_dump(int indent, bool verbose)
{
fprintf(DBUG_FILE,
"%*squick_group_min_max_select: index %s (%d), length: %d\n",
- indent, "", index_info->name, index, max_used_key_length);
+ indent, "", index_info->name.str, index, max_used_key_length);
if (key_infix_len > 0)
{
fprintf(DBUG_FILE, "%*susing key_infix with length %d:\n",
diff --git a/sql/opt_range.h b/sql/opt_range.h
index 1e6aca540d8..84b1cd08ba8 100644
--- a/sql/opt_range.h
+++ b/sql/opt_range.h
@@ -594,6 +594,17 @@ public:
};
+class SEL_ARG_IMPOSSIBLE: public SEL_ARG
+{
+public:
+ SEL_ARG_IMPOSSIBLE(Field *field)
+ :SEL_ARG(field, 0, 0)
+ {
+ type= SEL_ARG::IMPOSSIBLE;
+ }
+};
+
+
class RANGE_OPT_PARAM
{
public:
@@ -1037,6 +1048,10 @@ bool quick_range_seq_next(range_seq_t rseq, KEY_MULTI_RANGE *range);
class QUICK_RANGE_SELECT : public QUICK_SELECT_I
{
protected:
+ THD *thd;
+ bool no_alloc;
+ MEM_ROOT *parent_alloc;
+
/* true if we enabled key only reads */
handler *file;
@@ -1075,6 +1090,9 @@ public:
QUICK_RANGE_SELECT(THD *thd, TABLE *table,uint index_arg,bool no_alloc,
MEM_ROOT *parent_alloc, bool *create_err);
~QUICK_RANGE_SELECT();
+ virtual QUICK_RANGE_SELECT *clone(bool *create_error)
+ { return new QUICK_RANGE_SELECT(thd, head, index, no_alloc, parent_alloc,
+ create_error); }
void need_sorted_output();
int init();
@@ -1145,6 +1163,12 @@ public:
:QUICK_RANGE_SELECT(thd, table, index_arg, no_alloc, parent_alloc,
create_err)
{};
+ virtual QUICK_RANGE_SELECT *clone(bool *create_error)
+ {
+ DBUG_ASSERT(0);
+ return new QUICK_RANGE_SELECT_GEOM(thd, head, index, no_alloc,
+ parent_alloc, create_error);
+ }
virtual int get_next();
};
@@ -1574,6 +1598,8 @@ class QUICK_SELECT_DESC: public QUICK_RANGE_SELECT
{
public:
QUICK_SELECT_DESC(QUICK_RANGE_SELECT *q, uint used_key_parts);
+ virtual QUICK_RANGE_SELECT *clone(bool *create_error)
+ { DBUG_ASSERT(0); return new QUICK_SELECT_DESC(this, used_key_parts); }
int get_next();
bool reverse_sorted() { return 1; }
int get_type() { return QS_TYPE_RANGE_DESC; }
@@ -1641,6 +1667,38 @@ class SQL_SELECT :public Sql_alloc {
};
+class SQL_SELECT_auto
+{
+ SQL_SELECT *select;
+public:
+ SQL_SELECT_auto(): select(NULL)
+ {}
+ ~SQL_SELECT_auto()
+ {
+ delete select;
+ }
+ SQL_SELECT_auto&
+ operator= (SQL_SELECT *_select)
+ {
+ select= _select;
+ return *this;
+ }
+ operator SQL_SELECT * () const
+ {
+ return select;
+ }
+ SQL_SELECT *
+ operator-> () const
+ {
+ return select;
+ }
+ operator bool () const
+ {
+ return select;
+ }
+};
+
+
class FT_SELECT: public QUICK_RANGE_SELECT
{
public:
@@ -1648,6 +1706,8 @@ public:
QUICK_RANGE_SELECT (thd, table, key, 1, NULL, create_err)
{ (void) init(); }
~FT_SELECT() { file->ft_end(); }
+ virtual QUICK_RANGE_SELECT *clone(bool *create_error)
+ { DBUG_ASSERT(0); return new FT_SELECT(thd, head, index, create_error); }
int init() { return file->ft_init(); }
int reset() { return 0; }
int get_next() { return file->ha_ft_read(record); }
diff --git a/sql/opt_range_mrr.cc b/sql/opt_range_mrr.cc
index ef8ec028aec..85c1eec3ff3 100644
--- a/sql/opt_range_mrr.cc
+++ b/sql/opt_range_mrr.cc
@@ -137,7 +137,7 @@ static void step_down_to(SEL_ARG_RANGE_SEQ *arg, SEL_ARG *key_tree)
TRUE No more ranges in the sequence
*/
-#if (_MSC_FULL_VER == 160030319)
+#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER == 160030319)
/*
Workaround Visual Studio 2010 RTM compiler backend bug, the function enters
infinite loop.
@@ -336,7 +336,7 @@ walk_up_n_right:
return 0;
}
-#if (_MSC_FULL_VER == 160030319)
+#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER == 160030319)
/* VS2010 compiler bug workaround */
#pragma optimize("g", on)
#endif
diff --git a/sql/opt_split.cc b/sql/opt_split.cc
new file mode 100644
index 00000000000..c70fac49930
--- /dev/null
+++ b/sql/opt_split.cc
@@ -0,0 +1,1167 @@
+/*
+ Copyright (c) 2017 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 */
+
+/*
+ This file contains functions to support the splitting technique.
+ This optimization technique can be applied to equi-joins involving
+ materialized tables such as materialized views, materialized derived tables
+ and materialized CTEs. The technique also could be applied to materialized
+ semi-joins though the code below does not support this usage yet.
+
+ Here are the main ideas behind this technique that we'll call SM optimization
+ (SplitMaterialization).
+
+ Consider the query
+ SELECT t1.a, t.min
+ FROM t1, (SELECT t2.a, MIN(t2.b) as min FROM t2 GROUP BY t2.a) t
+ WHERE t1.a = t.a and t1.b < const
+
+ Re-write the query into
+ SELECT t1.a, t.min
+ FROM t1, LATERAL (SELECT t2.a, MIN(t2.b) as min
+ FROM t2 WHERE t2.a = t1.a GROUP BY t2.a) t
+ WHERE t1.b < const
+
+ The execution of the original query (Q1) does the following:
+ 1. Executes the query in the specification of the derived table
+ and puts the result set into a temporary table with an index
+ on the first column.
+ 2. Joins t1 with the temporary table using the its index.
+
+ The execution of the transformed query (Q1R) follows these steps:
+ 1. For each row of t1 where t1.b < const a temporary table
+ containing all rows of of t2 with t2.a = t1.a is created
+ 2. If there are any rows in the temporary table aggregation
+ is performed for them
+ 3. The result of the aggregation is joined with t1.
+
+ The second execution can win if:
+ a) There is an efficient way to select rows of t2 for which t2.a = t1.a
+ (For example if there is an index on t2.a)
+ and
+ b) The number of temporary tables created for partitions
+ is much smaller that the total number of partitions
+
+ It should be noted that for the transformed query aggregation
+ for a partition may be performed several times.
+
+ As we can see the optimization basically splits table t2 into
+ partitions and performs aggregation over each of them
+ independently.
+
+ If we have only one equi-join condition then we either push it as
+ for Q1R or we don't. In a general case we may have much more options.
+ Consider the query (Q3)
+ SELECT
+ FROM t1,t2 (SELECT t3.a, t3.b, MIN(t3.c) as min
+ FROM t3 GROUP BY a,b) t
+ WHERE t.a = t1.a AND t.b = t2.b
+ AND t1.c < c1 and t2.c < c2
+ AND P(t1,t2);
+ (P(t1,t2) designates some additional conditions over columns of t1,t2).
+
+ Assuming that there indexes on t3(a,b) and t3(b) here we have several
+ reasonable options to push equi-join conditions into the derived.
+ All these options should be taken into account when the optimizer
+ evaluates different join orders. When the join order (t1,t,t2) is
+ evaluated there is only one way of splitting : to push the condition
+ t.a = t1.a into t. With the join order (t2,t,t1) only the condition
+ t.b = t2.b can be pushed. When the join orders (t1,t2,t) and (t2,t1,t)
+ are evaluated then the optimizer should consider pushing t.a = t1.a,
+ t.b = t2.b and (t.a = t1.a AND t.b = t2.b) to choose the best condition
+ for splitting. Apparently here last condition is the best one because
+ it provides the miximum possible number of partitions.
+
+ If we dropped the index on t3(a,b) and created the index on t3(a) instead
+ then we would have two options for splitting: to push t.a = t1.a or to
+ push t.b = t2.b. If the selectivity of the index t3(a) is better than
+ the selectivity of t3(b) then the first option is preferred.
+
+ Although the condition (t.a = t1.a AND t.b = t2.b) provides a better
+ splitting than the condition t.a = t1.a the latter will be used for
+ splitting if the execution plan with the join order (t1,t,t2) turns out
+ to be the cheapest one. It's quite possible when the join condition
+ P(t1,t2) has a bad selectivity.
+
+ Whenever the optimizer evaluates the cost of using a splitting it
+ compares it with the cost of materialization without splitting.
+
+ If we just drop the index on t3(a,b) the chances that the splitting
+ will be used becomes much lower but they still exists providing that
+ the fanout of the partial join of t1 and t2 is small enough.
+*/
+
+/*
+ Splitting can be applied to a materialized table specified by the query
+ with post-join operations that require partitioning of the result set produced
+ by the join expression used in the FROM clause the query such as GROUP BY
+ operation and window function operation. In any of these cases the post-join
+ operation can be executed independently for any partition only over the rows
+ of this partition. Also if the set of all partitions is divided into disjoint
+ subsets the operation can applied to each subset independently. In this case
+ all rows are first partitioned into the groups each of which contains all the
+ rows from the partitions belonging the same subset and then each group
+ is subpartitioned into groups in the the post join operation.
+
+ The set of all rows belonging to the union of several partitions is called
+ here superpartition. If a grouping operation is defined by the list
+ e_1,...,e_n then any set S = {e_i1,...,e_ik} can be used to devide all rows
+ into superpartions such that for any two rows r1, r2 the following holds:
+ e_ij(r1) = e_ij(r2) for each e_ij from S. We use the splitting technique
+ only if S consists of references to colums of the joined tables.
+ For example if the GROUP BY list looks like this a, g(b), c we can consider
+ applying the splitting technique to the superpartitions defined by {a,c},
+ {a}, {c} (a and c here may be the references to the columns from different
+ tables).
+*/
+
+ /*
+ The following describes when and how the optimizer decides whether it
+ makes sense to employ the splitting technique.
+
+ 1. For each instance of a materialized table (derived/view/CTE) it is
+ checked that it is potentially splittable. Now it is done right after the
+ execution plan for the select specifying this table has been chosen.
+
+ 2. Any potentially splittable materialized table T is subject to two-phase
+ optimization. It means that the optimizer first builds the best execution
+ plan for join that specifies T. Then the control is passed back to the
+ optimization process of the embedding select Q. After the execution plan
+ for Q has been chosen the optimizer finishes the optimization of the join
+ specifying T.
+
+ 3. When the optimizer builds the container with the KEYUSE structures
+ for the join of embedding select it detects the equi-join conditions
+ PC that potentially could be pushed into a potentially splittable
+ materialized table T. The collected information about such conditions
+ is stored together with other facts on potential splittings for table T.
+
+ 4. When the optimizer starts looking for the best execution plan for the
+ embedding select Q for each potentially splittable materialized table T
+ it creates special KEYUSE structures for pushable equi-join conditions
+ PC. These structures are used to add new elements to the container
+ of KEYUSE structures built for T. The specifics of these elements is
+ that they can be ebabled and disabled during the process of choosing
+ the best plan for Q.
+
+ 5. When the optimizer extends a partial join order with a potentially
+ splittable materialized table T (in function best_access_path) it
+ first evaluates a new execution plan for the modified specification
+ of T that adds all equi-join conditions that can be pushed with
+ current join prefix to the WHERE conditions of the original
+ specification of T. If the cost of the new plan is better than the
+ the cost of the original materialized table then the optimizer
+ prefers to use splitting for the current join prefix. As the cost
+ of the plan depends only on the pushed conditions it makes sense
+ to cache this plan for other prefixes.
+
+ 6. The optimizer takes into account the cost of splitting / materialization
+ of a potentially splittable materialized table T as a startup cost
+ to access table T.
+
+ 7. When the optimizer finally chooses the best execution plan for
+ the embedding select Q and this plan prefers using splitting
+ for table T with pushed equi-join conditions PC then the execution
+ plan for the underlying join with these conditions is chosen for T.
+*/
+
+/*
+ The implementation of the splitting technique below allows to apply
+ the technique only to a materialized derived table / view / CTE whose
+ specification is either a select with GROUP BY or a non-grouping select
+ with window functions that share the same PARTITION BY list.
+*/
+
+#include "mariadb.h"
+#include "sql_select.h"
+
+/* Info on a splitting field */
+struct SplM_field_info
+{
+ /* Splitting field in the materialized table T */
+ Field *mat_field;
+ /* The item from the select list of the specification of T */
+ Item *producing_item;
+ /* The corresponding splitting field from the specification of T */
+ Field *underlying_field;
+};
+
+
+/* Info on the splitting execution plan saved in SplM_opt_info::cache */
+struct SplM_plan_info
+{
+ /* The cached splitting execution plan P */
+ struct st_position *best_positions;
+ /* The cost of the above plan */
+ double cost;
+ /* Selectivity of splitting used in P */
+ double split_sel;
+ /* For fast search of KEYUSE_EXT elements used for splitting in P */
+ struct KEYUSE_EXT *keyuse_ext_start;
+ /* The tables that contains the fields used for splitting in P */
+ TABLE *table;
+ /* The number of the key from 'table' used for splitting in P */
+ uint key;
+ /* Number of the components of 'key' used for splitting in P */
+ uint parts;
+};
+
+
+/*
+ The structure contains the information that is used by the optimizer
+ for potentially splittable materialization of T that is a materialized
+ derived_table / view / CTE
+*/
+class SplM_opt_info : public Sql_alloc
+{
+public:
+ /* The join for the select specifying T */
+ JOIN *join;
+ /* The map of tables from 'join' whose columns can be used for partitioning */
+ table_map tables_usable_for_splitting;
+ /* Info about the fields of the joined tables usable for splitting */
+ SplM_field_info *spl_fields;
+ /* The number of elements in the above list */
+ uint spl_field_cnt;
+ /* Contains the structures to generate all KEYUSEs for pushable equalities */
+ List<KEY_FIELD> added_key_fields;
+ /* The cache of evaluated execution plans for 'join' with pushed equalities */
+ List<SplM_plan_info> plan_cache;
+ /* Cost of best execution plan for join when nothing is pushed */
+ double unsplit_cost;
+ /* Cardinality of T when nothing is pushed */
+ double unsplit_card;
+ /* Lastly evaluated execution plan for 'join' with pushed equalities */
+ SplM_plan_info *last_plan;
+
+ SplM_plan_info *find_plan(TABLE *table, uint key, uint parts);
+};
+
+
+void TABLE::set_spl_opt_info(SplM_opt_info *spl_info)
+{
+ if (spl_info)
+ spl_info->join->spl_opt_info= spl_info;
+ spl_opt_info= spl_info;
+}
+
+
+void TABLE::deny_splitting()
+{
+ DBUG_ASSERT(spl_opt_info != NULL);
+ spl_opt_info->join->spl_opt_info= NULL;
+ spl_opt_info= NULL;
+}
+
+
+double TABLE::get_materialization_cost()
+{
+ DBUG_ASSERT(spl_opt_info != NULL);
+ return spl_opt_info->unsplit_cost;
+}
+
+
+/* This structure is auxiliary and used only in the function that follows it */
+struct SplM_field_ext_info: public SplM_field_info
+{
+ uint item_no;
+ bool is_usable_for_ref_access;
+};
+
+
+/**
+ @brief
+ Check whether this join is one for potentially splittable materialized table
+
+ @details
+ The function checks whether this join is for select that specifies
+ a potentially splittable materialized table T. If so, the collected
+ info on potential splittability of T is attached to the field spl_opt_info
+ of the TABLE structure for T.
+
+ The function returns a positive answer if the following holds:
+ 1. the optimizer switch 'split_materialized' is set 'on'
+ 2. the select owning this join specifies a materialized derived/view/cte T
+ 3. this is the only select in the specification of T
+ 4. condition pushdown is not prohibited into T
+ 5. T is not recursive
+ 6. not all of this join are constant or optimized away
+ 7. T is either
+ 7.1. a grouping table with GROUP BY list P
+ or
+ 7.2. a non-grouping table with window functions over the same non-empty
+ partition specified by the PARTITION BY list P
+ 8. P contains some references on the columns of the joined tables C
+ occurred also in the select list of this join
+ 9. There are defined some keys usable for ref access of fields from C
+ with available statistics.
+
+ @retval
+ true if the answer is positive
+ false otherwise
+*/
+
+bool JOIN::check_for_splittable_materialized()
+{
+ ORDER *partition_list= 0;
+ st_select_lex_unit *unit= select_lex->master_unit();
+ TABLE_LIST *derived= unit->derived;
+ if (!(optimizer_flag(thd, OPTIMIZER_SWITCH_SPLIT_MATERIALIZED)) || // !(1)
+ !(derived && derived->is_materialized_derived()) || // !(2)
+ (unit->first_select()->next_select()) || // !(3)
+ (derived->prohibit_cond_pushdown) || // !(4)
+ (derived->is_recursive_with_table()) || // !(5)
+ (table_count == 0 || const_tables == top_join_tab_count)) // !(6)
+ return false;
+ if (group_list) // (7.1)
+ {
+ if (!select_lex->have_window_funcs())
+ partition_list= group_list;
+ }
+ else if (select_lex->have_window_funcs() &&
+ select_lex->window_specs.elements == 1) // (7.2)
+ {
+ partition_list=
+ select_lex->window_specs.head()->partition_list->first;
+ }
+ if (!partition_list)
+ return false;
+
+ ORDER *ord;
+ Dynamic_array<SplM_field_ext_info> candidates;
+
+ /*
+ Select from partition_list all candidates for splitting.
+ A candidate must be
+ - field item or refer to such (8.1)
+ - item mentioned in the select list (8.2)
+ Put info about such candidates into the array candidates
+ */
+ table_map usable_tables= 0; // tables that contains the candidate
+ for (ord= partition_list; ord; ord= ord->next)
+ {
+ Item *ord_item= *ord->item;
+ if (ord_item->real_item()->type() != Item::FIELD_ITEM) // !(8.1)
+ continue;
+
+ Field *ord_field= ((Item_field *) (ord_item->real_item()))->field;
+
+ /* Ignore fields from of inner tables of outer joins */
+ TABLE_LIST *tbl= ord_field->table->pos_in_table_list;
+ if (tbl->is_inner_table_of_outer_join())
+ continue;
+
+ List_iterator<Item> li(fields_list);
+ Item *item;
+ uint item_no= 0;
+ while ((item= li++))
+ {
+ if ((*ord->item)->eq(item, 0)) // (8.2)
+ {
+ SplM_field_ext_info new_elem;
+ new_elem.producing_item= item;
+ new_elem.item_no= item_no;
+ new_elem.mat_field= derived->table->field[item_no];
+ new_elem.underlying_field= ord_field;
+ new_elem.is_usable_for_ref_access= false;
+ candidates.push(new_elem);
+ usable_tables|= ord_field->table->map;
+ break;
+ }
+ item_no++;
+ }
+ }
+ if (candidates.elements() == 0) // no candidates satisfying (8.1) && (8.2)
+ return false;
+
+ /*
+ For each table from this join find the keys that can be used for ref access
+ of the fields mentioned in the 'array candidates'
+ */
+
+ SplM_field_ext_info *cand;
+ SplM_field_ext_info *cand_start= &candidates.at(0);
+ SplM_field_ext_info *cand_end= cand_start + candidates.elements();
+
+ for (JOIN_TAB *tab= join_tab;
+ tab < join_tab + top_join_tab_count; tab++)
+ {
+ TABLE *table= tab->table;
+ if (!(table->map & usable_tables))
+ continue;
+
+ table->keys_usable_for_splitting.clear_all();
+ uint i;
+ for (i= 0; i < table->s->keys; i++)
+ {
+ if (!table->keys_in_use_for_query.is_set(i))
+ continue;
+ KEY *key_info= table->key_info + i;
+ uint key_parts= table->actual_n_key_parts(key_info);
+ uint usable_kp_cnt= 0;
+ for ( ; usable_kp_cnt < key_parts; usable_kp_cnt++)
+ {
+ if (key_info->actual_rec_per_key(usable_kp_cnt) == 0)
+ break;
+ int fldnr= key_info->key_part[usable_kp_cnt].fieldnr;
+
+ for (cand= cand_start; cand < cand_end; cand++)
+ {
+ if (cand->underlying_field->table == table &&
+ cand->underlying_field->field_index + 1 == fldnr)
+ {
+ cand->is_usable_for_ref_access= true;
+ break;
+ }
+ }
+ if (cand == cand_end)
+ break;
+ }
+ if (usable_kp_cnt)
+ table->keys_usable_for_splitting.set_bit(i);
+ }
+ }
+
+ /* Count the candidate fields that can be accessed by ref */
+ uint spl_field_cnt= (uint)candidates.elements();
+ for (cand= cand_start; cand < cand_end; cand++)
+ {
+ if (!cand->is_usable_for_ref_access)
+ spl_field_cnt--;
+ }
+
+ if (!spl_field_cnt) // No candidate field can be accessed by ref => !(9)
+ return false;
+
+ /*
+ Create a structure of the type SplM_opt_info and fill it with
+ the collected info on potential splittability of T
+ */
+ SplM_opt_info *spl_opt_info= new (thd->mem_root) SplM_opt_info();
+ SplM_field_info *spl_field=
+ (SplM_field_info *) (thd->calloc(sizeof(SplM_field_info) *
+ spl_field_cnt));
+
+ if (!(spl_opt_info && spl_field)) // consider T as not good for splitting
+ return false;
+
+ spl_opt_info->join= this;
+ spl_opt_info->tables_usable_for_splitting= 0;
+ spl_opt_info->spl_field_cnt= spl_field_cnt;
+ spl_opt_info->spl_fields= spl_field;
+ for (cand= cand_start; cand < cand_end; cand++)
+ {
+ if (!cand->is_usable_for_ref_access)
+ continue;
+ spl_field->producing_item= cand->producing_item;
+ spl_field->underlying_field= cand->underlying_field;
+ spl_field->mat_field= cand->mat_field;
+ spl_opt_info->tables_usable_for_splitting|=
+ cand->underlying_field->table->map;
+ spl_field++;
+ }
+
+ /* Attach this info to the table T */
+ derived->table->set_spl_opt_info(spl_opt_info);
+
+ /*
+ If this is specification of a materialized derived table T that is
+ potentially splittable and is used in the from list of the right operand
+ of an IN predicand transformed to a semi-join then the embedding semi-join
+ nest is not allowed to be materialized.
+ */
+ if (derived && derived->is_materialized_derived() &&
+ derived->embedding && derived->embedding->sj_subq_pred)
+ derived->embedding->sj_subq_pred->types_allow_materialization= FALSE;
+ return true;
+}
+
+
+/**
+ @brief
+ Collect info on KEY_FIELD usable for splitting
+
+ @param
+ key_field KEY_FIELD to collect info on
+
+ @details
+ The function assumes that this table is potentially splittable.
+ The function checks whether the KEY_FIELD structure key_field built for
+ this table was created for a splitting field f. If so, the function does
+ the following using info from key_field:
+ 1. Builds an equality of the form f = key_field->val that could be
+ pushed into this table.
+ 2. Creates a new KEY_FIELD structure for this equality and stores
+ a reference to this structure in this->spl_opt_info.
+*/
+
+void TABLE::add_splitting_info_for_key_field(KEY_FIELD *key_field)
+{
+ DBUG_ASSERT(spl_opt_info != NULL);
+ JOIN *join= spl_opt_info->join;
+ Field *field= key_field->field;
+ SplM_field_info *spl_field= spl_opt_info->spl_fields;
+ uint i= spl_opt_info->spl_field_cnt;
+ for ( ; i; i--, spl_field++)
+ {
+ if (spl_field->mat_field == field)
+ break;
+ }
+ if (!i) // field is not usable for splitting
+ return;
+
+ /*
+ Any equality condition that can be potentially pushed into the
+ materialized derived table is constructed now though later it may turn out
+ that it is not needed, because it is not used for splitting.
+ The reason for this is that the failure to construct it when it has to be
+ injected causes denial for further processing of the query.
+ Formally this equality is needed in the KEY_FIELD structure constructed
+ here that will be used to generate additional keyuses usable for splitting.
+ However key_field.cond could be used for this purpose (see implementations
+ of virtual function can_optimize_keypart_ref()).
+
+ The condition is built in such a form that it can be added to the WHERE
+ condition of the select that specifies this table.
+ */
+ THD *thd= in_use;
+ Item *left_item= spl_field->producing_item->build_clone(thd);
+ Item *right_item= key_field->val->build_clone(thd);
+ Item_func_eq *eq_item= 0;
+ if (left_item && right_item)
+ {
+ right_item->walk(&Item::set_fields_as_dependent_processor,
+ false, join->select_lex);
+ right_item->update_used_tables();
+ eq_item= new (thd->mem_root) Item_func_eq(thd, left_item, right_item);
+ }
+ if (!eq_item)
+ return;
+ KEY_FIELD *added_key_field=
+ (KEY_FIELD *) thd->alloc(sizeof(KEY_FIELD));
+ if (!added_key_field ||
+ spl_opt_info->added_key_fields.push_back(added_key_field,thd->mem_root))
+ return;
+ added_key_field->field= spl_field->underlying_field;
+ added_key_field->cond= eq_item;
+ added_key_field->val= key_field->val;
+ added_key_field->level= 0;
+ added_key_field->optimize= KEY_OPTIMIZE_EQ;
+ added_key_field->eq_func= true;
+
+ Item *real= key_field->val->real_item();
+ if ((real->type() == Item::FIELD_ITEM) &&
+ ((Item_field*)real)->field->maybe_null())
+ added_key_field->null_rejecting= true;
+ else
+ added_key_field->null_rejecting= false;
+
+ added_key_field->cond_guard= NULL;
+ added_key_field->sj_pred_no= UINT_MAX;
+ return;
+}
+
+
+static bool
+add_ext_keyuse_for_splitting(Dynamic_array<KEYUSE_EXT> *ext_keyuses,
+ KEY_FIELD *added_key_field, uint key, uint part)
+{
+ KEYUSE_EXT keyuse_ext;
+ Field *field= added_key_field->field;
+
+ JOIN_TAB *tab=field->table->reginfo.join_tab;
+ key_map possible_keys=field->get_possible_keys();
+ possible_keys.intersect(field->table->keys_usable_for_splitting);
+ tab->keys.merge(possible_keys);
+
+ Item_func_eq *eq_item= (Item_func_eq *) (added_key_field->cond);
+ keyuse_ext.table= field->table;
+ keyuse_ext.val= eq_item->arguments()[1];
+ keyuse_ext.key= key;
+ keyuse_ext.keypart=part;
+ keyuse_ext.keypart_map= (key_part_map) 1 << part;
+ keyuse_ext.used_tables= keyuse_ext.val->used_tables();
+ keyuse_ext.optimize= added_key_field->optimize & KEY_OPTIMIZE_REF_OR_NULL;
+ keyuse_ext.ref_table_rows= 0;
+ keyuse_ext.null_rejecting= added_key_field->null_rejecting;
+ keyuse_ext.cond_guard= added_key_field->cond_guard;
+ keyuse_ext.sj_pred_no= added_key_field->sj_pred_no;
+ keyuse_ext.validity_ref= 0;
+ keyuse_ext.needed_in_prefix= added_key_field->val->used_tables();
+ keyuse_ext.validity_var= false;
+ return ext_keyuses->push(keyuse_ext);
+}
+
+
+static int
+sort_ext_keyuse(KEYUSE_EXT *a, KEYUSE_EXT *b)
+{
+ if (a->table->tablenr != b->table->tablenr)
+ return (int) (a->table->tablenr - b->table->tablenr);
+ if (a->key != b->key)
+ return (int) (a->key - b->key);
+ return (int) (a->keypart - b->keypart);
+}
+
+
+static void
+sort_ext_keyuses(Dynamic_array<KEYUSE_EXT> *keyuses)
+{
+ KEYUSE_EXT *first_keyuse= &keyuses->at(0);
+ my_qsort(first_keyuse, keyuses->elements(), sizeof(KEYUSE_EXT),
+ (qsort_cmp) sort_ext_keyuse);
+}
+
+
+/**
+ @brief
+ Add info on keyuses usable for splitting into an array
+*/
+
+static bool
+add_ext_keyuses_for_splitting_field(Dynamic_array<KEYUSE_EXT> *ext_keyuses,
+ KEY_FIELD *added_key_field)
+{
+ Field *field= added_key_field->field;
+ TABLE *table= field->table;
+ for (uint key= 0; key < table->s->keys; key++)
+ {
+ if (!(table->keys_usable_for_splitting.is_set(key)))
+ continue;
+ KEY *key_info= table->key_info + key;
+ uint key_parts= table->actual_n_key_parts(key_info);
+ KEY_PART_INFO *key_part_info= key_info->key_part;
+ for (uint part=0; part < key_parts; part++, key_part_info++)
+ {
+ if (!field->eq(key_part_info->field))
+ continue;
+ if (add_ext_keyuse_for_splitting(ext_keyuses, added_key_field, key, part))
+ return true;
+ }
+ }
+ return false;
+}
+
+
+/*
+ @brief
+ Cost of the post join operation used in specification of splittable table
+*/
+
+static
+double spl_postjoin_oper_cost(THD *thd, double join_record_count, uint rec_len)
+{
+ double cost;
+ cost= get_tmp_table_write_cost(thd, join_record_count,rec_len) *
+ join_record_count; // cost to fill tmp table
+ cost+= get_tmp_table_lookup_cost(thd, join_record_count,rec_len) *
+ join_record_count; // cost to perform post join operation used here
+ cost+= get_tmp_table_lookup_cost(thd, join_record_count, rec_len) +
+ (join_record_count == 0 ? 0 :
+ join_record_count * log2 (join_record_count)) *
+ SORT_INDEX_CMP_COST; // cost to perform sorting
+ return cost;
+}
+
+/**
+ @brief
+ Add KEYUSE structures that can be usable for splitting
+
+ @details
+ This function is called only for joins created for potentially
+ splittable materialized tables. The function does the following:
+ 1. Creates the dynamic array ext_keyuses_for_splitting of KEYUSE_EXT
+ structures and fills is with info about all keyuses that
+ could be used for splitting.
+ 2. Sort the array ext_keyuses_for_splitting for fast access by key
+ on certain columns.
+ 3. Collects and stores cost and cardinality info on the best execution
+ plan that does not use splitting and save this plan together with
+ corresponding array of keyuses.
+ 4. Expand this array with KEYUSE elements built from the info stored
+ in ext_keyuses_for_splitting that could be produced by pushed
+ equalities employed for splitting.
+ 5. Prepare the extended array of keyuses to be used in the function
+ best_access_plan()
+*/
+
+void JOIN::add_keyuses_for_splitting()
+{
+ uint i;
+ uint idx;
+ KEYUSE_EXT *keyuse_ext;
+ KEYUSE_EXT keyuse_ext_end;
+ double oper_cost;
+ uint rec_len;
+ uint added_keyuse_count;
+ TABLE *table= select_lex->master_unit()->derived->table;
+ List_iterator_fast<KEY_FIELD> li(spl_opt_info->added_key_fields);
+ 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>))
+ goto err;
+ while ((added_key_field= li++))
+ {
+ (void) add_ext_keyuses_for_splitting_field(ext_keyuses_for_splitting,
+ added_key_field);
+ }
+ added_keyuse_count= (uint)ext_keyuses_for_splitting->elements();
+ if (!added_keyuse_count)
+ goto err;
+ sort_ext_keyuses(ext_keyuses_for_splitting);
+ bzero((char*) &keyuse_ext_end, sizeof(keyuse_ext_end));
+ if (ext_keyuses_for_splitting->push(keyuse_ext_end))
+ goto err;
+
+ spl_opt_info->unsplit_card= join_record_count;
+
+ rec_len= table->s->rec_buff_length;
+
+ oper_cost= spl_postjoin_oper_cost(thd, join_record_count, rec_len);
+
+ spl_opt_info->unsplit_cost= best_positions[table_count-1].read_time +
+ oper_cost;
+
+ if (!(save_qep= new Join_plan_state(table_count + 1)))
+ goto err;
+
+ save_query_plan(save_qep);
+
+ if (!keyuse.buffer &&
+ my_init_dynamic_array(&keyuse, sizeof(KEYUSE), 20, 64,
+ MYF(MY_THREAD_SPECIFIC)))
+ goto err;
+
+ if (allocate_dynamic(&keyuse,
+ save_qep->keyuse.elements +
+ added_keyuse_count))
+ goto err;
+
+ memcpy(keyuse.buffer,
+ save_qep->keyuse.buffer,
+ (size_t) save_qep->keyuse.elements * keyuse.size_of_element);
+ keyuse.elements= save_qep->keyuse.elements;
+
+ keyuse_ext= &ext_keyuses_for_splitting->at(0);
+ idx= save_qep->keyuse.elements;
+ for (i=0; i < added_keyuse_count; i++, keyuse_ext++, idx++)
+ {
+ set_dynamic(&keyuse, (KEYUSE *) keyuse_ext, idx);
+ KEYUSE *added_keyuse= ((KEYUSE *) (keyuse.buffer)) + idx;
+ added_keyuse->validity_ref= &keyuse_ext->validity_var;
+ }
+
+ if (sort_and_filter_keyuse(thd, &keyuse, true))
+ goto err;
+ optimize_keyuse(this, &keyuse);
+
+ for (uint i= 0; i < table_count; i++)
+ {
+ JOIN_TAB *tab= join_tab + i;
+ map2table[tab->table->tablenr]= tab;
+ }
+
+ return;
+
+err:
+ if (save_qep)
+ restore_query_plan(save_qep);
+ table->deny_splitting();
+ return;
+}
+
+
+/**
+ @brief
+ Add KEYUSE structures that can be usable for splitting of this joined table
+*/
+
+void JOIN_TAB::add_keyuses_for_splitting()
+{
+ DBUG_ASSERT(table->spl_opt_info != NULL);
+ SplM_opt_info *spl_opt_info= table->spl_opt_info;
+ spl_opt_info->join->add_keyuses_for_splitting();
+}
+
+
+/*
+ @brief
+ Find info on the splitting plan by the splitting key
+*/
+
+SplM_plan_info *SplM_opt_info::find_plan(TABLE *table, uint key, uint parts)
+{
+ List_iterator_fast<SplM_plan_info> li(plan_cache);
+ SplM_plan_info *spl_plan;
+ while ((spl_plan= li++))
+ {
+ if (spl_plan->table == table &&
+ spl_plan->key == key &&
+ spl_plan->parts == parts)
+ break;
+ }
+ return spl_plan;
+}
+
+
+/*
+ @breaf
+ Enable/Disable a keyuses that can be used for splitting
+ */
+
+static
+void reset_validity_vars_for_keyuses(KEYUSE_EXT *key_keyuse_ext_start,
+ TABLE *table, uint key,
+ table_map remaining_tables,
+ bool validity_val)
+{
+ KEYUSE_EXT *keyuse_ext= key_keyuse_ext_start;
+ do
+ {
+ if (!(keyuse_ext->needed_in_prefix & remaining_tables))
+ {
+ /*
+ The enabling/disabling flags are set just in KEYUSE_EXT structures.
+ Yet keyuses that are used by best_access_path() have pointers
+ to these flags.
+ */
+ keyuse_ext->validity_var= validity_val;
+ }
+ keyuse_ext++;
+ }
+ while (keyuse_ext->key == key && keyuse_ext->table == table);
+}
+
+
+/**
+ @brief
+ Choose the best splitting to extend the evaluated partial join
+
+ @param
+ record_count estimated cardinality of the extended partial join
+ remaining_tables tables not joined yet
+
+ @details
+ This function is called during the search for the best execution
+ plan of the join that contains this table T. The function is called
+ every time when the optimizer tries to extend a partial join by
+ joining it with table T. Depending on what tables are already in the
+ partial join different equalities usable for splitting can be pushed
+ into T. The function evaluates different variants and chooses the
+ best one. Then the function finds the plan for the materializing join
+ with the chosen equality conditions pushed into it. If the cost of the
+ plan turns out to be less than the cost of the best plan without
+ splitting the function set it as the true plan of materialization
+ of the table T.
+ The function caches the found plans for materialization of table T
+ together if the info what key was used for splitting. Next time when
+ the optimizer prefers to use the same key the plan is taken from
+ the cache of plans
+
+ @retval
+ Pointer to the info on the found plan that employs the pushed equalities
+ if the plan has been chosen, NULL - otherwise.
+*/
+
+SplM_plan_info * JOIN_TAB::choose_best_splitting(double record_count,
+ table_map remaining_tables)
+{
+ SplM_opt_info *spl_opt_info= table->spl_opt_info;
+ DBUG_ASSERT(spl_opt_info != NULL);
+ JOIN *join= spl_opt_info->join;
+ THD *thd= join->thd;
+ table_map tables_usable_for_splitting=
+ spl_opt_info->tables_usable_for_splitting;
+ KEYUSE_EXT *keyuse_ext= &join->ext_keyuses_for_splitting->at(0);
+ KEYUSE_EXT *UNINIT_VAR(best_key_keyuse_ext_start);
+ TABLE *best_table= 0;
+ double best_rec_per_key= DBL_MAX;
+ SplM_plan_info *spl_plan= 0;
+ uint best_key= 0;
+ uint best_key_parts= 0;
+
+ /*
+ Check whether there are keys that can be used to join T employing splitting
+ and if so, select the best out of such keys
+ */
+ for (uint tablenr= 0; tablenr < join->table_count; tablenr++)
+ {
+ if (!((1ULL << tablenr) & tables_usable_for_splitting))
+ continue;
+ JOIN_TAB *tab= join->map2table[tablenr];
+ TABLE *table= tab->table;
+ if (keyuse_ext->table != table)
+ continue;
+ do
+ {
+ uint key= keyuse_ext->key;
+ KEYUSE_EXT *key_keyuse_ext_start= keyuse_ext;
+ key_part_map found_parts= 0;
+ do
+ {
+ if (keyuse_ext->needed_in_prefix & remaining_tables)
+ {
+ keyuse_ext++;
+ continue;
+ }
+ if (!(keyuse_ext->keypart_map & found_parts))
+ {
+ if ((!found_parts && !keyuse_ext->keypart) ||
+ (found_parts && ((keyuse_ext->keypart_map >> 1) & found_parts)))
+ found_parts|= keyuse_ext->keypart_map;
+ else
+ {
+ do
+ {
+ keyuse_ext++;
+ }
+ while (keyuse_ext->key == key && keyuse_ext->table == table);
+ break;
+ }
+ }
+ KEY *key_info= table->key_info + key;
+ double rec_per_key=
+ key_info->actual_rec_per_key(keyuse_ext->keypart);
+ if (rec_per_key < best_rec_per_key)
+ {
+ best_table= keyuse_ext->table;
+ best_key= keyuse_ext->key;
+ best_key_parts= keyuse_ext->keypart + 1;
+ best_rec_per_key= rec_per_key;
+ best_key_keyuse_ext_start= key_keyuse_ext_start;
+ }
+ keyuse_ext++;
+ }
+ while (keyuse_ext->key == key && keyuse_ext->table == table);
+ }
+ while (keyuse_ext->table == table);
+ }
+ spl_opt_info->last_plan= 0;
+ if (best_table)
+ {
+ /*
+ The key for splitting was chosen, look for the plan for this key
+ in the cache
+ */
+ spl_plan= spl_opt_info->find_plan(best_table, best_key, best_key_parts);
+ if (!spl_plan &&
+ (spl_plan= (SplM_plan_info *) thd->alloc(sizeof(SplM_plan_info))) &&
+ (spl_plan->best_positions=
+ (POSITION *) thd->alloc(sizeof(POSITION) * join->table_count)) &&
+ !spl_opt_info->plan_cache.push_back(spl_plan))
+ {
+ /*
+ The plan for the chosen key has not been found in the cache.
+ Build a new plan and save info on it in the cache
+ */
+ table_map all_table_map= (((table_map) 1) << join->table_count) - 1;
+ reset_validity_vars_for_keyuses(best_key_keyuse_ext_start, best_table,
+ best_key, remaining_tables, true);
+ choose_plan(join, all_table_map & ~join->const_table_map);
+ spl_plan->keyuse_ext_start= best_key_keyuse_ext_start;
+ spl_plan->table= best_table;
+ spl_plan->key= best_key;
+ spl_plan->parts= best_key_parts;
+ spl_plan->split_sel= best_rec_per_key /
+ (spl_opt_info->unsplit_card ?
+ spl_opt_info->unsplit_card : 1);
+
+ uint rec_len= table->s->rec_buff_length;
+
+ double split_card= spl_opt_info->unsplit_card * spl_plan->split_sel;
+ double oper_cost= split_card *
+ spl_postjoin_oper_cost(thd, split_card, rec_len);
+ spl_plan->cost= join->best_positions[join->table_count-1].read_time +
+ + oper_cost;
+
+ memcpy((char *) spl_plan->best_positions,
+ (char *) join->best_positions,
+ sizeof(POSITION) * join->table_count);
+ reset_validity_vars_for_keyuses(best_key_keyuse_ext_start, best_table,
+ best_key, remaining_tables, false);
+ }
+ if (spl_plan)
+ {
+ if(record_count * spl_plan->cost < spl_opt_info->unsplit_cost)
+ {
+ /*
+ The best plan that employs splitting is cheaper than
+ the plan without splitting
+ */
+ spl_opt_info->last_plan= spl_plan;
+ }
+ }
+ }
+
+ /* Set the cost of the preferred materialization for this partial join */
+ records= (ha_rows)spl_opt_info->unsplit_card;
+ spl_plan= spl_opt_info->last_plan;
+ if (spl_plan)
+ {
+ startup_cost= record_count * spl_plan->cost;
+ records= (ha_rows) (records * spl_plan->split_sel);
+ }
+ else
+ startup_cost= spl_opt_info->unsplit_cost;
+ return spl_plan;
+}
+
+
+/**
+ @brief
+ Inject equalities for splitting used by the materialization join
+
+ @param
+ remaining_tables used to filter out the equalities that cannot
+ be pushed.
+
+ @details
+ This function is called by JOIN_TAB::fix_splitting that is used
+ to fix the chosen splitting of a splittable materialized table T
+ in the final query execution plan. In this plan the table T
+ is joined just before the 'remaining_tables'. So all equalities
+ usable for splitting whose right parts do not depend on any of
+ remaining tables can be pushed into join for T.
+ The function also marks the select that specifies T as
+ UNCACHEABLE_DEPENDENT_INJECTED.
+
+ @retval
+ false on success
+ true on failure
+*/
+
+bool JOIN::inject_best_splitting_cond(table_map remaining_tables)
+{
+ Item *inj_cond= 0;
+ List<Item> inj_cond_list;
+ List_iterator<KEY_FIELD> li(spl_opt_info->added_key_fields);
+ KEY_FIELD *added_key_field;
+ while ((added_key_field= li++))
+ {
+ if (remaining_tables & added_key_field->val->used_tables())
+ continue;
+ if (inj_cond_list.push_back(added_key_field->cond, thd->mem_root))
+ return true;
+ }
+ DBUG_ASSERT(inj_cond_list.elements);
+ switch (inj_cond_list.elements) {
+ case 1:
+ inj_cond= inj_cond_list.head(); break;
+ default:
+ inj_cond= new (thd->mem_root) Item_cond_and(thd, inj_cond_list);
+ if (!inj_cond)
+ return true;
+ }
+ if (inj_cond)
+ inj_cond->fix_fields(thd,0);
+
+ if (inject_cond_into_where(inj_cond))
+ return true;
+
+ select_lex->uncacheable|= UNCACHEABLE_DEPENDENT_INJECTED;
+ st_select_lex_unit *unit= select_lex->master_unit();
+ unit->uncacheable|= UNCACHEABLE_DEPENDENT_INJECTED;
+
+ return false;
+}
+
+
+/**
+ @brief
+ Fix the splitting chosen for a splittable table in the final query plan
+
+ @param
+ spl_plan info on the splitting plan chosen for the splittable table T
+ remaining_tables the table T is joined just before these tables
+ is_const_table the table T is a constant table
+
+ @details
+ If in the final query plan the optimizer has chosen a splitting plan
+ then the function sets this plan as the final execution plan to
+ materialized the table T. Otherwise the plan that does not use
+ splitting is set for the materialization.
+
+ @retval
+ false on success
+ true on failure
+*/
+
+bool JOIN_TAB::fix_splitting(SplM_plan_info *spl_plan,
+ table_map remaining_tables,
+ bool is_const_table)
+{
+ SplM_opt_info *spl_opt_info= table->spl_opt_info;
+ DBUG_ASSERT(table->spl_opt_info != 0);
+ JOIN *md_join= spl_opt_info->join;
+ if (spl_plan && !is_const_table)
+ {
+ memcpy((char *) md_join->best_positions,
+ (char *) spl_plan->best_positions,
+ sizeof(POSITION) * md_join->table_count);
+ if (md_join->inject_best_splitting_cond(remaining_tables))
+ return true;
+ /*
+ This is called for a proper work of JOIN::get_best_combination()
+ called for the join that materializes T
+ */
+ reset_validity_vars_for_keyuses(spl_plan->keyuse_ext_start,
+ spl_plan->table,
+ spl_plan->key,
+ remaining_tables,
+ true);
+ }
+ else if (md_join->save_qep)
+ {
+ md_join->restore_query_plan(md_join->save_qep);
+ }
+ return false;
+}
+
+
+/**
+ @brief
+ Fix the splittings chosen splittable tables in the final query plan
+
+ @details
+ The function calls JOIN_TAB::fix_splittins for all potentially
+ splittable tables in this join to set all final materialization
+ plans chosen for these tables.
+
+ @retval
+ false on success
+ true on failure
+*/
+
+bool JOIN::fix_all_splittings_in_plan()
+{
+ table_map prev_tables= 0;
+ table_map all_tables= (1 << table_count) - 1;
+ for (uint tablenr= 0; tablenr < table_count; tablenr++)
+ {
+ POSITION *cur_pos= &best_positions[tablenr];
+ JOIN_TAB *tab= cur_pos->table;
+ if (tab->table->is_splittable())
+ {
+ SplM_plan_info *spl_plan= cur_pos->spl_plan;
+ if (tab->fix_splitting(spl_plan, all_tables & ~prev_tables,
+ tablenr < const_tables ))
+ return true;
+ }
+ prev_tables|= tab->table->map;
+ }
+ return false;
+}
diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc
index 45aa625389d..83840d65876 100644
--- a/sql/opt_subselect.cc
+++ b/sql/opt_subselect.cc
@@ -26,7 +26,7 @@
#pragma implementation // gcc: Class implementation
#endif
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_base.h"
#include "sql_const.h"
#include "sql_select.h"
@@ -433,9 +433,9 @@ referred to, we can only generate equalities that refer to the outer (or inner)
tables. Note that this will disallow handling of cases like (CASE-FOR-SUBST).
Currently, solution #2 is implemented.
-
*/
+LEX_CSTRING weedout_key= {STRING_WITH_LEN("weedout_key")};
static
bool subquery_types_allow_materialization(Item_in_subselect *in_subs);
@@ -448,17 +448,13 @@ static bool convert_subq_to_jtbm(JOIN *parent_join,
Item_in_subselect *subq_pred, bool *remove);
static TABLE_LIST *alloc_join_nest(THD *thd);
static uint get_tmp_table_rec_length(Ref_ptr_array p_list, uint elements);
-static double get_tmp_table_lookup_cost(THD *thd, double row_count,
- uint row_size);
-static double get_tmp_table_write_cost(THD *thd, double row_count,
- uint row_size);
bool find_eq_ref_candidate(TABLE *table, table_map sj_inner_tables);
static SJ_MATERIALIZATION_INFO *
at_sjmat_pos(const JOIN *join, table_map remaining_tables, const JOIN_TAB *tab,
uint idx, bool *loose_scan);
static Item *create_subq_in_equalities(THD *thd, SJ_MATERIALIZATION_INFO *sjm,
Item_in_subselect *subq_pred);
-static void remove_sj_conds(THD *thd, Item **tree);
+static bool remove_sj_conds(THD *thd, Item **tree);
static bool is_cond_sj_in_equality(Item *item);
static bool sj_table_is_included(JOIN *join, JOIN_TAB *join_tab);
static Item *remove_additional_cond(Item* conds);
@@ -621,8 +617,8 @@ 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->fixed &&
- in_subs->left_expr->fix_fields(thd, &in_subs->left_expr);
+ bool failure= in_subs->left_expr->fix_fields_if_needed(thd,
+ &in_subs->left_expr);
thd->lex->current_select= current;
thd->where= save_where;
if (failure)
@@ -832,7 +828,12 @@ bool subquery_types_allow_materialization(Item_in_subselect *in_subs)
in_subs->types_allow_materialization= FALSE; // Assign default values
in_subs->sjm_scan_allowed= FALSE;
-
+
+ /*
+ The checks here must be kept in sync with the one in
+ Item_func_in::in_predicate_to_in_subs_transformer().
+ */
+
bool all_are_fields= TRUE;
uint32 total_key_length = 0;
for (uint i= 0; i < elements; i++)
@@ -842,34 +843,9 @@ bool subquery_types_allow_materialization(Item_in_subselect *in_subs)
all_are_fields &= (outer->real_item()->type() == Item::FIELD_ITEM &&
inner->real_item()->type() == Item::FIELD_ITEM);
total_key_length += inner->max_length;
- if (outer->cmp_type() != inner->cmp_type())
+ if (!inner->type_handler()->subquery_type_allows_materialization(inner,
+ outer))
DBUG_RETURN(FALSE);
- switch (outer->cmp_type()) {
- case STRING_RESULT:
- if (!(outer->collation.collation == inner->collation.collation))
- DBUG_RETURN(FALSE);
- // Materialization does not work with BLOB columns
- if (inner->field_type() == MYSQL_TYPE_BLOB ||
- inner->field_type() == MYSQL_TYPE_GEOMETRY)
- DBUG_RETURN(FALSE);
- /*
- Materialization also is unable to work when create_tmp_table() will
- create a blob column because item->max_length is too big.
- The following check is copied from Item::make_string_field():
- */
- if (inner->too_big_for_varchar())
- {
- DBUG_RETURN(FALSE);
- }
- break;
- case TIME_RESULT:
- if (mysql_type_to_time_type(outer->field_type()) !=
- mysql_type_to_time_type(inner->field_type()))
- DBUG_RETURN(FALSE);
- default:
- /* suitable for materialization */
- break;
- }
}
/*
@@ -1093,6 +1069,8 @@ bool convert_join_subqueries_to_semijoins(JOIN *join)
SELECT_LEX *subq_sel= in_subq->get_select_lex();
if (subq_sel->handle_derived(thd->lex, DT_MERGE))
DBUG_RETURN(TRUE);
+ if (subq_sel->join->transform_in_predicates_into_in_subq(thd))
+ DBUG_RETURN(TRUE);
subq_sel->update_used_tables();
}
@@ -1588,6 +1566,7 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred)
{
TABLE_LIST *outer_tbl= subq_pred->emb_on_expr_nest;
TABLE_LIST *wrap_nest;
+ LEX_CSTRING sj_wrap_name= { STRING_WITH_LEN("(sj-wrap)") };
/*
We're dealing with
@@ -1613,7 +1592,7 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred)
}
wrap_nest->embedding= outer_tbl->embedding;
wrap_nest->join_list= outer_tbl->join_list;
- wrap_nest->alias= (char*) "(sj-wrap)";
+ wrap_nest->alias= sj_wrap_name;
wrap_nest->nested_join->join_list.empty();
wrap_nest->nested_join->join_list.push_back(outer_tbl, thd->mem_root);
@@ -1652,6 +1631,7 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred)
TABLE_LIST *sj_nest;
NESTED_JOIN *nested_join;
+ LEX_CSTRING sj_nest_name= { STRING_WITH_LEN("(sj-nest)") };
if (!(sj_nest= alloc_join_nest(thd)))
{
DBUG_RETURN(TRUE);
@@ -1660,7 +1640,7 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred)
sj_nest->join_list= emb_join_list;
sj_nest->embedding= emb_tbl_nest;
- sj_nest->alias= (char*) "(sj-nest)";
+ 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();
@@ -1718,7 +1698,7 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred)
// The subqueries were replaced for Item_int(1) earlier
subq_pred->reset_strategy(SUBS_SEMI_JOIN); // for subsequent executions
- /*TODO: also reset the 'with_subselect' there. */
+ /*TODO: also reset the 'm_with_subquery' there. */
/* n. Adjust the parent_join->table_count counter */
uint table_no= parent_join->table_count;
@@ -1751,8 +1731,7 @@ 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->fixed &&
- subq_pred->left_expr->fix_fields(thd, &subq_pred->left_expr))
+ if (subq_pred->left_expr->fix_fields_if_needed(thd, &subq_pred->left_expr))
DBUG_RETURN(TRUE);
thd->lex->current_select=save_lex;
@@ -1838,9 +1817,9 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred)
*/
Item_row *row= new (thd->mem_root) Item_row(thd, subq_lex->pre_fix);
/* fix fields on subquery was call so they should be the same */
- DBUG_ASSERT(subq_pred->left_expr->cols() == row->cols());
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);
Item_func_eq *item_eq=
new (thd->mem_root) Item_func_eq(thd, subq_pred->left_expr_orig, row);
@@ -1863,8 +1842,7 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred)
to check for this at name resolution stage, but as a legacy of IN->EXISTS
we have in here).
*/
- if (!sj_nest->sj_on_expr->fixed &&
- sj_nest->sj_on_expr->fix_fields(thd, &sj_nest->sj_on_expr))
+ if (sj_nest->sj_on_expr->fix_fields_if_needed(thd, &sj_nest->sj_on_expr))
{
DBUG_RETURN(TRUE);
}
@@ -1890,9 +1868,8 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred)
emb_tbl_nest->on_expr= and_items(thd, emb_tbl_nest->on_expr,
sj_nest->sj_on_expr);
emb_tbl_nest->on_expr->top_level_item();
- if (!emb_tbl_nest->on_expr->fixed &&
- emb_tbl_nest->on_expr->fix_fields(thd,
- &emb_tbl_nest->on_expr))
+ if (emb_tbl_nest->on_expr->fix_fields_if_needed(thd,
+ &emb_tbl_nest->on_expr))
{
DBUG_RETURN(TRUE);
}
@@ -1908,9 +1885,7 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred)
*/
save_lex= thd->lex->current_select;
thd->lex->current_select=parent_join->select_lex;
- if (!parent_join->conds->fixed &&
- parent_join->conds->fix_fields(thd,
- &parent_join->conds))
+ if (parent_join->conds->fix_fields_if_needed(thd, &parent_join->conds))
{
DBUG_RETURN(1);
}
@@ -1927,19 +1902,22 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred)
}
parent_lex->have_merged_subqueries= TRUE;
- DBUG_RETURN(FALSE);
+ /* Fatal error may have been set to by fix_after_pullout() */
+ DBUG_RETURN(thd->is_fatal_error);
}
const int SUBQERY_TEMPTABLE_NAME_MAX_LEN= 20;
-static void create_subquery_temptable_name(char *to, uint number)
+static void create_subquery_temptable_name(LEX_STRING *str, uint number)
{
+ char *to= str->str;
DBUG_ASSERT(number < 10000);
to= strmov(to, "<subquery");
to= int10_to_str((int) number, to, 10);
to[0]= '>';
to[1]= 0;
+ str->length= (size_t) (to - str->str)+1;
}
@@ -1967,7 +1945,8 @@ static bool convert_subq_to_jtbm(JOIN *parent_join,
TABLE_LIST *tl;
bool optimization_delayed= TRUE;
TABLE_LIST *jtbm;
- char *tbl_alias;
+ LEX_STRING tbl_alias;
+ THD *thd= parent_join->thd;
DBUG_ENTER("convert_subq_to_jtbm");
subq_pred->set_strategy(SUBS_MATERIALIZATION);
@@ -1975,8 +1954,8 @@ static bool convert_subq_to_jtbm(JOIN *parent_join,
*remove_item= TRUE;
- if (!(tbl_alias= (char*)parent_join->thd->calloc(SUBQERY_TEMPTABLE_NAME_MAX_LEN)) ||
- !(jtbm= alloc_join_nest(parent_join->thd))) //todo: this is not a join nest!
+ if (!(tbl_alias.str= (char*)thd->calloc(SUBQERY_TEMPTABLE_NAME_MAX_LEN)) ||
+ !(jtbm= alloc_join_nest(thd))) //todo: this is not a join nest!
{
DBUG_RETURN(TRUE);
}
@@ -1988,13 +1967,13 @@ static bool convert_subq_to_jtbm(JOIN *parent_join,
/* Nests do not participate in those 'chains', so: */
/* jtbm->next_leaf= jtbm->next_local= jtbm->next_global == NULL*/
- emb_join_list->push_back(jtbm, parent_join->thd->mem_root);
+ emb_join_list->push_back(jtbm, thd->mem_root);
/*
Inject the jtbm table into TABLE_LIST::next_leaf list, so that
make_join_statistics() and co. can find it.
*/
- parent_lex->leaf_tables.push_back(jtbm, parent_join->thd->mem_root);
+ parent_lex->leaf_tables.push_back(jtbm, thd->mem_root);
if (subq_pred->unit->first_select()->options & OPTION_SCHEMA_TABLE)
parent_lex->options |= OPTION_SCHEMA_TABLE;
@@ -2015,11 +1994,12 @@ static bool convert_subq_to_jtbm(JOIN *parent_join,
jtbm->jtbm_table_no= parent_join->table_count;
- create_subquery_temptable_name(tbl_alias,
+ create_subquery_temptable_name(&tbl_alias,
subq_pred->unit->first_select()->select_number);
- jtbm->alias= tbl_alias;
+ jtbm->alias.str= tbl_alias.str;
+ jtbm->alias.length= tbl_alias.length;
parent_join->table_count++;
- DBUG_RETURN(FALSE);
+ DBUG_RETURN(thd->is_fatal_error);
}
subselect_hash_sj_engine *hash_sj_engine=
((subselect_hash_sj_engine*)subq_pred->engine);
@@ -2037,32 +2017,16 @@ static bool convert_subq_to_jtbm(JOIN *parent_join,
DBUG_EXECUTE("where", print_where(conds,"SJ-EXPR", QT_ORDINARY););
- create_subquery_temptable_name(tbl_alias, hash_sj_engine->materialize_join->
+ create_subquery_temptable_name(&tbl_alias, hash_sj_engine->materialize_join->
select_lex->select_number);
- jtbm->alias= tbl_alias;
+ jtbm->alias.str= tbl_alias.str;
+ jtbm->alias.length= tbl_alias.length;
parent_lex->have_merged_subqueries= TRUE;
-#if 0
- /* Inject sj_on_expr into the parent's WHERE or ON */
- if (emb_tbl_nest)
- {
- DBUG_ASSERT(0);
- /*emb_tbl_nest->on_expr= and_items(emb_tbl_nest->on_expr,
- sj_nest->sj_on_expr);
- emb_tbl_nest->on_expr->fix_fields(parent_join->thd, &emb_tbl_nest->on_expr);
- */
- }
- else
- {
- /* Inject into the WHERE */
- parent_join->conds= and_items(parent_join->conds, conds);
- parent_join->conds->fix_fields(parent_join->thd, &parent_join->conds);
- parent_join->select_lex->where= parent_join->conds;
- }
-#endif
+
/* Don't unlink the child subselect, as the subquery will be used. */
- DBUG_RETURN(FALSE);
+ DBUG_RETURN(thd->is_fatal_error);
}
@@ -2077,6 +2041,9 @@ static TABLE_LIST *alloc_join_nest(THD *thd)
return tbl;
}
+/*
+ @Note thd->is_fatal_error can be set in case of OOM
+*/
void fix_list_after_tbl_changes(SELECT_LEX *new_parent, List<TABLE_LIST> *tlist)
{
@@ -2419,7 +2386,7 @@ bool optimize_semijoin_nests(JOIN *join, table_map all_table_map)
&subjoin_out_rows);
sjm->materialization_cost.convert_from_cost(subjoin_read_time);
- sjm->rows= subjoin_out_rows;
+ sjm->rows_with_duplicates= sjm->rows= subjoin_out_rows;
// Don't use the following list because it has "stale" items. use
// ref_pointer_array instead:
@@ -2569,7 +2536,7 @@ static uint get_tmp_table_rec_length(Ref_ptr_array p_items, uint elements)
@return the cost of one lookup
*/
-static double
+double
get_tmp_table_lookup_cost(THD *thd, double row_count, uint row_size)
{
if (row_count > thd->variables.max_heap_table_size / (double) row_size)
@@ -2589,7 +2556,7 @@ get_tmp_table_lookup_cost(THD *thd, double row_count, uint row_size)
@return the cost of writing one row
*/
-static double
+double
get_tmp_table_write_cost(THD *thd, double row_count, uint row_size)
{
double lookup_cost= get_tmp_table_lookup_cost(thd, row_count, row_size);
@@ -3059,10 +3026,28 @@ bool Sj_materialization_picker::check_qep(JOIN *join,
disable_jbuf, prefix_rec_count, &curpos, &dummy);
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);
+ //TODO: take into account join condition selectivity here
}
*strategy= SJ_OPT_MATERIALIZE_SCAN;
*read_time= prefix_cost;
+ /*
+ Note: the next line means we did not remove the subquery's fanout from
+ *record_count. It needs to be removed, as the join prefix is
+
+ ntX SJM-SCAN(it1 ... itN) | (ot1 ... otN) ...
+
+ here, the SJM-SCAN may have introduced subquery's fanout (duplicate rows,
+ rows that don't have matches in ot1_i). All this fanout is gone after
+ table otN (or earlier) but taking it into account is hard.
+
+ Some consolation here is that SJM-Scan strategy is applicable when the
+ subquery is smaller than tables otX. If the subquery has large cardinality,
+ we can greatly overestimate *record_count here, but it doesn't matter as
+ SJ-Materialization-Lookup is a better strategy anyway.
+ */
*record_count= prefix_rec_count;
*handled_fanout= mat_nest->sj_inner_tables;
return TRUE;
@@ -3838,6 +3823,7 @@ bool setup_sj_materialization_part1(JOIN_TAB *sjm_tab)
sjm->sjm_table_param.init();
sjm->sjm_table_param.bit_fields_as_long= TRUE;
SELECT_LEX *subq_select= emb_sj_nest->sj_subq_pred->unit->first_select();
+ const LEX_CSTRING sj_materialize_name= { STRING_WITH_LEN("sj-materialize") };
List_iterator<Item> it(subq_select->item_list);
Item *item;
while((item= it++))
@@ -3847,10 +3833,10 @@ bool setup_sj_materialization_part1(JOIN_TAB *sjm_tab)
re-executing it will not be prepared. To use the Items from its
select list we have to prepare (fix_fields) them
*/
- if (!item->fixed && item->fix_fields(thd, it.ref()))
+ if (item->fix_fields_if_needed(thd, it.ref()))
DBUG_RETURN(TRUE);
item= *(it.ref()); // it can be changed by fix_fields
- DBUG_ASSERT(!item->name_length || item->name_length == strlen(item->name));
+ DBUG_ASSERT(!item->name.length || item->name.length == strlen(item->name.str));
sjm->sjm_table_cols.push_back(item, thd->mem_root);
}
@@ -3863,7 +3849,7 @@ bool setup_sj_materialization_part1(JOIN_TAB *sjm_tab)
1, /*save_sum_fields*/
thd->variables.option_bits | TMP_TABLE_ALL_COLUMNS,
HA_POS_ERROR /*rows_limit */,
- (char*)"sj-materialize")))
+ &sj_materialize_name)))
DBUG_RETURN(TRUE); /* purecov: inspected */
sjm->table->map= emb_sj_nest->nested_join->used_tables;
sjm->table->file->extra(HA_EXTRA_WRITE_CACHE);
@@ -3879,6 +3865,11 @@ bool setup_sj_materialization_part1(JOIN_TAB *sjm_tab)
DBUG_RETURN(FALSE);
}
+/**
+ @retval
+ FALSE ok
+ TRUE error
+*/
bool setup_sj_materialization_part2(JOIN_TAB *sjm_tab)
{
@@ -3891,8 +3882,6 @@ bool setup_sj_materialization_part2(JOIN_TAB *sjm_tab)
SJ_MATERIALIZATION_INFO *sjm= emb_sj_nest->sj_mat_info;
THD *thd= tab->join->thd;
uint i;
- //List<Item> &item_list= emb_sj_nest->sj_subq_pred->unit->first_select()->item_list;
- //List_iterator<Item> it(item_list);
if (!sjm->is_sj_scan)
{
@@ -3942,6 +3931,8 @@ bool setup_sj_materialization_part2(JOIN_TAB *sjm_tab)
null_count ? cur_ref_buff : 0,
cur_key_part->length, tab_ref->items[i],
FALSE);
+ if (!*ref_key)
+ DBUG_RETURN(TRUE);
cur_ref_buff+= cur_key_part->store_length;
}
*ref_key= NULL; /* End marker. */
@@ -3967,9 +3958,9 @@ bool setup_sj_materialization_part2(JOIN_TAB *sjm_tab)
*/
for (i= 0; i < sjm->tables; i++)
{
- remove_sj_conds(thd, &tab[i].select_cond);
- if (tab[i].select)
- remove_sj_conds(thd, &tab[i].select->cond);
+ if (remove_sj_conds(thd, &tab[i].select_cond) ||
+ (tab[i].select && remove_sj_conds(thd, &tab[i].select->cond)))
+ DBUG_RETURN(TRUE);
}
if (!(sjm->in_equality= create_subq_in_equalities(thd, sjm,
emb_sj_nest->sj_subq_pred)))
@@ -4006,7 +3997,9 @@ bool setup_sj_materialization_part2(JOIN_TAB *sjm_tab)
temptable record, we copy its columns to their corresponding columns
in the record buffers for the source tables.
*/
- sjm->copy_field= new Copy_field[sjm->sjm_table_cols.elements];
+ if (!(sjm->copy_field= new Copy_field[sjm->sjm_table_cols.elements]))
+ DBUG_RETURN(TRUE);
+
//it.rewind();
Ref_ptr_array p_items= emb_sj_nest->sj_subq_pred->unit->first_select()->ref_pointer_array;
for (uint i=0; i < sjm->sjm_table_cols.elements; i++)
@@ -4074,7 +4067,7 @@ bool setup_sj_materialization_part2(JOIN_TAB *sjm_tab)
sjm_tab->read_record.copy_field= sjm->copy_field;
sjm_tab->read_record.copy_field_end= sjm->copy_field +
sjm->sjm_table_cols.elements;
- sjm_tab->read_record.read_record= rr_sequential_and_unpack;
+ sjm_tab->read_record.read_record_func= rr_sequential_and_unpack;
}
sjm_tab->bush_children->end[-1].next_select= end_sj_materialize;
@@ -4133,16 +4126,20 @@ static Item *create_subq_in_equalities(THD *thd, SJ_MATERIALIZATION_INFO *sjm,
}
+/**
+ @retval
+ 0 ok
+ 1 error
+*/
-
-static void remove_sj_conds(THD *thd, Item **tree)
+static bool remove_sj_conds(THD *thd, Item **tree)
{
if (*tree)
{
if (is_cond_sj_in_equality(*tree))
{
*tree= NULL;
- return;
+ return 0;
}
else if ((*tree)->type() == Item::COND_ITEM)
{
@@ -4151,12 +4148,19 @@ static void remove_sj_conds(THD *thd, Item **tree)
while ((item= li++))
{
if (is_cond_sj_in_equality(item))
- li.replace(new (thd->mem_root) Item_int(thd, 1));
+ {
+ Item_int *tmp= new (thd->mem_root) Item_int(thd, 1);
+ if (!tmp)
+ return 1;
+ li.replace(tmp);
+ }
}
}
}
+ return 0;
}
+
/* Check if given Item was injected by semi-join equality */
static bool is_cond_sj_in_equality(Item *item)
{
@@ -4249,7 +4253,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, TABLE_ALLOC_BLOCK_SIZE, 0, MYF(MY_THREAD_SPECIFIC));
+ init_sql_alloc(&own_root, "SJ_TMP_TABLE",
+ TABLE_ALLOC_BLOCK_SIZE, 0, MYF(MY_THREAD_SPECIFIC));
if (!multi_alloc_root(&own_root,
&table, sizeof(*table),
&share, sizeof(*share),
@@ -4303,12 +4308,13 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd)
/* Create the field */
{
+ LEX_CSTRING field_name= {STRING_WITH_LEN("rowids") };
/*
For the sake of uniformity, always use Field_varstring (altough we could
use Field_string for shorter keys)
*/
- field= new Field_varstring(uniq_tuple_length_arg, FALSE, "rowids", share,
- &my_charset_bin);
+ field= new Field_varstring(uniq_tuple_length_arg, FALSE, &field_name,
+ share, &my_charset_bin);
if (!field)
DBUG_RETURN(0);
field->table= table;
@@ -4343,7 +4349,7 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd)
share->db_plugin= ha_lock_engine(0, heap_hton);
table->file= get_new_handler(share, &table->mem_root,
share->db_type());
- DBUG_ASSERT(uniq_tuple_length_arg <= table->file->max_key_length());
+ DBUG_ASSERT(!table->file || uniq_tuple_length_arg <= table->file->max_key_length());
}
if (!table->file)
goto err;
@@ -4397,7 +4403,7 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd)
field->reset();
/*
Test if there is a default field value. The test for ->ptr is to skip
- 'offset' fields generated by initalize_tables
+ 'offset' fields generated by initialize_tables
*/
// Initialize the table field:
bzero(field->ptr, field->pack_length());
@@ -4443,7 +4449,7 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd)
keyinfo->key_length=0;
keyinfo->rec_per_key=0;
keyinfo->algorithm= HA_KEY_ALG_UNDEF;
- keyinfo->name= (char*) "weedout_key";
+ keyinfo->name= weedout_key;
{
key_part_info->null_bit=0;
key_part_info->field= field;
@@ -4464,7 +4470,7 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd)
}
}
- if (thd->is_fatal_error) // If end of memory
+ if (unlikely(thd->is_fatal_error)) // If end of memory
goto err;
share->db_record_offset= 1;
table->no_rows= 1; // We don't need the data
@@ -4473,10 +4479,11 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd)
recinfo++;
if (share->db_type() == TMP_ENGINE_HTON)
{
- if (create_internal_tmp_table(table, keyinfo, start_recinfo, &recinfo, 0))
+ if (unlikely(create_internal_tmp_table(table, keyinfo, start_recinfo,
+ &recinfo, 0)))
goto err;
}
- if (open_tmp_table(table))
+ if (unlikely(open_tmp_table(table)))
goto err;
thd->mem_root= mem_root_save;
@@ -4588,7 +4595,7 @@ int SJ_TMP_TABLE::sj_weedout_check_row(THD *thd)
}
error= tmp_table->file->ha_write_tmp_row(tmp_table->record[0]);
- if (error)
+ if (unlikely(error))
{
/* create_internal_tmp_table_from_heap will generate error if needed */
if (!tmp_table->file->is_fatal_error(error, HA_CHECK_DUP))
@@ -5172,13 +5179,13 @@ int rewrite_to_index_subquery_engine(JOIN *join)
join->unit->item &&
join->unit->item->substype() == Item_subselect::IN_SUBS &&
join->table_count == 1 && join->conds &&
- !join->unit->is_union())
+ !join->unit->is_unit_op())
{
if (!join->having)
{
Item *where= join->conds;
if (join_tab[0].type == JT_EQ_REF &&
- join_tab[0].ref.items[0]->name == in_left_expr_name)
+ join_tab[0].ref.items[0]->name.str == in_left_expr_name.str)
{
remove_subq_pushed_predicates(join, &where);
save_index_subquery_explain_info(join_tab, where);
@@ -5192,7 +5199,7 @@ int rewrite_to_index_subquery_engine(JOIN *join)
where)));
}
else if (join_tab[0].type == JT_REF &&
- join_tab[0].ref.items[0]->name == in_left_expr_name)
+ join_tab[0].ref.items[0]->name.str == in_left_expr_name.str)
{
remove_subq_pushed_predicates(join, &where);
save_index_subquery_explain_info(join_tab, where);
@@ -5208,8 +5215,8 @@ int rewrite_to_index_subquery_engine(JOIN *join)
0)));
}
} else if (join_tab[0].type == JT_REF_OR_NULL &&
- join_tab[0].ref.items[0]->name == in_left_expr_name &&
- join->having->name == in_having_cond)
+ join_tab[0].ref.items[0]->name.str == in_left_expr_name.str &&
+ join->having->name.str == in_having_cond.str)
{
join_tab[0].type= JT_INDEX_SUBQUERY;
join->error= 0;
@@ -5240,7 +5247,7 @@ int rewrite_to_index_subquery_engine(JOIN *join)
static Item *remove_additional_cond(Item* conds)
{
- if (conds->name == in_additional_cond)
+ if (conds->name.str == in_additional_cond.str)
return 0;
if (conds->type() == Item::COND_ITEM)
{
@@ -5249,7 +5256,7 @@ static Item *remove_additional_cond(Item* conds)
Item *item;
while ((item= li++))
{
- if (item->name == in_additional_cond)
+ if (item->name.str == in_additional_cond.str)
{
li.remove();
if (cnd->argument_list()->elements == 1)
@@ -5409,7 +5416,8 @@ enum_nested_loop_state join_tab_execution_startup(JOIN_TAB *tab)
hash_sj_engine->materialize_join->exec();
hash_sj_engine->is_materialized= TRUE;
- if (hash_sj_engine->materialize_join->error || tab->join->thd->is_fatal_error)
+ if (unlikely(hash_sj_engine->materialize_join->error) ||
+ unlikely(tab->join->thd->is_fatal_error))
DBUG_RETURN(NESTED_LOOP_ERROR);
}
}
@@ -5461,7 +5469,11 @@ TABLE *create_dummy_tmp_table(THD *thd)
sjm_table_param.init();
sjm_table_param.field_count= 1;
List<Item> sjm_table_cols;
+ const LEX_CSTRING dummy_name= { STRING_WITH_LEN("dummy") };
Item *column_item= new (thd->mem_root) Item_int(thd, 1);
+ if (!column_item)
+ DBUG_RETURN(NULL);
+
sjm_table_cols.push_back(column_item, thd->mem_root);
if (!(table= create_tmp_table(thd, &sjm_table_param,
sjm_table_cols, (ORDER*) 0,
@@ -5470,7 +5482,7 @@ TABLE *create_dummy_tmp_table(THD *thd)
thd->variables.option_bits |
TMP_TABLE_ALL_COLUMNS,
HA_POS_ERROR /*rows_limit */,
- (char*)"dummy", TRUE /* Do not open */)))
+ &dummy_name, TRUE /* Do not open */)))
{
DBUG_RETURN(NULL);
}
@@ -5512,7 +5524,7 @@ int select_value_catcher::setup(List<Item> *items)
List_iterator<Item> li(*items);
for (uint i= 0; (sel_item= li++); i++)
{
- if (!(row[i]= Item_cache::get_cache(thd, sel_item)))
+ if (!(row[i]= sel_item->get_cache(thd)))
return TRUE;
row[i]->setup(thd, sel_item);
}
@@ -5669,8 +5681,7 @@ bool setup_jtbm_semi_joins(JOIN *join, List<TABLE_LIST> *join_list,
Item *sj_conds= hash_sj_engine->semi_join_conds;
(*join_where)= and_items(thd, *join_where, sj_conds);
- if (!(*join_where)->fixed)
- (*join_where)->fix_fields(thd, join_where);
+ (*join_where)->fix_fields_if_needed(thd, join_where);
}
table->table->maybe_null= MY_TEST(join->mixed_implicit_grouping);
}
diff --git a/sql/opt_subselect.h b/sql/opt_subselect.h
index 69f1cc33017..509fb370fd7 100644
--- a/sql/opt_subselect.h
+++ b/sql/opt_subselect.h
@@ -94,10 +94,14 @@ public:
Loose_scan_opt():
try_loosescan(false),
bound_sj_equalities(0),
- quick_uses_applicable_index(FALSE), quick_max_loose_keypart(0),
- best_loose_scan_key(0), best_loose_scan_records(0.0),
+ quick_uses_applicable_index(0),
+ quick_max_loose_keypart(0),
+ best_loose_scan_key(0),
+ best_loose_scan_cost(0),
+ best_loose_scan_records(0),
best_loose_scan_start_key(NULL),
- best_max_loose_keypart(0), best_ref_depend_map(0)
+ best_max_loose_keypart(0),
+ best_ref_depend_map(0)
{
}
@@ -298,7 +302,7 @@ public:
pos->table= tab;
pos->ref_depend_map= best_ref_depend_map;
DBUG_PRINT("info", ("Produced a LooseScan plan, key %s, %s",
- tab->table->key_info[best_loose_scan_key].name,
+ tab->table->key_info[best_loose_scan_key].name.str,
best_loose_scan_start_key? "(ref access)":
"(range/index access)"));
}
diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc
index 02b95dae44a..adfad29bb34 100644
--- a/sql/opt_sum.cc
+++ b/sql/opt_sum.cc
@@ -48,7 +48,7 @@
(assuming a index for column d of table t2 is defined)
*/
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "key.h" // key_cmp_if_same
#include "sql_select.h"
@@ -316,7 +316,7 @@ int opt_sum_query(THD *thd,
else
{
error= tl->table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
- if(error)
+ if (unlikely(error))
{
tl->table->file->print_error(error, MYF(ME_FATALERROR));
DBUG_RETURN(error);
@@ -398,18 +398,22 @@ int opt_sum_query(THD *thd,
const_result= 0;
break;
}
- if (!(error= table->file->ha_index_init((uint) ref.key, 1)))
+ longlong info_limit= 1;
+ table->file->info_push(INFO_KIND_FORCE_LIMIT_BEGIN, &info_limit);
+ if (likely(!(error= table->file->ha_index_init((uint) ref.key, 1))))
error= (is_max ?
get_index_max_value(table, &ref, range_fl) :
get_index_min_value(table, &ref, item_field, range_fl,
prefix_len));
/* Verify that the read tuple indeed matches the search key */
- if (!error && reckey_in_range(is_max, &ref, item_field->field,
- conds, range_fl, prefix_len))
+ if (!error &&
+ reckey_in_range(is_max, &ref, item_field->field,
+ conds, range_fl, prefix_len))
error= HA_ERR_KEY_NOT_FOUND;
table->file->ha_end_keyread();
table->file->ha_index_end();
+ table->file->info_push(INFO_KIND_FORCE_LIMIT_END, NULL);
if (error)
{
if (error == HA_ERR_KEY_NOT_FOUND || error == HA_ERR_END_OF_FILE)
@@ -475,7 +479,7 @@ int opt_sum_query(THD *thd,
}
}
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
DBUG_RETURN(thd->get_stmt_da()->sql_errno());
/*
diff --git a/sql/opt_table_elimination.cc b/sql/opt_table_elimination.cc
index 91b42b2cc12..c204ee4e066 100644
--- a/sql/opt_table_elimination.cc
+++ b/sql/opt_table_elimination.cc
@@ -28,6 +28,7 @@
#pragma implementation // gcc: Class implementation
#endif
+#include "mariadb.h"
#include "my_bit.h"
#include "sql_select.h"
@@ -1809,6 +1810,7 @@ static void mark_as_eliminated(JOIN *join, TABLE_LIST *tbl)
{
DBUG_PRINT("info", ("Eliminated table %s", table->alias.c_ptr()));
tab->type= JT_CONST;
+ tab->table->const_table= 1;
join->eliminated_tables |= table->map;
join->const_table_map|= table->map;
set_position(join, join->const_tables++, tab, (KEYUSE*)0);
@@ -1843,7 +1845,7 @@ void Dep_analysis_context::dbug_print_deps()
(long)(eq_mod - equality_mods),
str.c_ptr(),
eq_mod->field->table->table->alias.c_ptr(),
- eq_mod->field->field->field_name);
+ eq_mod->field->field->field_name.str);
}
else
{
@@ -1867,7 +1869,7 @@ void Dep_analysis_context::dbug_print_deps()
{
fprintf(DBUG_FILE, " field %s.%s ->",
table_dep->table->alias.c_ptr(),
- field_dep->field->field_name);
+ field_dep->field->field_name.str);
uint ofs= field_dep->bitmap_offset;
for (uint bit= ofs; bit < ofs + n_equality_mods; bit++)
{
diff --git a/sql/parse_file.cc b/sql/parse_file.cc
index 999f53bd681..59b4027a352 100644
--- a/sql/parse_file.cc
+++ b/sql/parse_file.cc
@@ -20,13 +20,12 @@
Text .frm files management routines
*/
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "parse_file.h"
#include "unireg.h" // CREATE_MODE
#include "sql_table.h" // build_table_filename
#include <m_ctype.h>
-#include <my_sys.h>
#include <my_dir.h>
/* from sql_db.cc */
@@ -136,7 +135,7 @@ static ulonglong view_algo_from_frm(ulonglong val)
static my_bool
-write_parameter(IO_CACHE *file, uchar* base, File_option *parameter)
+write_parameter(IO_CACHE *file, const uchar* base, File_option *parameter)
{
char num_buf[20]; // buffer for numeric operations
// string for numeric operations
@@ -248,8 +247,9 @@ write_parameter(IO_CACHE *file, uchar* base, File_option *parameter)
my_bool
-sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name,
- const LEX_STRING *type,
+sql_create_definition_file(const LEX_CSTRING *dir,
+ const LEX_CSTRING *file_name,
+ const LEX_CSTRING *type,
uchar* base, File_option *parameters)
{
File handler;
@@ -399,7 +399,7 @@ my_bool rename_in_schema_file(THD *thd,
*/
File_parser *
-sql_parse_prepare(const LEX_STRING *file_name, MEM_ROOT *mem_root,
+sql_parse_prepare(const LEX_CSTRING *file_name, MEM_ROOT *mem_root,
bool bad_format_errors)
{
MY_STAT stat_info;
@@ -483,8 +483,7 @@ frm_error:
my_error(ER_FPARSER_BAD_HEADER, MYF(0), file_name->str);
DBUG_RETURN(0);
}
- else
- DBUG_RETURN(parser); // upper level have to check parser->ok()
+ DBUG_RETURN(parser); // upper level have to check parser->ok()
}
@@ -598,13 +597,13 @@ read_escaped_string(const char *ptr, const char *eol, LEX_STRING *str)
const char *
parse_escaped_string(const char *ptr, const char *end, MEM_ROOT *mem_root,
- LEX_STRING *str)
+ LEX_CSTRING *str)
{
const char *eol= strchr(ptr, '\n');
if (eol == 0 || eol >= end ||
!(str->str= (char*) alloc_root(mem_root, (eol - ptr) + 1)) ||
- read_escaped_string(ptr, eol, str))
+ read_escaped_string(ptr, eol, (LEX_STRING*) str))
return 0;
return eol+1;
@@ -760,12 +759,12 @@ File_parser::parse(uchar* base, MEM_ROOT *mem_root,
{
File_option *parameter= parameters+first_param,
*parameters_end= parameters+required;
- int len= 0;
+ size_t len= 0;
for (; parameter < parameters_end; parameter++)
{
len= parameter->name.length;
// check length
- if (len < (end-ptr) && ptr[len] != '=')
+ if (len < (size_t)(end-ptr) && ptr[len] != '=')
continue;
// check keyword
if (memcmp(parameter->name.str, ptr, len) == 0)
@@ -802,7 +801,7 @@ File_parser::parse(uchar* base, MEM_ROOT *mem_root,
case FILE_OPTIONS_ESTRING:
{
if (!(ptr= parse_escaped_string(ptr, end, mem_root,
- (LEX_STRING *)
+ (LEX_CSTRING *)
(base + parameter->offset))))
{
my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0),
diff --git a/sql/parse_file.h b/sql/parse_file.h
index 54a2b86dca9..cbd41d16cbc 100644
--- a/sql/parse_file.h
+++ b/sql/parse_file.h
@@ -17,9 +17,8 @@
#ifndef _PARSE_FILE_H_
#define _PARSE_FILE_H_
-#include "my_global.h" // uchar
#include "sql_string.h" // LEX_STRING
-#include "sql_list.h" // Sql_alloc
+#include "sql_alloc.h" // Sql_alloc
class THD;
@@ -42,7 +41,7 @@ enum file_opt_type {
struct File_option
{
- LEX_STRING name; /**< Name of the option */
+ LEX_CSTRING name; /**< Name of the option */
my_ptrdiff_t offset; /**< offset to base address of value */
file_opt_type type; /**< Option type */
};
@@ -82,15 +81,16 @@ bool get_file_options_ulllist(const char *&ptr, const char *end,
const char *
parse_escaped_string(const char *ptr, const char *end, MEM_ROOT *mem_root,
- LEX_STRING *str);
+ LEX_CSTRING *str);
class File_parser;
-File_parser *sql_parse_prepare(const LEX_STRING *file_name,
+File_parser *sql_parse_prepare(const LEX_CSTRING *file_name,
MEM_ROOT *mem_root, bool bad_format_errors);
my_bool
-sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name,
- const LEX_STRING *type,
+sql_create_definition_file(const LEX_CSTRING *dir,
+ const LEX_CSTRING *file_name,
+ const LEX_CSTRING *type,
uchar* base, File_option *parameters);
my_bool rename_in_schema_file(THD *thd,
const char *schema, const char *old_name,
@@ -99,19 +99,19 @@ my_bool rename_in_schema_file(THD *thd,
class File_parser: public Sql_alloc
{
char *start, *end;
- LEX_STRING file_type;
+ LEX_CSTRING file_type;
bool content_ok;
public:
File_parser() :start(0), end(0), content_ok(0)
{ file_type.str= 0; file_type.length= 0; }
bool ok() { return content_ok; }
- const LEX_STRING *type() const { return &file_type; }
+ const LEX_CSTRING *type() const { return &file_type; }
my_bool parse(uchar* base, MEM_ROOT *mem_root,
struct File_option *parameters, uint required,
Unknown_key_hook *hook) const;
- friend File_parser *sql_parse_prepare(const LEX_STRING *file_name,
+ friend File_parser *sql_parse_prepare(const LEX_CSTRING *file_name,
MEM_ROOT *mem_root,
bool bad_format_errors);
};
diff --git a/sql/partition_element.h b/sql/partition_element.h
index 5241d00989f..ff0d0d59fc4 100644
--- a/sql/partition_element.h
+++ b/sql/partition_element.h
@@ -26,7 +26,8 @@ enum partition_type {
NOT_A_PARTITION= 0,
RANGE_PARTITION,
HASH_PARTITION,
- LIST_PARTITION
+ LIST_PARTITION,
+ VERSIONING_PARTITION
};
enum partition_state {
@@ -89,37 +90,55 @@ typedef struct p_elem_val
struct st_ddl_log_memory_entry;
-class partition_element :public Sql_alloc {
+enum stat_trx_field
+{
+ STAT_TRX_END= 0
+};
+
+class partition_element :public Sql_alloc
+{
public:
+ enum elem_type_enum
+ {
+ CONVENTIONAL= 0,
+ CURRENT,
+ HISTORY
+ };
+
List<partition_element> subpartitions;
List<part_elem_value> list_val_list;
ha_rows part_max_rows;
ha_rows part_min_rows;
longlong range_value;
- char *partition_name;
- char *tablespace_name;
+ const char *partition_name;
+ const char *tablespace_name;
struct st_ddl_log_memory_entry *log_entry;
- char* part_comment;
- char* data_file_name;
- char* index_file_name;
+ const char* part_comment;
+ const char* data_file_name;
+ const char* index_file_name;
handlerton *engine_type;
- LEX_STRING connect_string;
+ LEX_CSTRING connect_string;
enum partition_state part_state;
uint16 nodegroup_id;
bool has_null_value;
bool signed_flag; // Range value signed
bool max_value; // MAXVALUE range
+ uint32 id;
+ bool empty;
+ elem_type_enum type;
partition_element()
: part_max_rows(0), part_min_rows(0), range_value(0),
partition_name(NULL), tablespace_name(NULL),
log_entry(NULL), part_comment(NULL),
data_file_name(NULL), index_file_name(NULL),
- engine_type(NULL), connect_string(null_lex_str), part_state(PART_NORMAL),
+ engine_type(NULL), connect_string(null_clex_str), part_state(PART_NORMAL),
nodegroup_id(UNDEF_NODEGROUP), has_null_value(FALSE),
- signed_flag(FALSE), max_value(FALSE)
- {
- }
+ signed_flag(FALSE), max_value(FALSE),
+ id(UINT_MAX32),
+ empty(true),
+ type(CONVENTIONAL)
+ {}
partition_element(partition_element *part_elem)
: part_max_rows(part_elem->part_max_rows),
part_min_rows(part_elem->part_min_rows),
@@ -129,13 +148,23 @@ public:
data_file_name(part_elem->data_file_name),
index_file_name(part_elem->index_file_name),
engine_type(part_elem->engine_type),
- connect_string(null_lex_str),
+ connect_string(null_clex_str),
part_state(part_elem->part_state),
nodegroup_id(part_elem->nodegroup_id),
- has_null_value(FALSE)
+ has_null_value(FALSE),
+ id(part_elem->id),
+ empty(part_elem->empty),
+ type(CONVENTIONAL)
+ {}
+ ~partition_element() {}
+
+ part_column_list_val& get_col_val(uint idx)
{
+ part_elem_value *ev= list_val_list.head();
+ DBUG_ASSERT(ev);
+ DBUG_ASSERT(ev->col_val_array);
+ return ev->col_val_array[idx];
}
- ~partition_element() {}
};
#endif /* PARTITION_ELEMENT_INCLUDED */
diff --git a/sql/partition_info.cc b/sql/partition_info.cc
index ef817fffe1e..9f08964e62c 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, 2015, MariaDB
+ Copyright (c) 2010, 2018, 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,7 +20,9 @@
#pragma implementation
#endif
+#include "mariadb.h"
#include <my_global.h>
+#include <tztime.h>
#include "sql_priv.h"
// Required to get server definitions for mysql/plugin.h right
#include "sql_plugin.h"
@@ -30,6 +32,9 @@
#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"
#ifdef WITH_PARTITION_STORAGE_ENGINE
#include "ha_partition.h"
@@ -43,11 +48,9 @@ partition_info *partition_info::get_clone(THD *thd)
List_iterator<partition_element> part_it(partitions);
partition_element *part;
partition_info *clone= new (mem_root) partition_info(*this);
- if (!clone)
- {
- mem_alloc_error(sizeof(partition_info));
+ if (unlikely(!clone))
DBUG_RETURN(NULL);
- }
+
memset(&(clone->read_partitions), 0, sizeof(clone->read_partitions));
memset(&(clone->lock_partitions), 0, sizeof(clone->lock_partitions));
clone->bitmaps_are_initialized= FALSE;
@@ -59,20 +62,16 @@ partition_info *partition_info::get_clone(THD *thd)
partition_element *subpart;
partition_element *part_clone= new (mem_root) partition_element();
if (!part_clone)
- {
- mem_alloc_error(sizeof(partition_element));
DBUG_RETURN(NULL);
- }
+
*part_clone= *part;
part_clone->subpartitions.empty();
while ((subpart= (subpart_it++)))
{
partition_element *subpart_clone= new (mem_root) partition_element();
if (!subpart_clone)
- {
- mem_alloc_error(sizeof(partition_element));
DBUG_RETURN(NULL);
- }
+
*subpart_clone= *subpart;
part_clone->subpartitions.push_back(subpart_clone, mem_root);
}
@@ -83,20 +82,15 @@ partition_info *partition_info::get_clone(THD *thd)
(part_elem_value *)alloc_root(mem_root, sizeof(part_elem_value) *
part->list_val_list.elements);
if (!new_val_arr)
- {
- mem_alloc_error(sizeof(part_elem_value) * part->list_val_list.elements);
DBUG_RETURN(NULL);
- }
+
p_column_list_val *new_colval_arr=
(p_column_list_val*)alloc_root(mem_root, sizeof(p_column_list_val) *
num_columns *
part->list_val_list.elements);
if (!new_colval_arr)
- {
- mem_alloc_error(sizeof(p_column_list_val) * num_columns *
- part->list_val_list.elements);
DBUG_RETURN(NULL);
- }
+
part_elem_value *val;
while ((val= list_val_it++))
{
@@ -113,6 +107,19 @@ partition_info *partition_info::get_clone(THD *thd)
part_clone->list_val_list.push_back(new_val, mem_root);
}
}
+ if (part_type == VERSIONING_PARTITION && vers_info)
+ {
+ // clone Vers_part_info; set now_part, hist_part
+ clone->vers_info= new (mem_root) Vers_part_info(*vers_info);
+ List_iterator<partition_element> it(clone->partitions);
+ while ((part= it++))
+ {
+ if (vers_info->now_part && part->id == vers_info->now_part->id)
+ clone->vers_info->now_part= part;
+ else if (vers_info->hist_part && part->id == vers_info->hist_part->id)
+ clone->vers_info->hist_part= part;
+ } // while ((part= it++))
+ } // if (part_type == VERSIONING_PARTITION ...
DBUG_RETURN(clone);
}
@@ -127,8 +134,7 @@ partition_info *partition_info::get_clone(THD *thd)
@retval false Partition not found
*/
-bool partition_info::add_named_partition(const char *part_name,
- uint length)
+bool partition_info::add_named_partition(const char *part_name, size_t length)
{
HASH *part_name_hash;
PART_NAME_DEF *part_def;
@@ -179,8 +185,7 @@ bool partition_info::add_named_partition(const char *part_name,
@param part_elem Partition element that matched.
*/
-bool partition_info::set_named_partition_bitmap(const char *part_name,
- uint length)
+bool partition_info::set_named_partition_bitmap(const char *part_name, size_t length)
{
DBUG_ENTER("partition_info::set_named_partition_bitmap");
bitmap_clear_all(&read_partitions);
@@ -199,13 +204,55 @@ bool partition_info::set_named_partition_bitmap(const char *part_name,
@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.
+
+ @param partition_names list of names of partitions.
+
+ @return Operation status
@retval true Failure
@retval false Success
*/
-bool partition_info::prune_partition_bitmaps(TABLE_LIST *table_list)
+bool partition_info::prune_partition_bitmaps(List<String> *partition_names)
{
- List_iterator<String> partition_names_it(*(table_list->partition_names));
- uint num_names= table_list->partition_names->elements;
+ List_iterator<String> partition_names_it(*(partition_names));
+ uint num_names= partition_names->elements;
uint i= 0;
DBUG_ENTER("partition_info::prune_partition_bitmaps");
@@ -235,8 +282,7 @@ bool partition_info::prune_partition_bitmaps(TABLE_LIST *table_list)
/**
Set read/lock_partitions bitmap over non pruned partitions
- @param table_list Possible TABLE_LIST which can contain
- list of partition names to query
+ @param partition_names list of partition names to query
@return Operation status
@retval FALSE OK
@@ -246,7 +292,7 @@ bool partition_info::prune_partition_bitmaps(TABLE_LIST *table_list)
@note OK to call multiple times without the need for free_bitmaps.
*/
-bool partition_info::set_partition_bitmaps(TABLE_LIST *table_list)
+bool partition_info::set_partition_bitmaps(List<String> *partition_names)
{
DBUG_ENTER("partition_info::set_partition_bitmaps");
@@ -255,16 +301,15 @@ bool partition_info::set_partition_bitmaps(TABLE_LIST *table_list)
if (!bitmaps_are_initialized)
DBUG_RETURN(TRUE);
- if (table_list &&
- table_list->partition_names &&
- table_list->partition_names->elements)
+ if (partition_names &&
+ partition_names->elements)
{
if (table->s->db_type()->partition_flags() & HA_USE_AUTO_PARTITION)
{
my_error(ER_PARTITION_CLAUSE_ON_NONPARTITIONED, MYF(0));
DBUG_RETURN(true);
}
- if (prune_partition_bitmaps(table_list))
+ if (prune_partition_bitmaps(partition_names))
DBUG_RETURN(TRUE);
}
else
@@ -278,6 +323,27 @@ bool partition_info::set_partition_bitmaps(TABLE_LIST *table_list)
}
+/**
+ Set read/lock_partitions bitmap over non pruned partitions
+
+ @param table_list Possible TABLE_LIST which can contain
+ list of partition names to query
+
+ @return Operation status
+ @retval FALSE OK
+ @retval TRUE Failed to allocate memory for bitmap or list of partitions
+ did not match
+
+ @note OK to call multiple times without the need for free_bitmaps.
+*/
+bool partition_info::set_partition_bitmaps_from_table(TABLE_LIST *table_list)
+{
+ List<String> *partition_names= table_list ?
+ NULL : table_list->partition_names;
+ return set_partition_bitmaps(partition_names);
+}
+
+
/*
Create a memory area where default partition names are stored and fill it
up with the names.
@@ -317,10 +383,6 @@ char *partition_info::create_default_partition_names(THD *thd, uint part_no,
move_ptr+= MAX_PART_NAME_SIZE;
} while (++i < num_parts_arg);
}
- else
- {
- mem_alloc_error(num_parts_arg*MAX_PART_NAME_SIZE);
- }
DBUG_RETURN(ptr);
}
@@ -340,18 +402,13 @@ char *partition_info::create_default_partition_names(THD *thd, uint part_no,
char *partition_info::create_default_subpartition_name(THD *thd, uint subpart_no,
const char *part_name)
{
- uint size_alloc= strlen(part_name) + MAX_PART_NAME_SIZE;
+ size_t size_alloc= strlen(part_name) + MAX_PART_NAME_SIZE;
char *ptr= (char*) thd->calloc(size_alloc);
DBUG_ENTER("create_default_subpartition_name");
if (likely(ptr != NULL))
- {
my_snprintf(ptr, size_alloc, "%ssp%u", part_name, subpart_no);
- }
- else
- {
- mem_alloc_error(size_alloc);
- }
+
DBUG_RETURN(ptr);
}
@@ -394,6 +451,8 @@ bool partition_info::set_up_default_partitions(THD *thd, handler *file,
const char *error_string;
if (part_type == RANGE_PARTITION)
error_string= "RANGE";
+ else if (part_type == VERSIONING_PARTITION)
+ error_string= "SYSTEM_TIME";
else
error_string= "LIST";
my_error(ER_PARTITIONS_MUST_BE_DEFINED_ERROR, MYF(0), error_string);
@@ -428,10 +487,7 @@ bool partition_info::set_up_default_partitions(THD *thd, handler *file,
default_name+=MAX_PART_NAME_SIZE;
}
else
- {
- mem_alloc_error(sizeof(partition_element));
goto end;
- }
} while (++i < num_parts);
result= FALSE;
end:
@@ -497,10 +553,7 @@ bool partition_info::set_up_default_subpartitions(THD *thd, handler *file,
subpart_elem->partition_name= ptr;
}
else
- {
- mem_alloc_error(sizeof(partition_element));
goto end;
- }
} while (++j < num_subparts);
} while (++i < num_parts);
result= FALSE;
@@ -561,10 +614,11 @@ bool partition_info::set_up_defaults_for_partitioning(THD *thd, handler *file,
Check that the user haven't defined the same field twice in
key or column list partitioning.
*/
-char* partition_info::find_duplicate_field()
+
+const char* partition_info::find_duplicate_field()
{
- char *field_name_outer, *field_name_inner;
- List_iterator<char> it_outer(part_field_list);
+ const char *field_name_outer, *field_name_inner;
+ List_iterator<const char> it_outer(part_field_list);
uint num_fields= part_field_list.elements;
uint i,j;
DBUG_ENTER("partition_info::find_duplicate_field");
@@ -572,7 +626,7 @@ char* partition_info::find_duplicate_field()
for (i= 0; i < num_fields; i++)
{
field_name_outer= it_outer++;
- List_iterator<char> it_inner(part_field_list);
+ List_iterator<const char> it_inner(part_field_list);
for (j= 0; j < num_fields; j++)
{
field_name_inner= it_inner++;
@@ -778,6 +832,100 @@ bool partition_info::has_unique_name(partition_element *element)
DBUG_RETURN(TRUE);
}
+void partition_info::vers_set_hist_part(THD *thd)
+{
+ if (vers_info->limit)
+ {
+ ha_partition *hp= (ha_partition*)(table->file);
+ partition_element *next= NULL;
+ List_iterator<partition_element> it(partitions);
+ while (next != vers_info->hist_part)
+ next= it++;
+ ha_rows records= hp->part_records(next);
+ while ((next= it++) != vers_info->now_part)
+ {
+ ha_rows next_records= hp->part_records(next);
+ if (next_records == 0)
+ break;
+ vers_info->hist_part= next;
+ records= next_records;
+ }
+ if (records > vers_info->limit)
+ {
+ if (next == vers_info->now_part)
+ goto warn;
+ vers_info->hist_part= next;
+ }
+ return;
+ }
+
+ if (vers_info->interval.is_set())
+ {
+ if (vers_info->hist_part->range_value > thd->query_start())
+ return;
+
+ partition_element *next= NULL;
+ List_iterator<partition_element> it(partitions);
+ while (next != vers_info->hist_part)
+ next= it++;
+
+ while ((next= it++) != vers_info->now_part)
+ {
+ vers_info->hist_part= next;
+ if (next->range_value > thd->query_start())
+ 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;
+}
+
/*
Check that the partition/subpartition is setup to use the correct
@@ -940,394 +1088,6 @@ error:
}
-/*
- This routine allocates an array for all range constants to achieve a fast
- check what partition a certain value belongs to. At the same time it does
- also check that the range constants are defined in increasing order and
- that the expressions are constant integer expressions.
-
- SYNOPSIS
- check_range_constants()
- thd Thread object
-
- RETURN VALUE
- TRUE An error occurred during creation of range constants
- FALSE Successful creation of range constant mapping
-
- DESCRIPTION
- This routine is called from check_partition_info to get a quick error
- before we came too far into the CREATE TABLE process. It is also called
- from fix_partition_func every time we open the .frm file. It is only
- called for RANGE PARTITIONed tables.
-*/
-
-bool partition_info::check_range_constants(THD *thd)
-{
- partition_element* part_def;
- bool first= TRUE;
- uint i;
- List_iterator<partition_element> it(partitions);
- int result= TRUE;
- DBUG_ENTER("partition_info::check_range_constants");
- DBUG_PRINT("enter", ("RANGE with %d parts, column_list = %u", num_parts,
- column_list));
-
- if (column_list)
- {
- part_column_list_val *loc_range_col_array;
- part_column_list_val *UNINIT_VAR(current_largest_col_val);
- uint num_column_values= part_field_list.elements;
- uint size_entries= sizeof(part_column_list_val) * num_column_values;
- range_col_array= (part_column_list_val*) thd->calloc(num_parts *
- size_entries);
- if (unlikely(range_col_array == NULL))
- {
- mem_alloc_error(num_parts * size_entries);
- goto end;
- }
- loc_range_col_array= range_col_array;
- i= 0;
- do
- {
- part_def= it++;
- {
- List_iterator<part_elem_value> list_val_it(part_def->list_val_list);
- part_elem_value *range_val= list_val_it++;
- part_column_list_val *col_val= range_val->col_val_array;
-
- if (fix_column_value_functions(thd, range_val, i))
- goto end;
- memcpy(loc_range_col_array, (const void*)col_val, size_entries);
- loc_range_col_array+= num_column_values;
- if (!first)
- {
- if (compare_column_values((const void*)current_largest_col_val,
- (const void*)col_val) >= 0)
- goto range_not_increasing_error;
- }
- current_largest_col_val= col_val;
- }
- first= FALSE;
- } while (++i < num_parts);
- }
- else
- {
- longlong UNINIT_VAR(current_largest);
- longlong part_range_value;
- bool signed_flag= !part_expr->unsigned_flag;
-
- range_int_array= (longlong*) thd->alloc(num_parts * sizeof(longlong));
- if (unlikely(range_int_array == NULL))
- {
- mem_alloc_error(num_parts * sizeof(longlong));
- goto end;
- }
- i= 0;
- do
- {
- part_def= it++;
- if ((i != (num_parts - 1)) || !defined_max_value)
- {
- part_range_value= part_def->range_value;
- if (!signed_flag)
- part_range_value-= 0x8000000000000000ULL;
- }
- else
- part_range_value= LONGLONG_MAX;
-
- if (!first)
- {
- if (unlikely(current_largest > part_range_value) ||
- (unlikely(current_largest == part_range_value) &&
- (part_range_value < LONGLONG_MAX ||
- i != (num_parts - 1) ||
- !defined_max_value)))
- goto range_not_increasing_error;
- }
- range_int_array[i]= part_range_value;
- current_largest= part_range_value;
- first= FALSE;
- } while (++i < num_parts);
- }
- result= FALSE;
-end:
- DBUG_RETURN(result);
-
-range_not_increasing_error:
- my_error(ER_RANGE_NOT_INCREASING_ERROR, MYF(0));
- goto end;
-}
-
-
-/*
- Support routines for check_list_constants used by qsort to sort the
- constant list expressions. One routine for integers and one for
- column lists.
-
- SYNOPSIS
- list_part_cmp()
- a First list constant to compare with
- b Second list constant to compare with
-
- RETURN VALUE
- +1 a > b
- 0 a == b
- -1 a < b
-*/
-
-extern "C"
-int partition_info_list_part_cmp(const void* a, const void* b)
-{
- longlong a1= ((LIST_PART_ENTRY*)a)->list_value;
- longlong b1= ((LIST_PART_ENTRY*)b)->list_value;
- if (a1 < b1)
- return -1;
- else if (a1 > b1)
- return +1;
- else
- return 0;
-}
-
-
-int partition_info::list_part_cmp(const void* a, const void* b)
-{
- return partition_info_list_part_cmp(a, b);
-}
-
-
-/*
- Compare two lists of column values in RANGE/LIST partitioning
- SYNOPSIS
- compare_column_values()
- first First column list argument
- second Second column list argument
- RETURN VALUES
- 0 Equal
- -1 First argument is smaller
- +1 First argument is larger
-*/
-
-extern "C"
-int partition_info_compare_column_values(const void *first_arg,
- const void *second_arg)
-{
- const part_column_list_val *first= (part_column_list_val*)first_arg;
- const part_column_list_val *second= (part_column_list_val*)second_arg;
- partition_info *part_info= first->part_info;
- Field **field;
-
- for (field= part_info->part_field_array; *field;
- field++, first++, second++)
- {
- if (first->max_value || second->max_value)
- {
- if (first->max_value && second->max_value)
- return 0;
- if (second->max_value)
- return -1;
- else
- return +1;
- }
- if (first->null_value || second->null_value)
- {
- if (first->null_value && second->null_value)
- continue;
- if (second->null_value)
- return +1;
- else
- return -1;
- }
- int res= (*field)->cmp((const uchar*)first->column_value,
- (const uchar*)second->column_value);
- if (res)
- return res;
- }
- return 0;
-}
-
-
-int partition_info::compare_column_values(const void *first_arg,
- const void *second_arg)
-{
- return partition_info_compare_column_values(first_arg, second_arg);
-}
-
-
-/*
- This routine allocates an array for all list constants to achieve a fast
- check what partition a certain value belongs to. At the same time it does
- also check that there are no duplicates among the list constants and that
- that the list expressions are constant integer expressions.
-
- SYNOPSIS
- check_list_constants()
- thd Thread object
-
- RETURN VALUE
- TRUE An error occurred during creation of list constants
- FALSE Successful creation of list constant mapping
-
- DESCRIPTION
- This routine is called from check_partition_info to get a quick error
- before we came too far into the CREATE TABLE process. It is also called
- from fix_partition_func every time we open the .frm file. It is only
- called for LIST PARTITIONed tables.
-*/
-
-bool partition_info::check_list_constants(THD *thd)
-{
- uint i, size_entries, num_column_values;
- uint list_index= 0;
- part_elem_value *list_value;
- bool result= TRUE;
- longlong type_add, calc_value;
- void *curr_value;
- void *UNINIT_VAR(prev_value);
- partition_element* part_def;
- bool found_null= FALSE;
- qsort_cmp compare_func;
- void *ptr;
- List_iterator<partition_element> list_func_it(partitions);
- DBUG_ENTER("partition_info::check_list_constants");
-
- DBUG_ASSERT(part_type == LIST_PARTITION);
-
- num_list_values= 0;
- /*
- We begin by calculating the number of list values that have been
- defined in the first step.
-
- We use this number to allocate a properly sized array of structs
- to keep the partition id and the value to use in that partition.
- In the second traversal we assign them values in the struct array.
-
- Finally we sort the array of structs in order of values to enable
- a quick binary search for the proper value to discover the
- partition id.
- After sorting the array we check that there are no duplicates in the
- list.
- */
-
- i= 0;
- do
- {
- part_def= list_func_it++;
- if (part_def->has_null_value)
- {
- if (found_null)
- {
- my_error(ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR, MYF(0));
- goto end;
- }
- has_null_value= TRUE;
- has_null_part_id= i;
- found_null= TRUE;
- }
- num_list_values+= part_def->list_val_list.elements;
- } while (++i < num_parts);
- list_func_it.rewind();
- num_column_values= part_field_list.elements;
- size_entries= column_list ?
- (num_column_values * sizeof(part_column_list_val)) :
- sizeof(LIST_PART_ENTRY);
- if (unlikely(!(ptr= thd->calloc((num_list_values+1) * size_entries))))
- goto end;
- if (column_list)
- {
- part_column_list_val *loc_list_col_array;
- loc_list_col_array= (part_column_list_val*)ptr;
- list_col_array= (part_column_list_val*)ptr;
- compare_func= partition_info_compare_column_values;
- i= 0;
- do
- {
- part_def= list_func_it++;
- if (part_def->max_value)
- {
- // DEFAULT is not a real value so let's exclude it from sorting.
- DBUG_ASSERT(num_list_values);
- num_list_values--;
- continue;
- }
- List_iterator<part_elem_value> list_val_it2(part_def->list_val_list);
- while ((list_value= list_val_it2++))
- {
- part_column_list_val *col_val= list_value->col_val_array;
- if (unlikely(fix_column_value_functions(thd, list_value, i)))
- {
- DBUG_RETURN(TRUE);
- }
- memcpy(loc_list_col_array, (const void*)col_val, size_entries);
- loc_list_col_array+= num_column_values;
- }
- } while (++i < num_parts);
- }
- else
- {
- compare_func= partition_info_list_part_cmp;
- list_array= (LIST_PART_ENTRY*)ptr;
- i= 0;
- /*
- Fix to be able to reuse signed sort functions also for unsigned
- partition functions.
- */
- type_add= (longlong)(part_expr->unsigned_flag ?
- 0x8000000000000000ULL :
- 0ULL);
-
- do
- {
- part_def= list_func_it++;
- if (part_def->max_value && part_type == LIST_PARTITION)
- {
- // DEFAULT is not a real value so let's exclude it from sorting.
- DBUG_ASSERT(num_list_values);
- num_list_values--;
- continue;
- }
- List_iterator<part_elem_value> list_val_it2(part_def->list_val_list);
- while ((list_value= list_val_it2++))
- {
- calc_value= list_value->value - type_add;
- list_array[list_index].list_value= calc_value;
- list_array[list_index++].partition_id= i;
- }
- } while (++i < num_parts);
- }
- DBUG_ASSERT(fixed);
- if (num_list_values)
- {
- bool first= TRUE;
- /*
- list_array and list_col_array are unions, so this works for both
- variants of LIST partitioning.
- */
- my_qsort((void*)list_array, num_list_values, size_entries,
- compare_func);
-
- i= 0;
- do
- {
- DBUG_ASSERT(i < num_list_values);
- curr_value= column_list ? (void*)&list_col_array[num_column_values * i] :
- (void*)&list_array[i];
- if (likely(first || compare_func(curr_value, prev_value)))
- {
- prev_value= curr_value;
- first= FALSE;
- }
- else
- {
- my_error(ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR, MYF(0));
- goto end;
- }
- } while (++i < num_list_values);
- }
- result= FALSE;
-end:
- DBUG_RETURN(result);
-}
-
/**
Check if we allow DATA/INDEX DIRECTORY, if not warn and set them to NULL.
@@ -1378,12 +1138,14 @@ static void warn_if_dir_in_part_elem(THD *thd, partition_element *part_elem)
bool partition_info::check_partition_info(THD *thd, handlerton **eng_type,
handler *file, HA_CREATE_INFO *info,
- bool add_or_reorg_part)
+ partition_info *add_or_reorg_part)
{
handlerton *table_engine= default_engine_type;
uint i, tot_partitions;
bool result= TRUE, table_engine_set;
- char *same_name;
+ const char *same_name;
+ uint32 hist_parts= 0;
+ uint32 now_parts= 0;
DBUG_ENTER("partition_info::check_partition_info");
DBUG_ASSERT(default_engine_type != partition_hton);
@@ -1425,7 +1187,8 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type,
}
if (unlikely(is_sub_partitioned() &&
(!(part_type == RANGE_PARTITION ||
- part_type == LIST_PARTITION))))
+ part_type == LIST_PARTITION ||
+ part_type == VERSIONING_PARTITION))))
{
/* Only RANGE and LIST partitioning can be subpartitioned */
my_error(ER_SUBPARTITION_ERROR, MYF(0));
@@ -1487,6 +1250,19 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type,
my_error(ER_SAME_NAME_PARTITION, MYF(0), same_name);
goto end;
}
+
+ if (part_type == VERSIONING_PARTITION)
+ {
+ DBUG_ASSERT(vers_info);
+ if (num_parts < 2 || !vers_info->now_part)
+ {
+ DBUG_ASSERT(info);
+ DBUG_ASSERT(info->alias.str);
+ my_error(ER_VERS_WRONG_PARTS, MYF(0), info->alias.str);
+ goto end;
+ }
+ DBUG_ASSERT(num_parts == partitions.elements);
+ }
i= 0;
{
List_iterator<partition_element> part_it(partitions);
@@ -1567,6 +1343,18 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type,
}
}
}
+ if (part_type == VERSIONING_PARTITION)
+ {
+ if (part_elem->type == partition_element::HISTORY)
+ {
+ hist_parts++;
+ }
+ else
+ {
+ DBUG_ASSERT(part_elem->type == partition_element::CURRENT);
+ now_parts++;
+ }
+ }
} while (++i < num_parts);
if (!table_engine_set &&
num_parts_not_set != 0 &&
@@ -1584,6 +1372,24 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type,
goto end;
}
+ if (hist_parts > 1)
+ {
+ if (vers_info->limit == 0 && !vers_info->interval.is_set())
+ {
+ push_warning_printf(thd,
+ Sql_condition::WARN_LEVEL_WARN,
+ WARN_VERS_PARAMETERS,
+ ER_THD(thd, WARN_VERS_PARAMETERS),
+ "no rotation condition for multiple HISTORY partitions.");
+ }
+ }
+ if (unlikely(now_parts > 1))
+ {
+ my_error(ER_VERS_WRONG_PARTS, MYF(0), info->alias.str);
+ goto end;
+ }
+
+
DBUG_ASSERT(table_engine != partition_hton &&
default_engine_type == table_engine);
if (eng_type)
@@ -1598,12 +1404,13 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type,
if (add_or_reorg_part)
{
- if (unlikely((part_type == RANGE_PARTITION &&
- check_range_constants(thd)) ||
- (part_type == LIST_PARTITION &&
- check_list_constants(thd))))
+ if (unlikely(part_type == VERSIONING_PARTITION &&
+ vers_setup_expression(thd, add_or_reorg_part->partitions.elements)))
+ goto end;
+ if (check_constants(thd, this))
goto end;
}
+
result= FALSE;
end:
DBUG_RETURN(result);
@@ -1628,8 +1435,8 @@ void partition_info::print_no_partition_found(TABLE *table_arg, myf errflag)
THD *thd= current_thd;
table_list.reset();
- table_list.db= table_arg->s->db.str;
- table_list.table_name= table_arg->s->table_name.str;
+ table_list.db= table_arg->s->db;
+ table_list.table_name= table_arg->s->table_name;
if (check_single_table_access(thd, SELECT_ACL, &table_list, TRUE))
{
@@ -1668,17 +1475,8 @@ void partition_info::print_no_partition_found(TABLE *table_arg, myf errflag)
FALSE Success
*/
-bool partition_info::set_part_expr(THD *thd, char *start_token, Item *item_ptr,
- char *end_token, bool is_subpart)
+bool partition_info::set_part_expr(THD *thd, Item *item_ptr, bool is_subpart)
{
- size_t expr_len= end_token - start_token;
- char *func_string= (char*) thd->memdup(start_token, expr_len);
-
- if (!func_string)
- {
- mem_alloc_error(expr_len);
- return TRUE;
- }
if (is_subpart)
{
list_of_subpart_fields= FALSE;
@@ -1833,7 +1631,6 @@ bool partition_info::set_up_charset_field_preps(THD *thd)
}
DBUG_RETURN(FALSE);
error:
- mem_alloc_error(size);
DBUG_RETURN(TRUE);
}
@@ -1866,17 +1663,19 @@ bool check_partition_dirs(partition_info *part_info)
partition_element *subpart_elem;
while ((subpart_elem= sub_it++))
{
- if (error_if_data_home_dir(subpart_elem->data_file_name,
- "DATA DIRECTORY") ||
- error_if_data_home_dir(subpart_elem->index_file_name,
- "INDEX DIRECTORY"))
+ if (unlikely(error_if_data_home_dir(subpart_elem->data_file_name,
+ "DATA DIRECTORY")) ||
+ unlikely(error_if_data_home_dir(subpart_elem->index_file_name,
+ "INDEX DIRECTORY")))
return 1;
}
}
else
{
- if (error_if_data_home_dir(part_elem->data_file_name, "DATA DIRECTORY") ||
- error_if_data_home_dir(part_elem->index_file_name, "INDEX DIRECTORY"))
+ if (unlikely(error_if_data_home_dir(part_elem->data_file_name,
+ "DATA DIRECTORY")) ||
+ unlikely(error_if_data_home_dir(part_elem->index_file_name,
+ "INDEX DIRECTORY")))
return 1;
}
}
@@ -1917,7 +1716,7 @@ void partition_info::report_part_expr_error(bool use_subpart_expr)
!(type == HASH_PARTITION && list_of_fields))
{
my_error(ER_FIELD_TYPE_NOT_ALLOWED_AS_PARTITION_FIELD, MYF(0),
- item_field->name);
+ item_field->name.str);
DBUG_VOID_RETURN;
}
}
@@ -2140,10 +1939,8 @@ bool partition_info::init_column_part(THD *thd)
if (!(list_val=
(part_elem_value*) thd->calloc(sizeof(part_elem_value))) ||
p_elem->list_val_list.push_back(list_val, thd->mem_root))
- {
- mem_alloc_error(sizeof(part_elem_value));
DBUG_RETURN(TRUE);
- }
+
if (num_columns)
loc_num_columns= num_columns;
else
@@ -2151,10 +1948,8 @@ bool partition_info::init_column_part(THD *thd)
if (!(col_val_array=
(part_column_list_val*) thd->calloc(loc_num_columns *
sizeof(part_column_list_val))))
- {
- mem_alloc_error(loc_num_columns * sizeof(part_elem_value));
DBUG_RETURN(TRUE);
- }
+
list_val->col_val_array= col_val_array;
list_val->added_items= 0;
curr_list_val= list_val;
@@ -2346,11 +2141,10 @@ bool partition_info::fix_column_value_functions(THD *thd,
{
uchar *val_ptr;
uint len= field->pack_length();
- ulonglong save_sql_mode;
+ sql_mode_t save_sql_mode;
bool save_got_warning;
- if (!(column_item= get_column_item(column_item,
- field)))
+ if (!(column_item= get_column_item(column_item, field)))
{
result= TRUE;
goto end;
@@ -2363,6 +2157,7 @@ bool partition_info::fix_column_value_functions(THD *thd,
thd->got_warning)
{
my_error(ER_WRONG_TYPE_COLUMN_VALUE_ERROR, MYF(0));
+ thd->variables.sql_mode= save_sql_mode;
result= TRUE;
goto end;
}
@@ -2370,7 +2165,6 @@ bool partition_info::fix_column_value_functions(THD *thd,
thd->variables.sql_mode= save_sql_mode;
if (!(val_ptr= (uchar*) thd->memdup(field->ptr, len)))
{
- mem_alloc_error(len);
result= TRUE;
goto end;
}
@@ -2474,7 +2268,7 @@ bool partition_info::fix_parser_data(THD *thd)
part_elem= it++;
List_iterator<part_elem_value> list_val_it(part_elem->list_val_list);
num_elements= part_elem->list_val_list.elements;
- if (!num_elements && error_if_requires_values())
+ if (unlikely(!num_elements && error_if_requires_values()))
DBUG_RETURN(true);
DBUG_ASSERT(part_type == RANGE_PARTITION ?
num_elements == 1U : TRUE);
@@ -2597,7 +2391,7 @@ static bool strcmp_null(const char *a, const char *b)
such partitioned tables using numeric colums in the partitioning expression.
For more info see bug#14521864.
Does not check if columns etc has changed, i.e. only for
- alter_info->flags == ALTER_PARTITION.
+ alter_info->partition_flags == ALTER_PARTITION_INFO.
*/
bool partition_info::has_same_partitioning(partition_info *new_part_info)
@@ -2650,9 +2444,9 @@ bool partition_info::has_same_partitioning(partition_info *new_part_info)
}
/* Check that it will use the same fields in KEY (fields) list. */
- List_iterator<char> old_field_name_it(part_field_list);
- List_iterator<char> new_field_name_it(new_part_info->part_field_list);
- char *old_name, *new_name;
+ List_iterator<const char> old_field_name_it(part_field_list);
+ List_iterator<const char> new_field_name_it(new_part_info->part_field_list);
+ const char *old_name, *new_name;
while ((old_name= old_field_name_it++))
{
new_name= new_field_name_it++;
@@ -2665,9 +2459,9 @@ bool partition_info::has_same_partitioning(partition_info *new_part_info)
if (is_sub_partitioned())
{
/* Check that it will use the same fields in KEY subpart fields list. */
- List_iterator<char> old_field_name_it(subpart_field_list);
- List_iterator<char> new_field_name_it(new_part_info->subpart_field_list);
- char *old_name, *new_name;
+ List_iterator<const char> old_field_name_it(subpart_field_list);
+ List_iterator<const char> new_field_name_it(new_part_info->subpart_field_list);
+ const char *old_name, *new_name;
while ((old_name= old_field_name_it++))
{
new_name= new_field_name_it++;
@@ -2855,12 +2649,9 @@ part_column_list_val *partition_info::add_column_value(THD *thd)
return NULL;
}
-bool partition_info::set_part_expr(THD *thd, char *start_token, Item *item_ptr,
- char *end_token, bool is_subpart)
+bool partition_info::set_part_expr(THD *thd, Item *item_ptr, bool is_subpart)
{
- (void)start_token;
(void)item_ptr;
- (void)end_token;
(void)is_subpart;
return FALSE;
}
@@ -2895,11 +2686,25 @@ bool check_partition_dirs(partition_info *part_info)
#endif /* WITH_PARTITION_STORAGE_ENGINE */
+bool partition_info::vers_init_info(THD * thd)
+{
+ part_type= VERSIONING_PARTITION;
+ list_of_part_fields= true;
+ column_list= false;
+ vers_info= new (thd->mem_root) Vers_part_info;
+ if (unlikely(!vers_info))
+ return true;
+
+ return false;
+}
+
+
bool partition_info::error_if_requires_values() const
{
switch (part_type) {
case NOT_A_PARTITION:
case HASH_PARTITION:
+ case VERSIONING_PARTITION:
break;
case RANGE_PARTITION:
my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0), "RANGE", "LESS THAN");
diff --git a/sql/partition_info.h b/sql/partition_info.h
index d42ef380c8c..2aed6cbc5db 100644
--- a/sql/partition_info.h
+++ b/sql/partition_info.h
@@ -22,18 +22,60 @@
#include "sql_class.h"
#include "partition_element.h"
+#include "sql_partition.h"
class partition_info;
struct TABLE_LIST;
/* Some function typedefs */
-typedef int (*get_part_id_func)(partition_info *part_info,
- uint32 *part_id,
+typedef int (*get_part_id_func)(partition_info *part_info, uint32 *part_id,
longlong *func_value);
-typedef int (*get_subpart_id_func)(partition_info *part_info,
- uint32 *part_id);
+typedef int (*get_subpart_id_func)(partition_info *part_info, uint32 *part_id);
+typedef bool (*check_constants_func)(THD *thd, partition_info *part_info);
struct st_ddl_log_memory_entry;
+struct Vers_part_info : public Sql_alloc
+{
+ Vers_part_info() :
+ limit(0),
+ now_part(NULL),
+ hist_part(NULL)
+ {
+ interval.type= INTERVAL_LAST;
+ }
+ Vers_part_info(Vers_part_info &src) :
+ interval(src.interval),
+ limit(src.limit),
+ now_part(NULL),
+ hist_part(NULL)
+ {
+ }
+ bool initialized()
+ {
+ if (now_part)
+ {
+ DBUG_ASSERT(now_part->id != UINT_MAX32);
+ DBUG_ASSERT(now_part->type == partition_element::CURRENT);
+ if (hist_part)
+ {
+ DBUG_ASSERT(hist_part->id != UINT_MAX32);
+ DBUG_ASSERT(hist_part->type == partition_element::HISTORY);
+ }
+ return true;
+ }
+ return false;
+ }
+ struct {
+ my_time_t start;
+ INTERVAL step;
+ enum interval_type type;
+ bool is_set() { return type < INTERVAL_LAST; }
+ } interval;
+ ulonglong limit;
+ partition_element *now_part;
+ partition_element *hist_part;
+};
+
class partition_info : public Sql_alloc
{
public:
@@ -43,8 +85,8 @@ public:
List<partition_element> partitions;
List<partition_element> temp_partitions;
- List<char> part_field_list;
- List<char> subpart_field_list;
+ List<const char> part_field_list;
+ List<const char> subpart_field_list;
/*
If there is no subpartitioning, use only this func to get partition ids.
@@ -74,6 +116,8 @@ public:
get_part_id_func get_part_partition_id_charset;
get_subpart_id_func get_subpartition_id_charset;
+ check_constants_func check_constants;
+
/* NULL-terminated array of fields used in partitioned expression */
Field **part_field_array;
Field **subpart_field_array;
@@ -143,6 +187,8 @@ public:
part_column_list_val *range_col_array;
part_column_list_val *list_col_array;
};
+
+ Vers_part_info *vers_info;
/********************************************
* INTERVAL ANALYSIS
@@ -250,7 +296,7 @@ public:
part_expr(NULL), subpart_expr(NULL), item_free_list(NULL),
first_log_entry(NULL), exec_log_entry(NULL), frm_log_entry(NULL),
bitmaps_are_initialized(FALSE),
- list_array(NULL), err_value(0),
+ list_array(NULL), vers_info(NULL), err_value(0),
part_info_string(NULL),
curr_part_elem(NULL), current_partition(NULL),
curr_list_object(0), num_columns(0), table(NULL),
@@ -282,8 +328,9 @@ public:
~partition_info() {}
partition_info *get_clone(THD *thd);
- bool set_named_partition_bitmap(const char *part_name, uint length);
- bool set_partition_bitmaps(TABLE_LIST *table_list);
+ bool set_named_partition_bitmap(const char *part_name, size_t length);
+ bool set_partition_bitmaps(List<String> *partition_names);
+ bool set_partition_bitmaps_from_table(TABLE_LIST *table_list);
/* Answers the question if subpartitioning is used for a certain table */
bool is_sub_partitioned()
{
@@ -299,14 +346,12 @@ public:
bool set_up_defaults_for_partitioning(THD *thd, handler *file,
HA_CREATE_INFO *info,
uint start_no);
- char *find_duplicate_field();
+ const char *find_duplicate_field();
char *find_duplicate_name();
bool check_engine_mix(handlerton *engine_type, bool default_engine);
- bool check_range_constants(THD *thd);
- bool check_list_constants(THD *thd);
bool check_partition_info(THD *thd, handlerton **eng_type,
handler *file, HA_CREATE_INFO *info,
- bool check_partition_function);
+ partition_info *add_or_reorg_part= NULL);
void print_no_partition_found(TABLE *table, myf errflag);
void print_debug(const char *str, uint*);
Item* get_column_item(Item *item, Field *field);
@@ -321,9 +366,7 @@ public:
void init_col_val(part_column_list_val *col_val, Item *item);
int reorganize_into_single_field_col_val(THD *thd);
part_column_list_val *add_column_value(THD *thd);
- bool set_part_expr(THD *thd, char *start_token, Item *item_ptr,
- char *end_token, bool is_subpart);
- static int compare_column_values(const void *a, const void *b);
+ bool set_part_expr(THD *thd, Item *item_ptr, bool is_subpart);
bool set_up_charset_field_preps(THD *thd);
bool check_partition_field_length();
bool init_column_part(THD *thd);
@@ -334,7 +377,6 @@ public:
bool has_same_partitioning(partition_info *new_part_info);
bool error_if_requires_values() const;
private:
- static int list_part_cmp(const void* a, const void* b);
bool set_up_default_partitions(THD *thd, handler *file, HA_CREATE_INFO *info,
uint start_no);
bool set_up_default_subpartitions(THD *thd, handler *file,
@@ -343,11 +385,55 @@ private:
uint start_no);
char *create_default_subpartition_name(THD *thd, uint subpart_no,
const char *part_name);
- bool prune_partition_bitmaps(TABLE_LIST *table_list);
- bool add_named_partition(const char *part_name, uint length);
+ // FIXME: prune_partition_bitmaps() is duplicate of set_read_partitions()
+ bool prune_partition_bitmaps(List<String> *partition_names);
+ 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(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_limit(ulonglong limit)
+ {
+ DBUG_ASSERT(part_type == VERSIONING_PARTITION);
+ vers_info->limit= limit;
+ return !limit;
+ }
+ void vers_set_hist_part(THD *thd);
+ bool vers_setup_expression(THD *thd, uint32 alter_add= 0); /* Stage 1. */
+ partition_element *get_partition(uint part_id)
+ {
+ List_iterator<partition_element> it(partitions);
+ partition_element *el;
+ while ((el= it++))
+ {
+ if (el->id == part_id)
+ return el;
+ }
+ return NULL;
+ }
};
uint32 get_next_partition_id_range(struct st_partition_iter* part_iter);
diff --git a/sql/password.c b/sql/password.c
index 8ac6ee0ebae..d824e61586a 100644
--- a/sql/password.c
+++ b/sql/password.c
@@ -60,7 +60,7 @@
*****************************************************************************/
-#include <my_global.h>
+#include "mariadb.h"
#include <my_sys.h>
#include <m_string.h>
#include <password.h>
@@ -296,7 +296,7 @@ void make_password_from_salt_323(char *to, const ulong *salt)
buf+len*2
*/
-char *octet2hex(char *to, const char *str, uint len)
+char *octet2hex(char *to, const char *str, size_t len)
{
const char *str_end= str + len;
for (; str != str_end; ++str)
diff --git a/sql/procedure.cc b/sql/procedure.cc
index 75a3a4ea8a9..92df43bb0e3 100644
--- a/sql/procedure.cc
+++ b/sql/procedure.cc
@@ -20,7 +20,7 @@
#pragma implementation // gcc: Class implementation
#endif
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "procedure.h"
#include "sql_analyse.h" // Includes procedure
@@ -89,14 +89,14 @@ setup_procedure(THD *thd,ORDER *param,select_result *result,
for (i=0 ; i < array_elements(sql_procs) ; i++)
{
if (!my_strcasecmp(system_charset_info,
- (*param->item)->name,sql_procs[i].name))
+ (*param->item)->name.str, sql_procs[i].name))
{
Procedure *proc=(*sql_procs[i].init)(thd,param,result,field_list);
*error= !proc;
DBUG_RETURN(proc);
}
}
- my_error(ER_UNKNOWN_PROCEDURE, MYF(0), (*param->item)->name);
+ my_error(ER_UNKNOWN_PROCEDURE, MYF(0), (*param->item)->name.str);
*error=1;
DBUG_RETURN(0);
}
diff --git a/sql/procedure.h b/sql/procedure.h
index c1b97c9afb9..80930d6b707 100644
--- a/sql/procedure.h
+++ b/sql/procedure.h
@@ -40,17 +40,18 @@ class Item_proc :public Item
public:
Item_proc(THD *thd, const char *name_par): Item(thd)
{
- this->name=(char*) name_par;
+ this->name.str= name_par;
+ this->name.length= strlen(name_par);
}
enum Type type() const { return Item::PROC_ITEM; }
virtual void set(double nr)=0;
virtual void set(const char *str,uint length,CHARSET_INFO *cs)=0;
virtual void set(longlong nr)=0;
- virtual enum_field_types field_type() const=0;
+ const Type_handler *type_handler() const=0;
void set(const char *str) { set(str,(uint) strlen(str), default_charset()); }
- void make_field(THD *thd, Send_field *tmp_field)
+ void make_send_field(THD *thd, Send_field *tmp_field)
{
- init_make_field(tmp_field,field_type());
+ init_make_send_field(tmp_field,field_type());
}
unsigned int size_of() { return sizeof(*this);}
bool check_vcol_func_processor(void *arg)
@@ -58,7 +59,11 @@ public:
DBUG_ASSERT(0); // impossible
return mark_unsupported_function("proc", arg, VCOL_IMPOSSIBLE);
}
- Item* get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; }
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ {
+ return type_handler()->Item_get_date(this, ltime, fuzzydate);
+ }
+ Item* get_copy(THD *thd) { return 0; }
};
class Item_proc_real :public Item_proc
@@ -70,9 +75,7 @@ public:
{
decimals=dec; max_length=float_length(dec);
}
- enum Item_result result_type () const { return REAL_RESULT; }
- enum Item_result cmp_type () const { return REAL_RESULT; }
- enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; }
+ const Type_handler *type_handler() const { return &type_handler_double; }
void set(double nr) { value=nr; }
void set(longlong nr) { value=(double) nr; }
void set(const char *str,uint length,CHARSET_INFO *cs)
@@ -98,9 +101,7 @@ 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; }
- enum Item_result result_type () const { return INT_RESULT; }
- enum Item_result cmp_type () const { return INT_RESULT; }
- enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; }
+ const Type_handler *type_handler() const { return &type_handler_longlong; }
void set(double nr) { value=(longlong) nr; }
void set(longlong nr) { value=nr; }
void set(const char *str,uint length, CHARSET_INFO *cs)
@@ -118,9 +119,7 @@ class Item_proc_string :public Item_proc
public:
Item_proc_string(THD *thd, const char *name_par, uint length):
Item_proc(thd, name_par) { this->max_length=length; }
- enum Item_result result_type () const { return STRING_RESULT; }
- enum Item_result cmp_type () const { return STRING_RESULT; }
- enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; }
+ const Type_handler *type_handler() const { return &type_handler_varchar; }
void set(double nr) { str_value.set_real(nr, 2, default_charset()); }
void set(longlong nr) { str_value.set(nr, default_charset()); }
void set(const char *str, uint length, CHARSET_INFO *cs)
diff --git a/sql/protocol.cc b/sql/protocol.cc
index aa795b9d12a..02c95a42695 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -25,7 +25,7 @@
#pragma implementation // gcc: Class implementation
#endif
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "protocol.h"
#include "sql_class.h" // THD
@@ -86,7 +86,7 @@ bool Protocol_binary::net_store_data_cs(const uchar *from, size_t length,
{
uint dummy_errors;
/* Calculate maxumum possible result length */
- uint conv_length= to_cs->mbmaxlen * length / from_cs->mbminlen;
+ size_t conv_length= to_cs->mbmaxlen * length / from_cs->mbminlen;
if (conv_length > 250)
{
@@ -106,8 +106,8 @@ bool Protocol_binary::net_store_data_cs(const uchar *from, size_t length,
net_store_data((const uchar*) convert->ptr(), convert->length()));
}
- ulong packet_length= packet->length();
- ulong new_length= packet_length + conv_length + 1;
+ size_t packet_length= packet->length();
+ size_t new_length= packet_length + conv_length + 1;
if (new_length > packet->alloced_length() && packet->realloc(new_length))
return 1;
@@ -285,7 +285,7 @@ net_send_ok(THD *thd,
DBUG_ASSERT(store.length() <= MAX_PACKET_LENGTH);
error= my_net_write(net, (const unsigned char*)store.ptr(), store.length());
- if (!error && (!skip_flush || is_eof))
+ if (likely(!error) && (!skip_flush || is_eof))
error= net_flush(net);
thd->server_status&= ~SERVER_SESSION_STATE_CHANGED;
@@ -349,7 +349,7 @@ net_send_eof(THD *thd, uint server_status, uint statement_warn_count)
{
thd->get_stmt_da()->set_overwrite_status(true);
error= write_eof_packet(thd, net, server_status, statement_warn_count);
- if (!error)
+ if (likely(!error))
error= net_flush(net);
thd->get_stmt_da()->set_overwrite_status(false);
DBUG_PRINT("info", ("EOF sent, so no more error sending allowed"));
@@ -393,7 +393,7 @@ static bool write_eof_packet(THD *thd, NET *net,
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)
+ if (unlikely(thd->is_fatal_error))
server_status&= ~SERVER_MORE_RESULTS_EXISTS;
int2store(buff + 3, server_status);
error= my_net_write(net, buff, 5);
@@ -491,8 +491,9 @@ bool net_send_error_packet(THD *thd, uint sql_errno, const char *err,
- ulonglong for bigger numbers.
*/
-static uchar *net_store_length_fast(uchar *packet, uint length)
+static uchar *net_store_length_fast(uchar *packet, size_t length)
{
+ DBUG_ASSERT(length < UINT_MAX16);
if (length < 251)
{
*packet=(uchar) length;
@@ -600,7 +601,7 @@ void Protocol::end_statement()
thd->get_stmt_da()->skip_flush());
break;
}
- if (!error)
+ if (likely(!error))
thd->get_stmt_da()->set_is_sent(true);
DBUG_VOID_RETURN;
}
@@ -672,7 +673,7 @@ void net_send_progress_packet(THD *thd)
{
uchar buff[200], *pos;
const char *proc_info= thd->proc_info ? thd->proc_info : "";
- uint length= strlen(proc_info);
+ size_t length= strlen(proc_info);
ulonglong progress;
DBUG_ENTER("net_send_progress_packet");
@@ -802,8 +803,7 @@ bool Protocol::send_result_set_metadata(List<Item> *list, uint flags)
{
List_iterator_fast<Item> it(*list);
Item *item;
- uchar buff[MAX_FIELD_WIDTH];
- String tmp((char*) buff,sizeof(buff),&my_charset_bin);
+ ValueBuffer<MAX_FIELD_WIDTH> tmp;
Protocol_text prot(thd);
String *local_packet= prot.storage_packet();
CHARSET_INFO *thd_charset= thd->variables.character_set_results;
@@ -811,7 +811,9 @@ bool Protocol::send_result_set_metadata(List<Item> *list, uint flags)
if (flags & SEND_NUM_ROWS)
{ // Packet with number of elements
+ uchar buff[MAX_INT_WIDTH];
uchar *pos= net_store_length(buff, list->elements);
+ DBUG_ASSERT(pos <= buff + sizeof(buff));
if (my_net_write(&thd->net, buff, (size_t) (pos-buff)))
DBUG_RETURN(1);
}
@@ -830,7 +832,7 @@ bool Protocol::send_result_set_metadata(List<Item> *list, uint flags)
char *pos;
CHARSET_INFO *cs= system_charset_info;
Send_field field;
- item->make_field(thd, &field);
+ item->make_send_field(thd, &field);
/* limit number of decimals for float and double */
if (field.type == MYSQL_TYPE_FLOAT || field.type == MYSQL_TYPE_DOUBLE)
@@ -851,9 +853,9 @@ bool Protocol::send_result_set_metadata(List<Item> *list, uint flags)
cs, thd_charset) ||
prot.store(field.org_table_name, (uint) strlen(field.org_table_name),
cs, thd_charset) ||
- prot.store(field.col_name, (uint) strlen(field.col_name),
+ prot.store(field.col_name.str, (uint) field.col_name.length,
cs, thd_charset) ||
- prot.store(field.org_col_name, (uint) strlen(field.org_col_name),
+ prot.store(field.org_col_name.str, (uint) field.org_col_name.length,
cs, thd_charset) ||
local_packet->realloc(local_packet->length()+12))
goto err;
@@ -909,7 +911,7 @@ bool Protocol::send_result_set_metadata(List<Item> *list, uint flags)
{
if (prot.store(field.table_name, (uint) strlen(field.table_name),
cs, thd_charset) ||
- prot.store(field.col_name, (uint) strlen(field.col_name),
+ prot.store(field.col_name.str, (uint) field.col_name.length,
cs, thd_charset) ||
local_packet->realloc(local_packet->length()+10))
goto err;
@@ -979,29 +981,28 @@ bool Protocol::write()
bool Protocol::send_result_set_row(List<Item> *row_items)
{
- char buffer[MAX_FIELD_WIDTH];
- String str_buffer(buffer, sizeof (buffer), &my_charset_bin);
List_iterator_fast<Item> it(*row_items);
DBUG_ENTER("Protocol::send_result_set_row");
for (Item *item= it++; item; item= it++)
{
- if (item->send(this, &str_buffer))
+ /*
+ ValueBuffer::m_string can be altered during Item::send().
+ It's important to declare value_buffer inside the loop,
+ to have ValueBuffer::m_string point to ValueBuffer::buffer
+ on every iteration.
+ */
+ ValueBuffer<MAX_FIELD_WIDTH> value_buffer;
+ if (item->send(this, &value_buffer))
{
// If we're out of memory, reclaim some, to help us recover.
this->free();
DBUG_RETURN(TRUE);
}
/* Item::send() may generate an error. If so, abort the loop. */
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
DBUG_RETURN(TRUE);
-
- /*
- Reset str_buffer to its original state, as it may have been altered in
- Item::send().
- */
- str_buffer.set(buffer, sizeof(buffer), &my_charset_bin);
}
DBUG_RETURN(FALSE);
@@ -1026,7 +1027,7 @@ bool Protocol::store(const char *from, CHARSET_INFO *cs)
{
if (!from)
return store_null();
- uint length= strlen(from);
+ size_t length= strlen(from);
return store(from, length, cs);
}
@@ -1127,7 +1128,7 @@ bool Protocol_text::store(const char *from, size_t length,
#ifndef DBUG_OFF
DBUG_PRINT("info", ("Protocol_text::store field %u (%u): %.*s", field_pos,
field_count, (int) length, (length == 0 ? "" : from)));
- DBUG_ASSERT(field_pos < field_count);
+ DBUG_ASSERT(field_types == 0 || field_pos < field_count);
DBUG_ASSERT(field_types == 0 ||
field_types[field_pos] == MYSQL_TYPE_DECIMAL ||
field_types[field_pos] == MYSQL_TYPE_BIT ||
@@ -1219,7 +1220,7 @@ bool Protocol_text::store(float from, uint32 decimals, String *buffer)
field_types[field_pos] == MYSQL_TYPE_FLOAT);
field_pos++;
#endif
- buffer->set_real((double) from, decimals, thd->charset());
+ Float(from).to_string(buffer, decimals);
return net_store_data((uchar*) buffer->ptr(), buffer->length());
}
@@ -1246,7 +1247,7 @@ bool Protocol_text::store(Field *field)
char buff[MAX_FIELD_WIDTH];
String str(buff,sizeof(buff), &my_charset_bin);
CHARSET_INFO *tocs= this->thd->variables.character_set_results;
-#ifndef DBUG_OFF
+#ifdef DBUG_ASSERT_EXISTS
TABLE *table= field->table;
my_bitmap_map *old_map= 0;
if (table->file)
@@ -1254,7 +1255,7 @@ bool Protocol_text::store(Field *field)
#endif
field->val_str(&str);
-#ifndef DBUG_OFF
+#ifdef DBUG_ASSERT_EXISTS
if (old_map)
dbug_tmp_restore_column_map(table->read_set, old_map);
#endif
@@ -1338,6 +1339,7 @@ bool Protocol_text::send_out_parameters(List<Item_param> *sp_params)
continue;
}
+ DBUG_ASSERT(sparam->get_item_param() == NULL);
sparam->set_value(thd, thd->spcont, reinterpret_cast<Item **>(&item_param));
}
diff --git a/sql/proxy_protocol.cc b/sql/proxy_protocol.cc
new file mode 100644
index 00000000000..550813c6457
--- /dev/null
+++ b/sql/proxy_protocol.cc
@@ -0,0 +1,583 @@
+/* Copyright (c) 2017, 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 02110-1301, USA */
+
+#include <mariadb.h>
+#include <mysql.h>
+#include <mysql_com.h>
+#include <mysqld_error.h>
+#include <my_sys.h>
+#include <m_string.h>
+#include <my_net.h>
+#include <violite.h>
+#include <proxy_protocol.h>
+#include <log.h>
+#include <my_pthread.h>
+
+#define PROXY_PROTOCOL_V1_SIGNATURE "PROXY"
+#define PROXY_PROTOCOL_V2_SIGNATURE "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A"
+#define MAX_PROXY_HEADER_LEN 256
+
+static mysql_rwlock_t lock;
+
+/*
+ Parse proxy protocol version 1 header (text)
+*/
+static int parse_v1_header(char *hdr, size_t len, proxy_peer_info *peer_info)
+{
+ char address_family[MAX_PROXY_HEADER_LEN + 1];
+ char client_address[MAX_PROXY_HEADER_LEN + 1];
+ char server_address[MAX_PROXY_HEADER_LEN + 1];
+ int client_port;
+ int server_port;
+
+ int ret = sscanf(hdr, "PROXY %s %s %s %d %d",
+ address_family, client_address, server_address,
+ &client_port, &server_port);
+
+ if (ret != 5)
+ {
+ if (ret >= 1 && !strcmp(address_family, "UNKNOWN"))
+ {
+ peer_info->is_local_command= true;
+ return 0;
+ }
+ return -1;
+ }
+
+ if (client_port < 0 || client_port > 0xffff
+ || server_port < 0 || server_port > 0xffff)
+ return -1;
+
+ if (!strcmp(address_family, "UNKNOWN"))
+ {
+ peer_info->is_local_command= true;
+ return 0;
+ }
+ else if (!strcmp(address_family, "TCP4"))
+ {
+ /* Initialize IPv4 peer address.*/
+ peer_info->peer_addr.ss_family= AF_INET;
+ if (!inet_pton(AF_INET, client_address,
+ &((struct sockaddr_in *)(&peer_info->peer_addr))->sin_addr))
+ return -1;
+ }
+ else if (!strcmp(address_family, "TCP6"))
+ {
+ /* Initialize IPv6 peer address.*/
+ peer_info->peer_addr.ss_family= AF_INET6;
+ if (!inet_pton(AF_INET6, client_address,
+ &((struct sockaddr_in6 *)(&peer_info->peer_addr))->sin6_addr))
+ return -1;
+ }
+ peer_info->port= client_port;
+ /* Check if server address is legal.*/
+ char addr_bin[16];
+ if (!inet_pton(peer_info->peer_addr.ss_family,
+ server_address, addr_bin))
+ return -1;
+
+ return 0;
+}
+
+
+/*
+ Parse proxy protocol V2 (binary) header
+*/
+static int parse_v2_header(uchar *hdr, size_t len,proxy_peer_info *peer_info)
+{
+ /* V2 Signature */
+ if (memcmp(hdr, PROXY_PROTOCOL_V2_SIGNATURE, 12))
+ return -1;
+
+ /* version + command */
+ uint8 ver= (hdr[12] & 0xF0);
+ if (ver != 0x20)
+ return -1; /* Wrong version*/
+
+ uint cmd= (hdr[12] & 0xF);
+
+ /* Address family */
+ uchar fam= hdr[13];
+
+ if (cmd == 0)
+ {
+ /* LOCAL command*/
+ peer_info->is_local_command= true;
+ return 0;
+ }
+
+ if (cmd != 0x01)
+ {
+ /* Not PROXY COMMAND */
+ return -1;
+ }
+
+ struct sockaddr_in *sin= (struct sockaddr_in *)(&peer_info->peer_addr);
+ struct sockaddr_in6 *sin6= (struct sockaddr_in6 *)(&peer_info->peer_addr);
+ switch (fam)
+ {
+ case 0x11: /* TCPv4 */
+ sin->sin_family= AF_INET;
+ memcpy(&(sin->sin_addr), hdr + 16, 4);
+ peer_info->port= (hdr[24] << 8) + hdr[25];
+ break;
+ case 0x21: /* TCPv6 */
+ sin6->sin6_family= AF_INET6;
+ memcpy(&(sin6->sin6_addr), hdr + 16, 16);
+ peer_info->port= (hdr[48] << 8) + hdr[49];
+ break;
+ case 0x31: /* AF_UNIX, stream */
+ peer_info->peer_addr.ss_family= AF_UNIX;
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+
+bool has_proxy_protocol_header(NET *net)
+{
+ compile_time_assert(NET_HEADER_SIZE < sizeof(PROXY_PROTOCOL_V1_SIGNATURE));
+ compile_time_assert(NET_HEADER_SIZE < sizeof(PROXY_PROTOCOL_V2_SIGNATURE));
+
+ const uchar *preread_bytes= net->buff + net->where_b;
+ return !memcmp(preread_bytes, PROXY_PROTOCOL_V1_SIGNATURE, NET_HEADER_SIZE)||
+ !memcmp(preread_bytes, PROXY_PROTOCOL_V2_SIGNATURE, NET_HEADER_SIZE);
+}
+
+
+/**
+ Try to parse proxy header.
+ https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
+
+ Whenever this function is called, client is connecting, and
+ we have have pre-read 4 bytes (NET_HEADER_SIZE) from the network already.
+ These 4 bytes did not match MySQL packet header, and (unless the client
+ is buggy), those bytes must be proxy header.
+
+ @param[in] net - vio and already preread bytes from the header
+ @param[out] peer_info - parsed proxy header with client host and port
+ @return 0 in case of success, -1 if error.
+*/
+int parse_proxy_protocol_header(NET *net, proxy_peer_info *peer_info)
+{
+ uchar hdr[MAX_PROXY_HEADER_LEN];
+ size_t pos= 0;
+
+ DBUG_ASSERT(!net->compress);
+ const uchar *preread_bytes= net->buff + net->where_b;
+ bool have_v1_header= !memcmp(preread_bytes, PROXY_PROTOCOL_V1_SIGNATURE, NET_HEADER_SIZE);
+ bool have_v2_header=
+ !have_v1_header && !memcmp(preread_bytes, PROXY_PROTOCOL_V2_SIGNATURE, NET_HEADER_SIZE);
+ if (!have_v1_header && !have_v2_header)
+ {
+ // not a proxy protocol header
+ return -1;
+ }
+ memcpy(hdr, preread_bytes, NET_HEADER_SIZE);
+ pos= NET_HEADER_SIZE;
+ Vio *vio= net->vio;
+ memset(peer_info, 0, sizeof (*peer_info));
+
+ if (have_v1_header)
+ {
+ /* Read until end of header (newline character)*/
+ while(pos < sizeof(hdr))
+ {
+ long len= (long)vio_read(vio, hdr + pos, 1);
+ if (len < 0)
+ return -1;
+ pos++;
+ if (hdr[pos-1] == '\n')
+ break;
+ }
+ hdr[pos]= 0;
+
+ if (parse_v1_header((char *)hdr, pos, peer_info))
+ return -1;
+ }
+ else // if (have_v2_header)
+ {
+#define PROXY_V2_HEADER_LEN 16
+ /* read off 16 bytes of the header.*/
+ ssize_t len= vio_read(vio, hdr + pos, PROXY_V2_HEADER_LEN - pos);
+ if (len < 0)
+ return -1;
+ // 2 last bytes are the length in network byte order of the part following header
+ ushort trail_len= ((ushort)hdr[PROXY_V2_HEADER_LEN-2] >> 8) + hdr[PROXY_V2_HEADER_LEN-1];
+ if (trail_len > sizeof(hdr) - PROXY_V2_HEADER_LEN)
+ return -1;
+ if (trail_len > 0)
+ {
+ len= vio_read(vio, hdr + PROXY_V2_HEADER_LEN, trail_len);
+ if (len < 0)
+ return -1;
+ }
+ pos= PROXY_V2_HEADER_LEN + trail_len;
+ if (parse_v2_header(hdr, pos, peer_info))
+ return -1;
+ }
+
+ if (peer_info->peer_addr.ss_family == AF_INET6)
+ {
+ /*
+ Normalize IPv4 compatible or mapped IPv6 addresses.
+ They will be treated as IPv4.
+ */
+ sockaddr_storage tmp;
+ memset(&tmp, 0, sizeof(tmp));
+ vio_get_normalized_ip((const struct sockaddr *)&peer_info->peer_addr,
+ sizeof(sockaddr_storage), (struct sockaddr *)&tmp);
+ memcpy(&peer_info->peer_addr, &tmp, sizeof(tmp));
+ }
+ return 0;
+}
+
+
+/**
+ CIDR address matching etc (for the proxy_protocol_networks parameter)
+*/
+
+/**
+ Subnetwork address in CIDR format, e.g
+ 192.168.1.0/24 or 2001:db8::/32
+*/
+struct subnet
+{
+ char addr[16]; /* Binary representation of the address, big endian*/
+ unsigned short family; /* Address family, AF_INET or AF_INET6 */
+ unsigned short bits; /* subnetwork size */
+};
+
+static subnet* proxy_protocol_subnets;
+size_t proxy_protocol_subnet_count;
+
+#define MAX_MASK_BITS(family) (family == AF_INET ? 32 : 128)
+
+
+/** Convert IPv4 that are compat or mapped IPv4 to "normal" IPv4 */
+static int normalize_subnet(struct subnet *subnet)
+{
+ unsigned char *addr= (unsigned char*)subnet->addr;
+ if (subnet->family == AF_INET6)
+ {
+ const struct in6_addr *src_ip6=(in6_addr *)addr;
+ if (IN6_IS_ADDR_V4MAPPED(src_ip6) || IN6_IS_ADDR_V4COMPAT(src_ip6))
+ {
+ /* Copy the actual IPv4 address (4 last bytes) */
+ if (subnet->bits < 96)
+ return -1;
+ subnet->family= AF_INET;
+ memcpy(addr, addr+12, 4);
+ subnet->bits -= 96;
+ }
+ }
+ return 0;
+}
+
+/**
+ Convert string representation of a subnet to subnet struct.
+*/
+static int parse_subnet(char *addr_str, struct subnet *subnet)
+{
+ if (strchr(addr_str, ':'))
+ subnet->family= AF_INET6;
+ else if (strchr(addr_str, '.'))
+ subnet->family= AF_INET;
+ else if (!strcmp(addr_str, "localhost"))
+ {
+ subnet->family= AF_UNIX;
+ subnet->bits= 0;
+ return 0;
+ }
+
+ char *pmask= strchr(addr_str, '/');
+ if (!pmask)
+ {
+ subnet->bits= MAX_MASK_BITS(subnet->family);
+ }
+ else
+ {
+ *pmask= 0;
+ pmask++;
+ int b= 0;
+
+ do
+ {
+ if (*pmask < '0' || *pmask > '9')
+ return -1;
+ b= 10 * b + *pmask - '0';
+ if (b > MAX_MASK_BITS(subnet->family))
+ return -1;
+ pmask++;
+ }
+ while (*pmask);
+
+ subnet->bits= (unsigned short)b;
+ }
+
+ if (!inet_pton(subnet->family, addr_str, subnet->addr))
+ return -1;
+
+ if (normalize_subnet(subnet))
+ return -1;
+
+ return 0;
+}
+
+/**
+ Parse comma separated string subnet list into subnets array,
+ which is stored in 'proxy_protocol_subnets' variable
+
+ @param[in] subnets_str : networks in CIDR format,
+ separated by comma and/or space
+ @param[out] out_subnets : parsed subnets;
+ @param[out] out_count : number of parsed subnets
+
+ @return 0 if success, otherwise -1
+*/
+static int parse_networks(const char *subnets_str, subnet **out_subnets, size_t *out_count)
+{
+ int ret= -1;
+ subnet *subnets= 0;
+ size_t count= 0;
+ const char *p= subnets_str;
+ size_t max_subnets;
+
+ if (!subnets_str || !*subnets_str)
+ {
+ ret= 0;
+ goto end;
+ }
+
+ max_subnets= MY_MAX(3,strlen(subnets_str)/2);
+ subnets= (subnet *)my_malloc(max_subnets * sizeof(subnet),MY_ZEROFILL);
+
+ /* Check for special case '*'. */
+ if (strcmp(subnets_str, "*") == 0)
+ {
+ subnets[0].family= AF_INET;
+ subnets[1].family= AF_INET6;
+ subnets[2].family= AF_UNIX;
+ count= 3;
+ ret= 0;
+ goto end;
+ }
+
+ char token[256];
+ for(count= 0;; count++)
+ {
+ while(*p && (*p ==',' || *p == ' '))
+ p++;
+ if (!*p)
+ break;
+
+ size_t cnt= 0;
+ while(*p && *p != ',' && *p != ' ' && cnt < sizeof(token)-1)
+ token[cnt++]= *p++;
+
+ token[cnt++]=0;
+ if (cnt == sizeof(token))
+ goto end;
+
+ if (parse_subnet(token, &subnets[count]))
+ {
+ my_printf_error(ER_PARSE_ERROR,"Error parsing proxy_protocol_networks parameter, near '%s'",MYF(0),token);
+ goto end;
+ }
+ }
+
+ ret = 0;
+
+end:
+ if (ret)
+ {
+ my_free(subnets);
+ *out_subnets= NULL;
+ *out_count= 0;
+ return ret;
+ }
+ *out_subnets = subnets;
+ *out_count= count;
+ return 0;
+}
+
+/**
+ Check validity of proxy_protocol_networks parameter
+ @param[in] in - input string
+ @return : true, if input is list of CIDR-style networks
+ separated by command or space
+*/
+bool proxy_protocol_networks_valid(const char *in)
+{
+ subnet *new_subnets;
+ size_t new_count;
+ int ret= parse_networks(in, &new_subnets, &new_count);
+ my_free(new_subnets);
+ return !ret;
+}
+
+
+/**
+ Set 'proxy_protocol_networks' parameter.
+
+ @param[in] spec : networks in CIDR format,
+ separated by comma and/or space
+
+ @return 0 if success, otherwise -1
+*/
+int set_proxy_protocol_networks(const char *spec)
+{
+ subnet *new_subnets;
+ subnet *old_subnet = 0;
+ size_t new_count;
+
+ int ret= parse_networks(spec, &new_subnets, &new_count);
+ if (ret)
+ return ret;
+
+ mysql_rwlock_wrlock(&lock);
+ old_subnet = proxy_protocol_subnets;
+ proxy_protocol_subnets = new_subnets;
+ proxy_protocol_subnet_count = new_count;
+ mysql_rwlock_unlock(&lock);
+ my_free(old_subnet);
+ return ret;
+}
+
+
+/**
+ Compare memory areas, in memcmp().similar fashion.
+ The difference to memcmp() is that size parameter is the
+ bit count, not byte count.
+*/
+static int compare_bits(const void *s1, const void *s2, int bit_count)
+{
+ int result= 0;
+ int byte_count= bit_count / 8;
+ if (byte_count && (result= memcmp(s1, s2, byte_count)))
+ return result;
+ int rem= bit_count % 8;
+ if (rem)
+ {
+ // compare remaining bits i.e partial bytes.
+ unsigned char s1_bits= (((char *)s1)[byte_count]) >> (8 - rem);
+ unsigned char s2_bits= (((char *)s2)[byte_count]) >> (8 - rem);
+ if (s1_bits > s2_bits)
+ return 1;
+ if (s1_bits < s2_bits)
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ Check whether networks address matches network.
+*/
+bool addr_matches_subnet(const sockaddr *sock_addr, const subnet *subnet)
+{
+ DBUG_ASSERT(subnet->family == AF_UNIX ||
+ subnet->family == AF_INET ||
+ subnet->family == AF_INET6);
+
+ if (sock_addr->sa_family != subnet->family)
+ return false;
+
+ if (subnet->family == AF_UNIX)
+ return true;
+
+ void *addr= (subnet->family == AF_INET) ?
+ (void *)&((struct sockaddr_in *)sock_addr)->sin_addr :
+ (void *)&((struct sockaddr_in6 *)sock_addr)->sin6_addr;
+
+ return (compare_bits(subnet->addr, addr, subnet->bits) == 0);
+}
+
+
+/**
+ Check whether proxy header from client is allowed, as per
+ specification in 'proxy_protocol_networks' server variable.
+
+ The non-TCP "localhost" clients (unix socket, shared memory, pipes)
+ are accepted whenever 127.0.0.1 accepted in 'proxy_protocol_networks'
+*/
+bool is_proxy_protocol_allowed(const sockaddr *addr)
+{
+ if (proxy_protocol_subnet_count == 0)
+ return false;
+
+ sockaddr_storage addr_storage;
+ struct sockaddr *normalized_addr= (struct sockaddr *)&addr_storage;
+
+ /*
+ Non-TCP addresses (unix domain socket, windows pipe and shared memory
+ gets tranlated to TCP4 localhost address.
+
+ Note, that vio remote addresses are initialized with binary zeros
+ for these protocols (which is AF_UNSPEC everywhere).
+ */
+ switch(addr->sa_family)
+ {
+ case AF_UNSPEC:
+ case AF_UNIX:
+ normalized_addr->sa_family= AF_UNIX;
+ break;
+ case AF_INET:
+ case AF_INET6:
+ {
+ size_t len=
+ (addr->sa_family == AF_INET)?sizeof(sockaddr_in):sizeof (sockaddr_in6);
+ vio_get_normalized_ip(addr, len,normalized_addr);
+ }
+ break;
+ default:
+ DBUG_ASSERT(0);
+ }
+
+ bool ret= false;
+ mysql_rwlock_rdlock(&lock);
+ for (size_t i= 0; i < proxy_protocol_subnet_count; i++)
+ {
+ if (addr_matches_subnet(normalized_addr, &proxy_protocol_subnets[i]))
+ {
+ ret= true;
+ break;
+ }
+ }
+ mysql_rwlock_unlock(&lock);
+
+ return ret;
+}
+
+
+int init_proxy_protocol_networks(const char *spec)
+{
+#ifdef HAVE_PSI_INTERFACE
+ static PSI_rwlock_key psi_rwlock_key;
+ static PSI_rwlock_info psi_rwlock_info={ &psi_rwlock_key, "rwlock", 0 };
+ mysql_rwlock_register("proxy_proto", &psi_rwlock_info, 1);
+#endif
+
+ mysql_rwlock_init(psi_rwlock_key, &lock);
+ return set_proxy_protocol_networks(spec);
+}
+
+
+void destroy_proxy_protocol_networks()
+{
+ my_free(proxy_protocol_subnets);
+ mysql_rwlock_destroy(&lock);
+}
diff --git a/sql/proxy_protocol.h b/sql/proxy_protocol.h
new file mode 100644
index 00000000000..0f873e2492c
--- /dev/null
+++ b/sql/proxy_protocol.h
@@ -0,0 +1,19 @@
+#include "my_net.h"
+
+struct proxy_peer_info
+{
+ struct sockaddr_storage peer_addr;
+ int port;
+ bool is_local_command;
+};
+
+extern bool has_proxy_protocol_header(NET *net);
+extern int parse_proxy_protocol_header(NET *net, proxy_peer_info *peer_info);
+extern bool is_proxy_protocol_allowed(const sockaddr *remote_addr);
+
+extern int init_proxy_protocol_networks(const char *spec);
+extern void destroy_proxy_protocol_networks();
+
+extern int set_proxy_protocol_networks(const char *spec);
+extern bool proxy_protocol_networks_valid(const char *spec);
+
diff --git a/sql/records.cc b/sql/records.cc
index 8a152cda4c2..817caada8e2 100644
--- a/sql/records.cc
+++ b/sql/records.cc
@@ -26,7 +26,7 @@
Functions for easy reading of records, possible through a cache
*/
-#include <my_global.h>
+#include "mariadb.h"
#include "records.h"
#include "sql_priv.h"
#include "records.h"
@@ -77,20 +77,19 @@ bool init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table,
bzero((char*) info,sizeof(*info));
info->thd= thd;
info->table= table;
- info->record= table->record[0];
info->print_error= print_error;
info->unlock_row= rr_unlock_row;
table->status=0; /* And it's always found */
if (!table->file->inited &&
- (error= table->file->ha_index_init(idx, 1)))
+ unlikely(error= table->file->ha_index_init(idx, 1)))
{
if (print_error)
table->file->print_error(error, MYF(0));
}
- /* read_record will be changed to rr_index in rr_index_first */
- info->read_record= reverse ? rr_index_last : rr_index_first;
+ /* read_record_func will be changed to rr_index in rr_index_first */
+ info->read_record_func= reverse ? rr_index_last : rr_index_first;
DBUG_RETURN(error != 0);
}
@@ -194,7 +193,6 @@ bool init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
bzero((char*) info,sizeof(*info));
info->thd=thd;
info->table=table;
- info->forms= &info->table; /* Only one table */
info->addon_field= addon_field;
if ((table->s->tmp_table == INTERNAL_TMP_TABLE) &&
@@ -204,19 +202,17 @@ bool init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
if (addon_field)
{
info->rec_buf= (uchar*) filesort->addon_buf.str;
- info->ref_length= filesort->addon_buf.length;
+ info->ref_length= (uint)filesort->addon_buf.length;
info->unpack= filesort->unpack;
}
else
{
empty_record(table);
- info->record= table->record[0];
- info->ref_length= table->file->ref_length;
+ info->ref_length= (uint)table->file->ref_length;
}
info->select=select;
info->print_error=print_error;
info->unlock_row= rr_unlock_row;
- info->ignore_not_found_rows= 0;
table->status= 0; /* Rows are always found */
tempfile= 0;
@@ -228,13 +224,13 @@ 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= (addon_field ?
- rr_unpack_from_tempfile : rr_from_tempfile);
+ info->read_record_func=
+ addon_field ? rr_unpack_from_tempfile : rr_from_tempfile;
info->io_cache= tempfile;
reinit_io_cache(info->io_cache,READ_CACHE,0L,0,0);
info->ref_pos=table->file->ref;
if (!table->file->inited)
- if (table->file->ha_rnd_init_with_error(0))
+ if (unlikely(table->file->ha_rnd_init_with_error(0)))
DBUG_RETURN(1);
/*
@@ -259,32 +255,32 @@ bool init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
if (! init_rr_cache(thd, info))
{
DBUG_PRINT("info",("using rr_from_cache"));
- info->read_record=rr_from_cache;
+ info->read_record_func= rr_from_cache;
}
}
}
else if (select && select->quick)
{
DBUG_PRINT("info",("using rr_quick"));
- info->read_record=rr_quick;
+ info->read_record_func= rr_quick;
}
else if (filesort && filesort->record_pointers)
{
DBUG_PRINT("info",("using record_pointers"));
- if (table->file->ha_rnd_init_with_error(0))
+ 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= (addon_field ?
- rr_unpack_from_buffer : rr_from_pointers);
+ info->read_record_func=
+ addon_field ? rr_unpack_from_buffer : rr_from_pointers;
}
else if (table->file->keyread_enabled())
{
int error;
- info->read_record= rr_index_first;
+ info->read_record_func= rr_index_first;
if (!table->file->inited &&
- (error= table->file->ha_index_init(table->file->keyread, 1)))
+ unlikely((error= table->file->ha_index_init(table->file->keyread, 1))))
{
if (print_error)
table->file->print_error(error, MYF(0));
@@ -294,8 +290,8 @@ bool init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
else
{
DBUG_PRINT("info",("using rr_sequential"));
- info->read_record=rr_sequential;
- if (table->file->ha_rnd_init_with_error(1))
+ info->read_record_func= rr_sequential;
+ if (unlikely(table->file->ha_rnd_init_with_error(1)))
DBUG_RETURN(1);
/* We can use record cache if we don't update dynamic length tables */
if (!table->no_cache &&
@@ -327,7 +323,7 @@ void end_read_record(READ_RECORD *info)
{
if (info->table->is_created())
(void) info->table->file->extra(HA_EXTRA_NO_CACHE);
- if (info->read_record != rr_quick) // otherwise quick_range does it
+ if (info->read_record_func != rr_quick) // otherwise quick_range does it
(void) info->table->file->ha_index_or_rnd_end();
info->table=0;
}
@@ -372,11 +368,8 @@ static int rr_quick(READ_RECORD *info)
int tmp;
while ((tmp= info->select->quick->get_next()))
{
- if (info->thd->killed || (tmp != HA_ERR_RECORD_DELETED))
- {
- tmp= rr_handle_error(info, tmp);
- break;
- }
+ tmp= rr_handle_error(info, tmp);
+ break;
}
return tmp;
}
@@ -405,8 +398,8 @@ static int rr_index_first(READ_RECORD *info)
return tmp;
}
- tmp= info->table->file->ha_index_first(info->record);
- info->read_record= rr_index;
+ tmp= info->table->file->ha_index_first(info->record());
+ info->read_record_func= rr_index;
if (tmp)
tmp= rr_handle_error(info, tmp);
return tmp;
@@ -428,8 +421,8 @@ static int rr_index_first(READ_RECORD *info)
static int rr_index_last(READ_RECORD *info)
{
- int tmp= info->table->file->ha_index_last(info->record);
- info->read_record= rr_index_desc;
+ int tmp= info->table->file->ha_index_last(info->record());
+ info->read_record_func= rr_index_desc;
if (tmp)
tmp= rr_handle_error(info, tmp);
return tmp;
@@ -454,7 +447,7 @@ static int rr_index_last(READ_RECORD *info)
static int rr_index(READ_RECORD *info)
{
- int tmp= info->table->file->ha_index_next(info->record);
+ int tmp= info->table->file->ha_index_next(info->record());
if (tmp)
tmp= rr_handle_error(info, tmp);
return tmp;
@@ -479,7 +472,7 @@ static int rr_index(READ_RECORD *info)
static int rr_index_desc(READ_RECORD *info)
{
- int tmp= info->table->file->ha_index_prev(info->record);
+ int tmp= info->table->file->ha_index_prev(info->record());
if (tmp)
tmp= rr_handle_error(info, tmp);
return tmp;
@@ -489,17 +482,10 @@ static int rr_index_desc(READ_RECORD *info)
int rr_sequential(READ_RECORD *info)
{
int tmp;
- while ((tmp= info->table->file->ha_rnd_next(info->record)))
+ while ((tmp= info->table->file->ha_rnd_next(info->record())))
{
- /*
- rnd_next can return RECORD_DELETED for MyISAM when one thread is
- reading and another deleting without locks.
- */
- if (info->thd->killed || (tmp != HA_ERR_RECORD_DELETED))
- {
- tmp= rr_handle_error(info, tmp);
- break;
- }
+ tmp= rr_handle_error(info, tmp);
+ break;
}
return tmp;
}
@@ -512,11 +498,10 @@ static int rr_from_tempfile(READ_RECORD *info)
{
if (my_b_read(info->io_cache,info->ref_pos,info->ref_length))
return -1; /* End of file */
- if (!(tmp= info->table->file->ha_rnd_pos(info->record,info->ref_pos)))
+ if (!(tmp= info->table->file->ha_rnd_pos(info->record(), info->ref_pos)))
break;
/* The following is extremely unlikely to happen */
- if (tmp == HA_ERR_RECORD_DELETED ||
- (tmp == HA_ERR_KEY_NOT_FOUND && info->ignore_not_found_rows))
+ if (tmp == HA_ERR_KEY_NOT_FOUND)
continue;
tmp= rr_handle_error(info, tmp);
break;
@@ -563,12 +548,11 @@ int rr_from_pointers(READ_RECORD *info)
cache_pos= info->cache_pos;
info->cache_pos+= info->ref_length;
- if (!(tmp= info->table->file->ha_rnd_pos(info->record,cache_pos)))
+ if (!(tmp= info->table->file->ha_rnd_pos(info->record(), cache_pos)))
break;
/* The following is extremely unlikely to happen */
- if (tmp == HA_ERR_RECORD_DELETED ||
- (tmp == HA_ERR_KEY_NOT_FOUND && info->ignore_not_found_rows))
+ if (tmp == HA_ERR_KEY_NOT_FOUND)
continue;
tmp= rr_handle_error(info, tmp);
break;
@@ -603,33 +587,34 @@ static int rr_unpack_from_buffer(READ_RECORD *info)
}
/* cacheing of records from a database */
+static const uint STRUCT_LENGTH= 3 + MAX_REFLENGTH;
+
static int init_rr_cache(THD *thd, READ_RECORD *info)
{
- uint rec_cache_size;
+ uint rec_cache_size, cache_records;
DBUG_ENTER("init_rr_cache");
- info->struct_length= 3+MAX_REFLENGTH;
info->reclength= ALIGN_SIZE(info->table->s->reclength+1);
- if (info->reclength < info->struct_length)
- info->reclength= ALIGN_SIZE(info->struct_length);
+ if (info->reclength < STRUCT_LENGTH)
+ info->reclength= ALIGN_SIZE(STRUCT_LENGTH);
info->error_offset= info->table->s->reclength;
- info->cache_records= (thd->variables.read_rnd_buff_size /
- (info->reclength+info->struct_length));
- rec_cache_size= info->cache_records*info->reclength;
- info->rec_cache_size= info->cache_records*info->ref_length;
+ cache_records= thd->variables.read_rnd_buff_size /
+ (info->reclength + STRUCT_LENGTH);
+ rec_cache_size= cache_records * info->reclength;
+ info->rec_cache_size= cache_records * info->ref_length;
// We have to allocate one more byte to use uint3korr (see comments for it)
- if (info->cache_records <= 2 ||
- !(info->cache=(uchar*) my_malloc_lock(rec_cache_size+info->cache_records*
- info->struct_length+1,
- MYF(MY_THREAD_SPECIFIC))))
+ if (cache_records <= 2 ||
+ !(info->cache= (uchar*) my_malloc_lock(rec_cache_size + cache_records *
+ STRUCT_LENGTH + 1,
+ MYF(MY_THREAD_SPECIFIC))))
DBUG_RETURN(1);
#ifdef HAVE_valgrind
// Avoid warnings in qsort
- bzero(info->cache,rec_cache_size+info->cache_records* info->struct_length+1);
+ bzero(info->cache, rec_cache_size + cache_records * STRUCT_LENGTH + 1);
#endif
- DBUG_PRINT("info",("Allocated buffert for %d records",info->cache_records));
+ DBUG_PRINT("info", ("Allocated buffer for %d records", cache_records));
info->read_positions=info->cache+rec_cache_size;
info->cache_pos=info->cache_end=info->cache;
DBUG_RETURN(0);
@@ -649,7 +634,7 @@ static int rr_from_cache(READ_RECORD *info)
{
if (info->cache_pos != info->cache_end)
{
- if (info->cache_pos[info->error_offset])
+ if (unlikely(info->cache_pos[info->error_offset]))
{
shortget(error,info->cache_pos);
if (info->print_error)
@@ -658,7 +643,7 @@ static int rr_from_cache(READ_RECORD *info)
else
{
error=0;
- memcpy(info->record,info->cache_pos,
+ memcpy(info->record(), info->cache_pos,
(size_t) info->table->s->reclength);
}
info->cache_pos+=info->reclength;
@@ -684,8 +669,7 @@ static int rr_from_cache(READ_RECORD *info)
int3store(ref_position,(long) i);
ref_position+=3;
}
- my_qsort(info->read_positions, length, info->struct_length,
- (qsort_cmp) rr_cmp);
+ my_qsort(info->read_positions, length, STRUCT_LENGTH, (qsort_cmp) rr_cmp);
position=info->read_positions;
for (i=0 ; i < length ; i++)
@@ -695,7 +679,8 @@ static int rr_from_cache(READ_RECORD *info)
record=uint3korr(position);
position+=3;
record_pos=info->cache+record*info->reclength;
- if ((error=(int16) info->table->file->ha_rnd_pos(record_pos,info->ref_pos)))
+ if (unlikely((error= (int16) info->table->file->
+ ha_rnd_pos(record_pos,info->ref_pos))))
{
record_pos[info->error_offset]=1;
shortstore(record_pos,error);
diff --git a/sql/records.h b/sql/records.h
index dd63d3608bb..74f64cd286f 100644
--- a/sql/records.h
+++ b/sql/records.h
@@ -19,9 +19,10 @@
#pragma interface /* gcc class implementation */
#endif
+#include "table.h"
+
struct st_join_table;
class handler;
-struct TABLE;
class THD;
class SQL_SELECT;
class Copy_field;
@@ -53,24 +54,22 @@ struct READ_RECORD
typedef int (*Setup_func)(struct st_join_table*);
TABLE *table; /* Head-form */
- //handler *file;
- TABLE **forms; /* head and ref forms */
Unlock_row_func unlock_row;
- Read_func read_record;
+ Read_func read_record_func;
THD *thd;
SQL_SELECT *select;
- uint cache_records;
- uint ref_length,struct_length,reclength,rec_cache_size,error_offset;
- uint index;
+ uint ref_length, reclength, rec_cache_size, error_offset;
uchar *ref_pos; /* pointer to form->refpos */
- uchar *record;
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 */
struct st_io_cache *io_cache;
- bool print_error, ignore_not_found_rows;
+ 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]; }
+
/*
SJ-Materialization runtime may need to read fields from the materialized
table and unpack them into original table fields:
diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc
index 7a3725a7687..5b5b4931582 100644
--- a/sql/repl_failsafe.cc
+++ b/sql/repl_failsafe.cc
@@ -24,7 +24,7 @@
functions like register_slave()) are working.
*/
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "sql_parse.h" // check_access
#ifdef HAVE_REPLICATION
@@ -115,7 +115,7 @@ void unregister_slave(THD* thd, bool only_mine, bool need_mutex)
1 Error. Error message sent to client
*/
-int register_slave(THD* thd, uchar* packet, uint packet_length)
+int register_slave(THD* thd, uchar* packet, size_t packet_length)
{
int res;
SLAVE_INFO *si;
diff --git a/sql/repl_failsafe.h b/sql/repl_failsafe.h
index d11cfae1108..90415532c7d 100644
--- a/sql/repl_failsafe.h
+++ b/sql/repl_failsafe.h
@@ -19,7 +19,7 @@
#ifdef HAVE_REPLICATION
#include "mysql.h"
-#include "my_sys.h"
+#include <my_sys.h>
#include "slave.h"
typedef enum {RPL_AUTH_MASTER=0,RPL_IDLE_SLAVE,RPL_ACTIVE_SLAVE,
@@ -41,7 +41,7 @@ extern HASH slave_list;
bool show_slave_hosts(THD* thd);
void init_slave_list();
void end_slave_list();
-int register_slave(THD* thd, uchar* packet, uint packet_length);
+int register_slave(THD* thd, uchar* packet, size_t packet_length);
void unregister_slave(THD* thd, bool only_mine, bool need_mutex);
#endif /* HAVE_REPLICATION */
diff --git a/sql/replication.h b/sql/replication.h
index cd3d05d2159..49d896ff1a7 100644
--- a/sql/replication.h
+++ b/sql/replication.h
@@ -18,16 +18,14 @@
/***************************************************************************
NOTE: plugin locking.
- This API was created specifically for the semisync plugin and its locking
- logic is also matches semisync plugin usage pattern. In particular, a plugin
- is locked on Binlog_transmit_observer::transmit_start and is unlocked after
- Binlog_transmit_observer::transmit_stop. All other master observable events
- happen between these two and don't lock the plugin at all. This works well
- for the semisync_master plugin.
+
+ The plugin is locked on Binlog_transmit_observer::transmit_start and is
+ unlocked after Binlog_transmit_observer::transmit_stop. All other
+ master observable events happen between these two and don't lock the
+ plugin at all.
Also a plugin is locked on Binlog_relay_IO_observer::thread_start
- and unlocked after Binlog_relay_IO_observer::thread_stop. This works well for
- the semisync_slave plugin.
+ and unlocked after Binlog_relay_IO_observer::thread_stop.
***************************************************************************/
#include <mysql.h>
diff --git a/sql/rpl_filter.cc b/sql/rpl_filter.cc
index 4c4b56f591f..b167b849923 100644
--- a/sql/rpl_filter.cc
+++ b/sql/rpl_filter.cc
@@ -13,7 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "mysqld.h" // system_charset_info
#include "rpl_filter.h"
@@ -105,9 +105,9 @@ Rpl_filter::tables_ok(const char* db, TABLE_LIST* tables)
if (!tables->updating)
continue;
some_tables_updating= 1;
- end= strmov(hash_key, tables->db ? tables->db : db);
+ end= strmov(hash_key, tables->db.str ? tables->db.str : db);
*end++= '.';
- len= (uint) (strmov(end, tables->table_name) - hash_key);
+ len= (uint) (strmov(end, tables->table_name.str) - hash_key);
if (do_table_inited) // if there are any do's
{
if (my_hash_search(&do_table, (uchar*) hash_key, len))
diff --git a/sql/rpl_gtid.cc b/sql/rpl_gtid.cc
index 43fdeec9e6a..a8b39d6d15b 100644
--- a/sql/rpl_gtid.cc
+++ b/sql/rpl_gtid.cc
@@ -17,24 +17,24 @@
/* Definitions for MariaDB global transaction ID (GTID). */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
-#include "my_sys.h"
#include "unireg.h"
-#include "my_global.h"
+#include "mariadb.h"
#include "sql_base.h"
#include "sql_parse.h"
#include "key.h"
#include "rpl_gtid.h"
#include "rpl_rli.h"
+#include "slave.h"
#include "log_event.h"
-const LEX_STRING rpl_gtid_slave_state_table_name=
- { C_STRING_WITH_LEN("gtid_slave_pos") };
+const LEX_CSTRING rpl_gtid_slave_state_table_name=
+ { STRING_WITH_LEN("gtid_slave_pos") };
void
-rpl_slave_state::update_state_hash(uint64 sub_id, rpl_gtid *gtid,
+rpl_slave_state::update_state_hash(uint64 sub_id, rpl_gtid *gtid, void *hton,
rpl_group_info *rgi)
{
int err;
@@ -46,7 +46,7 @@ rpl_slave_state::update_state_hash(uint64 sub_id, rpl_gtid *gtid,
it is even committed.
*/
mysql_mutex_lock(&LOCK_slave_state);
- err= update(gtid->domain_id, gtid->server_id, sub_id, gtid->seq_no, rgi);
+ err= update(gtid->domain_id, gtid->server_id, sub_id, gtid->seq_no, hton, rgi);
mysql_mutex_unlock(&LOCK_slave_state);
if (err)
{
@@ -75,12 +75,14 @@ rpl_slave_state::record_and_update_gtid(THD *thd, rpl_group_info *rgi)
if (rgi->gtid_pending)
{
uint64 sub_id= rgi->gtid_sub_id;
+ void *hton= NULL;
+
rgi->gtid_pending= false;
if (rgi->gtid_ignore_duplicate_state!=rpl_group_info::GTID_DUPLICATE_IGNORE)
{
- if (record_gtid(thd, &rgi->current_gtid, sub_id, NULL, false))
+ if (record_gtid(thd, &rgi->current_gtid, sub_id, NULL, false, &hton))
DBUG_RETURN(1);
- update_state_hash(sub_id, &rgi->current_gtid, rgi);
+ update_state_hash(sub_id, &rgi->current_gtid, hton, rgi);
}
rgi->gtid_ignore_duplicate_state= rpl_group_info::GTID_DUPLICATE_NULL;
}
@@ -166,9 +168,8 @@ rpl_slave_state::check_duplicate_gtid(rpl_gtid *gtid, rpl_group_info *rgi)
break;
}
thd= rgi->thd;
- if (thd->check_killed())
+ if (unlikely(thd->check_killed()))
{
- thd->send_kill_message();
res= -1;
break;
}
@@ -244,7 +245,7 @@ rpl_slave_state_free_element(void *arg)
rpl_slave_state::rpl_slave_state()
- : last_sub_id(0), loaded(false)
+ : last_sub_id(0), gtid_pos_tables(0), loaded(false)
{
mysql_mutex_init(key_LOCK_slave_state, &LOCK_slave_state,
MY_MUTEX_INIT_SLOW);
@@ -256,6 +257,7 @@ rpl_slave_state::rpl_slave_state()
rpl_slave_state::~rpl_slave_state()
{
+ free_gtid_pos_tables((struct gtid_pos_table *)gtid_pos_tables);
truncate_hash();
my_hash_free(&hash);
delete_dynamic(&gtid_sort_array);
@@ -287,11 +289,12 @@ rpl_slave_state::truncate_hash()
int
rpl_slave_state::update(uint32 domain_id, uint32 server_id, uint64 sub_id,
- uint64 seq_no, rpl_group_info *rgi)
+ uint64 seq_no, void *hton, rpl_group_info *rgi)
{
element *elem= NULL;
list_element *list_elem= NULL;
+ DBUG_ASSERT(hton || !loaded);
if (!(elem= get_element(domain_id)))
return 1;
@@ -314,7 +317,7 @@ rpl_slave_state::update(uint32 domain_id, uint32 server_id, uint64 sub_id,
{
if (rgi->gtid_ignore_duplicate_state==rpl_group_info::GTID_DUPLICATE_OWNER)
{
-#ifndef DBUG_OFF
+#ifdef DBUG_ASSERT_EXISTS
Relay_log_info *rli= rgi->rli;
#endif
uint32 count= elem->owner_count;
@@ -340,6 +343,7 @@ rpl_slave_state::update(uint32 domain_id, uint32 server_id, uint64 sub_id,
list_elem->server_id= server_id;
list_elem->sub_id= sub_id;
list_elem->seq_no= seq_no;
+ list_elem->hton= hton;
elem->add(list_elem);
if (last_sub_id < sub_id)
@@ -410,10 +414,7 @@ rpl_slave_state::truncate_state_table(THD *thd)
int err= 0;
tmp_disable_binlog(thd);
- tlist.init_one_table(STRING_WITH_LEN("mysql"),
- rpl_gtid_slave_state_table_name.str,
- rpl_gtid_slave_state_table_name.length,
- NULL, TL_WRITE);
+ 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)))
{
tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED, "mysql",
@@ -441,17 +442,17 @@ rpl_slave_state::truncate_state_table(THD *thd)
static const TABLE_FIELD_TYPE mysql_rpl_slave_state_coltypes[4]= {
- { { C_STRING_WITH_LEN("domain_id") },
- { C_STRING_WITH_LEN("int(10) unsigned") },
+ { { STRING_WITH_LEN("domain_id") },
+ { STRING_WITH_LEN("int(10) unsigned") },
{NULL, 0} },
- { { C_STRING_WITH_LEN("sub_id") },
- { C_STRING_WITH_LEN("bigint(20) unsigned") },
+ { { STRING_WITH_LEN("sub_id") },
+ { STRING_WITH_LEN("bigint(20) unsigned") },
{NULL, 0} },
- { { C_STRING_WITH_LEN("server_id") },
- { C_STRING_WITH_LEN("int(10) unsigned") },
+ { { STRING_WITH_LEN("server_id") },
+ { STRING_WITH_LEN("int(10) unsigned") },
{NULL, 0} },
- { { C_STRING_WITH_LEN("seq_no") },
- { C_STRING_WITH_LEN("bigint(20) unsigned") },
+ { { STRING_WITH_LEN("seq_no") },
+ { STRING_WITH_LEN("bigint(20) unsigned") },
{NULL, 0} },
};
@@ -482,6 +483,94 @@ gtid_check_rpl_slave_state_table(TABLE *table)
/*
+ Attempt to find a mysql.gtid_slave_posXXX table that has a storage engine
+ that is already in use by the current transaction, if any.
+*/
+void
+rpl_slave_state::select_gtid_pos_table(THD *thd, LEX_CSTRING *out_tablename)
+{
+ struct gtid_pos_table *list, *table_entry, *default_entry;
+
+ /*
+ See comments on rpl_slave_state::gtid_pos_tables for rules around proper
+ access to the list.
+ */
+ list= (struct gtid_pos_table *)
+ my_atomic_loadptr_explicit(&gtid_pos_tables, MY_MEMORY_ORDER_ACQUIRE);
+
+ Ha_trx_info *ha_info;
+ uint count = 0;
+ for (ha_info= thd->transaction.all.ha_list; ha_info; ha_info= ha_info->next())
+ {
+ void *trx_hton= ha_info->ht();
+ table_entry= list;
+
+ if (!ha_info->is_trx_read_write() || trx_hton == binlog_hton)
+ continue;
+ while (table_entry)
+ {
+ if (table_entry->table_hton == trx_hton)
+ {
+ if (likely(table_entry->state == GTID_POS_AVAILABLE))
+ {
+ *out_tablename= table_entry->table_name;
+ /*
+ Check if this is a cross-engine transaction, so we can correctly
+ maintain the rpl_transactions_multi_engine status variable.
+ */
+ if (count >= 1)
+ statistic_increment(rpl_transactions_multi_engine, LOCK_status);
+ else
+ {
+ for (;;)
+ {
+ ha_info= ha_info->next();
+ if (!ha_info)
+ break;
+ if (ha_info->is_trx_read_write() && ha_info->ht() != binlog_hton)
+ {
+ statistic_increment(rpl_transactions_multi_engine, LOCK_status);
+ break;
+ }
+ }
+ }
+ return;
+ }
+ /*
+ This engine is marked to automatically create the table.
+ We cannot easily do this here (possibly in the middle of a
+ transaction). But we can request the slave background thread
+ to create it, and in a short while it should become available
+ for following transactions.
+ */
+#ifdef HAVE_REPLICATION
+ slave_background_gtid_pos_create_request(table_entry);
+#endif
+ break;
+ }
+ table_entry= table_entry->next;
+ }
+ ++count;
+ }
+ /*
+ If we cannot find any table whose engine matches an engine that is
+ already active in the transaction, or if there is no current transaction
+ engines available, we return the default gtid_slave_pos table.
+ */
+ default_entry= (struct gtid_pos_table *)
+ my_atomic_loadptr_explicit(&default_gtid_pos_table, MY_MEMORY_ORDER_ACQUIRE);
+ *out_tablename= default_entry->table_name;
+ /* Record in status that we failed to find a suitable gtid_pos table. */
+ if (count > 0)
+ {
+ statistic_increment(transactions_gtid_foreign_engine, LOCK_status);
+ if (count > 1)
+ statistic_increment(rpl_transactions_multi_engine, LOCK_status);
+ }
+}
+
+
+/*
Write a gtid to the replication slave state table.
gtid The global transaction id for this event group.
@@ -497,19 +586,24 @@ gtid_check_rpl_slave_state_table(TABLE *table)
*/
int
rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
- rpl_group_info *rgi, bool in_statement)
+ rpl_group_info *rgi, bool in_statement,
+ void **out_hton)
{
TABLE_LIST tlist;
- int err= 0;
+ int err= 0, not_sql_thread;
bool table_opened= false;
TABLE *table;
- list_element *elist= 0, *cur, *next;
+ list_element *delete_list= 0, *next, *cur, **next_ptr_ptr, **best_ptr_ptr;
+ uint64 best_sub_id;
element *elem;
ulonglong thd_saved_option= thd->variables.option_bits;
Query_tables_list lex_backup;
wait_for_commit* suspended_wfc;
+ void *hton= NULL;
+ LEX_CSTRING gtid_pos_table_name;
DBUG_ENTER("record_gtid");
+ *out_hton= NULL;
if (unlikely(!loaded))
{
/*
@@ -525,6 +619,25 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
if (!in_statement)
thd->reset_for_next_command();
+ /*
+ Only the SQL thread can call select_gtid_pos_table without a mutex
+ Other threads needs to use a mutex and take into account that the
+ result may change during execution, so we have to make a copy.
+ */
+
+ if ((not_sql_thread= (thd->system_thread != SYSTEM_THREAD_SLAVE_SQL)))
+ mysql_mutex_lock(&LOCK_slave_state);
+ select_gtid_pos_table(thd, &gtid_pos_table_name);
+ if (not_sql_thread)
+ {
+ LEX_CSTRING *tmp= thd->make_clex_string(gtid_pos_table_name.str,
+ gtid_pos_table_name.length);
+ mysql_mutex_unlock(&LOCK_slave_state);
+ if (!tmp)
+ DBUG_RETURN(1);
+ gtid_pos_table_name= *tmp;
+ }
+
DBUG_EXECUTE_IF("gtid_inject_record_gtid",
{
my_error(ER_CANNOT_UPDATE_GTID_STATE, MYF(0));
@@ -554,14 +667,12 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
*/
suspended_wfc= thd->suspend_subsequent_commits();
thd->lex->reset_n_backup_query_tables_list(&lex_backup);
- tlist.init_one_table(STRING_WITH_LEN("mysql"),
- rpl_gtid_slave_state_table_name.str,
- rpl_gtid_slave_state_table_name.length,
- NULL, TL_WRITE);
+ tlist.init_one_table(&MYSQL_SCHEMA_NAME, &gtid_pos_table_name, NULL, TL_WRITE);
if ((err= open_and_lock_tables(thd, &tlist, FALSE, 0)))
goto end;
table_opened= true;
table= tlist.table;
+ hton= table->s->db_type();
if ((err= gtid_check_rpl_slave_state_table(table)))
goto end;
@@ -597,6 +708,7 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
table->file->print_error(err, MYF(0));
goto end;
}
+ *out_hton= hton;
if(opt_bin_log &&
(err= mysql_bin_log.bump_seq_no_counter_if_needed(gtid->domain_id,
@@ -614,36 +726,62 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
err= 1;
goto end;
}
- if ((elist= elem->grab_list()) != NULL)
+
+ /* Now pull out all GTIDs that were recorded in this engine. */
+ delete_list = NULL;
+ next_ptr_ptr= &elem->list;
+ cur= elem->list;
+ best_sub_id= 0;
+ best_ptr_ptr= NULL;
+ while (cur)
{
- /* Delete any old stuff, but keep around the most recent one. */
- uint64 best_sub_id= elist->sub_id;
- list_element **best_ptr_ptr= &elist;
- cur= elist;
- while ((next= cur->next))
+ list_element *next= cur->next;
+ if (cur->hton == hton)
{
- if (next->sub_id > best_sub_id)
+ /* Belongs to same engine, so move it to the delete list. */
+ cur->next= delete_list;
+ delete_list= cur;
+ if (cur->sub_id > best_sub_id)
{
- best_sub_id= next->sub_id;
+ best_sub_id= cur->sub_id;
+ best_ptr_ptr= &delete_list;
+ }
+ else if (best_ptr_ptr == &delete_list)
best_ptr_ptr= &cur->next;
+ }
+ else
+ {
+ /* Another engine, leave it in the list. */
+ if (cur->sub_id > best_sub_id)
+ {
+ best_sub_id= cur->sub_id;
+ /* Current best is not on the delete list. */
+ best_ptr_ptr= NULL;
}
- cur= next;
+ *next_ptr_ptr= cur;
+ next_ptr_ptr= &cur->next;
}
- /*
- Delete the highest sub_id element from the old list, and put it back as
- the single-element new list.
- */
+ cur= next;
+ }
+ *next_ptr_ptr= NULL;
+ /*
+ If the highest sub_id element is on the delete list, put it back on the
+ original list, to preserve the highest sub_id element in the table for
+ GTID position recovery.
+ */
+ if (best_ptr_ptr)
+ {
cur= *best_ptr_ptr;
*best_ptr_ptr= cur->next;
- cur->next= NULL;
+ cur->next= elem->list;
elem->list= cur;
}
mysql_mutex_unlock(&LOCK_slave_state);
- if (!elist)
+ if (!delete_list)
goto end;
- /* Now delete any already committed rows. */
+ /* Now delete any already committed GTIDs. */
bitmap_set_bit(table->read_set, table->field[0]->field_index);
bitmap_set_bit(table->read_set, table->field[1]->field_index);
@@ -652,7 +790,7 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
table->file->print_error(err, MYF(0));
goto end;
}
- cur = elist;
+ cur = delete_list;
while (cur)
{
uchar key_buffer[4+8];
@@ -697,13 +835,13 @@ end:
if (err || (err= ha_commit_trans(thd, FALSE)))
{
/*
- If error, we need to put any remaining elist back into the HASH so we
- can do another delete attempt later.
+ If error, we need to put any remaining delete_list back into the HASH
+ so we can do another delete attempt later.
*/
- if (elist)
+ if (delete_list)
{
- put_back_list(gtid->domain_id, elist);
- elist = 0;
+ put_back_list(gtid->domain_id, delete_list);
+ delete_list = 0;
}
ha_rollback_trans(thd, FALSE);
@@ -721,14 +859,14 @@ end:
are rolled back and retried after record_gtid().
*/
#ifdef HAVE_REPLICATION
- rgi->pending_gtid_deletes_save(gtid->domain_id, elist);
+ rgi->pending_gtid_deletes_save(gtid->domain_id, delete_list);
#endif
}
else
{
thd->mdl_context.release_transactional_locks();
#ifdef HAVE_REPLICATION
- rpl_group_info::pending_gtid_deletes_free(elist);
+ rpl_group_info::pending_gtid_deletes_free(delete_list);
#endif
}
}
@@ -1014,24 +1152,24 @@ rpl_slave_state::domain_to_gtid(uint32 domain_id, rpl_gtid *out_gtid)
Returns 0 on ok, non-zero on parse error.
*/
static int
-gtid_parser_helper(char **ptr, char *end, rpl_gtid *out_gtid)
+gtid_parser_helper(const char **ptr, const char *end, rpl_gtid *out_gtid)
{
char *q;
- char *p= *ptr;
+ const char *p= *ptr;
uint64 v1, v2, v3;
int err= 0;
- q= end;
+ q= (char*) end;
v1= (uint64)my_strtoll10(p, &q, &err);
if (err != 0 || v1 > (uint32)0xffffffff || q == end || *q != '-')
return 1;
p= q+1;
- q= end;
+ q= (char*) end;
v2= (uint64)my_strtoll10(p, &q, &err);
if (err != 0 || v2 > (uint32)0xffffffff || q == end || *q != '-')
return 1;
p= q+1;
- q= end;
+ q= (char*) end;
v3= (uint64)my_strtoll10(p, &q, &err);
if (err != 0)
return 1;
@@ -1047,8 +1185,8 @@ gtid_parser_helper(char **ptr, char *end, rpl_gtid *out_gtid)
rpl_gtid *
gtid_parse_string_to_list(const char *str, size_t str_len, uint32 *out_len)
{
- char *p= const_cast<char *>(str);
- char *end= p + str_len;
+ const char *p= const_cast<char *>(str);
+ const char *end= p + str_len;
uint32 len= 0, alloc_len= 5;
rpl_gtid *list= NULL;
@@ -1093,10 +1231,10 @@ gtid_parse_string_to_list(const char *str, size_t str_len, uint32 *out_len)
Returns 0 if ok, non-zero if error.
*/
int
-rpl_slave_state::load(THD *thd, char *state_from_master, size_t len,
+rpl_slave_state::load(THD *thd, const char *state_from_master, size_t len,
bool reset, bool in_statement)
{
- char *end= state_from_master + len;
+ const char *end= state_from_master + len;
if (reset)
{
@@ -1110,11 +1248,12 @@ rpl_slave_state::load(THD *thd, char *state_from_master, size_t len,
{
rpl_gtid gtid;
uint64 sub_id;
+ void *hton= NULL;
if (gtid_parser_helper(&state_from_master, end, &gtid) ||
!(sub_id= next_sub_id(gtid.domain_id)) ||
- record_gtid(thd, &gtid, sub_id, NULL, in_statement) ||
- update(gtid.domain_id, gtid.server_id, sub_id, gtid.seq_no, NULL))
+ record_gtid(thd, &gtid, sub_id, NULL, in_statement, &hton) ||
+ update(gtid.domain_id, gtid.server_id, sub_id, gtid.seq_no, hton, NULL))
return 1;
if (state_from_master == end)
break;
@@ -1148,6 +1287,75 @@ rpl_slave_state::is_empty()
}
+void
+rpl_slave_state::free_gtid_pos_tables(struct rpl_slave_state::gtid_pos_table *list)
+{
+ struct gtid_pos_table *cur, *next;
+
+ cur= list;
+ while (cur)
+ {
+ next= cur->next;
+ my_free(cur);
+ cur= next;
+ }
+}
+
+
+/*
+ Replace the list of available mysql.gtid_slave_posXXX tables with a new list.
+ The caller must be holding LOCK_slave_state. Additionally, this function
+ must only be called while all SQL threads are stopped.
+*/
+void
+rpl_slave_state::set_gtid_pos_tables_list(rpl_slave_state::gtid_pos_table *new_list,
+ rpl_slave_state::gtid_pos_table *default_entry)
+{
+ gtid_pos_table *old_list;
+
+ mysql_mutex_assert_owner(&LOCK_slave_state);
+ old_list= (struct gtid_pos_table *)gtid_pos_tables;
+ my_atomic_storeptr_explicit(&gtid_pos_tables, new_list, MY_MEMORY_ORDER_RELEASE);
+ my_atomic_storeptr_explicit(&default_gtid_pos_table, default_entry,
+ MY_MEMORY_ORDER_RELEASE);
+ free_gtid_pos_tables(old_list);
+}
+
+
+void
+rpl_slave_state::add_gtid_pos_table(rpl_slave_state::gtid_pos_table *entry)
+{
+ mysql_mutex_assert_owner(&LOCK_slave_state);
+ entry->next= (struct gtid_pos_table *)gtid_pos_tables;
+ my_atomic_storeptr_explicit(&gtid_pos_tables, entry, MY_MEMORY_ORDER_RELEASE);
+}
+
+
+struct rpl_slave_state::gtid_pos_table *
+rpl_slave_state::alloc_gtid_pos_table(LEX_CSTRING *table_name, void *hton,
+ rpl_slave_state::gtid_pos_table_state state)
+{
+ 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))
+ {
+ my_error(ER_OUTOFMEMORY, MYF(0), (int)(sizeof(*p) + table_name->length+1));
+ return NULL;
+ }
+ memcpy(allocated_str, table_name->str, table_name->length+1); // Also copy '\0'
+ p->next = NULL;
+ p->table_hton= hton;
+ p->table_name.str= allocated_str;
+ p->table_name.length= table_name->length;
+ p->state= state;
+ return p;
+}
+
+
void rpl_binlog_state::init()
{
my_hash_init(&hash, &my_charset_bin, 32, offsetof(element, domain_id),
@@ -1493,7 +1701,6 @@ rpl_binlog_state::write_to_iocache(IO_CACHE *dest)
mysql_mutex_lock(&LOCK_binlog_state);
for (i= 0; i < hash.records; ++i)
{
- size_t res;
element *e= (element *)my_hash_element(&hash, i);
if (!e->last_gtid)
{
@@ -1513,8 +1720,8 @@ rpl_binlog_state::write_to_iocache(IO_CACHE *dest)
gtid= e->last_gtid;
longlong10_to_str(gtid->seq_no, buf, 10);
- res= my_b_printf(dest, "%u-%u-%s\n", gtid->domain_id, gtid->server_id, buf);
- if (res == (size_t) -1)
+ if (my_b_printf(dest, "%u-%u-%s\n", gtid->domain_id, gtid->server_id,
+ buf))
{
res= 1;
goto end;
@@ -1533,7 +1740,7 @@ rpl_binlog_state::read_from_iocache(IO_CACHE *src)
{
/* 10-digit - 10-digit - 20-digit \n \0 */
char buf[10+1+10+1+20+1+1];
- char *p, *end;
+ const char *p, *end;
rpl_gtid gtid;
int res= 0;
@@ -1945,9 +2152,9 @@ slave_connection_state::~slave_connection_state()
*/
int
-slave_connection_state::load(char *slave_request, size_t len)
+slave_connection_state::load(const char *slave_request, size_t len)
{
- char *p, *end;
+ const char *p, *end;
uchar *rec;
rpl_gtid *gtid;
const entry *e;
@@ -2080,15 +2287,14 @@ void
slave_connection_state::remove(const rpl_gtid *in_gtid)
{
uchar *rec= my_hash_search(&hash, (const uchar *)(&in_gtid->domain_id), 0);
-#ifndef DBUG_OFF
+#ifdef DBUG_ASSERT_EXISTS
bool err;
rpl_gtid *slave_gtid= &((entry *)rec)->gtid;
DBUG_ASSERT(rec /* We should never try to remove not present domain_id. */);
DBUG_ASSERT(slave_gtid->server_id == in_gtid->server_id);
DBUG_ASSERT(slave_gtid->seq_no == in_gtid->seq_no);
+ err=
#endif
-
- IF_DBUG(err=, )
my_hash_delete(&hash, rec);
DBUG_ASSERT(!err);
}
@@ -2429,7 +2635,7 @@ gtid_waiting::wait_for_gtid(THD *thd, rpl_gtid *wait_gtid,
&stage_master_gtid_wait_primary, &old_stage);
do
{
- if (thd->check_killed())
+ if (unlikely(thd->check_killed(1)))
break;
else if (wait_until)
{
@@ -2481,7 +2687,7 @@ gtid_waiting::wait_for_gtid(THD *thd, rpl_gtid *wait_gtid,
&stage_master_gtid_wait, &old_stage);
did_enter_cond= true;
}
- while (!elem.done && !thd->check_killed())
+ while (!elem.done && likely(!thd->check_killed(1)))
{
thd_wait_begin(thd, THD_WAIT_BINLOG);
if (wait_until)
diff --git a/sql/rpl_gtid.h b/sql/rpl_gtid.h
index e4949ff0d39..b3341b20165 100644
--- a/sql/rpl_gtid.h
+++ b/sql/rpl_gtid.h
@@ -23,7 +23,7 @@
/* Definitions for MariaDB global transaction ID (GTID). */
-extern const LEX_STRING rpl_gtid_slave_state_table_name;
+extern const LEX_CSTRING rpl_gtid_slave_state_table_name;
class String;
@@ -120,6 +120,12 @@ struct rpl_slave_state
uint64 sub_id;
uint64 seq_no;
uint32 server_id;
+ /*
+ hton of mysql.gtid_slave_pos* table used to record this GTID.
+ Can be NULL if the gtid table failed to load (eg. missing
+ mysql.gtid_slave_pos table following an upgrade).
+ */
+ void *hton;
};
/* Elements in the HASH that hold the state for one domain_id. */
@@ -163,6 +169,26 @@ struct rpl_slave_state
}
};
+ /* Descriptor for mysql.gtid_slave_posXXX table in specific engine. */
+ enum gtid_pos_table_state {
+ GTID_POS_AUTO_CREATE,
+ GTID_POS_CREATE_REQUESTED,
+ GTID_POS_CREATE_IN_PROGRESS,
+ GTID_POS_AVAILABLE
+ };
+ struct gtid_pos_table {
+ struct gtid_pos_table *next;
+ /*
+ Use a void * here, rather than handlerton *, to make explicit that we
+ are not using the value to access any functionality in the engine. It
+ is just used as an opaque value to identify which engine we are using
+ for each GTID row.
+ */
+ void *table_hton;
+ LEX_CSTRING table_name;
+ uint8 state;
+ };
+
/* Mapping from domain_id to its element. */
HASH hash;
/* Mutex protecting access to the state. */
@@ -171,6 +197,30 @@ struct rpl_slave_state
DYNAMIC_ARRAY gtid_sort_array;
uint64 last_sub_id;
+ /*
+ List of tables available for durably storing the slave GTID position.
+
+ Accesses to this table is protected by LOCK_slave_state. However for
+ efficiency, there is also a provision for read access to it from a running
+ slave without lock.
+
+ An element can be added at the head of a list by storing the new
+ gtid_pos_tables pointer atomically with release semantics, to ensure that
+ the next pointer of the new element is visible to readers of the new list.
+ Other changes (like deleting or replacing elements) must happen only while
+ all SQL driver threads are stopped. LOCK_slave_state must be held in any
+ case.
+
+ The list can be read without lock by an SQL driver thread or worker thread
+ by reading the gtid_pos_tables pointer atomically with acquire semantics,
+ to ensure that it will see the correct next pointer of a new head element.
+
+ The type is struct gtid_pos_table *, but needs to be void * to allow using
+ my_atomic operations without violating C strict aliasing semantics.
+ */
+ void * volatile gtid_pos_tables;
+ /* The default entry in gtid_pos_tables, mysql.gtid_slave_pos. */
+ void * volatile default_gtid_pos_table;
bool loaded;
rpl_slave_state();
@@ -179,27 +229,35 @@ struct rpl_slave_state
void truncate_hash();
ulong count() const { return hash.records; }
int update(uint32 domain_id, uint32 server_id, uint64 sub_id,
- uint64 seq_no, rpl_group_info *rgi);
+ uint64 seq_no, void *hton, rpl_group_info *rgi);
int truncate_state_table(THD *thd);
+ void select_gtid_pos_table(THD *thd, LEX_CSTRING *out_tablename);
int record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
- rpl_group_info *rgi, bool in_statement);
+ rpl_group_info *rgi, bool in_statement, void **out_hton);
uint64 next_sub_id(uint32 domain_id);
int iterate(int (*cb)(rpl_gtid *, void *), void *data,
rpl_gtid *extra_gtids, uint32 num_extra,
bool sort);
int tostring(String *dest, rpl_gtid *extra_gtids, uint32 num_extra);
bool domain_to_gtid(uint32 domain_id, rpl_gtid *out_gtid);
- int load(THD *thd, char *state_from_master, size_t len, bool reset,
+ int load(THD *thd, const char *state_from_master, size_t len, bool reset,
bool in_statement);
bool is_empty();
element *get_element(uint32 domain_id);
int put_back_list(uint32 domain_id, list_element *list);
- void update_state_hash(uint64 sub_id, rpl_gtid *gtid, rpl_group_info *rgi);
+ void update_state_hash(uint64 sub_id, rpl_gtid *gtid, void *hton,
+ rpl_group_info *rgi);
int record_and_update_gtid(THD *thd, struct rpl_group_info *rgi);
int check_duplicate_gtid(rpl_gtid *gtid, rpl_group_info *rgi);
void release_domain_owner(rpl_group_info *rgi);
+ void set_gtid_pos_tables_list(gtid_pos_table *new_list,
+ gtid_pos_table *default_entry);
+ void add_gtid_pos_table(gtid_pos_table *entry);
+ struct gtid_pos_table *alloc_gtid_pos_table(LEX_CSTRING *table_name,
+ void *hton, rpl_slave_state::gtid_pos_table_state state);
+ void free_gtid_pos_tables(struct gtid_pos_table *list);
};
@@ -279,8 +337,12 @@ struct slave_connection_state
rpl_gtid gtid;
uint32 flags;
};
- static const uint32 START_OWN_SLAVE_POS= 0x1;
- static const uint32 START_ON_EMPTY_DOMAIN= 0x2;
+ /* Bits for 'flags' */
+ enum start_flags
+ {
+ START_OWN_SLAVE_POS= 0x1,
+ START_ON_EMPTY_DOMAIN= 0x2
+ };
/* Mapping from domain_id to the entry with GTID requested for that domain. */
HASH hash;
@@ -292,7 +354,7 @@ struct slave_connection_state
~slave_connection_state();
void reset() { my_hash_reset(&hash); }
- int load(char *slave_request, size_t len);
+ int load(const char *slave_request, size_t len);
int load(const rpl_gtid *gtid_list, uint32 count);
int load(rpl_slave_state *state, rpl_gtid *extra_gtids, uint32 num_extra);
rpl_gtid *find(uint32 domain_id);
diff --git a/sql/rpl_handler.cc b/sql/rpl_handler.cc
deleted file mode 100644
index 9582e087672..00000000000
--- a/sql/rpl_handler.cc
+++ /dev/null
@@ -1,553 +0,0 @@
-/* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; version 2 of the License.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-
-#include <my_global.h>
-#include "sql_priv.h"
-#include "unireg.h"
-
-#include "rpl_mi.h"
-#include "sql_repl.h"
-#include "log_event.h"
-#include "rpl_filter.h"
-#include <my_dir.h>
-#include "rpl_handler.h"
-
-Trans_delegate *transaction_delegate;
-Binlog_storage_delegate *binlog_storage_delegate;
-#ifdef HAVE_REPLICATION
-Binlog_transmit_delegate *binlog_transmit_delegate;
-Binlog_relay_IO_delegate *binlog_relay_io_delegate;
-#endif /* HAVE_REPLICATION */
-
-/*
- structure to save transaction log filename and position
-*/
-typedef struct Trans_binlog_info {
- my_off_t log_pos;
- char log_file[FN_REFLEN];
-} Trans_binlog_info;
-
-int get_user_var_int(const char *name,
- long long int *value, int *null_value)
-{
- bool null_val;
- user_var_entry *entry=
- (user_var_entry*) my_hash_search(&current_thd->user_vars,
- (uchar*) name, strlen(name));
- if (!entry)
- return 1;
- *value= entry->val_int(&null_val);
- if (null_value)
- *null_value= null_val;
- return 0;
-}
-
-int get_user_var_real(const char *name,
- double *value, int *null_value)
-{
- bool null_val;
- user_var_entry *entry=
- (user_var_entry*) my_hash_search(&current_thd->user_vars,
- (uchar*) name, strlen(name));
- if (!entry)
- return 1;
- *value= entry->val_real(&null_val);
- if (null_value)
- *null_value= null_val;
- return 0;
-}
-
-int get_user_var_str(const char *name, char *value,
- size_t len, unsigned int precision, int *null_value)
-{
- String str;
- bool null_val;
- user_var_entry *entry=
- (user_var_entry*) my_hash_search(&current_thd->user_vars,
- (uchar*) name, strlen(name));
- if (!entry)
- return 1;
- entry->val_str(&null_val, &str, precision);
- strncpy(value, str.c_ptr(), len);
- if (null_value)
- *null_value= null_val;
- return 0;
-}
-
-int delegates_init()
-{
- static my_aligned_storage<sizeof(Trans_delegate), MY_ALIGNOF(long)> trans_mem;
- static my_aligned_storage<sizeof(Binlog_storage_delegate),
- MY_ALIGNOF(long)> storage_mem;
-#ifdef HAVE_REPLICATION
- static my_aligned_storage<sizeof(Binlog_transmit_delegate),
- MY_ALIGNOF(long)> transmit_mem;
- static my_aligned_storage<sizeof(Binlog_relay_IO_delegate),
- MY_ALIGNOF(long)> relay_io_mem;
-#endif
-
- void *place_trans_mem= trans_mem.data;
- void *place_storage_mem= storage_mem.data;
-
- transaction_delegate= new (place_trans_mem) Trans_delegate;
-
- if (!transaction_delegate->is_inited())
- {
- sql_print_error("Initialization of transaction delegates failed. "
- "Please report a bug.");
- return 1;
- }
-
- binlog_storage_delegate= new (place_storage_mem) Binlog_storage_delegate;
-
- if (!binlog_storage_delegate->is_inited())
- {
- sql_print_error("Initialization binlog storage delegates failed. "
- "Please report a bug.");
- return 1;
- }
-
-#ifdef HAVE_REPLICATION
- void *place_transmit_mem= transmit_mem.data;
- void *place_relay_io_mem= relay_io_mem.data;
-
- binlog_transmit_delegate= new (place_transmit_mem) Binlog_transmit_delegate;
-
- if (!binlog_transmit_delegate->is_inited())
- {
- sql_print_error("Initialization of binlog transmit delegates failed. "
- "Please report a bug.");
- return 1;
- }
-
- binlog_relay_io_delegate= new (place_relay_io_mem) Binlog_relay_IO_delegate;
-
- if (!binlog_relay_io_delegate->is_inited())
- {
- sql_print_error("Initialization binlog relay IO delegates failed. "
- "Please report a bug.");
- return 1;
- }
-#endif
-
- return 0;
-}
-
-void delegates_destroy()
-{
- if (transaction_delegate)
- transaction_delegate->~Trans_delegate();
- if (binlog_storage_delegate)
- binlog_storage_delegate->~Binlog_storage_delegate();
-#ifdef HAVE_REPLICATION
- if (binlog_transmit_delegate)
- binlog_transmit_delegate->~Binlog_transmit_delegate();
- if (binlog_relay_io_delegate)
- binlog_relay_io_delegate->~Binlog_relay_IO_delegate();
-#endif /* HAVE_REPLICATION */
-}
-
-/*
- This macro is used by almost all the Delegate methods to iterate
- over all the observers running given callback function of the
- delegate.
- */
-#define FOREACH_OBSERVER(r, f, do_lock, args) \
- param.server_id= thd->variables.server_id; \
- read_lock(); \
- Observer_info_iterator iter= observer_info_iter(); \
- Observer_info *info= iter++; \
- for (; info; info= iter++) \
- { \
- if (do_lock) plugin_lock(thd, plugin_int_to_ref(info->plugin_int)); \
- if (((Observer *)info->observer)->f \
- && ((Observer *)info->observer)->f args) \
- { \
- r= 1; \
- sql_print_error("Run function '" #f "' in plugin '%s' failed", \
- info->plugin_int->name.str); \
- break; \
- } \
- } \
- unlock();
-
-
-int Trans_delegate::after_commit(THD *thd, bool all)
-{
- Trans_param param;
- Trans_binlog_info *log_info;
- bool is_real_trans= (all || thd->transaction.all.ha_list == 0);
- int ret= 0;
-
- param.flags = is_real_trans ? TRANS_IS_REAL_TRANS : 0;
-
- log_info= thd->semisync_info;
-
- param.log_file= log_info && log_info->log_file[0] ? log_info->log_file : 0;
- param.log_pos= log_info ? log_info->log_pos : 0;
-
- FOREACH_OBSERVER(ret, after_commit, false, (&param));
-
- /*
- This is the end of a real transaction or autocommit statement, we
- can mark the memory unused.
- */
- if (is_real_trans && log_info)
- {
- log_info->log_file[0]= 0;
- log_info->log_pos= 0;
- }
- return ret;
-}
-
-int Trans_delegate::after_rollback(THD *thd, bool all)
-{
- Trans_param param;
- Trans_binlog_info *log_info;
- bool is_real_trans= (all || thd->transaction.all.ha_list == 0);
- int ret= 0;
-
- param.flags = is_real_trans ? TRANS_IS_REAL_TRANS : 0;
-
- log_info= thd->semisync_info;
-
- param.log_file= log_info && log_info->log_file[0] ? log_info->log_file : 0;
- param.log_pos= log_info ? log_info->log_pos : 0;
-
- FOREACH_OBSERVER(ret, after_rollback, false, (&param));
-
- /*
- This is the end of a real transaction or autocommit statement, we
- can mark the memory unused.
- */
- if (is_real_trans && log_info)
- {
- log_info->log_file[0]= 0;
- log_info->log_pos= 0;
- }
- return ret;
-}
-
-int Binlog_storage_delegate::after_flush(THD *thd,
- const char *log_file,
- my_off_t log_pos,
- bool synced,
- bool first_in_group,
- bool last_in_group)
-{
- Binlog_storage_param param;
- Trans_binlog_info *log_info;
- uint32 flags=0;
- int ret= 0;
-
- if (synced)
- flags |= BINLOG_STORAGE_IS_SYNCED;
- if (first_in_group)
- flags|= BINLOG_GROUP_COMMIT_LEADER;
- if (last_in_group)
- flags|= BINLOG_GROUP_COMMIT_TRAILER;
-
- if (!(log_info= thd->semisync_info))
- {
- if(!(log_info=
- (Trans_binlog_info*) my_malloc(sizeof(Trans_binlog_info), MYF(0))))
- return 1;
- thd->semisync_info= log_info;
- }
-
- strmake_buf(log_info->log_file, log_file+dirname_length(log_file));
- log_info->log_pos = log_pos;
-
- FOREACH_OBSERVER(ret, after_flush, false,
- (&param, log_info->log_file, log_info->log_pos, flags));
- return ret;
-}
-
-int Binlog_storage_delegate::after_sync(THD *thd,
- const char *log_file,
- my_off_t log_pos,
- bool first_in_group,
- bool last_in_group)
-{
- Binlog_storage_param param;
- uint32 flags=0;
-
- if (first_in_group)
- flags|= BINLOG_GROUP_COMMIT_LEADER;
- if (last_in_group)
- flags|= BINLOG_GROUP_COMMIT_TRAILER;
-
- int ret= 0;
- FOREACH_OBSERVER(ret, after_sync, false,
- (&param, log_file+dirname_length(log_file), log_pos, flags));
-
- return ret;
-}
-
-#ifdef HAVE_REPLICATION
-int Binlog_transmit_delegate::transmit_start(THD *thd, ushort flags,
- const char *log_file,
- my_off_t log_pos)
-{
- Binlog_transmit_param param;
- param.flags= flags;
-
- int ret= 0;
- FOREACH_OBSERVER(ret, transmit_start, true, (&param, log_file, log_pos));
- return ret;
-}
-
-int Binlog_transmit_delegate::transmit_stop(THD *thd, ushort flags)
-{
- Binlog_transmit_param param;
- param.flags= flags;
-
- int ret= 0;
- FOREACH_OBSERVER(ret, transmit_stop, false, (&param));
- return ret;
-}
-
-int Binlog_transmit_delegate::reserve_header(THD *thd, ushort flags,
- String *packet)
-{
- /* NOTE2ME: Maximum extra header size for each observer, I hope 32
- bytes should be enough for each Observer to reserve their extra
- header. If later found this is not enough, we can increase this
- /HEZX
- */
-#define RESERVE_HEADER_SIZE 32
- unsigned char header[RESERVE_HEADER_SIZE];
- ulong hlen;
- Binlog_transmit_param param;
- param.flags= flags;
- param.server_id= thd->variables.server_id;
-
- int ret= 0;
- read_lock();
- Observer_info_iterator iter= observer_info_iter();
- Observer_info *info= iter++;
- for (; info; info= iter++)
- {
- hlen= 0;
- if (((Observer *)info->observer)->reserve_header
- && ((Observer *)info->observer)->reserve_header(&param,
- header,
- RESERVE_HEADER_SIZE,
- &hlen))
- {
- ret= 1;
- break;
- }
- if (hlen == 0)
- continue;
- if (hlen > RESERVE_HEADER_SIZE || packet->append((char *)header, hlen))
- {
- ret= 1;
- break;
- }
- }
- unlock();
- return ret;
-}
-
-int Binlog_transmit_delegate::before_send_event(THD *thd, ushort flags,
- String *packet,
- const char *log_file,
- my_off_t log_pos)
-{
- Binlog_transmit_param param;
- param.flags= flags;
-
- int ret= 0;
- FOREACH_OBSERVER(ret, before_send_event, false,
- (&param, (uchar *)packet->c_ptr(),
- packet->length(),
- log_file+dirname_length(log_file), log_pos));
- return ret;
-}
-
-int Binlog_transmit_delegate::after_send_event(THD *thd, ushort flags,
- String *packet)
-{
- Binlog_transmit_param param;
- param.flags= flags;
-
- int ret= 0;
- FOREACH_OBSERVER(ret, after_send_event, false,
- (&param, packet->c_ptr(), packet->length()));
- return ret;
-}
-
-int Binlog_transmit_delegate::after_reset_master(THD *thd, ushort flags)
-
-{
- Binlog_transmit_param param;
- param.flags= flags;
-
- int ret= 0;
- FOREACH_OBSERVER(ret, after_reset_master, false, (&param));
- return ret;
-}
-
-void Binlog_relay_IO_delegate::init_param(Binlog_relay_IO_param *param,
- Master_info *mi)
-{
- param->mysql= mi->mysql;
- param->user= mi->user;
- param->host= mi->host;
- param->port= mi->port;
- param->master_log_name= mi->master_log_name;
- param->master_log_pos= mi->master_log_pos;
-}
-
-int Binlog_relay_IO_delegate::thread_start(THD *thd, Master_info *mi)
-{
- Binlog_relay_IO_param param;
- init_param(&param, mi);
-
- int ret= 0;
- FOREACH_OBSERVER(ret, thread_start, true, (&param));
- return ret;
-}
-
-
-int Binlog_relay_IO_delegate::thread_stop(THD *thd, Master_info *mi)
-{
-
- Binlog_relay_IO_param param;
- init_param(&param, mi);
-
- int ret= 0;
- FOREACH_OBSERVER(ret, thread_stop, false, (&param));
- return ret;
-}
-
-int Binlog_relay_IO_delegate::before_request_transmit(THD *thd,
- Master_info *mi,
- ushort flags)
-{
- Binlog_relay_IO_param param;
- init_param(&param, mi);
-
- int ret= 0;
- FOREACH_OBSERVER(ret, before_request_transmit, false, (&param, (uint32)flags));
- return ret;
-}
-
-int Binlog_relay_IO_delegate::after_read_event(THD *thd, Master_info *mi,
- const char *packet, ulong len,
- const char **event_buf,
- ulong *event_len)
-{
- Binlog_relay_IO_param param;
- init_param(&param, mi);
-
- int ret= 0;
- FOREACH_OBSERVER(ret, after_read_event, false,
- (&param, packet, len, event_buf, event_len));
- return ret;
-}
-
-int Binlog_relay_IO_delegate::after_queue_event(THD *thd, Master_info *mi,
- const char *event_buf,
- ulong event_len,
- bool synced)
-{
- Binlog_relay_IO_param param;
- init_param(&param, mi);
-
- uint32 flags=0;
- if (synced)
- flags |= BINLOG_STORAGE_IS_SYNCED;
-
- int ret= 0;
- FOREACH_OBSERVER(ret, after_queue_event, false,
- (&param, event_buf, event_len, flags));
- return ret;
-}
-
-int Binlog_relay_IO_delegate::after_reset_slave(THD *thd, Master_info *mi)
-
-{
- Binlog_relay_IO_param param;
- init_param(&param, mi);
-
- int ret= 0;
- FOREACH_OBSERVER(ret, after_reset_slave, false, (&param));
- return ret;
-}
-#endif /* HAVE_REPLICATION */
-
-int register_trans_observer(Trans_observer *observer, void *p)
-{
- return transaction_delegate->add_observer(observer, (st_plugin_int *)p);
-}
-
-int unregister_trans_observer(Trans_observer *observer, void *p)
-{
- return transaction_delegate->remove_observer(observer, (st_plugin_int *)p);
-}
-
-int register_binlog_storage_observer(Binlog_storage_observer *observer, void *p)
-{
- return binlog_storage_delegate->add_observer(observer, (st_plugin_int *)p);
-}
-
-int unregister_binlog_storage_observer(Binlog_storage_observer *observer, void *p)
-{
- return binlog_storage_delegate->remove_observer(observer, (st_plugin_int *)p);
-}
-
-#ifdef HAVE_REPLICATION
-int register_binlog_transmit_observer(Binlog_transmit_observer *observer, void *p)
-{
- return binlog_transmit_delegate->add_observer(observer, (st_plugin_int *)p);
-}
-
-int unregister_binlog_transmit_observer(Binlog_transmit_observer *observer, void *p)
-{
- return binlog_transmit_delegate->remove_observer(observer, (st_plugin_int *)p);
-}
-
-int register_binlog_relay_io_observer(Binlog_relay_IO_observer *observer, void *p)
-{
- return binlog_relay_io_delegate->add_observer(observer, (st_plugin_int *)p);
-}
-
-int unregister_binlog_relay_io_observer(Binlog_relay_IO_observer *observer, void *p)
-{
- return binlog_relay_io_delegate->remove_observer(observer, (st_plugin_int *)p);
-}
-#else
-int register_binlog_transmit_observer(Binlog_transmit_observer *observer, void *p)
-{
- return 0;
-}
-
-int unregister_binlog_transmit_observer(Binlog_transmit_observer *observer, void *p)
-{
- return 0;
-}
-
-int register_binlog_relay_io_observer(Binlog_relay_IO_observer *observer, void *p)
-{
- return 0;
-}
-
-int unregister_binlog_relay_io_observer(Binlog_relay_IO_observer *observer, void *p)
-{
- return 0;
-}
-#endif /* HAVE_REPLICATION */
diff --git a/sql/rpl_handler.h b/sql/rpl_handler.h
deleted file mode 100644
index b53f93dca38..00000000000
--- a/sql/rpl_handler.h
+++ /dev/null
@@ -1,216 +0,0 @@
-/* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; version 2 of the License.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-
-#ifndef RPL_HANDLER_H
-#define RPL_HANDLER_H
-
-#include "sql_priv.h"
-#include "rpl_mi.h"
-#include "rpl_rli.h"
-#include "sql_plugin.h"
-#include "replication.h"
-
-class Observer_info {
-public:
- void *observer;
- st_plugin_int *plugin_int;
-
- Observer_info(void *ob, st_plugin_int *p)
- :observer(ob), plugin_int(p)
- { }
-};
-
-class Delegate {
-public:
- typedef List<Observer_info> Observer_info_list;
- typedef List_iterator<Observer_info> Observer_info_iterator;
-
- int add_observer(void *observer, st_plugin_int *plugin)
- {
- int ret= FALSE;
- if (!inited)
- return TRUE;
- write_lock();
- Observer_info_iterator iter(observer_info_list);
- Observer_info *info= iter++;
- while (info && info->observer != observer)
- info= iter++;
- if (!info)
- {
- info= new Observer_info(observer, plugin);
- if (!info || observer_info_list.push_back(info, &memroot))
- ret= TRUE;
- }
- else
- ret= TRUE;
- unlock();
- return ret;
- }
-
- int remove_observer(void *observer, st_plugin_int *plugin)
- {
- int ret= FALSE;
- if (!inited)
- return TRUE;
- write_lock();
- Observer_info_iterator iter(observer_info_list);
- Observer_info *info= iter++;
- while (info && info->observer != observer)
- info= iter++;
- if (info)
- {
- iter.remove();
- delete info;
- }
- else
- ret= TRUE;
- unlock();
- return ret;
- }
-
- inline Observer_info_iterator observer_info_iter()
- {
- return Observer_info_iterator(observer_info_list);
- }
-
- inline bool is_empty()
- {
- return observer_info_list.is_empty();
- }
-
- inline int read_lock()
- {
- if (!inited)
- return TRUE;
- return rw_rdlock(&lock);
- }
-
- inline int write_lock()
- {
- if (!inited)
- return TRUE;
- return rw_wrlock(&lock);
- }
-
- inline int unlock()
- {
- if (!inited)
- return TRUE;
- return rw_unlock(&lock);
- }
-
- inline bool is_inited()
- {
- return inited;
- }
-
- Delegate()
- {
- inited= FALSE;
- if (my_rwlock_init(&lock, NULL))
- return;
- init_sql_alloc(&memroot, 1024, 0, MYF(0));
- inited= TRUE;
- }
- ~Delegate()
- {
- inited= FALSE;
- rwlock_destroy(&lock);
- free_root(&memroot, MYF(0));
- }
-
-private:
- Observer_info_list observer_info_list;
- rw_lock_t lock;
- MEM_ROOT memroot;
- bool inited;
-};
-
-class Trans_delegate
- :public Delegate {
-public:
- typedef Trans_observer Observer;
- int before_commit(THD *thd, bool all);
- int before_rollback(THD *thd, bool all);
- int after_commit(THD *thd, bool all);
- int after_rollback(THD *thd, bool all);
-};
-
-class Binlog_storage_delegate
- :public Delegate {
-public:
- typedef Binlog_storage_observer Observer;
- int after_flush(THD *thd, const char *log_file,
- my_off_t log_pos, bool synced,
- bool first_in_group, bool last_in_group);
- int after_sync(THD *thd, const char *log_file, my_off_t log_pos,
- bool first_in_group, bool last_in_group);
-};
-
-#ifdef HAVE_REPLICATION
-class Binlog_transmit_delegate
- :public Delegate {
-public:
- typedef Binlog_transmit_observer Observer;
- int transmit_start(THD *thd, ushort flags,
- const char *log_file, my_off_t log_pos);
- int transmit_stop(THD *thd, ushort flags);
- int reserve_header(THD *thd, ushort flags, String *packet);
- int before_send_event(THD *thd, ushort flags,
- String *packet, const
- char *log_file, my_off_t log_pos );
- int after_send_event(THD *thd, ushort flags,
- String *packet);
- int after_reset_master(THD *thd, ushort flags);
-};
-
-class Binlog_relay_IO_delegate
- :public Delegate {
-public:
- typedef Binlog_relay_IO_observer Observer;
- int thread_start(THD *thd, Master_info *mi);
- int thread_stop(THD *thd, Master_info *mi);
- int before_request_transmit(THD *thd, Master_info *mi, ushort flags);
- int after_read_event(THD *thd, Master_info *mi,
- const char *packet, ulong len,
- const char **event_buf, ulong *event_len);
- int after_queue_event(THD *thd, Master_info *mi,
- const char *event_buf, ulong event_len,
- bool synced);
- int after_reset_slave(THD *thd, Master_info *mi);
-private:
- void init_param(Binlog_relay_IO_param *param, Master_info *mi);
-};
-#endif /* HAVE_REPLICATION */
-
-int delegates_init();
-void delegates_destroy();
-
-extern Trans_delegate *transaction_delegate;
-extern Binlog_storage_delegate *binlog_storage_delegate;
-#ifdef HAVE_REPLICATION
-extern Binlog_transmit_delegate *binlog_transmit_delegate;
-extern Binlog_relay_IO_delegate *binlog_relay_io_delegate;
-#endif /* HAVE_REPLICATION */
-
-/*
- if there is no observers in the delegate, we can return 0
- immediately.
-*/
-#define RUN_HOOK(group, hook, args) \
- (group ##_delegate->is_empty() ? \
- 0 : group ##_delegate->hook args)
-
-#endif /* RPL_HANDLER_H */
diff --git a/sql/rpl_injector.cc b/sql/rpl_injector.cc
index 53999d57bf6..597a357e4e2 100644
--- a/sql/rpl_injector.cc
+++ b/sql/rpl_injector.cc
@@ -13,7 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "rpl_injector.h"
#include "transaction.h"
@@ -105,7 +105,7 @@ int injector::transaction::use_table(server_id_type sid, table tbl)
int error;
- if ((error= check_state(TABLE_STATE)))
+ if (unlikely((error= check_state(TABLE_STATE))))
DBUG_RETURN(error);
server_id_type save_id= m_thd->variables.server_id;
@@ -180,15 +180,18 @@ void injector::new_trans(THD *thd, injector::transaction *ptr)
int injector::record_incident(THD *thd, Incident incident)
{
Incident_log_event ev(thd, incident);
- if (int error= mysql_bin_log.write(&ev))
+ int error;
+ if (unlikely((error= mysql_bin_log.write(&ev))))
return error;
return mysql_bin_log.rotate_and_purge(true);
}
-int injector::record_incident(THD *thd, Incident incident, LEX_STRING const message)
+int injector::record_incident(THD *thd, Incident incident,
+ const LEX_CSTRING *message)
{
Incident_log_event ev(thd, incident, message);
- if (int error= mysql_bin_log.write(&ev))
+ int error;
+ if (unlikely((error= mysql_bin_log.write(&ev))))
return error;
return mysql_bin_log.rotate_and_purge(true);
}
diff --git a/sql/rpl_injector.h b/sql/rpl_injector.h
index cd4568cdb08..ecf16ba28cf 100644
--- a/sql/rpl_injector.h
+++ b/sql/rpl_injector.h
@@ -17,7 +17,6 @@
#define INJECTOR_H
/* Pull in 'byte', 'my_off_t', and 'uint32' */
-#include <my_global.h>
#include <my_bitmap.h>
#include "rpl_constants.h"
@@ -303,7 +302,7 @@ public:
void new_trans(THD *, transaction *);
int record_incident(THD*, Incident incident);
- int record_incident(THD*, Incident incident, LEX_STRING const message);
+ int record_incident(THD*, Incident incident, const LEX_CSTRING *message);
private:
explicit injector();
diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc
index 82a462d742b..87bb995ba9a 100644
--- a/sql/rpl_mi.cc
+++ b/sql/rpl_mi.cc
@@ -14,7 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h> // For HAVE_REPLICATION
+#include "mariadb.h" // For HAVE_REPLICATION
#include "sql_priv.h"
#include <my_dir.h>
#include "rpl_mi.h"
@@ -28,7 +28,7 @@
static void init_master_log_pos(Master_info* mi);
-Master_info::Master_info(LEX_STRING *connection_name_arg,
+Master_info::Master_info(LEX_CSTRING *connection_name_arg,
bool is_slave_recovery)
:Slave_reporting_capability("I/O"),
ssl(0), ssl_verify_server_cert(1), fd(-1), io_thd(0),
@@ -42,8 +42,10 @@ Master_info::Master_info(LEX_STRING *connection_name_arg,
using_gtid(USE_GTID_NO), events_queued_since_last_gtid(0),
gtid_reconnect_event_skip_count(0), gtid_event_seen(false),
in_start_all_slaves(0), in_stop_all_slaves(0), in_flush_all_relay_logs(0),
- users(0), killed(0)
+ users(0), killed(0),
+ total_ddl_groups(0), total_non_trans_groups(0), total_trans_groups(0)
{
+ char *tmp;
host[0] = 0; user[0] = 0; password[0] = 0;
ssl_ca[0]= 0; ssl_capath[0]= 0; ssl_cert[0]= 0;
ssl_cipher[0]= 0; ssl_key[0]= 0;
@@ -55,16 +57,14 @@ Master_info::Master_info(LEX_STRING *connection_name_arg,
*/
connection_name.length= cmp_connection_name.length=
connection_name_arg->length;
- if ((connection_name.str= (char*) my_malloc(connection_name_arg->length*2+2,
- MYF(MY_WME))))
+ if ((connection_name.str= tmp= (char*)
+ my_malloc(connection_name_arg->length*2+2, MYF(MY_WME))))
{
- cmp_connection_name.str= (connection_name.str +
- connection_name_arg->length+1);
- strmake(connection_name.str, connection_name_arg->str,
- connection_name.length);
- memcpy(cmp_connection_name.str, connection_name_arg->str,
- connection_name.length+1);
- my_casedn_str(system_charset_info, cmp_connection_name.str);
+ strmake(tmp, connection_name_arg->str, connection_name.length);
+ tmp+= connection_name_arg->length+1;
+ cmp_connection_name.str= tmp;
+ memcpy(tmp, connection_name_arg->str, connection_name.length+1);
+ my_casedn_str(system_charset_info, tmp);
}
/*
When MySQL restarted, all Rpl_filter settings which aren't in the my.cnf
@@ -115,7 +115,7 @@ void Master_info::wait_until_free()
Master_info::~Master_info()
{
wait_until_free();
- my_free(connection_name.str);
+ my_free(const_cast<char*>(connection_name.str));
delete_dynamic(&ignore_server_ids);
mysql_mutex_destroy(&run_lock);
mysql_mutex_destroy(&data_lock);
@@ -669,7 +669,7 @@ file '%s')", fname);
mi->rli.is_relay_log_recovery= FALSE;
// now change cache READ -> WRITE - must do this before flush_master_info
reinit_io_cache(&mi->file, WRITE_CACHE, 0L, 0, 1);
- if ((error= MY_TEST(flush_master_info(mi, TRUE, TRUE))))
+ if (unlikely((error= MY_TEST(flush_master_info(mi, TRUE, TRUE)))))
sql_print_error("Failed to flush master info file");
mysql_mutex_unlock(&mi->data_lock);
DBUG_RETURN(error);
@@ -892,7 +892,7 @@ void free_key_master_info(Master_info *mi)
1 error
*/
-bool check_master_connection_name(LEX_STRING *name)
+bool check_master_connection_name(LEX_CSTRING *name)
{
if (name->length >= MAX_CONNECTION_NAME)
return 1;
@@ -922,7 +922,7 @@ bool check_master_connection_name(LEX_STRING *name)
void create_logfile_name_with_suffix(char *res_file_name, size_t length,
const char *info_file, bool append,
- LEX_STRING *suffix)
+ LEX_CSTRING *suffix)
{
char buff[MAX_CONNECTION_NAME+1],
res[MAX_CONNECTION_NAME * MAX_FILENAME_MBWIDTH+1], *p;
@@ -1115,7 +1115,7 @@ bool Master_info_index::init_all_master_info()
while (!init_strvar_from_file(sign, sizeof(sign),
&index_file, NULL))
{
- LEX_STRING connection_name;
+ LEX_CSTRING connection_name;
Master_info *mi;
char buf_master_info_file[FN_REFLEN];
char buf_relay_log_info_file[FN_REFLEN];
@@ -1173,7 +1173,7 @@ bool Master_info_index::init_all_master_info()
}
else
{
- /* Initialization of Master_info succeded. Add it to HASH */
+ /* Initialization of Master_info succeeded. Add it to HASH */
if (global_system_variables.log_warnings > 1)
sql_print_information("Initialized Master_info from '%s'",
buf_master_info_file);
@@ -1244,7 +1244,7 @@ error:
/* Write new master.info to master.info.index File */
-bool Master_info_index::write_master_name_to_index_file(LEX_STRING *name,
+bool Master_info_index::write_master_name_to_index_file(LEX_CSTRING *name,
bool do_sync)
{
DBUG_ASSERT(my_b_inited(&index_file) != 0);
@@ -1281,7 +1281,7 @@ bool Master_info_index::write_master_name_to_index_file(LEX_STRING *name,
WARN_LEVEL_ERROR-> Issue error if not exists
*/
-Master_info *get_master_info(const LEX_STRING *connection_name,
+Master_info *get_master_info(const LEX_CSTRING *connection_name,
Sql_condition::enum_warning_level warning)
{
Master_info *mi;
@@ -1347,12 +1347,12 @@ void Master_info::release()
*/
Master_info *
-Master_info_index::get_master_info(const LEX_STRING *connection_name,
+Master_info_index::get_master_info(const LEX_CSTRING *connection_name,
Sql_condition::enum_warning_level warning)
{
Master_info *mi;
char buff[MAX_CONNECTION_NAME+1], *res;
- uint buff_length;
+ size_t buff_length;
DBUG_ENTER("get_master_info");
DBUG_PRINT("enter",
("connection_name: '%.*s'", (int) connection_name->length,
@@ -1378,7 +1378,7 @@ Master_info_index::get_master_info(const LEX_STRING *connection_name,
/* Check Master_host & Master_port is duplicated or not */
-bool Master_info_index::check_duplicate_master_info(LEX_STRING *name_arg,
+bool Master_info_index::check_duplicate_master_info(LEX_CSTRING *name_arg,
const char *host,
uint port)
{
@@ -1549,6 +1549,9 @@ bool give_error_if_slave_running(bool already_locked)
/**
any_slave_sql_running()
+ @param
+ already_locked 0 if we need to lock, 1 if we have LOCK_active_mi_locked
+
@return
0 No Slave SQL thread is running
# Number of slave SQL thread running
@@ -1559,26 +1562,28 @@ bool give_error_if_slave_running(bool already_locked)
hash entries can't be accessed.
*/
-uint any_slave_sql_running()
+uint any_slave_sql_running(bool already_locked)
{
uint count= 0;
HASH *hash;
DBUG_ENTER("any_slave_sql_running");
- mysql_mutex_lock(&LOCK_active_mi);
+ if (!already_locked)
+ mysql_mutex_lock(&LOCK_active_mi);
if (unlikely(shutdown_in_progress || !master_info_index))
+ count= 1;
+ else
{
- mysql_mutex_unlock(&LOCK_active_mi);
- DBUG_RETURN(1);
- }
- hash= &master_info_index->master_info_hash;
- for (uint i= 0; i< hash->records; ++i)
- {
- Master_info *mi= (Master_info *)my_hash_element(hash, i);
- if (mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN)
- count++;
+ hash= &master_info_index->master_info_hash;
+ for (uint i= 0; i< hash->records; ++i)
+ {
+ Master_info *mi= (Master_info *)my_hash_element(hash, i);
+ if (mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN)
+ count++;
+ }
}
- mysql_mutex_unlock(&LOCK_active_mi);
+ if (!already_locked)
+ mysql_mutex_unlock(&LOCK_active_mi);
DBUG_RETURN(count);
}
@@ -1635,7 +1640,7 @@ bool Master_info_index::start_all_slaves(THD *thd)
error= start_slave(thd, mi, 1);
mi->release();
mysql_mutex_lock(&LOCK_active_mi);
- if (error)
+ if (unlikely(error))
{
my_error(ER_CANT_START_STOP_SLAVE, MYF(0),
"START",
@@ -1708,7 +1713,7 @@ bool Master_info_index::stop_all_slaves(THD *thd)
error= stop_slave(thd, mi, 1);
mi->release();
mysql_mutex_lock(&LOCK_active_mi);
- if (error)
+ if (unlikely(error))
{
my_error(ER_CANT_START_STOP_SLAVE, MYF(0),
"STOP",
@@ -1901,14 +1906,14 @@ char *Domain_id_filter::as_string(enum_list_type type)
return NULL;
// Store the total number of elements followed by the individual elements.
- ulong cur_len= sprintf(buf, "%u", ids->elements);
+ size_t cur_len= sprintf(buf, "%u", ids->elements);
sz-= cur_len;
for (uint i= 0; i < ids->elements; i++)
{
ulong domain_id;
get_dynamic(ids, (void *) &domain_id, i);
- cur_len+= my_snprintf(buf + cur_len, sz, " %u", domain_id);
+ cur_len+= my_snprintf(buf + cur_len, sz, " %lu", domain_id);
sz-= cur_len;
}
return buf;
@@ -2007,7 +2012,7 @@ bool Master_info_index::flush_all_relay_logs()
mi->release();
mysql_mutex_lock(&LOCK_active_mi);
- if (error)
+ if (unlikely(error))
{
result= true;
break;
diff --git a/sql/rpl_mi.h b/sql/rpl_mi.h
index 12574285de0..308d5af6f16 100644
--- a/sql/rpl_mi.h
+++ b/sql/rpl_mi.h
@@ -20,7 +20,7 @@
#include "rpl_rli.h"
#include "rpl_reporting.h"
-#include "my_sys.h"
+#include <my_sys.h>
#include "rpl_filter.h"
#include "keycaches.h"
@@ -133,6 +133,19 @@ public:
extern TYPELIB slave_parallel_mode_typelib;
+typedef struct st_rows_event_tracker
+{
+ char binlog_file_name[FN_REFLEN];
+ my_off_t first_seen;
+ my_off_t last_seen;
+ bool stmt_end_seen;
+ void update(const char* file_name, my_off_t pos,
+ const char* buf,
+ const Format_description_log_event *fdle);
+ void reset();
+ bool check_and_report(const char* file_name, my_off_t pos);
+} Rows_event_tracker;
+
/*****************************************************************************
Replication IO Thread
@@ -172,7 +185,7 @@ class Master_info : public Slave_reporting_capability
USE_GTID_NO= 0, USE_GTID_CURRENT_POS= 1, USE_GTID_SLAVE_POS= 2
};
- Master_info(LEX_STRING *connection_name, bool is_slave_recovery);
+ Master_info(LEX_CSTRING *connection_name, bool is_slave_recovery);
~Master_info();
bool shall_ignore_server_id(ulong s_id);
void clear_in_memory_info(bool all);
@@ -197,8 +210,8 @@ class Master_info : public Slave_reporting_capability
char host[HOSTNAME_LENGTH*SYSTEM_CHARSET_MBMAXLEN+1];
char user[USERNAME_LENGTH+1];
char password[MAX_PASSWORD_LENGTH*SYSTEM_CHARSET_MBMAXLEN+1];
- LEX_STRING connection_name; /* User supplied connection name */
- LEX_STRING cmp_connection_name; /* Connection name in lower case */
+ LEX_CSTRING connection_name; /* User supplied connection name */
+ LEX_CSTRING cmp_connection_name; /* Connection name in lower case */
bool ssl; // enables use of SSL connection if true
char ssl_ca[FN_REFLEN], ssl_capath[FN_REFLEN], ssl_cert[FN_REFLEN];
char ssl_cipher[FN_REFLEN], ssl_key[FN_REFLEN];
@@ -301,16 +314,39 @@ class Master_info : public Slave_reporting_capability
uint64 gtid_reconnect_event_skip_count;
/* gtid_event_seen is false until we receive first GTID event from master. */
bool gtid_event_seen;
+ /**
+ The struct holds some history of Rows- log-event reading/queuing
+ by the receiver thread. Its fields are updated per each such event
+ at time of queue_event(), and they are checked to detect
+ the Rows- event group integrity violation at time of first non-Rows-
+ event gets handled.
+ */
+ Rows_event_tracker rows_event_tracker;
bool in_start_all_slaves, in_stop_all_slaves;
bool in_flush_all_relay_logs;
uint users; /* Active user for object */
uint killed;
+
+ /* No of DDL event group */
+ volatile uint64 total_ddl_groups;
+
+ /* No of non-transactional event group*/
+ volatile uint64 total_non_trans_groups;
+
+ /* No of transactional event group*/
+ volatile uint64 total_trans_groups;
+
/* domain-id based filter */
Domain_id_filter domain_id_filter;
/* The parallel replication mode. */
enum_slave_parallel_mode parallel_mode;
+ /*
+ semi_ack is used to identify if the current binlog event needs an
+ ACK from slave, or if delay_master is enabled.
+ */
+ int semi_ack;
};
int init_master_info(Master_info* mi, const char* master_info_fname,
@@ -343,14 +379,14 @@ public:
HASH master_info_hash;
bool init_all_master_info();
- bool write_master_name_to_index_file(LEX_STRING *connection_name,
+ bool write_master_name_to_index_file(LEX_CSTRING *connection_name,
bool do_sync);
- bool check_duplicate_master_info(LEX_STRING *connection_name,
+ bool check_duplicate_master_info(LEX_CSTRING *connection_name,
const char *host, uint port);
bool add_master_info(Master_info *mi, bool write_to_file);
bool remove_master_info(Master_info *mi);
- Master_info *get_master_info(const LEX_STRING *connection_name,
+ Master_info *get_master_info(const LEX_CSTRING *connection_name,
Sql_condition::enum_warning_level warning);
bool start_all_slaves(THD *thd);
bool stop_all_slaves(THD *thd);
@@ -368,18 +404,18 @@ public:
};
-Master_info *get_master_info(const LEX_STRING *connection_name,
+Master_info *get_master_info(const LEX_CSTRING *connection_name,
Sql_condition::enum_warning_level warning);
-bool check_master_connection_name(LEX_STRING *name);
+bool check_master_connection_name(LEX_CSTRING *name);
void create_logfile_name_with_suffix(char *res_file_name, size_t length,
const char *info_file,
bool append,
- LEX_STRING *suffix);
+ LEX_CSTRING *suffix);
uchar *get_key_master_info(Master_info *mi, size_t *length,
my_bool not_used __attribute__((unused)));
void free_key_master_info(Master_info *mi);
-uint any_slave_sql_running();
+uint any_slave_sql_running(bool already_locked);
bool give_error_if_slave_running(bool already_lock);
#endif /* HAVE_REPLICATION */
diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc
index 4cf87ba73b7..eb0bdbcf868 100644
--- a/sql/rpl_parallel.cc
+++ b/sql/rpl_parallel.cc
@@ -1,4 +1,4 @@
-#include "my_global.h"
+#include "mariadb.h"
#include "rpl_parallel.h"
#include "slave.h"
#include "rpl_mi.h"
@@ -269,8 +269,8 @@ signal_error_to_sql_driver_thread(THD *thd, rpl_group_info *rgi, int err)
rgi->rli->abort_slave= true;
rgi->rli->stop_for_until= false;
mysql_mutex_lock(rgi->rli->relay_log.get_log_lock());
+ rgi->rli->relay_log.signal_relay_log_update();
mysql_mutex_unlock(rgi->rli->relay_log.get_log_lock());
- rgi->rli->relay_log.signal_update();
}
@@ -343,7 +343,7 @@ do_gco_wait(rpl_group_info *rgi, group_commit_orderer *gco,
thd->set_time_for_next_stage();
do
{
- if (thd->check_killed() && !rgi->worker_error)
+ if (!rgi->worker_error && unlikely(thd->check_killed(1)))
{
DEBUG_SYNC(thd, "rpl_parallel_start_waiting_for_prior_killed");
thd->clear_error();
@@ -412,9 +412,8 @@ do_ftwrl_wait(rpl_group_info *rgi,
aborted= true;
break;
}
- if (thd->check_killed())
+ if (unlikely(thd->check_killed()))
{
- thd->send_kill_message();
slave_output_error_info(rgi, thd);
signal_error_to_sql_driver_thread(thd, rgi, 1);
break;
@@ -463,9 +462,8 @@ pool_mark_busy(rpl_parallel_thread_pool *pool, THD *thd)
}
while (pool->busy)
{
- if (thd && thd->check_killed())
+ if (thd && unlikely(thd->check_killed()))
{
- thd->send_kill_message();
res= 1;
break;
}
@@ -596,9 +594,8 @@ rpl_pause_for_ftwrl(THD *thd)
e->last_committed_sub_id < e->pause_sub_id &&
!err)
{
- if (thd->check_killed())
+ if (unlikely(thd->check_killed()))
{
- thd->send_kill_message();
err= 1;
break;
}
@@ -749,7 +746,7 @@ do_retry:
unregistering (and later re-registering) the wait.
*/
if(thd->wait_for_commit_ptr)
- thd->wait_for_commit_ptr->unregister_wait_for_prior_commit();
+ thd->wait_for_commit_ptr->unregister_wait_for_prior_commit();
DBUG_EXECUTE_IF("inject_mdev8031", {
/* Simulate that we get deadlock killed at this exact point. */
rgi->killed_for_retry= rpl_group_info::RETRY_KILL_KILLED;
@@ -863,8 +860,8 @@ do_retry:
}
DBUG_EXECUTE_IF("inject_mdev8031", {
/* Simulate pending KILL caught in read_relay_log_description_event(). */
- if (thd->check_killed()) {
- thd->send_kill_message();
+ if (unlikely(thd->check_killed()))
+ {
err= 1;
goto err;
}
@@ -881,19 +878,19 @@ do_retry:
for (;;)
{
old_offset= cur_offset;
- ev= Log_event::read_log_event(&rlog, 0, description_event,
+ ev= Log_event::read_log_event(&rlog, description_event,
opt_slave_sql_verify_checksum);
cur_offset= my_b_tell(&rlog);
if (ev)
break;
- if (rlog.error < 0)
+ if (unlikely(rlog.error < 0))
{
errmsg= "slave SQL thread aborted because of I/O error";
err= 1;
goto check_retry;
}
- if (rlog.error > 0)
+ if (unlikely(rlog.error > 0))
{
sql_print_error("Slave SQL thread: I/O error reading "
"event(errno: %d cur_log->error: %d)",
@@ -1061,9 +1058,10 @@ handle_rpl_parallel_thread(void *arg)
thd->system_thread= SYSTEM_THREAD_SLAVE_SQL;
thd->security_ctx->skip_grants();
thd->variables.max_allowed_packet= slave_max_allowed_packet;
+ /* Ensure that slave can exeute any alter table it gets from master */
+ thd->variables.alter_algorithm= (ulong) Alter_info::ALTER_TABLE_ALGORITHM_DEFAULT;
thd->slave_thread= 1;
- thd->variables.sql_log_slow= opt_log_slow_slave_statements;
- thd->variables.log_slow_filter= global_system_variables.log_slow_filter;
+
set_slave_thread_options(thd);
thd->client_capabilities = CLIENT_LOCAL_FILES;
thd->net.reading_or_writing= 0;
@@ -1324,7 +1322,7 @@ handle_rpl_parallel_thread(void *arg)
if (!err)
#endif
{
- if (thd->check_killed())
+ if (unlikely(thd->check_killed()))
{
thd->clear_error();
thd->get_stmt_da()->reset_diagnostics_area();
@@ -1337,7 +1335,7 @@ handle_rpl_parallel_thread(void *arg)
delete_or_keep_event_post_apply(rgi, event_type, qev->ev);
DBUG_EXECUTE_IF("rpl_parallel_simulate_temp_err_gtid_0_x_100",
err= dbug_simulate_tmp_error(rgi, thd););
- if (err)
+ if (unlikely(err))
{
convert_kill_to_deadlock_error(rgi);
if (has_temporary_error(thd) && slave_trans_retries > 0)
@@ -1464,7 +1462,7 @@ handle_rpl_parallel_thread(void *arg)
thd->clear_error();
thd->catalog= 0;
thd->reset_query();
- thd->reset_db(NULL, 0);
+ thd->reset_db(&null_clex_str);
thd_proc_info(thd, "Slave worker thread exiting");
thd->temporary_tables= 0;
@@ -1532,7 +1530,7 @@ rpl_parallel_change_thread_count(rpl_parallel_thread_pool *pool,
*/
if (!new_count && !force)
{
- if (any_slave_sql_running())
+ if (any_slave_sql_running(false))
{
DBUG_PRINT("warning",
("SQL threads running while trying to reset parallel pool"));
@@ -1687,7 +1685,7 @@ err:
int rpl_parallel_resize_pool_if_no_slaves(void)
{
/* master_info_index is set to NULL on shutdown */
- if (opt_slave_parallel_threads > 0 && !any_slave_sql_running())
+ if (opt_slave_parallel_threads > 0 && !any_slave_sql_running(false))
return rpl_parallel_inactivate_pool(&global_rpl_thread_pool);
return 0;
}
@@ -2121,7 +2119,7 @@ rpl_parallel_entry::choose_thread(rpl_group_info *rgi, bool *did_enter_cond,
/* The thread is ready to queue into. */
break;
}
- else if (rli->sql_driver_thd->check_killed())
+ else if (unlikely(rli->sql_driver_thd->check_killed(1)))
{
unlock_or_exit_cond(rli->sql_driver_thd, &thr->LOCK_rpl_thread,
did_enter_cond, old_stage);
@@ -2447,9 +2445,8 @@ rpl_parallel::wait_for_workers_idle(THD *thd)
&stage_waiting_for_workers_idle, &old_stage);
while (e->current_sub_id > e->last_committed_sub_id)
{
- if (thd->check_killed())
+ if (unlikely(thd->check_killed()))
{
- thd->send_kill_message();
err= 1;
break;
}
diff --git a/sql/rpl_parallel.h b/sql/rpl_parallel.h
index a0faeae815c..4579d0da9bc 100644
--- a/sql/rpl_parallel.h
+++ b/sql/rpl_parallel.h
@@ -68,23 +68,27 @@ struct group_commit_orderer {
*/
bool installed;
- /*
- This flag is set for a GCO in which we have event groups with multiple
- different commit_id values from the master. This happens when we
- optimistically try to execute in parallel transactions not known to be
- conflict-free.
-
- When this flag is set, in case of DDL we need to start a new GCO regardless
- of current commit_id, as DDL is not safe to speculatively apply in parallel
- with prior event groups.
- */
- static const uint8 MULTI_BATCH = 0x01;
- /*
- This flag is set for a GCO that contains DDL. If set, it forces a switch to
- a new GCO upon seeing a new commit_id, as DDL is not safe to speculatively
- replicate in parallel with subsequent transactions.
- */
- static const uint8 FORCE_SWITCH = 0x02;
+ enum force_switch_bits
+ {
+ /*
+ This flag is set for a GCO in which we have event groups with multiple
+ different commit_id values from the master. This happens when we
+ optimistically try to execute in parallel transactions not known to be
+ conflict-free.
+
+ When this flag is set, in case of DDL we need to start a new GCO
+ regardless of current commit_id, as DDL is not safe to
+ speculatively apply in parallel with prior event groups.
+ */
+ MULTI_BATCH= 1,
+ /*
+ This flag is set for a GCO that contains DDL. If set, it forces
+ a switch to a new GCO upon seeing a new commit_id, as DDL is not
+ safe to speculatively replicate in parallel with subsequent
+ transactions.
+ */
+ FORCE_SWITCH= 2
+ };
uint8 flags;
};
diff --git a/sql/rpl_record.cc b/sql/rpl_record.cc
index 92d2c90be26..b1fe34dc23d 100644
--- a/sql/rpl_record.cc
+++ b/sql/rpl_record.cc
@@ -14,7 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#include "rpl_rli.h"
@@ -107,7 +107,7 @@ pack_row(TABLE *table, MY_BITMAP const* cols,
field->max_data_length());
DBUG_PRINT("debug", ("field: %s; real_type: %d, pack_ptr: %p;"
" pack_ptr':%p; bytes: %d",
- field->field_name, field->real_type(),
+ field->field_name.str, field->real_type(),
old_pack_ptr,pack_ptr,
(int) (pack_ptr - old_pack_ptr)));
DBUG_DUMP("packed_data", old_pack_ptr, pack_ptr - old_pack_ptr);
@@ -196,6 +196,7 @@ unpack_row(rpl_group_info *rgi,
uchar const **const current_row_end, ulong *const master_reclength,
uchar const *const row_end)
{
+ int error;
DBUG_ENTER("unpack_row");
DBUG_ASSERT(row_data);
DBUG_ASSERT(table);
@@ -254,7 +255,7 @@ unpack_row(rpl_group_info *rgi,
conv_field ? conv_field : *field_ptr;
DBUG_PRINT("debug", ("Conversion %srequired for field '%s' (#%ld)",
conv_field ? "" : "not ",
- (*field_ptr)->field_name,
+ (*field_ptr)->field_name.str,
(long) (field_ptr - begin_ptr)));
DBUG_ASSERT(f != NULL);
@@ -305,7 +306,7 @@ unpack_row(rpl_group_info *rgi,
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_BAD_NULL_ERROR,
ER_THD(thd, ER_BAD_NULL_ERROR),
- f->field_name);
+ f->field_name.str);
}
}
else
@@ -323,7 +324,7 @@ unpack_row(rpl_group_info *rgi,
pack_ptr= f->unpack(f->ptr, pack_ptr, row_end, metadata);
DBUG_PRINT("debug", ("field: %s; metadata: 0x%x;"
" pack_ptr: %p; pack_ptr': %p; bytes: %d",
- f->field_name, metadata,
+ f->field_name.str, metadata,
old_pack_ptr, pack_ptr,
(int) (pack_ptr - old_pack_ptr)));
if (!pack_ptr)
@@ -338,7 +339,7 @@ unpack_row(rpl_group_info *rgi,
WSREP_WARN("ROW event unpack field: %s metadata: 0x%x;"
" conv_table %p conv_field %p table %s"
" row_end: %p",
- f->field_name, metadata, conv_table, conv_field,
+ f->field_name.str, metadata, conv_table, conv_field,
(table_found) ? "found" : "not found", row_end
);
}
@@ -346,7 +347,7 @@ unpack_row(rpl_group_info *rgi,
rgi->rli->report(ERROR_LEVEL, ER_SLAVE_CORRUPT_EVENT,
rgi->gtid_info(),
"Could not read field '%s' of table '%s.%s'",
- f->field_name, table->s->db.str,
+ f->field_name.str, table->s->db.str,
table->s->table_name.str);
DBUG_RETURN(HA_ERR_CORRUPT_EVENT);
}
@@ -369,7 +370,7 @@ unpack_row(rpl_group_info *rgi,
conv_field->sql_type(source_type);
conv_field->val_str(&value_string);
DBUG_PRINT("debug", ("Copying field '%s' of type '%s' with value '%s'",
- (*field_ptr)->field_name,
+ (*field_ptr)->field_name.str,
source_type.c_ptr_safe(), value_string.c_ptr_safe()));
#endif
copy.set(*field_ptr, f, TRUE);
@@ -380,7 +381,7 @@ unpack_row(rpl_group_info *rgi,
(*field_ptr)->sql_type(target_type);
(*field_ptr)->val_str(&value_string);
DBUG_PRINT("debug", ("Value of field '%s' of type '%s' is now '%s'",
- (*field_ptr)->field_name,
+ (*field_ptr)->field_name.str,
target_type.c_ptr_safe(), value_string.c_ptr_safe()));
#endif
}
@@ -418,7 +419,7 @@ unpack_row(rpl_group_info *rgi,
/*
Add Extra slave persistent columns
*/
- if (int error= fill_extra_persistent_columns(table, cols->n_bits))
+ if (unlikely(error= fill_extra_persistent_columns(table, cols->n_bits)))
DBUG_RETURN(error);
/*
@@ -488,7 +489,7 @@ int prepare_record(TABLE *const table, const uint skip, const bool check)
Sql_condition::WARN_LEVEL_WARN,
ER_NO_DEFAULT_FOR_FIELD,
ER_THD(thd, ER_NO_DEFAULT_FOR_FIELD),
- f->field_name);
+ f->field_name.str);
}
}
diff --git a/sql/rpl_record.h b/sql/rpl_record.h
index 36b654a45e0..357dc7619f9 100644
--- a/sql/rpl_record.h
+++ b/sql/rpl_record.h
@@ -18,7 +18,6 @@
#define RPL_RECORD_H
#include <rpl_reporting.h>
-#include "my_global.h" /* uchar */
struct rpl_group_info;
struct TABLE;
diff --git a/sql/rpl_record_old.cc b/sql/rpl_record_old.cc
index baedf60cfde..496e781d2eb 100644
--- a/sql/rpl_record_old.cc
+++ b/sql/rpl_record_old.cc
@@ -13,7 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "rpl_rli.h"
#include "rpl_record_old.h"
@@ -143,7 +143,7 @@ unpack_row_old(rpl_group_info *rgi,
{
rgi->rli->report(ERROR_LEVEL, ER_SLAVE_CORRUPT_EVENT, NULL,
"Could not read field `%s` of table `%s`.`%s`",
- f->field_name, table->s->db.str,
+ f->field_name.str, table->s->db.str,
table->s->table_name.str);
return(ER_SLAVE_CORRUPT_EVENT);
}
@@ -186,7 +186,7 @@ unpack_row_old(rpl_group_info *rgi,
rgi->rli->report(ERROR_LEVEL, ER_NO_DEFAULT_FOR_FIELD, NULL,
"Field `%s` of table `%s`.`%s` "
"has no default value and cannot be NULL",
- (*field_ptr)->field_name, table->s->db.str,
+ (*field_ptr)->field_name.str, table->s->db.str,
table->s->table_name.str);
error = ER_NO_DEFAULT_FOR_FIELD;
}
diff --git a/sql/rpl_reporting.cc b/sql/rpl_reporting.cc
index d6107e94987..738ae52782d 100644
--- a/sql/rpl_reporting.cc
+++ b/sql/rpl_reporting.cc
@@ -14,7 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "rpl_reporting.h"
#include "log.h" // sql_print_error, sql_print_warning,
diff --git a/sql/rpl_reporting.h b/sql/rpl_reporting.h
index 6867b77eb0a..62b934c1527 100644
--- a/sql/rpl_reporting.h
+++ b/sql/rpl_reporting.h
@@ -16,7 +16,7 @@
#ifndef RPL_REPORTING_H
#define RPL_REPORTING_H
-#include "my_sys.h" /* loglevel */
+#include <my_sys.h> /* loglevel */
/**
Maximum size of an error message from a slave thread.
diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
index 99f8da2c928..fe699d4c920 100644
--- a/sql/rpl_rli.cc
+++ b/sql/rpl_rli.cc
@@ -14,7 +14,7 @@
along with this program; if not, write to the Free Software Foundation,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h" // HAVE_*
#include "rpl_mi.h"
@@ -31,6 +31,8 @@
#include "slave.h"
#include <mysql/plugin.h>
#include <mysql/service_thd_wait.h>
+#include "lock.h"
+#include "sql_table.h"
static int count_relay_log_space(Relay_log_info* rli);
@@ -69,10 +71,12 @@ Relay_log_info::Relay_log_info(bool is_slave_recovery)
relay_log_state.init();
#ifdef HAVE_PSI_INTERFACE
relay_log.set_psi_keys(key_RELAYLOG_LOCK_index,
- key_RELAYLOG_update_cond,
+ key_RELAYLOG_COND_relay_log_updated,
+ key_RELAYLOG_COND_bin_log_updated,
key_file_relaylog,
key_file_relaylog_index,
- key_RELAYLOG_COND_queue_busy);
+ key_RELAYLOG_COND_queue_busy,
+ key_LOCK_relaylog_end_pos);
#endif
group_relay_log_name[0]= event_relay_log_name[0]=
@@ -139,7 +143,7 @@ int Relay_log_info::init(const char* info_fname)
log_space_limit= relay_log_space_limit;
log_space_total= 0;
- if (error_on_rli_init_info)
+ if (unlikely(error_on_rli_init_info))
goto err;
char pattern[FN_REFLEN];
@@ -303,7 +307,7 @@ Failed to open the existing relay log info file '%s' (errno %d)",
fname);
error= 1;
}
- if (error)
+ if (unlikely(error))
{
if (info_fd >= 0)
mysql_file_close(info_fd, MYF(0));
@@ -412,7 +416,7 @@ Failed to open the existing relay log info file '%s' (errno %d)",
before Relay_log_info::flush()
*/
reinit_io_cache(&info_file, WRITE_CACHE,0L,0,1);
- if ((error= flush()))
+ if (unlikely((error= flush())))
{
msg= "Failed to flush relay log info file";
goto err;
@@ -539,7 +543,7 @@ read_relay_log_description_event(IO_CACHE *cur_log, ulonglong start_pos,
if (my_b_tell(cur_log) >= start_pos)
break;
- if (!(ev= Log_event::read_log_event(cur_log, 0, fdev,
+ if (!(ev= Log_event::read_log_event(cur_log, fdev,
opt_slave_sql_verify_checksum)))
{
DBUG_PRINT("info",("could not read event, cur_log->error=%d",
@@ -1566,41 +1570,21 @@ Relay_log_info::update_relay_log_state(rpl_gtid *gtid_list, uint32 count)
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-int
-rpl_load_gtid_slave_state(THD *thd)
+struct gtid_pos_element { uint64 sub_id; rpl_gtid gtid; void *hton; };
+
+static int
+scan_one_gtid_slave_pos_table(THD *thd, HASH *hash, DYNAMIC_ARRAY *array,
+ LEX_CSTRING *tablename, void **out_hton)
{
TABLE_LIST tlist;
- TABLE *table;
+ TABLE *UNINIT_VAR(table);
bool table_opened= false;
bool table_scanned= false;
- bool array_inited= false;
- struct local_element { uint64 sub_id; rpl_gtid gtid; };
- struct local_element tmp_entry, *entry;
- HASH hash;
- DYNAMIC_ARRAY array;
+ struct gtid_pos_element tmp_entry, *entry;
int err= 0;
- uint32 i;
- DBUG_ENTER("rpl_load_gtid_slave_state");
-
- mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state);
- bool loaded= rpl_global_gtid_slave_state->loaded;
- mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
- if (loaded)
- DBUG_RETURN(0);
-
- my_hash_init(&hash, &my_charset_bin, 32,
- offsetof(local_element, gtid) + offsetof(rpl_gtid, domain_id),
- sizeof(uint32), NULL, my_free, HASH_UNIQUE);
- if ((err= my_init_dynamic_array(&array, sizeof(local_element), 0, 0, MYF(0))))
- goto end;
- array_inited= true;
thd->reset_for_next_command();
-
- tlist.init_one_table(STRING_WITH_LEN("mysql"),
- rpl_gtid_slave_state_table_name.str,
- rpl_gtid_slave_state_table_name.length,
- NULL, TL_READ);
+ tlist.init_one_table(&MYSQL_SCHEMA_NAME, tablename, NULL, TL_READ);
if ((err= open_and_lock_tables(thd, &tlist, FALSE, 0)))
goto end;
table_opened= true;
@@ -1610,11 +1594,9 @@ rpl_load_gtid_slave_state(THD *thd)
goto end;
bitmap_set_all(table->read_set);
- if ((err= table->file->ha_rnd_init_with_error(1)))
- {
- table->file->print_error(err, MYF(0));
+ if (unlikely(err= table->file->ha_rnd_init_with_error(1)))
goto end;
- }
+
table_scanned= true;
for (;;)
{
@@ -1624,9 +1606,7 @@ rpl_load_gtid_slave_state(THD *thd)
if ((err= table->file->ha_rnd_next(table->record[0])))
{
- if (err == HA_ERR_RECORD_DELETED)
- continue;
- else if (err == HA_ERR_END_OF_FILE)
+ if (err == HA_ERR_END_OF_FILE)
break;
else
{
@@ -1638,7 +1618,7 @@ rpl_load_gtid_slave_state(THD *thd)
sub_id= (ulonglong)table->field[1]->val_int();
server_id= (uint32)table->field[2]->val_int();
seq_no= (ulonglong)table->field[3]->val_int();
- DBUG_PRINT("info", ("Read slave state row: %u-%u-%lu sub_id=%lu\n",
+ DBUG_PRINT("info", ("Read slave state row: %u-%u-%lu sub_id=%lu",
(unsigned)domain_id, (unsigned)server_id,
(ulong)seq_no, (ulong)sub_id));
@@ -1646,26 +1626,28 @@ rpl_load_gtid_slave_state(THD *thd)
tmp_entry.gtid.domain_id= domain_id;
tmp_entry.gtid.server_id= server_id;
tmp_entry.gtid.seq_no= seq_no;
- if ((err= insert_dynamic(&array, (uchar *)&tmp_entry)))
+ tmp_entry.hton= table->s->db_type();
+ if ((err= insert_dynamic(array, (uchar *)&tmp_entry)))
{
my_error(ER_OUT_OF_RESOURCES, MYF(0));
goto end;
}
- if ((rec= my_hash_search(&hash, (const uchar *)&domain_id, 0)))
+ if ((rec= my_hash_search(hash, (const uchar *)&domain_id, 0)))
{
- entry= (struct local_element *)rec;
+ entry= (struct gtid_pos_element *)rec;
if (entry->sub_id >= sub_id)
continue;
entry->sub_id= sub_id;
DBUG_ASSERT(entry->gtid.domain_id == domain_id);
entry->gtid.server_id= server_id;
entry->gtid.seq_no= seq_no;
+ entry->hton= table->s->db_type();
}
else
{
- if (!(entry= (struct local_element *)my_malloc(sizeof(*entry),
- MYF(MY_WME))))
+ if (!(entry= (struct gtid_pos_element *)my_malloc(sizeof(*entry),
+ MYF(MY_WME))))
{
my_error(ER_OUTOFMEMORY, MYF(0), (int)sizeof(*entry));
err= 1;
@@ -1675,7 +1657,8 @@ rpl_load_gtid_slave_state(THD *thd)
entry->gtid.domain_id= domain_id;
entry->gtid.server_id= server_id;
entry->gtid.seq_no= seq_no;
- if ((err= my_hash_insert(&hash, (uchar *)entry)))
+ entry->hton= table->s->db_type();
+ if ((err= my_hash_insert(hash, (uchar *)entry)))
{
my_free(entry);
my_error(ER_OUT_OF_RESOURCES, MYF(0));
@@ -1683,6 +1666,250 @@ rpl_load_gtid_slave_state(THD *thd)
}
}
}
+ err= 0; /* Clear HA_ERR_END_OF_FILE */
+
+end:
+ if (table_scanned)
+ {
+ table->file->ha_index_or_rnd_end();
+ ha_commit_trans(thd, FALSE);
+ ha_commit_trans(thd, TRUE);
+ }
+ if (table_opened)
+ {
+ *out_hton= table->s->db_type();
+ close_thread_tables(thd);
+ thd->mdl_context.release_transactional_locks();
+ }
+ return err;
+}
+
+
+/*
+ Look for all tables mysql.gtid_slave_pos*. Read all rows from each such
+ table found into ARRAY. For each domain id, put the row with highest sub_id
+ into HASH.
+*/
+static int
+scan_all_gtid_slave_pos_table(THD *thd, int (*cb)(THD *, LEX_CSTRING *, void *),
+ void *cb_data)
+{
+ char path[FN_REFLEN];
+ MY_DIR *dirp;
+
+ thd->reset_for_next_command();
+ if (lock_schema_name(thd, MYSQL_SCHEMA_NAME.str))
+ return 1;
+
+ build_table_filename(path, sizeof(path) - 1, MYSQL_SCHEMA_NAME.str, "", "", 0);
+ if (!(dirp= my_dir(path, MYF(MY_DONT_SORT))))
+ {
+ my_error(ER_FILE_NOT_FOUND, MYF(0), path, my_errno);
+ close_thread_tables(thd);
+ thd->mdl_context.release_transactional_locks();
+ return 1;
+ }
+ else
+ {
+ size_t i;
+ Dynamic_array<LEX_CSTRING*> files(dirp->number_of_files);
+ Discovered_table_list tl(thd, &files);
+ int err;
+
+ err= ha_discover_table_names(thd, &MYSQL_SCHEMA_NAME, dirp, &tl, false);
+ my_dirend(dirp);
+ close_thread_tables(thd);
+ thd->mdl_context.release_transactional_locks();
+ if (err)
+ return err;
+
+ for (i = 0; i < files.elements(); ++i)
+ {
+ if (strncmp(files.at(i)->str,
+ rpl_gtid_slave_state_table_name.str,
+ rpl_gtid_slave_state_table_name.length) == 0)
+ {
+ if ((err= (*cb)(thd, files.at(i), cb_data)))
+ return err;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+struct load_gtid_state_cb_data {
+ HASH *hash;
+ DYNAMIC_ARRAY *array;
+ struct rpl_slave_state::gtid_pos_table *table_list;
+ struct rpl_slave_state::gtid_pos_table *default_entry;
+};
+
+static int
+process_gtid_pos_table(THD *thd, LEX_CSTRING *table_name, void *hton,
+ struct load_gtid_state_cb_data *data)
+{
+ struct rpl_slave_state::gtid_pos_table *p, *entry, **next_ptr;
+ bool is_default=
+ (strcmp(table_name->str, rpl_gtid_slave_state_table_name.str) == 0);
+
+ /*
+ Ignore tables with duplicate storage engine, with a warning.
+ Prefer the default mysql.gtid_slave_pos over another table
+ mysql.gtid_slave_posXXX with the same storage engine.
+ */
+ next_ptr= &data->table_list;
+ entry= data->table_list;
+ while (entry)
+ {
+ if (entry->table_hton == hton)
+ {
+ static const char *warning_msg= "Ignoring redundant table mysql.%s "
+ "since mysql.%s has the same storage engine";
+ if (!is_default)
+ {
+ /* Ignore the redundant table. */
+ sql_print_warning(warning_msg, table_name->str, entry->table_name.str);
+ return 0;
+ }
+ else
+ {
+ sql_print_warning(warning_msg, entry->table_name.str, table_name->str);
+ /* Delete the redundant table, and proceed to add this one instead. */
+ *next_ptr= entry->next;
+ my_free(entry);
+ break;
+ }
+ }
+ next_ptr= &entry->next;
+ entry= entry->next;
+ }
+
+ p= rpl_global_gtid_slave_state->alloc_gtid_pos_table(table_name,
+ hton, rpl_slave_state::GTID_POS_AVAILABLE);
+ if (!p)
+ return 1;
+ p->next= data->table_list;
+ data->table_list= p;
+ if (is_default)
+ data->default_entry= p;
+ return 0;
+}
+
+
+/*
+ Put tables corresponding to @@gtid_pos_auto_engines at the end of the list,
+ marked to be auto-created if needed.
+*/
+static int
+gtid_pos_auto_create_tables(rpl_slave_state::gtid_pos_table **list_ptr)
+{
+ plugin_ref *auto_engines;
+ int err= 0;
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ for (auto_engines= opt_gtid_pos_auto_plugins;
+ !err && auto_engines && *auto_engines;
+ ++auto_engines)
+ {
+ void *hton= plugin_hton(*auto_engines);
+ char buf[FN_REFLEN+1];
+ LEX_CSTRING table_name;
+ char *p;
+ rpl_slave_state::gtid_pos_table *entry, **next_ptr;
+
+ /* See if this engine is already in the list. */
+ next_ptr= list_ptr;
+ entry= *list_ptr;
+ while (entry)
+ {
+ if (entry->table_hton == hton)
+ break;
+ next_ptr= &entry->next;
+ entry= entry->next;
+ }
+ if (entry)
+ continue;
+
+ /* Add an auto-create entry for this engine at end of list. */
+ p= strmake(buf, rpl_gtid_slave_state_table_name.str, FN_REFLEN);
+ p= strmake(p, "_", FN_REFLEN - (p - buf));
+ p= strmake(p, plugin_name(*auto_engines)->str, FN_REFLEN - (p - buf));
+ table_name.str= buf;
+ table_name.length= p - buf;
+ table_case_convert(const_cast<char*>(table_name.str),
+ static_cast<uint>(table_name.length));
+ entry= rpl_global_gtid_slave_state->alloc_gtid_pos_table
+ (&table_name, hton, rpl_slave_state::GTID_POS_AUTO_CREATE);
+ if (!entry)
+ {
+ err= 1;
+ break;
+ }
+ *next_ptr= entry;
+ }
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ return err;
+}
+
+
+static int
+load_gtid_state_cb(THD *thd, LEX_CSTRING *table_name, void *arg)
+{
+ int err;
+ load_gtid_state_cb_data *data= static_cast<load_gtid_state_cb_data *>(arg);
+ void *hton;
+
+ if ((err= scan_one_gtid_slave_pos_table(thd, data->hash, data->array,
+ table_name, &hton)))
+ return err;
+ return process_gtid_pos_table(thd, table_name, hton, data);
+}
+
+
+int
+rpl_load_gtid_slave_state(THD *thd)
+{
+ bool array_inited= false;
+ struct gtid_pos_element tmp_entry, *entry;
+ HASH hash;
+ DYNAMIC_ARRAY array;
+ int err= 0;
+ uint32 i;
+ load_gtid_state_cb_data cb_data;
+ DBUG_ENTER("rpl_load_gtid_slave_state");
+
+ mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+ bool loaded= rpl_global_gtid_slave_state->loaded;
+ mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+ if (loaded)
+ DBUG_RETURN(0);
+
+ cb_data.table_list= NULL;
+ cb_data.default_entry= NULL;
+ my_hash_init(&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))))
+ goto end;
+ array_inited= true;
+
+ cb_data.hash = &hash;
+ cb_data.array = &array;
+ if ((err= scan_all_gtid_slave_pos_table(thd, load_gtid_state_cb, &cb_data)))
+ goto end;
+
+ if (!cb_data.default_entry)
+ {
+ /*
+ If the mysql.gtid_slave_pos table does not exist, but at least one other
+ table is available, arbitrarily pick the first in the list to use as
+ default.
+ */
+ cb_data.default_entry= cb_data.table_list;
+ }
+ if ((err= gtid_pos_auto_create_tables(&cb_data.table_list)))
+ goto end;
mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state);
if (rpl_global_gtid_slave_state->loaded)
@@ -1691,14 +1918,24 @@ rpl_load_gtid_slave_state(THD *thd)
goto end;
}
+ if (!cb_data.table_list)
+ {
+ my_error(ER_NO_SUCH_TABLE, MYF(0), "mysql",
+ rpl_gtid_slave_state_table_name.str);
+ mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+ err= 1;
+ goto end;
+ }
+
for (i= 0; i < array.elements; ++i)
{
get_dynamic(&array, (uchar *)&tmp_entry, i);
if ((err= rpl_global_gtid_slave_state->update(tmp_entry.gtid.domain_id,
- tmp_entry.gtid.server_id,
- tmp_entry.sub_id,
- tmp_entry.gtid.seq_no,
- NULL)))
+ tmp_entry.gtid.server_id,
+ tmp_entry.sub_id,
+ tmp_entry.gtid.seq_no,
+ tmp_entry.hton,
+ NULL)))
{
mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
my_error(ER_OUT_OF_RESOURCES, MYF(0));
@@ -1708,7 +1945,7 @@ rpl_load_gtid_slave_state(THD *thd)
for (i= 0; i < hash.records; ++i)
{
- entry= (struct local_element *)my_hash_element(&hash, i);
+ entry= (struct gtid_pos_element *)my_hash_element(&hash, i);
if (opt_bin_log &&
mysql_bin_log.bump_seq_no_counter_if_needed(entry->gtid.domain_id,
entry->gtid.seq_no))
@@ -1719,27 +1956,174 @@ rpl_load_gtid_slave_state(THD *thd)
}
}
+ rpl_global_gtid_slave_state->set_gtid_pos_tables_list(cb_data.table_list,
+ cb_data.default_entry);
+ cb_data.table_list= NULL;
rpl_global_gtid_slave_state->loaded= true;
mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
- err= 0; /* Clear HA_ERR_END_OF_FILE */
+end:
+ if (array_inited)
+ delete_dynamic(&array);
+ my_hash_free(&hash);
+ if (cb_data.table_list)
+ rpl_global_gtid_slave_state->free_gtid_pos_tables(cb_data.table_list);
+ DBUG_RETURN(err);
+}
+
+
+static int
+find_gtid_pos_tables_cb(THD *thd, LEX_CSTRING *table_name, void *arg)
+{
+ load_gtid_state_cb_data *data= static_cast<load_gtid_state_cb_data *>(arg);
+ TABLE_LIST tlist;
+ TABLE *table= NULL;
+ int err;
+
+ thd->reset_for_next_command();
+ tlist.init_one_table(&MYSQL_SCHEMA_NAME, table_name, NULL, TL_READ);
+ if ((err= open_and_lock_tables(thd, &tlist, FALSE, 0)))
+ goto end;
+ table= tlist.table;
+
+ if ((err= gtid_check_rpl_slave_state_table(table)))
+ goto end;
+ err= process_gtid_pos_table(thd, table_name, table->s->db_type(), data);
end:
- if (table_scanned)
+ if (table)
{
- table->file->ha_index_or_rnd_end();
ha_commit_trans(thd, FALSE);
ha_commit_trans(thd, TRUE);
- }
- if (table_opened)
- {
close_thread_tables(thd);
thd->mdl_context.release_transactional_locks();
}
- if (array_inited)
- delete_dynamic(&array);
- my_hash_free(&hash);
- DBUG_RETURN(err);
+
+ return err;
+}
+
+
+/*
+ Re-compute the list of available mysql.gtid_slave_posXXX tables.
+
+ This is done at START SLAVE to pick up any newly created tables without
+ requiring server restart.
+*/
+int
+find_gtid_slave_pos_tables(THD *thd)
+{
+ int err= 0;
+ load_gtid_state_cb_data cb_data;
+ uint num_running;
+
+ mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+ bool loaded= rpl_global_gtid_slave_state->loaded;
+ mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+ if (!loaded)
+ return 0;
+
+ cb_data.table_list= NULL;
+ cb_data.default_entry= NULL;
+ if ((err= scan_all_gtid_slave_pos_table(thd, find_gtid_pos_tables_cb, &cb_data)))
+ goto end;
+
+ if (!cb_data.table_list)
+ {
+ my_error(ER_NO_SUCH_TABLE, MYF(0), "mysql",
+ rpl_gtid_slave_state_table_name.str);
+ err= 1;
+ goto end;
+ }
+ if (!cb_data.default_entry)
+ {
+ /*
+ If the mysql.gtid_slave_pos table does not exist, but at least one other
+ table is available, arbitrarily pick the first in the list to use as
+ default.
+ */
+ cb_data.default_entry= cb_data.table_list;
+ }
+ if ((err= gtid_pos_auto_create_tables(&cb_data.table_list)))
+ goto end;
+
+ mysql_mutex_lock(&LOCK_active_mi);
+ num_running= any_slave_sql_running(true);
+ mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+ if (num_running <= 1)
+ {
+ /*
+ If no slave is running now, the count will be 1, since this SQL thread
+ which is starting is included in the count. In this case, we can safely
+ replace the list, no-one can be trying to read it without lock.
+ */
+ DBUG_ASSERT(num_running == 1);
+ rpl_global_gtid_slave_state->set_gtid_pos_tables_list(cb_data.table_list,
+ cb_data.default_entry);
+ cb_data.table_list= NULL;
+ }
+ else
+ {
+ /*
+ If there are SQL threads running, we cannot safely remove the old list.
+ However we can add new entries, and warn about any tables that
+ disappeared, but may still be visible to running SQL threads.
+ */
+ rpl_slave_state::gtid_pos_table *old_entry, *new_entry, **next_ptr_ptr;
+
+ old_entry= (rpl_slave_state::gtid_pos_table *)
+ rpl_global_gtid_slave_state->gtid_pos_tables;
+ while (old_entry)
+ {
+ new_entry= cb_data.table_list;
+ while (new_entry)
+ {
+ if (new_entry->table_hton == old_entry->table_hton)
+ break;
+ new_entry= new_entry->next;
+ }
+ if (!new_entry)
+ sql_print_warning("The table mysql.%s was removed. "
+ "This change will not take full effect "
+ "until all SQL threads have been restarted",
+ old_entry->table_name.str);
+ old_entry= old_entry->next;
+ }
+ next_ptr_ptr= &cb_data.table_list;
+ new_entry= cb_data.table_list;
+ while (new_entry)
+ {
+ /* Check if we already have a table with this storage engine. */
+ old_entry= (rpl_slave_state::gtid_pos_table *)
+ rpl_global_gtid_slave_state->gtid_pos_tables;
+ while (old_entry)
+ {
+ if (new_entry->table_hton == old_entry->table_hton)
+ break;
+ old_entry= old_entry->next;
+ }
+ if (old_entry)
+ {
+ /* This new_entry is already available in the list. */
+ next_ptr_ptr= &new_entry->next;
+ new_entry= new_entry->next;
+ }
+ else
+ {
+ /* Move this new_entry to the list. */
+ rpl_slave_state::gtid_pos_table *next= new_entry->next;
+ rpl_global_gtid_slave_state->add_gtid_pos_table(new_entry);
+ *next_ptr_ptr= next;
+ new_entry= next;
+ }
+ }
+ }
+ mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+ mysql_mutex_unlock(&LOCK_active_mi);
+
+end:
+ if (cb_data.table_list)
+ rpl_global_gtid_slave_state->free_gtid_pos_tables(cb_data.table_list);
+ return err;
}
@@ -1880,7 +2264,7 @@ void rpl_group_info::cleanup_context(THD *thd, bool error)
to rollback before continuing with the next events.
4) so we need this "context cleanup" function.
*/
- if (error)
+ if (unlikely(error))
{
trans_rollback_stmt(thd); // if a "statement transaction"
/* trans_rollback() also resets OPTION_GTID_BEGIN */
@@ -1900,7 +2284,7 @@ void rpl_group_info::cleanup_context(THD *thd, bool error)
m_table_map.clear_tables();
slave_close_thread_tables(thd);
- if (error)
+ if (unlikely(error))
{
thd->mdl_context.release_transactional_locks();
diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h
index 2bc0a80268a..cfe581ebb7a 100644
--- a/sql/rpl_rli.h
+++ b/sql/rpl_rli.h
@@ -767,7 +767,7 @@ struct rpl_group_info
Runtime state for printing a note when slave is taking
too long while processing a row event.
*/
- time_t row_stmt_start_timestamp;
+ longlong row_stmt_start_timestamp;
bool long_find_row_note_printed;
/* Needs room for "Gtid D-S-N\x00". */
char gtid_info_buf[5+10+1+10+1+20+1];
@@ -924,17 +924,15 @@ struct rpl_group_info
void pending_gtid_deletes_put_back();
void pending_gtid_deletes_clear();
- time_t get_row_stmt_start_timestamp()
+ longlong get_row_stmt_start_timestamp()
{
return row_stmt_start_timestamp;
}
- time_t set_row_stmt_start_timestamp()
+ void set_row_stmt_start_timestamp()
{
if (row_stmt_start_timestamp == 0)
- row_stmt_start_timestamp= my_time(0);
-
- return row_stmt_start_timestamp;
+ row_stmt_start_timestamp= microsecond_interval_timer();
}
void reset_row_stmt_start_timestamp()
@@ -993,6 +991,7 @@ extern struct rpl_slave_state *rpl_global_gtid_slave_state;
extern gtid_waiting rpl_global_gtid_waiting;
int rpl_load_gtid_slave_state(THD *thd);
+int find_gtid_slave_pos_tables(THD *thd);
int event_group_new_gtid(rpl_group_info *rgi, Gtid_log_event *gev);
void delete_or_keep_event_post_apply(rpl_group_info *rgi,
Log_event_type typ, Log_event *ev);
diff --git a/sql/rpl_tblmap.cc b/sql/rpl_tblmap.cc
index 2ec48162786..b2da9092e3a 100644
--- a/sql/rpl_tblmap.cc
+++ b/sql/rpl_tblmap.cc
@@ -13,7 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#ifdef HAVE_REPLICATION
@@ -46,7 +46,8 @@ table_mapping::table_mapping()
offsetof(entry,table_id),sizeof(ulonglong),
0,0,0);
/* We don't preallocate any block, this is consistent with m_free=0 above */
- init_alloc_root(&m_mem_root, TABLE_ID_HASH_SIZE*sizeof(entry), 0, MYF(0));
+ init_alloc_root(&m_mem_root, "table_mapping",
+ 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 c28019e4dab..437d58d772f 100644
--- a/sql/rpl_utility.cc
+++ b/sql/rpl_utility.cc
@@ -14,7 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include <my_bit.h>
#include "rpl_utility.h"
#include "log_event.h"
@@ -42,6 +42,12 @@ max_display_length_for_temporal2_field(uint32 int_display_length,
@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)
@@ -128,6 +134,8 @@ max_display_length_for_field(enum_field_types sql_type, unsigned int metadata)
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
@@ -139,22 +147,23 @@ max_display_length_for_field(enum_field_types sql_type, unsigned int metadata)
*/
case MYSQL_TYPE_TINY_BLOB:
- return my_set_bits(1 * 8);
+ return (uint32)my_set_bits(1 * 8);
case MYSQL_TYPE_MEDIUM_BLOB:
- return my_set_bits(3 * 8);
+ 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 my_set_bits(metadata * 8);
+ return (uint32)my_set_bits(metadata * 8);
case MYSQL_TYPE_LONG_BLOB:
case MYSQL_TYPE_GEOMETRY:
- return my_set_bits(4 * 8);
+ return (uint32)my_set_bits(4 * 8);
default:
return ~(uint32) 0;
@@ -294,6 +303,7 @@ uint32 table_def::calc_field_size(uint col, uchar *master_data) const
break;
}
case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_VARCHAR_COMPRESSED:
{
length= m_field_metadata[col] > 255 ? 2 : 1; // c&p of Field_varstring::data_length()
length+= length == 1 ? (uint32) *master_data : uint2korr(master_data);
@@ -303,6 +313,7 @@ uint32 table_def::calc_field_size(uint col, uchar *master_data) const
case MYSQL_TYPE_MEDIUM_BLOB:
case MYSQL_TYPE_LONG_BLOB:
case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_BLOB_COMPRESSED:
case MYSQL_TYPE_GEOMETRY:
{
/*
@@ -407,9 +418,10 @@ void show_sql_type(enum_field_types type, uint16 metadata, String *str,
case MYSQL_TYPE_VAR_STRING:
case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_VARCHAR_COMPRESSED:
{
CHARSET_INFO *cs= str->charset();
- uint32 length=0;
+ size_t length=0;
if (char_with_octets)
length= cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(),
"varchar(%u octets)", metadata);
@@ -424,7 +436,7 @@ void show_sql_type(enum_field_types type, uint16 metadata, String *str,
{
CHARSET_INFO *cs= str->charset();
int bit_length= 8 * (metadata >> 8) + (metadata & 0xFF);
- uint32 length=
+ size_t length=
cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(),
"bit(%d)", bit_length);
str->length(length);
@@ -434,7 +446,7 @@ void show_sql_type(enum_field_types type, uint16 metadata, String *str,
case MYSQL_TYPE_DECIMAL:
{
CHARSET_INFO *cs= str->charset();
- uint32 length=
+ size_t length=
cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(),
"decimal(%d,?)/*old*/", metadata);
str->length(length);
@@ -444,7 +456,7 @@ void show_sql_type(enum_field_types type, uint16 metadata, String *str,
case MYSQL_TYPE_NEWDECIMAL:
{
CHARSET_INFO *cs= str->charset();
- uint32 length=
+ size_t length=
cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(),
"decimal(%d,%d)", metadata >> 8, metadata & 0xff);
str->length(length);
@@ -460,6 +472,7 @@ void show_sql_type(enum_field_types type, uint16 metadata, String *str,
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
@@ -487,6 +500,9 @@ void show_sql_type(enum_field_types type, uint16 metadata, String *str,
DBUG_ASSERT(0);
break;
}
+
+ if (type == MYSQL_TYPE_BLOB_COMPRESSED)
+ str->append(STRING_WITH_LEN(" compressed"));
break;
case MYSQL_TYPE_STRING:
@@ -496,7 +512,7 @@ void show_sql_type(enum_field_types type, uint16 metadata, String *str,
*/
CHARSET_INFO *cs= str->charset();
uint bytes= (((metadata >> 4) & 0x300) ^ 0x300) + (metadata & 0x00ff);
- uint32 length=0;
+ size_t length=0;
if (char_with_octets)
length= cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(),
"char(%u octets)", bytes);
@@ -592,6 +608,7 @@ can_convert_field_to(Field *field,
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);
@@ -599,11 +616,30 @@ can_convert_field_to(Field *field,
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 (field->real_type() == source_type)
+ if (same_type)
{
if (metadata == 0) // Metadata can only be zero if no metadata was provided
{
@@ -714,6 +750,16 @@ can_convert_field_to(Field *field,
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));
@@ -740,18 +786,22 @@ can_convert_field_to(Field *field,
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
@@ -864,7 +914,7 @@ table_def::compatible_with(THD *thd, rpl_group_info *rgi,
{
DBUG_PRINT("debug", ("Checking column %d -"
" field '%s' can be converted - order: %d",
- col, field->field_name, order));
+ col, field->field_name.str, order));
DBUG_ASSERT(order >= -1 && order <= 1);
/*
@@ -894,7 +944,7 @@ table_def::compatible_with(THD *thd, rpl_group_info *rgi,
{
DBUG_PRINT("debug", ("Checking column %d -"
" field '%s' can not be converted",
- col, field->field_name));
+ 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);
@@ -934,7 +984,7 @@ table_def::compatible_with(THD *thd, rpl_group_info *rgi,
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,
+ tmp_table->field[col]->field_name.str,
source_type.c_ptr_safe(), target_type.c_ptr_safe()));
}
}
@@ -971,7 +1021,7 @@ public:
(int) sql_type,
target_field->table->s->db.str,
target_field->table->s->table_name.str,
- target_field->field_name);
+ target_field->field_name.str);
return true;
}
Field *tmp= handler->make_conversion_table_field(this, metadata,
@@ -981,7 +1031,7 @@ public:
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,
+ sql_type, target_field->field_name.str,
tmp->field_length, tmp->decimals(), TRUE,
tmp->flags, tmp->pack_length()));
return false;
@@ -1023,7 +1073,7 @@ TABLE *table_def::create_conversion_table(THD *thd, rpl_group_info *rgi,
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));
+ target_table->field[col]->field_name.str));
goto err;
}
}
@@ -1079,6 +1129,7 @@ table_def::table_def(unsigned char *types, ulong size,
switch (binlog_type(i)) {
case MYSQL_TYPE_TINY_BLOB:
case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_BLOB_COMPRESSED:
case MYSQL_TYPE_MEDIUM_BLOB:
case MYSQL_TYPE_LONG_BLOB:
case MYSQL_TYPE_DOUBLE:
@@ -1109,6 +1160,7 @@ table_def::table_def(unsigned char *types, ulong size,
break;
}
case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_VARCHAR_COMPRESSED:
{
/*
These types store two bytes.
@@ -1170,7 +1222,7 @@ bool event_checksum_test(uchar *event_buf, ulong event_len, enum enum_binlog_che
if (event_buf[EVENT_TYPE_OFFSET] == FORMAT_DESCRIPTION_EVENT)
{
-#ifndef DBUG_OFF
+#ifdef DBUG_ASSERT_EXISTS
int8 fd_alg= event_buf[event_len - BINLOG_CHECKSUM_LEN -
BINLOG_CHECKSUM_ALG_DESC_LEN];
#endif
diff --git a/sql/scheduler.cc b/sql/scheduler.cc
index 1d2f506cde6..5a20566c89e 100644
--- a/sql/scheduler.cc
+++ b/sql/scheduler.cc
@@ -22,6 +22,7 @@
#pragma implementation
#endif
+#include "mariadb.h"
#include "mysqld.h"
#include "sql_connect.h" // init_new_connection_handler_thread
#include "scheduler.h"
diff --git a/sql/scheduler.h b/sql/scheduler.h
index a32d090f563..42895134c83 100644
--- a/sql/scheduler.h
+++ b/sql/scheduler.h
@@ -25,8 +25,6 @@
#pragma interface
#endif
-#include <my_global.h>
-
class THD;
/* Functions used when manipulating threads */
diff --git a/sql/semisync.cc b/sql/semisync.cc
new file mode 100644
index 00000000000..e3638d8bf7a
--- /dev/null
+++ b/sql/semisync.cc
@@ -0,0 +1,32 @@
+/* Copyright (C) 2007 Google Inc.
+ Copyright (C) 2008 MySQL AB
+ Use is subject to license terms
+
+ 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>
+#include "semisync.h"
+
+const unsigned char Repl_semi_sync_base::k_packet_magic_num= 0xef;
+const unsigned char Repl_semi_sync_base::k_packet_flag_sync= 0x01;
+
+
+const unsigned long Trace::k_trace_general= 0x0001;
+const unsigned long Trace::k_trace_detail= 0x0010;
+const unsigned long Trace::k_trace_net_wait= 0x0020;
+const unsigned long Trace::k_trace_function= 0x0040;
+
+const unsigned char Repl_semi_sync_base::k_sync_header[2]=
+ {Repl_semi_sync_base::k_packet_magic_num, 0};
diff --git a/sql/semisync.h b/sql/semisync.h
new file mode 100644
index 00000000000..44f236606fd
--- /dev/null
+++ b/sql/semisync.h
@@ -0,0 +1,73 @@
+/* Copyright (C) 2007 Google Inc.
+ Copyright (C) 2008 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
+
+
+#ifndef SEMISYNC_H
+#define SEMISYNC_H
+
+#include "mysqld.h"
+#include "log_event.h"
+#include "replication.h"
+
+/**
+ This class is used to trace function calls and other process
+ information
+*/
+class Trace {
+public:
+ static const unsigned long k_trace_function;
+ static const unsigned long k_trace_general;
+ static const unsigned long k_trace_detail;
+ static const unsigned long k_trace_net_wait;
+
+ unsigned long m_trace_level; /* the level for tracing */
+
+ Trace()
+ :m_trace_level(0L)
+ {}
+ Trace(unsigned long trace_level)
+ :m_trace_level(trace_level)
+ {}
+};
+
+/**
+ Base class for semi-sync master and slave classes
+*/
+class Repl_semi_sync_base
+ :public Trace {
+public:
+ static const unsigned char k_sync_header[2]; /* three byte packet header */
+
+ /* Constants in network packet header. */
+ static const unsigned char k_packet_magic_num;
+ static const unsigned char k_packet_flag_sync;
+};
+
+/* The layout of a semisync slave reply packet:
+ 1 byte for the magic num
+ 8 bytes for the binlog positon
+ n bytes for the binlog filename, terminated with a '\0'
+*/
+#define REPLY_MAGIC_NUM_LEN 1
+#define REPLY_BINLOG_POS_LEN 8
+#define REPLY_BINLOG_NAME_LEN (FN_REFLEN + 1)
+#define REPLY_MAGIC_NUM_OFFSET 0
+#define REPLY_BINLOG_POS_OFFSET (REPLY_MAGIC_NUM_OFFSET + REPLY_MAGIC_NUM_LEN)
+#define REPLY_BINLOG_NAME_OFFSET (REPLY_BINLOG_POS_OFFSET + REPLY_BINLOG_POS_LEN)
+#define REPLY_MESSAGE_MAX_LENGTH \
+ (REPLY_MAGIC_NUM_LEN + REPLY_BINLOG_POS_LEN + REPLY_BINLOG_NAME_LEN)
+
+#endif /* SEMISYNC_H */
diff --git a/sql/semisync_master.cc b/sql/semisync_master.cc
new file mode 100644
index 00000000000..b239a9776a7
--- /dev/null
+++ b/sql/semisync_master.cc
@@ -0,0 +1,1352 @@
+/* Copyright (C) 2007 Google Inc.
+ Copyright (c) 2008, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2011, 2016, MariaDB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+
+#include <my_global.h>
+#include "semisync_master.h"
+
+#define TIME_THOUSAND 1000
+#define TIME_MILLION 1000000
+#define TIME_BILLION 1000000000
+
+/* This indicates whether semi-synchronous replication is enabled. */
+my_bool rpl_semi_sync_master_enabled= 0;
+unsigned long long rpl_semi_sync_master_request_ack = 0;
+unsigned long long rpl_semi_sync_master_get_ack = 0;
+my_bool rpl_semi_sync_master_wait_no_slave = 1;
+my_bool rpl_semi_sync_master_status = 0;
+ulong rpl_semi_sync_master_wait_point =
+ SEMI_SYNC_MASTER_WAIT_POINT_AFTER_STORAGE_COMMIT;
+ulong rpl_semi_sync_master_timeout;
+ulong rpl_semi_sync_master_trace_level;
+ulong rpl_semi_sync_master_yes_transactions = 0;
+ulong rpl_semi_sync_master_no_transactions = 0;
+ulong rpl_semi_sync_master_off_times = 0;
+ulong rpl_semi_sync_master_timefunc_fails = 0;
+ulong rpl_semi_sync_master_wait_timeouts = 0;
+ulong rpl_semi_sync_master_wait_sessions = 0;
+ulong rpl_semi_sync_master_wait_pos_backtraverse = 0;
+ulong rpl_semi_sync_master_avg_trx_wait_time = 0;
+ulonglong rpl_semi_sync_master_trx_wait_num = 0;
+ulong rpl_semi_sync_master_avg_net_wait_time = 0;
+ulonglong rpl_semi_sync_master_net_wait_num = 0;
+ulong rpl_semi_sync_master_clients = 0;
+ulonglong rpl_semi_sync_master_net_wait_time = 0;
+ulonglong rpl_semi_sync_master_trx_wait_time = 0;
+
+Repl_semi_sync_master repl_semisync_master;
+Ack_receiver ack_receiver;
+
+/*
+ structure to save transaction log filename and position
+*/
+typedef struct Trans_binlog_info {
+ my_off_t log_pos;
+ char log_file[FN_REFLEN];
+} Trans_binlog_info;
+
+static int get_wait_time(const struct timespec& start_ts);
+
+static ulonglong timespec_to_usec(const struct timespec *ts)
+{
+ return (ulonglong) ts->tv_sec * TIME_MILLION + ts->tv_nsec / TIME_THOUSAND;
+}
+
+/*******************************************************************************
+ *
+ * <Active_tranx> class : manage all active transaction nodes
+ *
+ ******************************************************************************/
+
+Active_tranx::Active_tranx(mysql_mutex_t *lock,
+ ulong trace_level)
+ : Trace(trace_level), m_allocator(max_connections),
+ m_num_entries(max_connections << 1), /* Transaction hash table size
+ * is set to double the size
+ * of max_connections */
+ m_lock(lock)
+{
+ /* No transactions are in the list initially. */
+ m_trx_front = NULL;
+ m_trx_rear = NULL;
+
+ /* Create the hash table to find a transaction's ending event. */
+ m_trx_htb = new Tranx_node *[m_num_entries];
+ for (int idx = 0; idx < m_num_entries; ++idx)
+ m_trx_htb[idx] = NULL;
+
+ sql_print_information("Semi-sync replication initialized for transactions.");
+}
+
+Active_tranx::~Active_tranx()
+{
+ delete [] m_trx_htb;
+ m_trx_htb = NULL;
+ m_num_entries = 0;
+}
+
+unsigned int Active_tranx::calc_hash(const unsigned char *key, size_t length)
+{
+ unsigned int nr = 1, nr2 = 4;
+
+ /* The hash implementation comes from calc_hashnr() in mysys/hash.c. */
+ while (length--)
+ {
+ nr ^= (((nr & 63)+nr2)*((unsigned int) (unsigned char) *key++))+ (nr << 8);
+ nr2 += 3;
+ }
+ return((unsigned int) nr);
+}
+
+unsigned int Active_tranx::get_hash_value(const char *log_file_name,
+ my_off_t log_file_pos)
+{
+ unsigned int hash1 = calc_hash((const unsigned char *)log_file_name,
+ strlen(log_file_name));
+ unsigned int hash2 = calc_hash((const unsigned char *)(&log_file_pos),
+ sizeof(log_file_pos));
+
+ return (hash1 + hash2) % m_num_entries;
+}
+
+int Active_tranx::compare(const char *log_file_name1, my_off_t log_file_pos1,
+ const char *log_file_name2, my_off_t log_file_pos2)
+{
+ int cmp = strcmp(log_file_name1, log_file_name2);
+
+ if (cmp != 0)
+ return cmp;
+
+ if (log_file_pos1 > log_file_pos2)
+ return 1;
+ else if (log_file_pos1 < log_file_pos2)
+ return -1;
+ return 0;
+}
+
+int Active_tranx::insert_tranx_node(const char *log_file_name,
+ my_off_t log_file_pos)
+{
+ Tranx_node *ins_node;
+ int result = 0;
+ unsigned int hash_val;
+
+ DBUG_ENTER("Active_tranx:insert_tranx_node");
+
+ ins_node = m_allocator.allocate_node();
+ if (!ins_node)
+ {
+ sql_print_error("%s: transaction node allocation failed for: (%s, %lu)",
+ "Active_tranx:insert_tranx_node",
+ log_file_name, (ulong)log_file_pos);
+ result = -1;
+ goto l_end;
+ }
+
+ /* insert the binlog position in the active transaction list. */
+ strncpy(ins_node->log_name, log_file_name, FN_REFLEN-1);
+ ins_node->log_name[FN_REFLEN-1] = 0; /* make sure it ends properly */
+ ins_node->log_pos = log_file_pos;
+
+ if (!m_trx_front)
+ {
+ /* The list is empty. */
+ m_trx_front = m_trx_rear = ins_node;
+ }
+ else
+ {
+ int cmp = compare(ins_node, m_trx_rear);
+ if (cmp > 0)
+ {
+ /* Compare with the tail first. If the transaction happens later in
+ * binlog, then make it the new tail.
+ */
+ m_trx_rear->next = ins_node;
+ m_trx_rear = ins_node;
+ }
+ else
+ {
+ /* Otherwise, it is an error because the transaction should hold the
+ * mysql_bin_log.LOCK_log when appending events.
+ */
+ sql_print_error("%s: binlog write out-of-order, tail (%s, %lu), "
+ "new node (%s, %lu)", "Active_tranx:insert_tranx_node",
+ m_trx_rear->log_name, (ulong)m_trx_rear->log_pos,
+ ins_node->log_name, (ulong)ins_node->log_pos);
+ result = -1;
+ goto l_end;
+ }
+ }
+
+ hash_val = get_hash_value(ins_node->log_name, ins_node->log_pos);
+ ins_node->hash_next = m_trx_htb[hash_val];
+ m_trx_htb[hash_val] = ins_node;
+
+ DBUG_PRINT("semisync", ("%s: insert (%s, %lu) in entry(%u)",
+ "Active_tranx:insert_tranx_node",
+ ins_node->log_name, (ulong)ins_node->log_pos,
+ hash_val));
+ l_end:
+
+ DBUG_RETURN(result);
+}
+
+bool Active_tranx::is_tranx_end_pos(const char *log_file_name,
+ my_off_t log_file_pos)
+{
+ DBUG_ENTER("Active_tranx::is_tranx_end_pos");
+
+ unsigned int hash_val = get_hash_value(log_file_name, log_file_pos);
+ Tranx_node *entry = m_trx_htb[hash_val];
+
+ while (entry != NULL)
+ {
+ if (compare(entry, log_file_name, log_file_pos) == 0)
+ break;
+
+ entry = entry->hash_next;
+ }
+
+ DBUG_PRINT("semisync", ("%s: probe (%s, %lu) in entry(%u)",
+ "Active_tranx::is_tranx_end_pos",
+ log_file_name, (ulong)log_file_pos, hash_val));
+
+ DBUG_RETURN(entry != NULL);
+}
+
+void Active_tranx::clear_active_tranx_nodes(const char *log_file_name,
+ my_off_t log_file_pos)
+{
+ Tranx_node *new_front;
+
+ DBUG_ENTER("Active_tranx::::clear_active_tranx_nodes");
+
+ if (log_file_name != NULL)
+ {
+ new_front = m_trx_front;
+
+ while (new_front)
+ {
+ if (compare(new_front, log_file_name, log_file_pos) > 0)
+ break;
+ new_front = new_front->next;
+ }
+ }
+ else
+ {
+ /* If log_file_name is NULL, clear everything. */
+ new_front = NULL;
+ }
+
+ if (new_front == NULL)
+ {
+ /* No active transaction nodes after the call. */
+
+ /* Clear the hash table. */
+ memset(m_trx_htb, 0, m_num_entries * sizeof(Tranx_node *));
+ m_allocator.free_all_nodes();
+
+ /* Clear the active transaction list. */
+ if (m_trx_front != NULL)
+ {
+ m_trx_front = NULL;
+ m_trx_rear = NULL;
+ }
+
+ DBUG_PRINT("semisync", ("%s: cleared all nodes",
+ "Active_tranx::::clear_active_tranx_nodes"));
+ }
+ else if (new_front != m_trx_front)
+ {
+ Tranx_node *curr_node, *next_node;
+
+ /* Delete all transaction nodes before the confirmation point. */
+ int n_frees = 0;
+ curr_node = m_trx_front;
+ while (curr_node != new_front)
+ {
+ next_node = curr_node->next;
+ n_frees++;
+
+ /* Remove the node from the hash table. */
+ unsigned int hash_val = get_hash_value(curr_node->log_name, curr_node->log_pos);
+ Tranx_node **hash_ptr = &(m_trx_htb[hash_val]);
+ while ((*hash_ptr) != NULL)
+ {
+ if ((*hash_ptr) == curr_node)
+ {
+ (*hash_ptr) = curr_node->hash_next;
+ break;
+ }
+ hash_ptr = &((*hash_ptr)->hash_next);
+ }
+
+ curr_node = next_node;
+ }
+
+ m_trx_front = new_front;
+ m_allocator.free_nodes_before(m_trx_front);
+
+ DBUG_PRINT("semisync", ("%s: cleared %d nodes back until pos (%s, %lu)",
+ "Active_tranx::::clear_active_tranx_nodes",
+ n_frees,
+ m_trx_front->log_name, (ulong)m_trx_front->log_pos));
+ }
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*******************************************************************************
+ *
+ * <Repl_semi_sync_master> class: the basic code layer for syncsync master.
+ * <Repl_semi_sync_slave> class: the basic code layer for syncsync slave.
+ *
+ * The most important functions during semi-syn replication listed:
+ *
+ * Master:
+ * . report_reply_binlog(): called by the binlog dump thread when it receives
+ * the slave's status information.
+ * . update_sync_header(): based on transaction waiting information, decide
+ * whether to request the slave to reply.
+ * . write_tranx_in_binlog(): called by the transaction thread when it finishes
+ * writing all transaction events in binlog.
+ * . commit_trx(): transaction thread wait for the slave reply.
+ *
+ * Slave:
+ * . slave_read_sync_header(): read the semi-sync header from the master, get
+ * the sync status and get the payload for events.
+ * . slave_reply(): reply to the master about the replication progress.
+ *
+ ******************************************************************************/
+
+Repl_semi_sync_master::Repl_semi_sync_master()
+ : m_active_tranxs(NULL),
+ m_init_done(false),
+ m_reply_file_name_inited(false),
+ m_reply_file_pos(0L),
+ m_wait_file_name_inited(false),
+ m_wait_file_pos(0),
+ m_master_enabled(false),
+ m_wait_timeout(0L),
+ m_state(0),
+ m_wait_point(0)
+{
+ strcpy(m_reply_file_name, "");
+ strcpy(m_wait_file_name, "");
+}
+
+int Repl_semi_sync_master::init_object()
+{
+ int result= 0;
+
+ m_init_done = true;
+
+ /* References to the parameter works after set_options(). */
+ set_wait_timeout(rpl_semi_sync_master_timeout);
+ set_trace_level(rpl_semi_sync_master_trace_level);
+ set_wait_point(rpl_semi_sync_master_wait_point);
+
+ /* Mutex initialization can only be done after MY_INIT(). */
+ mysql_mutex_init(key_LOCK_rpl_semi_sync_master_enabled,
+ &LOCK_rpl_semi_sync_master_enabled, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_binlog,
+ &LOCK_binlog, MY_MUTEX_INIT_FAST);
+ mysql_cond_init(key_COND_binlog_send,
+ &COND_binlog_send, NULL);
+
+ if (rpl_semi_sync_master_enabled)
+ {
+ result = enable_master();
+ if (!result)
+ {
+ result= ack_receiver.start(); /* Start the ACK thread. */
+ /*
+ If rpl_semi_sync_master_wait_no_slave is disabled, let's temporarily
+ switch off semisync to avoid hang if there's none active slave.
+ */
+ if (!rpl_semi_sync_master_wait_no_slave)
+ switch_off();
+ }
+ }
+ else
+ {
+ disable_master();
+ }
+
+ return result;
+}
+
+int Repl_semi_sync_master::enable_master()
+{
+ int result = 0;
+
+ /* Must have the lock when we do enable of disable. */
+ lock();
+
+ if (!get_master_enabled())
+ {
+ m_active_tranxs = new Active_tranx(&LOCK_binlog, m_trace_level);
+ if (m_active_tranxs != NULL)
+ {
+ m_commit_file_name_inited = false;
+ m_reply_file_name_inited = false;
+ m_wait_file_name_inited = false;
+
+ set_master_enabled(true);
+ m_state = true;
+ sql_print_information("Semi-sync replication enabled on the master.");
+ }
+ else
+ {
+ sql_print_error("Cannot allocate memory to enable semi-sync on the master.");
+ result = -1;
+ }
+ }
+
+ unlock();
+
+ return result;
+}
+
+void Repl_semi_sync_master::disable_master()
+{
+ /* Must have the lock when we do enable of disable. */
+ lock();
+
+ if (get_master_enabled())
+ {
+ /* Switch off the semi-sync first so that waiting transaction will be
+ * waken up.
+ */
+ switch_off();
+
+ assert(m_active_tranxs != NULL);
+ delete m_active_tranxs;
+ m_active_tranxs = NULL;
+
+ m_reply_file_name_inited = false;
+ m_wait_file_name_inited = false;
+ m_commit_file_name_inited = false;
+
+ set_master_enabled(false);
+ sql_print_information("Semi-sync replication disabled on the master.");
+ }
+
+ unlock();
+}
+
+void Repl_semi_sync_master::cleanup()
+{
+ if (m_init_done)
+ {
+ mysql_mutex_destroy(&LOCK_rpl_semi_sync_master_enabled);
+ mysql_mutex_destroy(&LOCK_binlog);
+ mysql_cond_destroy(&COND_binlog_send);
+ m_init_done= 0;
+ }
+
+ delete m_active_tranxs;
+}
+
+void Repl_semi_sync_master::lock()
+{
+ mysql_mutex_lock(&LOCK_binlog);
+}
+
+void Repl_semi_sync_master::unlock()
+{
+ mysql_mutex_unlock(&LOCK_binlog);
+}
+
+void Repl_semi_sync_master::cond_broadcast()
+{
+ mysql_cond_broadcast(&COND_binlog_send);
+}
+
+int Repl_semi_sync_master::cond_timewait(struct timespec *wait_time)
+{
+ int wait_res;
+
+ DBUG_ENTER("Repl_semi_sync_master::cond_timewait()");
+
+ wait_res= mysql_cond_timedwait(&COND_binlog_send,
+ &LOCK_binlog, wait_time);
+
+ DBUG_RETURN(wait_res);
+}
+
+void Repl_semi_sync_master::add_slave()
+{
+ lock();
+ rpl_semi_sync_master_clients++;
+ unlock();
+}
+
+void Repl_semi_sync_master::remove_slave()
+{
+ lock();
+ rpl_semi_sync_master_clients--;
+
+ /* Only switch off if semi-sync is enabled and is on */
+ if (get_master_enabled() && is_on())
+ {
+ /* If user has chosen not to wait if no semi-sync slave available
+ and the last semi-sync slave exits, turn off semi-sync on master
+ immediately.
+ */
+ if (!rpl_semi_sync_master_wait_no_slave &&
+ rpl_semi_sync_master_clients == 0)
+ switch_off();
+ }
+ unlock();
+}
+
+int Repl_semi_sync_master::report_reply_packet(uint32 server_id,
+ const uchar *packet,
+ ulong packet_len)
+{
+ int result= -1;
+ char log_file_name[FN_REFLEN+1];
+ my_off_t log_file_pos;
+ ulong log_file_len = 0;
+
+ DBUG_ENTER("Repl_semi_sync_master::report_reply_packet");
+
+ if (unlikely(packet[REPLY_MAGIC_NUM_OFFSET] !=
+ Repl_semi_sync_master::k_packet_magic_num))
+ {
+ sql_print_error("Read semi-sync reply magic number error");
+ goto l_end;
+ }
+
+ if (unlikely(packet_len < REPLY_BINLOG_NAME_OFFSET))
+ {
+ sql_print_error("Read semi-sync reply length error: packet is too small");
+ goto l_end;
+ }
+
+ log_file_pos = uint8korr(packet + REPLY_BINLOG_POS_OFFSET);
+ log_file_len = packet_len - REPLY_BINLOG_NAME_OFFSET;
+ if (unlikely(log_file_len >= FN_REFLEN))
+ {
+ sql_print_error("Read semi-sync reply binlog file length too large");
+ goto l_end;
+ }
+ strncpy(log_file_name, (const char*)packet + REPLY_BINLOG_NAME_OFFSET, log_file_len);
+ log_file_name[log_file_len] = 0;
+
+ DBUG_ASSERT(dirname_length(log_file_name) == 0);
+
+ DBUG_PRINT("semisync", ("%s: Got reply(%s, %lu) from server %u",
+ "Repl_semi_sync_master::report_reply_packet",
+ log_file_name, (ulong)log_file_pos, server_id));
+
+ rpl_semi_sync_master_get_ack++;
+ report_reply_binlog(server_id, log_file_name, log_file_pos);
+
+l_end:
+
+ DBUG_RETURN(result);
+}
+
+int Repl_semi_sync_master::report_reply_binlog(uint32 server_id,
+ const char *log_file_name,
+ my_off_t log_file_pos)
+{
+ int cmp;
+ bool can_release_threads = false;
+ bool need_copy_send_pos = true;
+
+ DBUG_ENTER("Repl_semi_sync_master::report_reply_binlog");
+
+ if (!(get_master_enabled()))
+ DBUG_RETURN(0);
+
+ lock();
+
+ /* This is the real check inside the mutex. */
+ if (!get_master_enabled())
+ goto l_end;
+
+ if (!is_on())
+ /* We check to see whether we can switch semi-sync ON. */
+ try_switch_on(server_id, log_file_name, log_file_pos);
+
+ /* The position should increase monotonically, if there is only one
+ * thread sending the binlog to the slave.
+ * In reality, to improve the transaction availability, we allow multiple
+ * sync replication slaves. So, if any one of them get the transaction,
+ * the transaction session in the primary can move forward.
+ */
+ if (m_reply_file_name_inited)
+ {
+ cmp = Active_tranx::compare(log_file_name, log_file_pos,
+ m_reply_file_name, m_reply_file_pos);
+
+ /* If the requested position is behind the sending binlog position,
+ * would not adjust sending binlog position.
+ * We based on the assumption that there are multiple semi-sync slave,
+ * and at least one of them shou/ld be up to date.
+ * If all semi-sync slaves are behind, at least initially, the primary
+ * can find the situation after the waiting timeout. After that, some
+ * slaves should catch up quickly.
+ */
+ if (cmp < 0)
+ {
+ /* If the position is behind, do not copy it. */
+ need_copy_send_pos = false;
+ }
+ }
+
+ if (need_copy_send_pos)
+ {
+ strmake_buf(m_reply_file_name, log_file_name);
+ m_reply_file_pos = log_file_pos;
+ m_reply_file_name_inited = true;
+
+ /* Remove all active transaction nodes before this point. */
+ assert(m_active_tranxs != NULL);
+ m_active_tranxs->clear_active_tranx_nodes(log_file_name, log_file_pos);
+
+ DBUG_PRINT("semisync", ("%s: Got reply at (%s, %lu)",
+ "Repl_semi_sync_master::report_reply_binlog",
+ log_file_name, (ulong)log_file_pos));
+ }
+
+ if (rpl_semi_sync_master_wait_sessions > 0)
+ {
+ /* Let us check if some of the waiting threads doing a trx
+ * commit can now proceed.
+ */
+ cmp = Active_tranx::compare(m_reply_file_name, m_reply_file_pos,
+ m_wait_file_name, m_wait_file_pos);
+ if (cmp >= 0)
+ {
+ /* Yes, at least one waiting thread can now proceed:
+ * let us release all waiting threads with a broadcast
+ */
+ can_release_threads = true;
+ m_wait_file_name_inited = false;
+ }
+ }
+
+ l_end:
+ unlock();
+
+ if (can_release_threads)
+ {
+ DBUG_PRINT("semisync", ("%s: signal all waiting threads.",
+ "Repl_semi_sync_master::report_reply_binlog"));
+
+ cond_broadcast();
+ }
+
+ DBUG_RETURN(0);
+}
+
+int Repl_semi_sync_master::wait_after_sync(const char *log_file, my_off_t log_pos)
+{
+ if (!get_master_enabled())
+ return 0;
+
+ int ret= 0;
+ if(log_pos &&
+ wait_point() == SEMI_SYNC_MASTER_WAIT_POINT_AFTER_BINLOG_SYNC)
+ ret= commit_trx(log_file + dirname_length(log_file), log_pos);
+
+ return ret;
+}
+
+int Repl_semi_sync_master::wait_after_commit(THD* thd, bool all)
+{
+ if (!get_master_enabled())
+ return 0;
+
+ int ret= 0;
+ const char *log_file;
+ my_off_t log_pos;
+
+ bool is_real_trans=
+ (all || thd->transaction.all.ha_list == 0);
+ /*
+ The coordinates are propagated to this point having been computed
+ in report_binlog_update
+ */
+ Trans_binlog_info *log_info= thd->semisync_info;
+ log_file= log_info && log_info->log_file[0] ? log_info->log_file : 0;
+ log_pos= log_info ? log_info->log_pos : 0;
+
+ DBUG_ASSERT(!log_file || dirname_length(log_file) == 0);
+
+ if (is_real_trans &&
+ log_pos &&
+ wait_point() == SEMI_SYNC_MASTER_WAIT_POINT_AFTER_STORAGE_COMMIT)
+ ret= commit_trx(log_file, log_pos);
+
+ if (is_real_trans && log_info)
+ {
+ log_info->log_file[0]= 0;
+ log_info->log_pos= 0;
+ }
+
+ return ret;
+}
+
+int Repl_semi_sync_master::wait_after_rollback(THD *thd, bool all)
+{
+ return wait_after_commit(thd, all);
+}
+
+/**
+ The method runs after flush to binary log is done.
+*/
+int Repl_semi_sync_master::report_binlog_update(THD* thd, const char *log_file,
+ my_off_t log_pos)
+{
+ if (get_master_enabled())
+ {
+ Trans_binlog_info *log_info;
+
+ if (!(log_info= thd->semisync_info))
+ {
+ if(!(log_info=
+ (Trans_binlog_info*) my_malloc(sizeof(Trans_binlog_info), MYF(0))))
+ return 1;
+ thd->semisync_info= log_info;
+ }
+ strcpy(log_info->log_file, log_file + dirname_length(log_file));
+ log_info->log_pos = log_pos;
+
+ return write_tranx_in_binlog(log_info->log_file, log_pos);
+ }
+
+ return 0;
+}
+
+int Repl_semi_sync_master::dump_start(THD* thd,
+ const char *log_file,
+ my_off_t log_pos)
+{
+ if (!thd->semi_sync_slave)
+ return 0;
+
+ if (ack_receiver.add_slave(thd))
+ {
+ sql_print_error("Failed to register slave to semi-sync ACK receiver "
+ "thread. Turning off semisync");
+ thd->semi_sync_slave= 0;
+ return 1;
+ }
+
+ add_slave();
+ report_reply_binlog(thd->variables.server_id,
+ log_file + dirname_length(log_file), log_pos);
+ sql_print_information("Start semi-sync binlog_dump to slave "
+ "(server_id: %ld), pos(%s, %lu)",
+ (long) thd->variables.server_id, log_file,
+ (ulong) log_pos);
+
+ return 0;
+}
+
+void Repl_semi_sync_master::dump_end(THD* thd)
+{
+ if (!thd->semi_sync_slave)
+ return;
+
+ sql_print_information("Stop semi-sync binlog_dump to slave (server_id: %ld)",
+ (long) thd->variables.server_id);
+
+ remove_slave();
+ ack_receiver.remove_slave(thd);
+
+ return;
+}
+
+int Repl_semi_sync_master::commit_trx(const char* trx_wait_binlog_name,
+ my_off_t trx_wait_binlog_pos)
+{
+ DBUG_ENTER("Repl_semi_sync_master::commit_trx");
+
+ if (get_master_enabled() && trx_wait_binlog_name)
+ {
+ struct timespec start_ts;
+ struct timespec abstime;
+ int wait_result;
+ PSI_stage_info old_stage;
+ THD *thd= current_thd;
+
+ set_timespec(start_ts, 0);
+
+ DEBUG_SYNC(thd, "rpl_semisync_master_commit_trx_before_lock");
+ /* Acquire the mutex. */
+ lock();
+
+ /* This must be called after acquired the lock */
+ THD_ENTER_COND(thd, &COND_binlog_send, &LOCK_binlog,
+ & stage_waiting_for_semi_sync_ack_from_slave,
+ & old_stage);
+
+ /* This is the real check inside the mutex. */
+ if (!get_master_enabled() || !is_on())
+ goto l_end;
+
+ DBUG_PRINT("semisync", ("%s: wait pos (%s, %lu), repl(%d)",
+ "Repl_semi_sync_master::commit_trx",
+ trx_wait_binlog_name, (ulong)trx_wait_binlog_pos,
+ (int)is_on()));
+
+ while (is_on() && !thd_killed(thd))
+ {
+ if (m_reply_file_name_inited)
+ {
+ int cmp = Active_tranx::compare(m_reply_file_name, m_reply_file_pos,
+ trx_wait_binlog_name,
+ trx_wait_binlog_pos);
+ if (cmp >= 0)
+ {
+ /* We have already sent the relevant binlog to the slave: no need to
+ * wait here.
+ */
+ DBUG_PRINT("semisync", ("%s: Binlog reply is ahead (%s, %lu),",
+ "Repl_semi_sync_master::commit_trx",
+ m_reply_file_name,
+ (ulong)m_reply_file_pos));
+ break;
+ }
+ }
+
+ /* Let us update the info about the minimum binlog position of waiting
+ * threads.
+ */
+ if (m_wait_file_name_inited)
+ {
+ int cmp = Active_tranx::compare(trx_wait_binlog_name,
+ trx_wait_binlog_pos,
+ m_wait_file_name, m_wait_file_pos);
+ if (cmp <= 0)
+ {
+ /* This thd has a lower position, let's update the minimum info. */
+ strmake_buf(m_wait_file_name, trx_wait_binlog_name);
+ m_wait_file_pos = trx_wait_binlog_pos;
+
+ rpl_semi_sync_master_wait_pos_backtraverse++;
+ DBUG_PRINT("semisync", ("%s: move back wait position (%s, %lu),",
+ "Repl_semi_sync_master::commit_trx",
+ m_wait_file_name, (ulong)m_wait_file_pos));
+ }
+ }
+ else
+ {
+ strmake_buf(m_wait_file_name, trx_wait_binlog_name);
+ m_wait_file_pos = trx_wait_binlog_pos;
+ m_wait_file_name_inited = true;
+
+ DBUG_PRINT("semisync", ("%s: init wait position (%s, %lu),",
+ "Repl_semi_sync_master::commit_trx",
+ m_wait_file_name, (ulong)m_wait_file_pos));
+ }
+
+ /* Calcuate the waiting period. */
+ long diff_secs = (long) (m_wait_timeout / TIME_THOUSAND);
+ long diff_nsecs = (long) ((m_wait_timeout % TIME_THOUSAND) * TIME_MILLION);
+ long nsecs = start_ts.tv_nsec + diff_nsecs;
+ abstime.tv_sec = start_ts.tv_sec + diff_secs + nsecs/TIME_BILLION;
+ abstime.tv_nsec = nsecs % TIME_BILLION;
+
+ /* In semi-synchronous replication, we wait until the binlog-dump
+ * thread has received the reply on the relevant binlog segment from the
+ * replication slave.
+ *
+ * Let us suspend this thread to wait on the condition;
+ * when replication has progressed far enough, we will release
+ * these waiting threads.
+ */
+ rpl_semi_sync_master_wait_sessions++;
+
+ DBUG_PRINT("semisync", ("%s: wait %lu ms for binlog sent (%s, %lu)",
+ "Repl_semi_sync_master::commit_trx",
+ m_wait_timeout,
+ m_wait_file_name, (ulong)m_wait_file_pos));
+
+ wait_result = cond_timewait(&abstime);
+ rpl_semi_sync_master_wait_sessions--;
+
+ if (wait_result != 0)
+ {
+ /* This is a real wait timeout. */
+ sql_print_warning("Timeout waiting for reply of binlog (file: %s, pos: %lu), "
+ "semi-sync up to file %s, position %lu.",
+ trx_wait_binlog_name, (ulong)trx_wait_binlog_pos,
+ m_reply_file_name, (ulong)m_reply_file_pos);
+ rpl_semi_sync_master_wait_timeouts++;
+
+ /* switch semi-sync off */
+ switch_off();
+ }
+ else
+ {
+ int wait_time;
+
+ wait_time = get_wait_time(start_ts);
+ if (wait_time < 0)
+ {
+ DBUG_PRINT("semisync", ("Replication semi-sync getWaitTime fail at "
+ "wait position (%s, %lu)",
+ trx_wait_binlog_name,
+ (ulong)trx_wait_binlog_pos));
+ rpl_semi_sync_master_timefunc_fails++;
+ }
+ else
+ {
+ rpl_semi_sync_master_trx_wait_num++;
+ rpl_semi_sync_master_trx_wait_time += wait_time;
+ }
+ }
+ }
+
+ /*
+ At this point, the binlog file and position of this transaction
+ must have been removed from Active_tranx.
+ m_active_tranxs may be NULL if someone disabled semi sync during
+ cond_timewait()
+ */
+ assert(thd_killed(thd) || !m_active_tranxs ||
+ !m_active_tranxs->is_tranx_end_pos(trx_wait_binlog_name,
+ trx_wait_binlog_pos));
+
+ l_end:
+ /* Update the status counter. */
+ if (is_on())
+ rpl_semi_sync_master_yes_transactions++;
+ else
+ rpl_semi_sync_master_no_transactions++;
+
+ /* The lock held will be released by thd_exit_cond, so no need to
+ call unlock() here */
+ THD_EXIT_COND(thd, &old_stage);
+ }
+
+ DBUG_RETURN(0);
+}
+
+/* Indicate that semi-sync replication is OFF now.
+ *
+ * What should we do when it is disabled? The problem is that we want
+ * the semi-sync replication enabled again when the slave catches up
+ * later. But, it is not that easy to detect that the slave has caught
+ * up. This is caused by the fact that MySQL's replication protocol is
+ * asynchronous, meaning that if the master does not use the semi-sync
+ * protocol, the slave would not send anything to the master.
+ * Still, if the master is sending (N+1)-th event, we assume that it is
+ * an indicator that the slave has received N-th event and earlier ones.
+ *
+ * If semi-sync is disabled, all transactions still update the wait
+ * position with the last position in binlog. But no transactions will
+ * wait for confirmations and the active transaction list would not be
+ * maintained. In binlog dump thread, update_sync_header() checks whether
+ * the current sending event catches up with last wait position. If it
+ * does match, semi-sync will be switched on again.
+ */
+void Repl_semi_sync_master::switch_off()
+{
+ DBUG_ENTER("Repl_semi_sync_master::switch_off");
+
+ m_state = false;
+
+ /* Clear the active transaction list. */
+ assert(m_active_tranxs != NULL);
+ m_active_tranxs->clear_active_tranx_nodes(NULL, 0);
+
+ rpl_semi_sync_master_off_times++;
+ m_wait_file_name_inited = false;
+ m_reply_file_name_inited = false;
+ sql_print_information("Semi-sync replication switched OFF.");
+ cond_broadcast(); /* wake up all waiting threads */
+
+ DBUG_VOID_RETURN;
+}
+
+int Repl_semi_sync_master::try_switch_on(int server_id,
+ const char *log_file_name,
+ my_off_t log_file_pos)
+{
+ bool semi_sync_on = false;
+
+ DBUG_ENTER("Repl_semi_sync_master::try_switch_on");
+
+ /* If the current sending event's position is larger than or equal to the
+ * 'largest' commit transaction binlog position, the slave is already
+ * catching up now and we can switch semi-sync on here.
+ * If m_commit_file_name_inited indicates there are no recent transactions,
+ * we can enable semi-sync immediately.
+ */
+ if (m_commit_file_name_inited)
+ {
+ int cmp = Active_tranx::compare(log_file_name, log_file_pos,
+ m_commit_file_name, m_commit_file_pos);
+ semi_sync_on = (cmp >= 0);
+ }
+ else
+ {
+ semi_sync_on = true;
+ }
+
+ if (semi_sync_on)
+ {
+ /* Switch semi-sync replication on. */
+ m_state = true;
+
+ sql_print_information("Semi-sync replication switched ON with slave (server_id: %d) "
+ "at (%s, %lu)",
+ server_id, log_file_name,
+ (ulong)log_file_pos);
+ }
+
+ DBUG_RETURN(0);
+}
+
+int Repl_semi_sync_master::reserve_sync_header(String* packet)
+{
+ DBUG_ENTER("Repl_semi_sync_master::reserve_sync_header");
+
+ /* Set the magic number and the sync status. By default, no sync
+ * is required.
+ */
+ packet->append(reinterpret_cast<const char*>(k_sync_header),
+ sizeof(k_sync_header));
+ DBUG_RETURN(0);
+}
+
+int Repl_semi_sync_master::update_sync_header(THD* thd, unsigned char *packet,
+ const char *log_file_name,
+ my_off_t log_file_pos,
+ bool* need_sync)
+{
+ int cmp = 0;
+ bool sync = false;
+
+ DBUG_ENTER("Repl_semi_sync_master::update_sync_header");
+
+ /* If the semi-sync master is not enabled, or the slave is not a semi-sync
+ * target, do not request replies from the slave.
+ */
+ if (!get_master_enabled() || !thd->semi_sync_slave)
+ {
+ *need_sync = false;
+ DBUG_RETURN(0);
+ }
+
+ lock();
+
+ /* This is the real check inside the mutex. */
+ if (!get_master_enabled())
+ {
+ assert(sync == false);
+ goto l_end;
+ }
+
+ if (is_on())
+ {
+ /* semi-sync is ON */
+ sync = false; /* No sync unless a transaction is involved. */
+
+ if (m_reply_file_name_inited)
+ {
+ cmp = Active_tranx::compare(log_file_name, log_file_pos,
+ m_reply_file_name, m_reply_file_pos);
+ if (cmp <= 0)
+ {
+ /* If we have already got the reply for the event, then we do
+ * not need to sync the transaction again.
+ */
+ goto l_end;
+ }
+ }
+
+ if (m_wait_file_name_inited)
+ {
+ cmp = Active_tranx::compare(log_file_name, log_file_pos,
+ m_wait_file_name, m_wait_file_pos);
+ }
+ else
+ {
+ cmp = 1;
+ }
+
+ /* If we are already waiting for some transaction replies which
+ * are later in binlog, do not wait for this one event.
+ */
+ if (cmp >= 0)
+ {
+ /*
+ * We only wait if the event is a transaction's ending event.
+ */
+ assert(m_active_tranxs != NULL);
+ sync = m_active_tranxs->is_tranx_end_pos(log_file_name,
+ log_file_pos);
+ }
+ }
+ else
+ {
+ if (m_commit_file_name_inited)
+ {
+ int cmp = Active_tranx::compare(log_file_name, log_file_pos,
+ m_commit_file_name, m_commit_file_pos);
+ sync = (cmp >= 0);
+ }
+ else
+ {
+ sync = true;
+ }
+ }
+
+ DBUG_PRINT("semisync", ("%s: server(%lu), (%s, %lu) sync(%d), repl(%d)",
+ "Repl_semi_sync_master::update_sync_header",
+ thd->variables.server_id, log_file_name,
+ (ulong)log_file_pos, sync, (int)is_on()));
+ *need_sync= sync;
+
+ l_end:
+ unlock();
+
+ /* We do not need to clear sync flag because we set it to 0 when we
+ * reserve the packet header.
+ */
+ if (sync)
+ {
+ (packet)[2] = k_packet_flag_sync;
+ }
+
+ DBUG_RETURN(0);
+}
+
+int Repl_semi_sync_master::write_tranx_in_binlog(const char* log_file_name,
+ my_off_t log_file_pos)
+{
+ int result = 0;
+
+ DBUG_ENTER("Repl_semi_sync_master::write_tranx_in_binlog");
+
+ lock();
+
+ /* This is the real check inside the mutex. */
+ if (!get_master_enabled())
+ goto l_end;
+
+ /* Update the 'largest' transaction commit position seen so far even
+ * though semi-sync is switched off.
+ * It is much better that we update m_commit_file* here, instead of
+ * inside commit_trx(). This is mostly because update_sync_header()
+ * will watch for m_commit_file* to decide whether to switch semi-sync
+ * on. The detailed reason is explained in function update_sync_header().
+ */
+ if (m_commit_file_name_inited)
+ {
+ int cmp = Active_tranx::compare(log_file_name, log_file_pos,
+ m_commit_file_name, m_commit_file_pos);
+ if (cmp > 0)
+ {
+ /* This is a larger position, let's update the maximum info. */
+ strncpy(m_commit_file_name, log_file_name, FN_REFLEN-1);
+ m_commit_file_name[FN_REFLEN-1] = 0; /* make sure it ends properly */
+ m_commit_file_pos = log_file_pos;
+ }
+ }
+ else
+ {
+ strncpy(m_commit_file_name, log_file_name, FN_REFLEN-1);
+ m_commit_file_name[FN_REFLEN-1] = 0; /* make sure it ends properly */
+ m_commit_file_pos = log_file_pos;
+ m_commit_file_name_inited = true;
+ }
+
+ if (is_on())
+ {
+ assert(m_active_tranxs != NULL);
+ if(m_active_tranxs->insert_tranx_node(log_file_name, log_file_pos))
+ {
+ /*
+ if insert tranx_node failed, print a warning message
+ and turn off semi-sync
+ */
+ sql_print_warning("Semi-sync failed to insert tranx_node for binlog file: %s, position: %lu",
+ log_file_name, (ulong)log_file_pos);
+ switch_off();
+ }
+ else
+ {
+ rpl_semi_sync_master_request_ack++;
+ }
+ }
+
+ l_end:
+ unlock();
+
+ DBUG_RETURN(result);
+}
+
+int Repl_semi_sync_master::flush_net(THD *thd,
+ const char *event_buf)
+{
+ int result = -1;
+ NET* net= &thd->net;
+
+ DBUG_ENTER("Repl_semi_sync_master::flush_net");
+
+ assert((unsigned char)event_buf[1] == k_packet_magic_num);
+ if ((unsigned char)event_buf[2] != k_packet_flag_sync)
+ {
+ /* current event does not require reply */
+ result = 0;
+ goto l_end;
+ }
+
+ /* We flush to make sure that the current event is sent to the network,
+ * instead of being buffered in the TCP/IP stack.
+ */
+ if (net_flush(net))
+ {
+ sql_print_error("Semi-sync master failed on net_flush() "
+ "before waiting for slave reply");
+ goto l_end;
+ }
+
+ net_clear(net, 0);
+ net->pkt_nr++;
+ result = 0;
+ rpl_semi_sync_master_net_wait_num++;
+
+ l_end:
+ thd->clear_error();
+
+ DBUG_RETURN(result);
+}
+
+int Repl_semi_sync_master::after_reset_master()
+{
+ int result = 0;
+
+ DBUG_ENTER("Repl_semi_sync_master::after_reset_master");
+
+ if (rpl_semi_sync_master_enabled)
+ {
+ sql_print_information("Enable Semi-sync Master after reset master");
+ enable_master();
+ }
+
+ lock();
+
+ if (rpl_semi_sync_master_clients == 0 &&
+ !rpl_semi_sync_master_wait_no_slave)
+ m_state = 0;
+ else
+ m_state = get_master_enabled()? 1 : 0;
+
+ m_wait_file_name_inited = false;
+ m_reply_file_name_inited = false;
+ m_commit_file_name_inited = false;
+
+ rpl_semi_sync_master_yes_transactions = 0;
+ rpl_semi_sync_master_no_transactions = 0;
+ rpl_semi_sync_master_off_times = 0;
+ rpl_semi_sync_master_timefunc_fails = 0;
+ rpl_semi_sync_master_wait_sessions = 0;
+ rpl_semi_sync_master_wait_pos_backtraverse = 0;
+ rpl_semi_sync_master_trx_wait_num = 0;
+ rpl_semi_sync_master_trx_wait_time = 0;
+ rpl_semi_sync_master_net_wait_num = 0;
+ rpl_semi_sync_master_net_wait_time = 0;
+
+ unlock();
+
+ DBUG_RETURN(result);
+}
+
+int Repl_semi_sync_master::before_reset_master()
+{
+ int result = 0;
+
+ DBUG_ENTER("Repl_semi_sync_master::before_reset_master");
+
+ if (rpl_semi_sync_master_enabled)
+ disable_master();
+
+ DBUG_RETURN(result);
+}
+
+void Repl_semi_sync_master::check_and_switch()
+{
+ lock();
+ if (get_master_enabled() && is_on())
+ {
+ if (!rpl_semi_sync_master_wait_no_slave
+ && rpl_semi_sync_master_clients == 0)
+ switch_off();
+ }
+ unlock();
+}
+
+void Repl_semi_sync_master::set_export_stats()
+{
+ lock();
+
+ rpl_semi_sync_master_status = m_state;
+ rpl_semi_sync_master_avg_trx_wait_time=
+ ((rpl_semi_sync_master_trx_wait_num) ?
+ (ulong)((double)rpl_semi_sync_master_trx_wait_time /
+ ((double)rpl_semi_sync_master_trx_wait_num)) : 0);
+ rpl_semi_sync_master_avg_net_wait_time=
+ ((rpl_semi_sync_master_net_wait_num) ?
+ (ulong)((double)rpl_semi_sync_master_net_wait_time /
+ ((double)rpl_semi_sync_master_net_wait_num)) : 0);
+
+ unlock();
+}
+
+/* Get the waiting time given the wait's staring time.
+ *
+ * Return:
+ * >= 0: the waiting time in microsecons(us)
+ * < 0: error in get time or time back traverse
+ */
+static int get_wait_time(const struct timespec& start_ts)
+{
+ ulonglong start_usecs, end_usecs;
+ struct timespec end_ts;
+
+ /* Starting time in microseconds(us). */
+ start_usecs = timespec_to_usec(&start_ts);
+
+ /* Get the wait time interval. */
+ set_timespec(end_ts, 0);
+
+ /* Ending time in microseconds(us). */
+ end_usecs = timespec_to_usec(&end_ts);
+
+ if (end_usecs < start_usecs)
+ return -1;
+
+ return (int)(end_usecs - start_usecs);
+}
+
+void semi_sync_master_deinit()
+{
+ repl_semisync_master.cleanup();
+ ack_receiver.cleanup();
+}
diff --git a/sql/semisync_master.h b/sql/semisync_master.h
new file mode 100644
index 00000000000..74f6c24c8ea
--- /dev/null
+++ b/sql/semisync_master.h
@@ -0,0 +1,673 @@
+/* Copyright (C) 2007 Google Inc.
+ Copyright (c) 2008 MySQL AB, 2009 Sun Microsystems, Inc.
+ Use is subject to license terms.
+
+ 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 SEMISYNC_MASTER_H
+#define SEMISYNC_MASTER_H
+
+#include "semisync.h"
+#include "semisync_master_ack_receiver.h"
+
+#ifdef HAVE_PSI_INTERFACE
+extern PSI_mutex_key key_LOCK_rpl_semi_sync_master_enabled;
+extern PSI_mutex_key key_LOCK_binlog;
+extern PSI_cond_key key_COND_binlog_send;
+#endif
+
+struct Tranx_node {
+ char log_name[FN_REFLEN];
+ my_off_t log_pos;
+ struct Tranx_node *next; /* the next node in the sorted list */
+ struct Tranx_node *hash_next; /* the next node during hash collision */
+};
+
+/**
+ @class Tranx_node_allocator
+
+ This class provides memory allocating and freeing methods for
+ Tranx_node. The main target is performance.
+
+ @section ALLOCATE How to allocate a node
+ The pointer of the first node after 'last_node' in current_block is
+ returned. current_block will move to the next free Block when all nodes of
+ it are in use. A new Block is allocated and is put into the rear of the
+ Block link table if no Block is free.
+
+ The list starts up empty (ie, there is no allocated Block).
+
+ After some nodes are freed, there probably are some free nodes before
+ the sequence of the allocated nodes, but we do not reuse it. It is better
+ to keep the allocated nodes are in the sequence, for it is more efficient
+ for allocating and freeing Tranx_node.
+
+ @section FREENODE How to free nodes
+ There are two methods for freeing nodes. They are free_all_nodes and
+ free_nodes_before.
+
+ 'A Block is free' means all of its nodes are free.
+ @subsection free_nodes_before
+ As all allocated nodes are in the sequence, 'Before one node' means all
+ nodes before given node in the same Block and all Blocks before the Block
+ which containing the given node. As such, all Blocks before the given one
+ ('node') are free Block and moved into the rear of the Block link table.
+ The Block containing the given 'node', however, is not. For at least the
+ given 'node' is still in use. This will waste at most one Block, but it is
+ more efficient.
+ */
+#define BLOCK_TRANX_NODES 16
+class Tranx_node_allocator
+{
+public:
+ /**
+ @param reserved_nodes
+ The number of reserved Tranx_nodes. It is used to set 'reserved_blocks'
+ which can contain at least 'reserved_nodes' number of Tranx_nodes. When
+ freeing memory, we will reserve at least reserved_blocks of Blocks not
+ freed.
+ */
+ Tranx_node_allocator(uint reserved_nodes) :
+ reserved_blocks(reserved_nodes/BLOCK_TRANX_NODES +
+ (reserved_nodes%BLOCK_TRANX_NODES > 1 ? 2 : 1)),
+ first_block(NULL), last_block(NULL),
+ current_block(NULL), last_node(-1), block_num(0) {}
+
+ ~Tranx_node_allocator()
+ {
+ Block *block= first_block;
+ while (block != NULL)
+ {
+ Block *next= block->next;
+ free_block(block);
+ block= next;
+ }
+ }
+
+ /**
+ The pointer of the first node after 'last_node' in current_block is
+ returned. current_block will move to the next free Block when all nodes of
+ it are in use. A new Block is allocated and is put into the rear of the
+ Block link table if no Block is free.
+
+ @return Return a Tranx_node *, or NULL if an error occurred.
+ */
+ Tranx_node *allocate_node()
+ {
+ Tranx_node *trx_node;
+ Block *block= current_block;
+
+ if (last_node == BLOCK_TRANX_NODES-1)
+ {
+ current_block= current_block->next;
+ last_node= -1;
+ }
+
+ if (current_block == NULL && allocate_block())
+ {
+ current_block= block;
+ if (current_block)
+ last_node= BLOCK_TRANX_NODES-1;
+ return NULL;
+ }
+
+ trx_node= &(current_block->nodes[++last_node]);
+ trx_node->log_name[0] = '\0';
+ trx_node->log_pos= 0;
+ trx_node->next= 0;
+ trx_node->hash_next= 0;
+ return trx_node;
+ }
+
+ /**
+ All nodes are freed.
+
+ @return Return 0, or 1 if an error occurred.
+ */
+ int free_all_nodes()
+ {
+ current_block= first_block;
+ last_node= -1;
+ free_blocks();
+ return 0;
+ }
+
+ /**
+ All Blocks before the given 'node' are free Block and moved into the rear
+ of the Block link table.
+
+ @param node All nodes before 'node' will be freed
+
+ @return Return 0, or 1 if an error occurred.
+ */
+ int free_nodes_before(Tranx_node* node)
+ {
+ Block *block;
+ Block *prev_block= NULL;
+
+ block= first_block;
+ while (block != current_block->next)
+ {
+ /* Find the Block containing the given node */
+ if (&(block->nodes[0]) <= node && &(block->nodes[BLOCK_TRANX_NODES]) >= node)
+ {
+ /* All Blocks before the given node are put into the rear */
+ if (first_block != block)
+ {
+ last_block->next= first_block;
+ first_block= block;
+ last_block= prev_block;
+ last_block->next= NULL;
+ free_blocks();
+ }
+ return 0;
+ }
+ prev_block= block;
+ block= block->next;
+ }
+
+ /* Node does not find should never happen */
+ DBUG_ASSERT(0);
+ return 1;
+ }
+
+private:
+ uint reserved_blocks;
+
+ /**
+ A sequence memory which contains BLOCK_TRANX_NODES Tranx_nodes.
+
+ BLOCK_TRANX_NODES The number of Tranx_nodes which are in a Block.
+
+ next Every Block has a 'next' pointer which points to the next Block.
+ These linking Blocks constitute a Block link table.
+ */
+ struct Block {
+ Block *next;
+ Tranx_node nodes[BLOCK_TRANX_NODES];
+ };
+
+ /**
+ The 'first_block' is the head of the Block link table;
+ */
+ Block *first_block;
+ /**
+ The 'last_block' is the rear of the Block link table;
+ */
+ Block *last_block;
+
+ /**
+ current_block always points the Block in the Block link table in
+ which the last allocated node is. The Blocks before it are all in use
+ and the Blocks after it are all free.
+ */
+ Block *current_block;
+
+ /**
+ It always points to the last node which has been allocated in the
+ current_block.
+ */
+ int last_node;
+
+ /**
+ How many Blocks are in the Block link table.
+ */
+ uint block_num;
+
+ /**
+ Allocate a block and then assign it to current_block.
+ */
+ int allocate_block()
+ {
+ Block *block= (Block *)my_malloc(sizeof(Block), MYF(0));
+ if (block)
+ {
+ block->next= NULL;
+
+ if (first_block == NULL)
+ first_block= block;
+ else
+ last_block->next= block;
+
+ /* New Block is always put into the rear */
+ last_block= block;
+ /* New Block is always the current_block */
+ current_block= block;
+ ++block_num;
+ return 0;
+ }
+ return 1;
+ }
+
+ /**
+ Free a given Block.
+ @param block The Block will be freed.
+ */
+ void free_block(Block *block)
+ {
+ my_free(block);
+ --block_num;
+ }
+
+
+ /**
+ If there are some free Blocks and the total number of the Blocks in the
+ Block link table is larger than the 'reserved_blocks', Some free Blocks
+ will be freed until the total number of the Blocks is equal to the
+ 'reserved_blocks' or there is only one free Block behind the
+ 'current_block'.
+ */
+ void free_blocks()
+ {
+ if (current_block == NULL || current_block->next == NULL)
+ return;
+
+ /* One free Block is always kept behind the current block */
+ Block *block= current_block->next->next;
+ while (block_num > reserved_blocks && block != NULL)
+ {
+ Block *next= block->next;
+ free_block(block);
+ block= next;
+ }
+ current_block->next->next= block;
+ if (block == NULL)
+ last_block= current_block->next;
+ }
+};
+
+/**
+ This class manages memory for active transaction list.
+
+ We record each active transaction with a Tranx_node, each session
+ can have only one open transaction. Because of EVENT, the total
+ active transaction nodes can exceed the maximum allowed
+ connections.
+*/
+class Active_tranx
+ :public Trace {
+private:
+
+ Tranx_node_allocator m_allocator;
+ /* These two record the active transaction list in sort order. */
+ Tranx_node *m_trx_front, *m_trx_rear;
+
+ Tranx_node **m_trx_htb; /* A hash table on active transactions. */
+
+ int m_num_entries; /* maximum hash table entries */
+ mysql_mutex_t *m_lock; /* mutex lock */
+
+ inline void assert_lock_owner();
+
+ inline unsigned int calc_hash(const unsigned char *key, size_t length);
+ unsigned int get_hash_value(const char *log_file_name, my_off_t log_file_pos);
+
+ int compare(const char *log_file_name1, my_off_t log_file_pos1,
+ const Tranx_node *node2) {
+ return compare(log_file_name1, log_file_pos1,
+ node2->log_name, node2->log_pos);
+ }
+ int compare(const Tranx_node *node1,
+ const char *log_file_name2, my_off_t log_file_pos2) {
+ return compare(node1->log_name, node1->log_pos,
+ log_file_name2, log_file_pos2);
+ }
+ int compare(const Tranx_node *node1, const Tranx_node *node2) {
+ return compare(node1->log_name, node1->log_pos,
+ node2->log_name, node2->log_pos);
+ }
+
+public:
+ Active_tranx(mysql_mutex_t *lock, unsigned long trace_level);
+ ~Active_tranx();
+
+ /* Insert an active transaction node with the specified position.
+ *
+ * Return:
+ * 0: success; non-zero: error
+ */
+ int insert_tranx_node(const char *log_file_name, my_off_t log_file_pos);
+
+ /* Clear the active transaction nodes until(inclusive) the specified
+ * position.
+ * If log_file_name is NULL, everything will be cleared: the sorted
+ * list and the hash table will be reset to empty.
+ */
+ void clear_active_tranx_nodes(const char *log_file_name,
+ my_off_t log_file_pos);
+
+ /* Given a position, check to see whether the position is an active
+ * transaction's ending position by probing the hash table.
+ */
+ bool is_tranx_end_pos(const char *log_file_name, my_off_t log_file_pos);
+
+ /* Given two binlog positions, compare which one is bigger based on
+ * (file_name, file_position).
+ */
+ static int compare(const char *log_file_name1, my_off_t log_file_pos1,
+ const char *log_file_name2, my_off_t log_file_pos2);
+
+};
+
+/**
+ The extension class for the master of semi-synchronous replication
+*/
+class Repl_semi_sync_master
+ :public Repl_semi_sync_base {
+ Active_tranx *m_active_tranxs; /* active transaction list: the list will
+ be cleared when semi-sync switches off. */
+
+ /* True when init_object has been called */
+ bool m_init_done;
+
+ /* This cond variable is signaled when enough binlog has been sent to slave,
+ * so that a waiting trx can return the 'ok' to the client for a commit.
+ */
+ mysql_cond_t COND_binlog_send;
+
+ /* Mutex that protects the following state variables and the active
+ * transaction list.
+ * Under no cirumstances we can acquire mysql_bin_log.LOCK_log if we are
+ * already holding m_LOCK_binlog because it can cause deadlocks.
+ */
+ mysql_mutex_t LOCK_binlog;
+
+ /* This is set to true when m_reply_file_name contains meaningful data. */
+ bool m_reply_file_name_inited;
+
+ /* The binlog name up to which we have received replies from any slaves. */
+ char m_reply_file_name[FN_REFLEN];
+
+ /* The position in that file up to which we have the reply from any slaves. */
+ my_off_t m_reply_file_pos;
+
+ /* This is set to true when we know the 'smallest' wait position. */
+ bool m_wait_file_name_inited;
+
+ /* NULL, or the 'smallest' filename that a transaction is waiting for
+ * slave replies.
+ */
+ char m_wait_file_name[FN_REFLEN];
+
+ /* The smallest position in that file that a trx is waiting for: the trx
+ * can proceed and send an 'ok' to the client when the master has got the
+ * reply from the slave indicating that it already got the binlog events.
+ */
+ my_off_t m_wait_file_pos;
+
+ /* This is set to true when we know the 'largest' transaction commit
+ * position in the binlog file.
+ * We always maintain the position no matter whether semi-sync is switched
+ * on switched off. When a transaction wait timeout occurs, semi-sync will
+ * switch off. Binlog-dump thread can use the three fields to detect when
+ * slaves catch up on replication so that semi-sync can switch on again.
+ */
+ bool m_commit_file_name_inited;
+
+ /* The 'largest' binlog filename that a commit transaction is seeing. */
+ char m_commit_file_name[FN_REFLEN];
+
+ /* The 'largest' position in that file that a commit transaction is seeing. */
+ my_off_t m_commit_file_pos;
+
+ /* All global variables which can be set by parameters. */
+ volatile bool m_master_enabled; /* semi-sync is enabled on the master */
+ unsigned long m_wait_timeout; /* timeout period(ms) during tranx wait */
+
+ bool m_state; /* whether semi-sync is switched */
+
+ /*Waiting for ACK before/after innodb commit*/
+ ulong m_wait_point;
+
+ void lock();
+ void unlock();
+ void cond_broadcast();
+ int cond_timewait(struct timespec *wait_time);
+
+ /* Is semi-sync replication on? */
+ bool is_on() {
+ return (m_state);
+ }
+
+ void set_master_enabled(bool enabled) {
+ m_master_enabled = enabled;
+ }
+
+ /* Switch semi-sync off because of timeout in transaction waiting. */
+ void switch_off();
+
+ /* Switch semi-sync on when slaves catch up. */
+ int try_switch_on(int server_id,
+ const char *log_file_name, my_off_t log_file_pos);
+
+ public:
+ Repl_semi_sync_master();
+ ~Repl_semi_sync_master() {}
+
+ void cleanup();
+
+ bool get_master_enabled() {
+ return m_master_enabled;
+ }
+ void set_trace_level(unsigned long trace_level) {
+ m_trace_level = trace_level;
+ if (m_active_tranxs)
+ m_active_tranxs->m_trace_level = trace_level;
+ }
+
+ /* Set the transaction wait timeout period, in milliseconds. */
+ void set_wait_timeout(unsigned long wait_timeout) {
+ m_wait_timeout = wait_timeout;
+ }
+
+ /*set the ACK point, after binlog sync or after transaction commit*/
+ void set_wait_point(unsigned long ack_point)
+ {
+ m_wait_point = ack_point;
+ }
+
+ ulong wait_point() //no cover line
+ {
+ return m_wait_point; //no cover line
+ }
+
+ /* Initialize this class after MySQL parameters are initialized. this
+ * function should be called once at bootstrap time.
+ */
+ int init_object();
+
+ /* Enable the object to enable semi-sync replication inside the master. */
+ int enable_master();
+
+ /* Disable the object to disable semi-sync replication inside the master. */
+ void disable_master();
+
+ /* Add a semi-sync replication slave */
+ void add_slave();
+
+ /* Remove a semi-sync replication slave */
+ void remove_slave();
+
+ /* It parses a reply packet and call report_reply_binlog to handle it. */
+ int report_reply_packet(uint32 server_id, const uchar *packet,
+ ulong packet_len);
+
+ /* In semi-sync replication, reports up to which binlog position we have
+ * received replies from the slave indicating that it already get the events.
+ *
+ * Input:
+ * server_id - (IN) master server id number
+ * log_file_name - (IN) binlog file name
+ * end_offset - (IN) the offset in the binlog file up to which we have
+ * the replies from the slave
+ *
+ * Return:
+ * 0: success; non-zero: error
+ */
+ int report_reply_binlog(uint32 server_id,
+ const char* log_file_name,
+ my_off_t end_offset);
+
+ /* Commit a transaction in the final step. This function is called from
+ * InnoDB before returning from the low commit. If semi-sync is switch on,
+ * the function will wait to see whether binlog-dump thread get the reply for
+ * the events of the transaction. Remember that this is not a direct wait,
+ * instead, it waits to see whether the binlog-dump thread has reached the
+ * point. If the wait times out, semi-sync status will be switched off and
+ * all other transaction would not wait either.
+ *
+ * Input: (the transaction events' ending binlog position)
+ * trx_wait_binlog_name - (IN) ending position's file name
+ * trx_wait_binlog_pos - (IN) ending position's file offset
+ *
+ * Return:
+ * 0: success; non-zero: error
+ */
+ int commit_trx(const char* trx_wait_binlog_name,
+ my_off_t trx_wait_binlog_pos);
+
+ /*Wait for ACK after writing/sync binlog to file*/
+ int wait_after_sync(const char* log_file, my_off_t log_pos);
+
+ /*Wait for ACK after commting the transaction*/
+ int wait_after_commit(THD* thd, bool all);
+
+ /*Wait after the transaction is rollback*/
+ int wait_after_rollback(THD *thd, bool all);
+ /*Store the current binlog position in m_active_tranxs. This position should
+ * be acked by slave*/
+ int report_binlog_update(THD *thd, const char *log_file,my_off_t log_pos);
+
+ int dump_start(THD* thd,
+ const char *log_file,
+ my_off_t log_pos);
+
+ void dump_end(THD* thd);
+
+ /* Reserve space in the replication event packet header:
+ * . slave semi-sync off: 1 byte - (0)
+ * . slave semi-sync on: 3 byte - (0, 0xef, 0/1}
+ *
+ * Input:
+ * packet - (IN) the header buffer
+ *
+ * Return:
+ * size of the bytes reserved for header
+ */
+ int reserve_sync_header(String* packet);
+
+ /* Update the sync bit in the packet header to indicate to the slave whether
+ * the master will wait for the reply of the event. If semi-sync is switched
+ * off and we detect that the slave is catching up, we switch semi-sync on.
+ *
+ * Input:
+ * THD - (IN) current dump thread
+ * packet - (IN) the packet containing the replication event
+ * log_file_name - (IN) the event ending position's file name
+ * log_file_pos - (IN) the event ending position's file offset
+ * need_sync - (IN) identify if flush_net is needed to call.
+ * server_id - (IN) master server id number
+ *
+ * Return:
+ * 0: success; non-zero: error
+ */
+ int update_sync_header(THD* thd, unsigned char *packet,
+ const char *log_file_name,
+ my_off_t log_file_pos,
+ bool* need_sync);
+
+ /* Called when a transaction finished writing binlog events.
+ * . update the 'largest' transactions' binlog event position
+ * . insert the ending position in the active transaction list if
+ * semi-sync is on
+ *
+ * Input: (the transaction events' ending binlog position)
+ * log_file_name - (IN) transaction ending position's file name
+ * log_file_pos - (IN) transaction ending position's file offset
+ *
+ * Return:
+ * 0: success; non-zero: error
+ */
+ int write_tranx_in_binlog(const char* log_file_name, my_off_t log_file_pos);
+
+ /* Read the slave's reply so that we know how much progress the slave makes
+ * on receive replication events.
+ */
+ int flush_net(THD* thd, const char *event_buf);
+
+ /* Export internal statistics for semi-sync replication. */
+ void set_export_stats();
+
+ /* 'reset master' command is issued from the user and semi-sync need to
+ * go off for that.
+ */
+ int after_reset_master();
+
+ /*called before reset master*/
+ int before_reset_master();
+
+ void check_and_switch();
+
+ mysql_mutex_t LOCK_rpl_semi_sync_master_enabled;
+};
+
+enum rpl_semi_sync_master_wait_point_t {
+ SEMI_SYNC_MASTER_WAIT_POINT_AFTER_BINLOG_SYNC,
+ SEMI_SYNC_MASTER_WAIT_POINT_AFTER_STORAGE_COMMIT,
+};
+
+extern Repl_semi_sync_master repl_semisync_master;
+extern Ack_receiver ack_receiver;
+
+/* System and status variables for the master component */
+extern my_bool rpl_semi_sync_master_enabled;
+extern my_bool rpl_semi_sync_master_status;
+extern ulong rpl_semi_sync_master_wait_point;
+extern ulong rpl_semi_sync_master_clients;
+extern ulong rpl_semi_sync_master_timeout;
+extern ulong rpl_semi_sync_master_trace_level;
+extern ulong rpl_semi_sync_master_yes_transactions;
+extern ulong rpl_semi_sync_master_no_transactions;
+extern ulong rpl_semi_sync_master_off_times;
+extern ulong rpl_semi_sync_master_wait_timeouts;
+extern ulong rpl_semi_sync_master_timefunc_fails;
+extern ulong rpl_semi_sync_master_num_timeouts;
+extern ulong rpl_semi_sync_master_wait_sessions;
+extern ulong rpl_semi_sync_master_wait_pos_backtraverse;
+extern ulong rpl_semi_sync_master_avg_trx_wait_time;
+extern ulong rpl_semi_sync_master_avg_net_wait_time;
+extern ulonglong rpl_semi_sync_master_net_wait_num;
+extern ulonglong rpl_semi_sync_master_trx_wait_num;
+extern ulonglong rpl_semi_sync_master_net_wait_time;
+extern ulonglong rpl_semi_sync_master_trx_wait_time;
+extern unsigned long long rpl_semi_sync_master_request_ack;
+extern unsigned long long rpl_semi_sync_master_get_ack;
+
+/*
+ This indicates whether we should keep waiting if no semi-sync slave
+ is available.
+ 0 : stop waiting if detected no avaialable semi-sync slave.
+ 1 (default) : keep waiting until timeout even no available semi-sync slave.
+*/
+extern char rpl_semi_sync_master_wait_no_slave;
+extern Repl_semi_sync_master repl_semisync_master;
+
+extern PSI_stage_info stage_waiting_for_semi_sync_ack_from_slave;
+extern PSI_stage_info stage_reading_semi_sync_ack;
+extern PSI_stage_info stage_waiting_for_semi_sync_slave;
+
+void semi_sync_master_deinit();
+
+#endif /* SEMISYNC_MASTER_H */
diff --git a/sql/semisync_master_ack_receiver.cc b/sql/semisync_master_ack_receiver.cc
new file mode 100644
index 00000000000..81f494c9d34
--- /dev/null
+++ b/sql/semisync_master_ack_receiver.cc
@@ -0,0 +1,291 @@
+/* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include <my_global.h>
+#include "semisync_master.h"
+#include "semisync_master_ack_receiver.h"
+
+#ifdef HAVE_PSI_MUTEX_INTERFACE
+extern PSI_mutex_key key_LOCK_ack_receiver;
+extern PSI_cond_key key_COND_ack_receiver;
+#endif
+#ifdef HAVE_PSI_THREAD_INTERFACE
+extern PSI_thread_key key_thread_ack_receiver;
+#endif
+extern Repl_semi_sync_master repl_semisync;
+
+/* Callback function of ack receive thread */
+pthread_handler_t ack_receive_handler(void *arg)
+{
+ Ack_receiver *recv= reinterpret_cast<Ack_receiver *>(arg);
+
+ my_thread_init();
+ recv->run();
+ my_thread_end();
+
+ return NULL;
+}
+
+Ack_receiver::Ack_receiver()
+{
+ DBUG_ENTER("Ack_receiver::Ack_receiver");
+
+ m_status= ST_DOWN;
+ mysql_mutex_init(key_LOCK_ack_receiver, &m_mutex, NULL);
+ mysql_cond_init(key_COND_ack_receiver, &m_cond, NULL);
+ m_pid= 0;
+
+ DBUG_VOID_RETURN;
+}
+
+void Ack_receiver::cleanup()
+{
+ DBUG_ENTER("Ack_receiver::~Ack_receiver");
+
+ stop();
+ mysql_mutex_destroy(&m_mutex);
+ mysql_cond_destroy(&m_cond);
+
+ DBUG_VOID_RETURN;
+}
+
+bool Ack_receiver::start()
+{
+ DBUG_ENTER("Ack_receiver::start");
+
+ mysql_mutex_lock(&m_mutex);
+ if(m_status == ST_DOWN)
+ {
+ pthread_attr_t attr;
+
+ m_status= ST_UP;
+
+ if (DBUG_EVALUATE_IF("rpl_semisync_simulate_create_thread_failure", 1, 0) ||
+ pthread_attr_init(&attr) != 0 ||
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE) != 0 ||
+#ifndef _WIN32
+ pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM) != 0 ||
+#endif
+ mysql_thread_create(key_thread_ack_receiver, &m_pid,
+ &attr, ack_receive_handler, this))
+ {
+ sql_print_error("Failed to start semi-sync ACK receiver thread, "
+ " could not create thread(errno:%d)", errno);
+
+ m_status= ST_DOWN;
+ mysql_mutex_unlock(&m_mutex);
+
+ DBUG_RETURN(true);
+ }
+ (void) pthread_attr_destroy(&attr);
+ }
+ mysql_mutex_unlock(&m_mutex);
+
+ DBUG_RETURN(false);
+}
+
+void Ack_receiver::stop()
+{
+ DBUG_ENTER("Ack_receiver::stop");
+
+ mysql_mutex_lock(&m_mutex);
+ if (m_status == ST_UP)
+ {
+ m_status= ST_STOPPING;
+ mysql_cond_broadcast(&m_cond);
+
+ while (m_status == ST_STOPPING)
+ mysql_cond_wait(&m_cond, &m_mutex);
+
+ DBUG_ASSERT(m_status == ST_DOWN);
+
+ m_pid= 0;
+ }
+ mysql_mutex_unlock(&m_mutex);
+
+ DBUG_VOID_RETURN;
+}
+
+bool Ack_receiver::add_slave(THD *thd)
+{
+ Slave *slave;
+ DBUG_ENTER("Ack_receiver::add_slave");
+
+ if (!(slave= new Slave))
+ DBUG_RETURN(true);
+
+ slave->thd= thd;
+ slave->vio= *thd->net.vio;
+ slave->vio.mysql_socket.m_psi= NULL;
+ slave->vio.read_timeout= 1;
+
+ mysql_mutex_lock(&m_mutex);
+ m_slaves.push_back(slave);
+ m_slaves_changed= true;
+ mysql_cond_broadcast(&m_cond);
+ mysql_mutex_unlock(&m_mutex);
+
+ DBUG_RETURN(false);
+}
+
+void Ack_receiver::remove_slave(THD *thd)
+{
+ I_List_iterator<Slave> it(m_slaves);
+ Slave *slave;
+ DBUG_ENTER("Ack_receiver::remove_slave");
+
+ mysql_mutex_lock(&m_mutex);
+
+ while ((slave= it++))
+ {
+ if (slave->thd == thd)
+ {
+ delete slave;
+ m_slaves_changed= true;
+ break;
+ }
+ }
+ mysql_mutex_unlock(&m_mutex);
+
+ DBUG_VOID_RETURN;
+}
+
+inline void Ack_receiver::set_stage_info(const PSI_stage_info &stage)
+{
+ MYSQL_SET_STAGE(stage.m_key, __FILE__, __LINE__);
+}
+
+inline void Ack_receiver::wait_for_slave_connection()
+{
+ set_stage_info(stage_waiting_for_semi_sync_slave);
+ mysql_cond_wait(&m_cond, &m_mutex);
+}
+
+/* Auxilary function to initialize a NET object with given net buffer. */
+static void init_net(NET *net, unsigned char *buff, unsigned int buff_len)
+{
+ memset(net, 0, sizeof(NET));
+ net->max_packet= buff_len;
+ net->buff= buff;
+ net->buff_end= buff + buff_len;
+ net->read_pos= net->buff;
+}
+
+void Ack_receiver::run()
+{
+ THD *thd= new THD(next_thread_id());
+ NET net;
+ unsigned char net_buff[REPLY_MESSAGE_MAX_LENGTH];
+
+ my_thread_init();
+
+ DBUG_ENTER("Ack_receiver::run");
+
+#ifdef HAVE_POLL
+ Poll_socket_listener listener(m_slaves);
+#else
+ Select_socket_listener listener(m_slaves);
+#endif //HAVE_POLL
+
+ sql_print_information("Starting ack receiver thread");
+ thd->system_thread= SYSTEM_THREAD_SEMISYNC_MASTER_BACKGROUND;
+ thd->thread_stack= (char*) &thd;
+ thd->store_globals();
+ thd->security_ctx->skip_grants();
+ thread_safe_increment32(&service_thread_count);
+ thd->set_command(COM_DAEMON);
+ init_net(&net, net_buff, REPLY_MESSAGE_MAX_LENGTH);
+
+ mysql_mutex_lock(&m_mutex);
+ m_slaves_changed= true;
+ mysql_mutex_unlock(&m_mutex);
+
+ while (1)
+ {
+ int ret;
+ uint slave_count __attribute__((unused))= 0;
+ Slave *slave;
+
+ mysql_mutex_lock(&m_mutex);
+ if (unlikely(m_status == ST_STOPPING))
+ goto end;
+
+ set_stage_info(stage_waiting_for_semi_sync_ack_from_slave);
+ if (unlikely(m_slaves_changed))
+ {
+ if (unlikely(m_slaves.is_empty()))
+ {
+ wait_for_slave_connection();
+ mysql_mutex_unlock(&m_mutex);
+ continue;
+ }
+
+ if ((slave_count= listener.init_slave_sockets()) == 0)
+ goto end;
+ m_slaves_changed= false;
+#ifdef HAVE_POLL
+ DBUG_PRINT("info", ("fd count %u", slave_count));
+#else
+ DBUG_PRINT("info", ("fd count %u, max_fd %d", slave_count,
+ (int) listener.get_max_fd()));
+#endif
+ }
+
+ ret= listener.listen_on_sockets();
+ if (ret <= 0)
+ {
+ mysql_mutex_unlock(&m_mutex);
+
+ ret= DBUG_EVALUATE_IF("rpl_semisync_simulate_select_error", -1, ret);
+
+ if (ret == -1 && errno != EINTR)
+ sql_print_information("Failed to wait on semi-sync sockets, "
+ "error: errno=%d", socket_errno);
+ /* Sleep 1us, so other threads can catch the m_mutex easily. */
+ my_sleep(1);
+ continue;
+ }
+
+ set_stage_info(stage_reading_semi_sync_ack);
+ Slave_ilist_iterator it(m_slaves);
+ while ((slave= it++))
+ {
+ if (listener.is_socket_active(slave))
+ {
+ ulong len;
+
+ net_clear(&net, 0);
+ net.vio= &slave->vio;
+
+ len= my_net_read(&net);
+ if (likely(len != packet_error))
+ repl_semisync_master.report_reply_packet(slave->server_id(),
+ net.read_pos, len);
+ else if (net.last_errno == ER_NET_READ_ERROR)
+ listener.clear_socket_info(slave);
+ }
+ }
+ mysql_mutex_unlock(&m_mutex);
+ }
+end:
+ sql_print_information("Stopping ack receiver thread");
+ m_status= ST_DOWN;
+ delete thd;
+ thread_safe_decrement32(&service_thread_count);
+ signal_thd_deleted();
+ mysql_cond_broadcast(&m_cond);
+ mysql_mutex_unlock(&m_mutex);
+ DBUG_VOID_RETURN;
+}
diff --git a/sql/semisync_master_ack_receiver.h b/sql/semisync_master_ack_receiver.h
new file mode 100644
index 00000000000..138f7b5aeed
--- /dev/null
+++ b/sql/semisync_master_ack_receiver.h
@@ -0,0 +1,240 @@
+/* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef SEMISYNC_MASTER_ACK_RECEIVER_DEFINED
+#define SEMISYNC_MASTER_ACK_RECEIVER_DEFINED
+
+#include "my_global.h"
+#include "my_pthread.h"
+#include "sql_class.h"
+#include "semisync.h"
+#include <vector>
+
+struct Slave :public ilink
+{
+ THD *thd;
+ Vio vio;
+#ifdef HAVE_POLL
+ uint m_fds_index;
+#endif
+ my_socket sock_fd() const { return vio.mysql_socket.fd; }
+ uint server_id() const { return thd->variables.server_id; }
+};
+
+typedef I_List<Slave> Slave_ilist;
+typedef I_List_iterator<Slave> Slave_ilist_iterator;
+
+/**
+ Ack_receiver is responsible to control ack receive thread and maintain
+ slave information used by ack receive thread.
+
+ There are mainly four operations on ack receive thread:
+ start: start ack receive thread
+ stop: stop ack receive thread
+ add_slave: maintain a new semisync slave's information
+ remove_slave: remove a semisync slave's information
+ */
+class Ack_receiver : public Repl_semi_sync_base
+{
+public:
+ Ack_receiver();
+ ~Ack_receiver() {}
+ void cleanup();
+ /**
+ Notify ack receiver to receive acks on the dump session.
+
+ It adds the given dump thread into the slave list and wakes
+ up ack thread if it is waiting for any slave coming.
+
+ @param[in] thd THD of a dump thread.
+
+ @return it return false if succeeds, otherwise true is returned.
+ */
+ bool add_slave(THD *thd);
+
+ /**
+ Notify ack receiver not to receive ack on the dump session.
+
+ it removes the given dump thread from slave list.
+
+ @param[in] thd THD of a dump thread.
+ */
+ void remove_slave(THD *thd);
+
+ /**
+ Start ack receive thread
+
+ @return it return false if succeeds, otherwise true is returned.
+ */
+ bool start();
+
+ /**
+ Stop ack receive thread
+ */
+ void stop();
+
+ /**
+ The core of ack receive thread.
+
+ It monitors all slaves' sockets and receives acks when they come.
+ */
+ void run();
+
+ void set_trace_level(unsigned long trace_level)
+ {
+ m_trace_level= trace_level;
+ }
+private:
+ enum status {ST_UP, ST_DOWN, ST_STOPPING};
+ uint8 m_status;
+ /*
+ Protect m_status, m_slaves_changed and m_slaves. ack thread and other
+ session may access the variables at the same time.
+ */
+ mysql_mutex_t m_mutex;
+ mysql_cond_t m_cond;
+ /* If slave list is updated(add or remove). */
+ bool m_slaves_changed;
+
+ Slave_ilist m_slaves;
+ pthread_t m_pid;
+
+/* Declare them private, so no one can copy the object. */
+ Ack_receiver(const Ack_receiver &ack_receiver);
+ Ack_receiver& operator=(const Ack_receiver &ack_receiver);
+
+ void set_stage_info(const PSI_stage_info &stage);
+ void wait_for_slave_connection();
+};
+
+
+#ifdef HAVE_POLL
+#include <sys/poll.h>
+#include <vector>
+
+class Poll_socket_listener
+{
+public:
+ Poll_socket_listener(const Slave_ilist &slaves)
+ :m_slaves(slaves)
+ {
+ }
+
+ bool listen_on_sockets()
+ {
+ return poll(m_fds.data(), m_fds.size(), 1000 /*1 Second timeout*/);
+ }
+
+ bool is_socket_active(const Slave *slave)
+ {
+ return m_fds[slave->m_fds_index].revents & POLLIN;
+ }
+
+ void clear_socket_info(const Slave *slave)
+ {
+ m_fds[slave->m_fds_index].fd= -1;
+ m_fds[slave->m_fds_index].events= 0;
+ }
+
+ uint init_slave_sockets()
+ {
+ Slave_ilist_iterator it(const_cast<Slave_ilist&>(m_slaves));
+ Slave *slave;
+ uint fds_index= 0;
+
+ m_fds.clear();
+ while ((slave= it++))
+ {
+ pollfd poll_fd;
+ poll_fd.fd= slave->sock_fd();
+ poll_fd.events= POLLIN;
+ m_fds.push_back(poll_fd);
+ slave->m_fds_index= fds_index++;
+ }
+ return fds_index;
+ }
+
+private:
+ const Slave_ilist &m_slaves;
+ std::vector<pollfd> m_fds;
+};
+
+#else //NO POLL
+
+class Select_socket_listener
+{
+public:
+ Select_socket_listener(const Slave_ilist &slaves)
+ :m_slaves(slaves), m_max_fd(INVALID_SOCKET)
+ {
+ }
+
+ bool listen_on_sockets()
+ {
+ /* Reinitialze the fds with active fds before calling select */
+ m_fds= m_init_fds;
+ struct timeval tv= {1,0};
+ /* select requires max fd + 1 for the first argument */
+ return select((int) m_max_fd+1, &m_fds, NULL, NULL, &tv);
+ }
+
+ bool is_socket_active(const Slave *slave)
+ {
+ return FD_ISSET(slave->sock_fd(), &m_fds);
+ }
+
+ void clear_socket_info(const Slave *slave)
+ {
+ FD_CLR(slave->sock_fd(), &m_init_fds);
+ }
+
+ uint init_slave_sockets()
+ {
+ Slave_ilist_iterator it(const_cast<Slave_ilist&>(m_slaves));
+ Slave *slave;
+ uint fds_index= 0;
+
+ FD_ZERO(&m_init_fds);
+ while ((slave= it++))
+ {
+ my_socket socket_id= slave->sock_fd();
+ m_max_fd= (socket_id > m_max_fd ? socket_id : m_max_fd);
+#ifndef _WIN32
+ if (socket_id > FD_SETSIZE)
+ {
+ sql_print_error("Semisync slave socket fd is %u. "
+ "select() cannot handle if the socket fd is "
+ "greater than %u (FD_SETSIZE).", socket_id, FD_SETSIZE);
+ return 0;
+ }
+#endif //_WIN32
+ FD_SET(socket_id, &m_init_fds);
+ fds_index++;
+ }
+ return fds_index;
+ }
+ my_socket get_max_fd() { return m_max_fd; }
+
+private:
+ const Slave_ilist &m_slaves;
+ my_socket m_max_fd;
+ fd_set m_init_fds;
+ fd_set m_fds;
+};
+
+#endif //HAVE_POLL
+
+extern Ack_receiver ack_receiver;
+#endif
diff --git a/sql/semisync_slave.cc b/sql/semisync_slave.cc
new file mode 100644
index 00000000000..df8baf045ac
--- /dev/null
+++ b/sql/semisync_slave.cc
@@ -0,0 +1,250 @@
+/* Copyright (c) 2008 MySQL AB, 2009 Sun Microsystems, Inc.
+ Use is subject to license terms.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+
+#include <my_global.h>
+#include "semisync_slave.h"
+
+Repl_semi_sync_slave repl_semisync_slave;
+
+my_bool rpl_semi_sync_slave_enabled= 0;
+
+char rpl_semi_sync_slave_delay_master;
+my_bool rpl_semi_sync_slave_status= 0;
+ulong rpl_semi_sync_slave_trace_level;
+
+/*
+ indicate whether or not the slave should send a reply to the master.
+
+ This is set to true in repl_semi_slave_read_event if the current
+ event read is the last event of a transaction. And the value is
+ checked in repl_semi_slave_queue_event.
+*/
+bool semi_sync_need_reply= false;
+unsigned int rpl_semi_sync_slave_kill_conn_timeout;
+unsigned long long rpl_semi_sync_slave_send_ack = 0;
+
+int Repl_semi_sync_slave::init_object()
+{
+ int result= 0;
+
+ m_init_done = true;
+
+ /* References to the parameter works after set_options(). */
+ set_slave_enabled(rpl_semi_sync_slave_enabled);
+ set_trace_level(rpl_semi_sync_slave_trace_level);
+ set_delay_master(rpl_semi_sync_slave_delay_master);
+ set_kill_conn_timeout(rpl_semi_sync_slave_kill_conn_timeout);
+
+ return result;
+}
+
+int Repl_semi_sync_slave::slave_read_sync_header(const char *header,
+ unsigned long total_len,
+ int *semi_flags,
+ const char **payload,
+ unsigned long *payload_len)
+{
+ int read_res = 0;
+ DBUG_ENTER("Repl_semi_sync_slave::slave_read_sync_header");
+
+ if (rpl_semi_sync_slave_status)
+ {
+ if (DBUG_EVALUATE_IF("semislave_corrupt_log", 0, 1)
+ && (unsigned char)(header[0]) == k_packet_magic_num)
+ {
+ semi_sync_need_reply = (header[1] & k_packet_flag_sync);
+ *payload_len = total_len - 2;
+ *payload = header + 2;
+
+ DBUG_PRINT("semisync", ("%s: reply - %d",
+ "Repl_semi_sync_slave::slave_read_sync_header",
+ semi_sync_need_reply));
+
+ if (semi_sync_need_reply)
+ *semi_flags |= SEMI_SYNC_NEED_ACK;
+ if (is_delay_master())
+ *semi_flags |= SEMI_SYNC_SLAVE_DELAY_SYNC;
+ }
+ else
+ {
+ sql_print_error("Missing magic number for semi-sync packet, packet "
+ "len: %lu", total_len);
+ read_res = -1;
+ }
+ } else {
+ *payload= header;
+ *payload_len= total_len;
+ }
+
+ DBUG_RETURN(read_res);
+}
+
+int Repl_semi_sync_slave::slave_start(Master_info *mi)
+{
+ bool semi_sync= get_slave_enabled();
+
+ sql_print_information("Slave I/O thread: Start %s replication to\
+ master '%s@%s:%d' in log '%s' at position %lu",
+ semi_sync ? "semi-sync" : "asynchronous",
+ const_cast<char *>(mi->user), mi->host, mi->port,
+ const_cast<char *>(mi->master_log_name),
+ (unsigned long)(mi->master_log_pos));
+
+ if (semi_sync && !rpl_semi_sync_slave_status)
+ rpl_semi_sync_slave_status= 1;
+
+ /*clear the counter*/
+ rpl_semi_sync_slave_send_ack= 0;
+ return 0;
+}
+
+int Repl_semi_sync_slave::slave_stop(Master_info *mi)
+{
+ if (rpl_semi_sync_slave_status)
+ rpl_semi_sync_slave_status= 0;
+ if (get_slave_enabled())
+ kill_connection(mi->mysql);
+ return 0;
+}
+
+int Repl_semi_sync_slave::reset_slave(Master_info *mi)
+{
+ return 0;
+}
+
+void Repl_semi_sync_slave::kill_connection(MYSQL *mysql)
+{
+ if (!mysql)
+ return;
+
+ char kill_buffer[30];
+ MYSQL *kill_mysql = NULL;
+ kill_mysql = mysql_init(kill_mysql);
+ mysql_options(kill_mysql, MYSQL_OPT_CONNECT_TIMEOUT, &m_kill_conn_timeout);
+ mysql_options(kill_mysql, MYSQL_OPT_READ_TIMEOUT, &m_kill_conn_timeout);
+ mysql_options(kill_mysql, MYSQL_OPT_WRITE_TIMEOUT, &m_kill_conn_timeout);
+
+ bool ret= (!mysql_real_connect(kill_mysql, mysql->host,
+ mysql->user, mysql->passwd,0, mysql->port, mysql->unix_socket, 0));
+ if (DBUG_EVALUATE_IF("semisync_slave_failed_kill", 1, 0) || ret)
+ {
+ sql_print_information("cannot connect to master to kill slave io_thread's "
+ "connection");
+ mysql_close(kill_mysql);
+ return;
+ }
+ size_t kill_buffer_length = my_snprintf(kill_buffer, 30, "KILL %lu",
+ mysql->thread_id);
+ mysql_real_query(kill_mysql, kill_buffer, (ulong)kill_buffer_length);
+ mysql_close(kill_mysql);
+}
+
+int Repl_semi_sync_slave::request_transmit(Master_info *mi)
+{
+ MYSQL *mysql= mi->mysql;
+ MYSQL_RES *res= 0;
+ MYSQL_ROW row;
+ const char *query;
+
+ if (!get_slave_enabled())
+ return 0;
+
+ query= "SHOW VARIABLES LIKE 'rpl_semi_sync_master_enabled'";
+ if (mysql_real_query(mysql, query, (ulong)strlen(query)) ||
+ !(res= mysql_store_result(mysql)))
+ {
+ sql_print_error("Execution failed on master: %s, error :%s", query, mysql_error(mysql));
+ return 1;
+ }
+
+ row= mysql_fetch_row(res);
+ if (DBUG_EVALUATE_IF("master_not_support_semisync", 1, 0)
+ || !row)
+ {
+ /* Master does not support semi-sync */
+ sql_print_warning("Master server does not support semi-sync, "
+ "fallback to asynchronous replication");
+ rpl_semi_sync_slave_status= 0;
+ mysql_free_result(res);
+ return 0;
+ }
+ mysql_free_result(res);
+
+ /*
+ Tell master dump thread that we want to do semi-sync
+ replication
+ */
+ query= "SET @rpl_semi_sync_slave= 1";
+ if (mysql_real_query(mysql, query, (ulong)strlen(query)))
+ {
+ sql_print_error("Set 'rpl_semi_sync_slave=1' on master failed");
+ return 1;
+ }
+ mysql_free_result(mysql_store_result(mysql));
+ rpl_semi_sync_slave_status= 1;
+
+ return 0;
+}
+
+int Repl_semi_sync_slave::slave_reply(Master_info *mi)
+{
+ MYSQL* mysql= mi->mysql;
+ const char *binlog_filename= const_cast<char *>(mi->master_log_name);
+ my_off_t binlog_filepos= mi->master_log_pos;
+
+ NET *net= &mysql->net;
+ uchar reply_buffer[REPLY_MAGIC_NUM_LEN
+ + REPLY_BINLOG_POS_LEN
+ + REPLY_BINLOG_NAME_LEN];
+ int reply_res = 0;
+ size_t name_len = strlen(binlog_filename);
+
+ DBUG_ENTER("Repl_semi_sync_slave::slave_reply");
+
+ if (rpl_semi_sync_slave_status && semi_sync_need_reply)
+ {
+ /* Prepare the buffer of the reply. */
+ reply_buffer[REPLY_MAGIC_NUM_OFFSET] = k_packet_magic_num;
+ int8store(reply_buffer + REPLY_BINLOG_POS_OFFSET, binlog_filepos);
+ memcpy(reply_buffer + REPLY_BINLOG_NAME_OFFSET,
+ binlog_filename,
+ name_len + 1 /* including trailing '\0' */);
+
+ DBUG_PRINT("semisync", ("%s: reply (%s, %lu)",
+ "Repl_semi_sync_slave::slave_reply",
+ binlog_filename, (ulong)binlog_filepos));
+
+ net_clear(net, 0);
+ /* Send the reply. */
+ reply_res = my_net_write(net, reply_buffer,
+ name_len + REPLY_BINLOG_NAME_OFFSET);
+ if (!reply_res)
+ {
+ reply_res = DBUG_EVALUATE_IF("semislave_failed_net_flush", 1, net_flush(net));
+ if (reply_res)
+ sql_print_error("Semi-sync slave net_flush() reply failed");
+ rpl_semi_sync_slave_send_ack++;
+ }
+ else
+ {
+ sql_print_error("Semi-sync slave send reply failed: %s (%d)",
+ net->last_error, net->last_errno);
+ }
+ }
+
+ DBUG_RETURN(reply_res);
+}
diff --git a/sql/semisync_slave.h b/sql/semisync_slave.h
new file mode 100644
index 00000000000..d65262f151d
--- /dev/null
+++ b/sql/semisync_slave.h
@@ -0,0 +1,115 @@
+/* Copyright (c) 2006 MySQL AB, 2009 Sun Microsystems, Inc.
+ Use is subject to license terms.
+
+ 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 SEMISYNC_SLAVE_H
+#define SEMISYNC_SLAVE_H
+
+#include "semisync.h"
+#include "my_global.h"
+#include "sql_priv.h"
+#include "rpl_mi.h"
+#include "mysql.h"
+
+class Master_info;
+
+/**
+ The extension class for the slave of semi-synchronous replication
+*/
+class Repl_semi_sync_slave
+ :public Repl_semi_sync_base {
+public:
+ Repl_semi_sync_slave() :m_slave_enabled(false) {}
+ ~Repl_semi_sync_slave() {}
+
+ void set_trace_level(unsigned long trace_level) {
+ m_trace_level = trace_level;
+ }
+
+ /* Initialize this class after MySQL parameters are initialized. this
+ * function should be called once at bootstrap time.
+ */
+ int init_object();
+
+ bool get_slave_enabled() {
+ return m_slave_enabled;
+ }
+
+ void set_slave_enabled(bool enabled) {
+ m_slave_enabled = enabled;
+ }
+
+ bool is_delay_master(){
+ return m_delay_master;
+ }
+
+ void set_delay_master(bool enabled) {
+ m_delay_master = enabled;
+ }
+
+ void set_kill_conn_timeout(unsigned int timeout) {
+ m_kill_conn_timeout = timeout;
+ }
+
+ /* A slave reads the semi-sync packet header and separate the metadata
+ * from the payload data.
+ *
+ * Input:
+ * header - (IN) packet header pointer
+ * total_len - (IN) total packet length: metadata + payload
+ * semi_flags - (IN) store flags: SEMI_SYNC_SLAVE_DELAY_SYNC and
+ SEMI_SYNC_NEED_ACK
+ * payload - (IN) payload: the replication event
+ * payload_len - (IN) payload length
+ *
+ * Return:
+ * 0: success; non-zero: error
+ */
+ int slave_read_sync_header(const char *header, unsigned long total_len,
+ int *semi_flags,
+ const char **payload, unsigned long *payload_len);
+
+ /* A slave replies to the master indicating its replication process. It
+ * indicates that the slave has received all events before the specified
+ * binlog position.
+ */
+ int slave_reply(Master_info* mi);
+ int slave_start(Master_info *mi);
+ int slave_stop(Master_info *mi);
+ int request_transmit(Master_info*);
+ void kill_connection(MYSQL *mysql);
+ int reset_slave(Master_info *mi);
+
+private:
+ /* True when init_object has been called */
+ bool m_init_done;
+ bool m_slave_enabled; /* semi-sycn is enabled on the slave */
+ bool m_delay_master;
+ unsigned int m_kill_conn_timeout;
+};
+
+
+/* System and status variables for the slave component */
+extern my_bool rpl_semi_sync_slave_enabled;
+extern my_bool rpl_semi_sync_slave_status;
+extern ulong rpl_semi_sync_slave_trace_level;
+extern Repl_semi_sync_slave repl_semisync_slave;
+
+extern char rpl_semi_sync_slave_delay_master;
+extern unsigned int rpl_semi_sync_slave_kill_conn_timeout;
+extern unsigned long long rpl_semi_sync_slave_send_ack;
+
+#endif /* SEMISYNC_SLAVE_H */
diff --git a/sql/session_tracker.cc b/sql/session_tracker.cc
index b73bd1d1d4a..35cd55394a9 100644
--- a/sql/session_tracker.cc
+++ b/sql/session_tracker.cc
@@ -15,10 +15,7 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#ifndef EMBEDDED_LIBRARY
#include "sql_plugin.h"
-#include "session_tracker.h"
-
#include "hash.h"
#include "table.h"
#include "rpl_gtid.h"
@@ -35,264 +32,12 @@ void State_tracker::mark_as_changed(THD *thd, LEX_CSTRING *tracked_item_name)
}
-class Not_implemented_tracker : public State_tracker
-{
-public:
- bool enable(THD *thd)
- { return false; }
- bool update(THD *, set_var *)
- { return false; }
- bool store(THD *, String *)
- { return false; }
- void mark_as_changed(THD *, LEX_CSTRING *tracked_item_name)
- {}
-
-};
-
-/**
- Session_sysvars_tracker
-
- This is a tracker class that enables & manages the tracking of session
- system variables. It internally maintains a hash of user supplied variable
- references and a boolean field to store if the variable was changed by the
- last statement.
-*/
-
-class Session_sysvars_tracker : public State_tracker
-{
-private:
-
- struct sysvar_node_st {
- sys_var *m_svar;
- bool *test_load;
- bool m_changed;
- };
-
- class vars_list
- {
- private:
- /**
- Registered system variables. (@@session_track_system_variables)
- A hash to store the name of all the system variables specified by the
- user.
- */
- HASH m_registered_sysvars;
- /** Size of buffer for string representation */
- size_t buffer_length;
- myf m_mem_flag;
- /**
- If TRUE then we want to check all session variable.
- */
- bool track_all;
- void init()
- {
- my_hash_init(&m_registered_sysvars,
- &my_charset_bin,
- 4, 0, 0, (my_hash_get_key) sysvars_get_key,
- my_free, MYF(HASH_UNIQUE |
- ((m_mem_flag & MY_THREAD_SPECIFIC) ?
- HASH_THREAD_SPECIFIC : 0)));
- }
- void free_hash()
- {
- if (my_hash_inited(&m_registered_sysvars))
- {
- my_hash_free(&m_registered_sysvars);
- }
- }
-
- uchar* search(const sys_var *svar)
- {
- return (my_hash_search(&m_registered_sysvars, (const uchar *)&svar,
- sizeof(sys_var *)));
- }
-
- public:
- vars_list() :
- buffer_length(0)
- {
- m_mem_flag= current_thd ? MY_THREAD_SPECIFIC : 0;
- init();
- }
-
- size_t get_buffer_length()
- {
- DBUG_ASSERT(buffer_length != 0); // asked earlier then should
- return buffer_length;
- }
- ~vars_list()
- {
- /* free the allocated hash. */
- if (my_hash_inited(&m_registered_sysvars))
- {
- my_hash_free(&m_registered_sysvars);
- }
- }
-
- uchar* insert_or_search(sysvar_node_st *node, const sys_var *svar)
- {
- uchar *res;
- res= search(svar);
- if (!res)
- {
- if (track_all)
- {
- insert(node, svar, m_mem_flag);
- return search(svar);
- }
- }
- return res;
- }
-
- bool insert(sysvar_node_st *node, const sys_var *svar, myf mem_flag);
- void reinit();
- void reset();
- inline bool is_enabled()
- {
- return track_all || m_registered_sysvars.records;
- }
- void copy(vars_list* from, THD *thd);
- bool parse_var_list(THD *thd, LEX_STRING var_list, bool throw_error,
- CHARSET_INFO *char_set, bool take_mutex);
- bool construct_var_list(char *buf, size_t buf_len);
- bool store(THD *thd, String *buf);
- };
- /**
- Two objects of vars_list type are maintained to manage
- various operations.
- */
- vars_list *orig_list, *tool_list;
-
-public:
- Session_sysvars_tracker()
- {
- orig_list= new (std::nothrow) vars_list();
- tool_list= new (std::nothrow) vars_list();
- }
-
- ~Session_sysvars_tracker()
- {
- if (orig_list)
- delete orig_list;
- if (tool_list)
- delete tool_list;
- }
-
- size_t get_buffer_length()
- {
- return orig_list->get_buffer_length();
- }
- bool construct_var_list(char *buf, size_t buf_len)
- {
- return orig_list->construct_var_list(buf, buf_len);
- }
-
- /**
- Method used to check the validity of string provided
- for session_track_system_variables during the server
- startup.
- */
- static bool server_init_check(THD *thd, CHARSET_INFO *char_set,
- LEX_STRING var_list)
- {
- return check_var_list(thd, var_list, false, char_set, false);
- }
-
- static bool server_init_process(THD *thd, CHARSET_INFO *char_set,
- LEX_STRING var_list)
- {
- vars_list dummy;
- bool result;
- result= dummy.parse_var_list(thd, var_list, false, char_set, false);
- if (!result)
- dummy.construct_var_list(var_list.str, var_list.length + 1);
- return result;
- }
-
- void reset();
- bool enable(THD *thd);
- bool check_str(THD *thd, LEX_STRING *val);
- bool update(THD *thd, set_var *var);
- bool store(THD *thd, String *buf);
- void mark_as_changed(THD *thd, LEX_CSTRING *tracked_item_name);
- /* callback */
- static uchar *sysvars_get_key(const char *entry, size_t *length,
- my_bool not_used __attribute__((unused)));
-
- // hash iterators
- static my_bool name_array_filler(void *ptr, void *data_ptr);
- static my_bool store_variable(void *ptr, void *data_ptr);
- static my_bool reset_variable(void *ptr, void *data_ptr);
-
- static bool check_var_list(THD *thd, LEX_STRING var_list, bool throw_error,
- CHARSET_INFO *char_set, bool take_mutex);
-};
-
-
-
-/**
- Current_schema_tracker,
-
- This is a tracker class that enables & manages the tracking of current
- schema for a particular connection.
-*/
-
-class Current_schema_tracker : public State_tracker
-{
-private:
- bool schema_track_inited;
- void reset();
-
-public:
-
- Current_schema_tracker()
- {
- schema_track_inited= false;
- }
-
- bool enable(THD *thd)
- { return update(thd, NULL); }
- bool update(THD *thd, set_var *var);
- bool store(THD *thd, String *buf);
-};
-
-/*
- Session_state_change_tracker
-
- This is a boolean tracker class that will monitor any change that contributes
- to a session state change.
- Attributes that contribute to session state change include:
- - Successful change to System variables
- - User defined variables assignments
- - temporary tables created, altered or deleted
- - prepared statements added or removed
- - change in current database
- - change of current role
-*/
-
-class Session_state_change_tracker : public State_tracker
-{
-private:
-
- void reset();
-
-public:
- Session_state_change_tracker();
- bool enable(THD *thd)
- { return update(thd, NULL); };
- bool update(THD *thd, set_var *var);
- bool store(THD *thd, String *buf);
- bool is_state_changed(THD*);
-};
-
-
/* To be used in expanding the buffer. */
static const unsigned int EXTRA_ALLOC= 1024;
void Session_sysvars_tracker::vars_list::reinit()
{
- buffer_length= 0;
track_all= 0;
if (m_registered_sysvars.records)
my_hash_reset(&m_registered_sysvars);
@@ -310,10 +55,8 @@ void Session_sysvars_tracker::vars_list::reinit()
void Session_sysvars_tracker::vars_list::copy(vars_list* from, THD *thd)
{
- reinit();
track_all= from->track_all;
free_hash();
- buffer_length= from->buffer_length;
m_registered_sysvars= from->m_registered_sysvars;
from->init();
}
@@ -321,26 +64,20 @@ void Session_sysvars_tracker::vars_list::copy(vars_list* from, THD *thd)
/**
Inserts the variable to be tracked into m_registered_sysvars hash.
- @param node Node to be inserted.
@param svar address of the system variable
@retval false success
@retval true error
*/
-bool Session_sysvars_tracker::vars_list::insert(sysvar_node_st *node,
- const sys_var *svar,
- myf mem_flag)
+bool Session_sysvars_tracker::vars_list::insert(const sys_var *svar)
{
- if (!node)
- {
- if (!(node= (sysvar_node_st *) my_malloc(sizeof(sysvar_node_st),
- MYF(MY_WME | mem_flag))))
- {
- reinit();
- return true;
- }
- }
+ sysvar_node_st *node;
+ if (!(node= (sysvar_node_st *) my_malloc(sizeof(sysvar_node_st),
+ MYF(MY_WME |
+ (mysqld_server_initialized ?
+ MY_THREAD_SPECIFIC : 0)))))
+ return true;
node->m_svar= (sys_var *)svar;
node->test_load= node->m_svar->test_load;
@@ -351,7 +88,6 @@ bool Session_sysvars_tracker::vars_list::insert(sysvar_node_st *node,
if (!search((sys_var *)svar))
{
//EOF (error is already reported)
- reinit();
return true;
}
}
@@ -373,7 +109,6 @@ bool Session_sysvars_tracker::vars_list::insert(sysvar_node_st *node,
in case of invalid/duplicate values.
@param char_set [IN] charecter set information used for string
manipulations.
- @param take_mutex [IN] take LOCK_plugin
@return
true Error
@@ -382,44 +117,28 @@ bool Session_sysvars_tracker::vars_list::insert(sysvar_node_st *node,
bool Session_sysvars_tracker::vars_list::parse_var_list(THD *thd,
LEX_STRING var_list,
bool throw_error,
- CHARSET_INFO *char_set,
- bool take_mutex)
+ CHARSET_INFO *char_set)
{
const char separator= ',';
char *token, *lasts= NULL;
size_t rest= var_list.length;
- reinit();
if (!var_list.str || var_list.length == 0)
- {
- buffer_length= 1;
return false;
- }
- if(!strcmp(var_list.str,(const char *)"*"))
+ if(!strcmp(var_list.str, "*"))
{
track_all= true;
- buffer_length= 2;
return false;
}
- buffer_length= var_list.length + 1;
token= var_list.str;
track_all= false;
- /*
- If Lock to the plugin mutex is not acquired here itself, it results
- in having to acquire it multiple times in find_sys_var_ex for each
- token value. Hence the mutex is handled here to avoid a performance
- overhead.
- */
- if (!thd || take_mutex)
- mysql_mutex_lock(&LOCK_plugin);
for (;;)
{
sys_var *svar;
- LEX_STRING var;
- uint not_used;
+ LEX_CSTRING var;
lasts= (char *) memchr(token, separator, rest);
@@ -433,17 +152,16 @@ bool Session_sysvars_tracker::vars_list::parse_var_list(THD *thd,
var.length= rest;
/* Remove leading/trailing whitespace. */
- trim_whitespace(char_set, &var, &not_used);
+ trim_whitespace(char_set, &var);
- if(!strcmp(var.str,(const char *)"*"))
+ if(!strcmp(var.str, "*"))
{
track_all= true;
}
- else if ((svar=
- find_sys_var_ex(thd, var.str, var.length, throw_error, true)))
+ else if ((svar= find_sys_var(thd, var.str, var.length, throw_error)))
{
- if (insert(NULL, svar, m_mem_flag) == TRUE)
- goto error;
+ if (insert(svar) == TRUE)
+ return true;
}
else if (throw_error && thd)
{
@@ -453,55 +171,41 @@ bool Session_sysvars_tracker::vars_list::parse_var_list(THD *thd,
"be ignored.", (int)var.length, token);
}
else
- goto error;
+ return true;
if (lasts)
token= lasts + 1;
else
break;
}
- if (!thd || take_mutex)
- mysql_mutex_unlock(&LOCK_plugin);
-
return false;
-
-error:
- if (!thd || take_mutex)
- mysql_mutex_unlock(&LOCK_plugin);
- return true;
}
-bool Session_sysvars_tracker::check_var_list(THD *thd,
- LEX_STRING var_list,
- bool throw_error,
- CHARSET_INFO *char_set,
- bool take_mutex)
+bool sysvartrack_validate_value(THD *thd, const char *str, size_t len)
{
+ LEX_STRING var_list= { (char *) str, len };
const char separator= ',';
char *token, *lasts= NULL;
size_t rest= var_list.length;
- if (!var_list.str || var_list.length == 0 ||
- !strcmp(var_list.str,(const char *)"*"))
+ if (!var_list.str)
+ {
+ my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0),
+ "session_track_system_variables", "NULL");
+ return false;
+ }
+ if (var_list.length == 0 ||
+ !strcmp(var_list.str, "*"))
{
return false;
}
token= var_list.str;
- /*
- If Lock to the plugin mutex is not acquired here itself, it results
- in having to acquire it multiple times in find_sys_var_ex for each
- token value. Hence the mutex is handled here to avoid a performance
- overhead.
- */
- if (!thd || take_mutex)
- mysql_mutex_lock(&LOCK_plugin);
for (;;)
{
- LEX_STRING var;
- uint not_used;
+ LEX_CSTRING var;
lasts= (char *) memchr(token, separator, rest);
@@ -515,55 +219,19 @@ bool Session_sysvars_tracker::check_var_list(THD *thd,
var.length= rest;
/* Remove leading/trailing whitespace. */
- trim_whitespace(char_set, &var, &not_used);
+ trim_whitespace(system_charset_info, &var);
- if(!strcmp(var.str,(const char *)"*") &&
- !find_sys_var_ex(thd, var.str, var.length, throw_error, true))
- {
- if (throw_error && take_mutex && thd)
- {
- push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
- ER_WRONG_VALUE_FOR_VAR,
- "%.*s is not a valid system variable and will"
- "be ignored.", (int)var.length, token);
- }
- else
- {
- if (!thd || take_mutex)
- mysql_mutex_unlock(&LOCK_plugin);
- return true;
- }
- }
+ if (!strcmp(var.str, "*") && !find_sys_var(thd, var.str, var.length))
+ return true;
if (lasts)
token= lasts + 1;
else
break;
}
- if (!thd || take_mutex)
- mysql_mutex_unlock(&LOCK_plugin);
-
return false;
}
-struct name_array_filler_data
-{
- LEX_CSTRING **names;
- uint idx;
-
-};
-
-/** Collects variable references into array */
-my_bool Session_sysvars_tracker::name_array_filler(void *ptr,
- void *data_ptr)
-{
- Session_sysvars_tracker::sysvar_node_st *node=
- (Session_sysvars_tracker::sysvar_node_st *)ptr;
- name_array_filler_data *data= (struct name_array_filler_data *)data_ptr;
- if (*node->test_load)
- data->names[data->idx++]= &node->m_svar->name;
- return FALSE;
-}
/* Sorts variable references array */
static int name_array_sorter(const void *a, const void *b)
@@ -583,7 +251,8 @@ static int name_array_sorter(const void *a, const void *b)
bool Session_sysvars_tracker::vars_list::construct_var_list(char *buf,
size_t buf_len)
{
- struct name_array_filler_data data;
+ LEX_CSTRING **names;
+ uint idx;
size_t left= buf_len;
size_t names_size= m_registered_sysvars.records * sizeof(LEX_CSTRING *);
const char separator= ',';
@@ -606,16 +275,19 @@ bool Session_sysvars_tracker::vars_list::construct_var_list(char *buf,
return false;
}
- data.names= (LEX_CSTRING**)my_safe_alloca(names_size);
-
- if (unlikely(!data.names))
+ if (unlikely(!(names= (LEX_CSTRING**) my_safe_alloca(names_size))))
return true;
- data.idx= 0;
+ idx= 0;
mysql_mutex_lock(&LOCK_plugin);
- my_hash_iterate(&m_registered_sysvars, &name_array_filler, &data);
- DBUG_ASSERT(data.idx <= m_registered_sysvars.records);
+ for (ulong i= 0; i < m_registered_sysvars.records; i++)
+ {
+ sysvar_node_st *node= at(i);
+ if (*node->test_load)
+ names[idx++]= &node->m_svar->name;
+ }
+ DBUG_ASSERT(idx <= m_registered_sysvars.records);
/*
We check number of records again here because number of variables
@@ -628,17 +300,16 @@ bool Session_sysvars_tracker::vars_list::construct_var_list(char *buf,
return false;
}
- my_qsort(data.names, data.idx, sizeof(LEX_CSTRING *),
- &name_array_sorter);
+ my_qsort(names, idx, sizeof(LEX_CSTRING*), &name_array_sorter);
- for(uint i= 0; i < data.idx; i++)
+ for(uint i= 0; i < idx; i++)
{
- LEX_CSTRING *nm= data.names[i];
+ LEX_CSTRING *nm= names[i];
size_t ln= nm->length + 1;
if (ln > left)
{
mysql_mutex_unlock(&LOCK_plugin);
- my_safe_afree(data.names, names_size);
+ my_safe_afree(names, names_size);
return true;
}
memcpy(buf, nm->str, nm->length);
@@ -649,58 +320,48 @@ bool Session_sysvars_tracker::vars_list::construct_var_list(char *buf,
mysql_mutex_unlock(&LOCK_plugin);
buf--; buf[0]= '\0';
- my_safe_afree(data.names, names_size);
+ my_safe_afree(names, names_size);
return false;
}
-/**
- Enable session tracker by parsing global value of tracked variables.
- @param thd [IN] The thd handle.
+void Session_sysvars_tracker::init(THD *thd)
+{
+ mysql_mutex_assert_owner(&LOCK_global_system_variables);
+ DBUG_ASSERT(thd->variables.session_track_system_variables ==
+ 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,
+ MYF(MY_WME | MY_THREAD_SPECIFIC));
+}
- @retval true Error
- @retval false Success
-*/
-bool Session_sysvars_tracker::enable(THD *thd)
+void Session_sysvars_tracker::deinit(THD *thd)
{
- mysql_mutex_lock(&LOCK_plugin);
- LEX_STRING tmp;
- tmp.str= global_system_variables.session_track_system_variables;
- tmp.length= safe_strlen(tmp.str);
- if (tool_list->parse_var_list(thd, tmp,
- true, thd->charset(), false) == true)
- {
- mysql_mutex_unlock(&LOCK_plugin);
- return true;
- }
- mysql_mutex_unlock(&LOCK_plugin);
- orig_list->copy(tool_list, thd);
- m_enabled= true;
-
- return false;
+ my_free(thd->variables.session_track_system_variables);
+ thd->variables.session_track_system_variables= 0;
}
/**
- Check system variable name(s).
-
- @note This function is called from the ON_CHECK() function of the
- session_track_system_variables' sys_var class.
+ Enable session tracker by parsing global value of tracked variables.
@param thd [IN] The thd handle.
- @param var [IN] A pointer to set_var holding the specified list of
- system variable names.
@retval true Error
@retval false Success
*/
-inline bool Session_sysvars_tracker::check_str(THD *thd, LEX_STRING *val)
+bool Session_sysvars_tracker::enable(THD *thd)
{
- return Session_sysvars_tracker::check_var_list(thd, *val, true,
- thd->charset(), true);
+ orig_list.reinit();
+ m_parsed= false;
+ m_enabled= thd->variables.session_track_system_variables &&
+ *thd->variables.session_track_system_variables;
+ reset_changed();
+ return false;
}
@@ -710,6 +371,9 @@ inline bool Session_sysvars_tracker::check_str(THD *thd, LEX_STRING *val)
Session_sysvars_tracker::vars_list::copy updating the hash in orig_list
which represents the system variables to be tracked.
+ We are doing via tool list because there possible errors with memory
+ in this case value will be unchanged.
+
@note This function is called from the ON_UPDATE() function of the
session_track_system_variables' sys_var class.
@@ -721,37 +385,44 @@ inline bool Session_sysvars_tracker::check_str(THD *thd, LEX_STRING *val)
bool Session_sysvars_tracker::update(THD *thd, set_var *var)
{
- /*
- We are doing via tool list because there possible errors with memory
- in this case value will be unchanged.
- */
- tool_list->reinit();
- if (tool_list->parse_var_list(thd, var->save_result.string_value, true,
- thd->charset(), true))
+ vars_list tool_list;
+ size_t length= 1;
+ void *copy= var->save_result.string_value.str ?
+ my_memdup(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));
+
+ if (!copy)
return true;
- orig_list->copy(tool_list, thd);
- return false;
-}
+ if (tool_list.parse_var_list(thd, var->save_result.string_value, true,
+ thd->charset()))
+ {
+ my_free(copy);
+ return true;
+ }
-/*
- Function and structure to support storing variables from hash to the buffer.
-*/
+ my_free(thd->variables.session_track_system_variables);
+ thd->variables.session_track_system_variables= static_cast<char*>(copy);
-struct st_store_variable_param
-{
- THD *thd;
- String *buf;
-};
+ m_parsed= true;
+ orig_list.copy(&tool_list, thd);
+ orig_list.construct_var_list(thd->variables.session_track_system_variables,
+ length);
+ return false;
+}
-my_bool Session_sysvars_tracker::store_variable(void *ptr, void *data_ptr)
+
+bool Session_sysvars_tracker::vars_list::store(THD *thd, String *buf)
{
- Session_sysvars_tracker::sysvar_node_st *node=
- (Session_sysvars_tracker::sysvar_node_st *)ptr;
- if (node->m_changed)
+ for (ulong i= 0; i < m_registered_sysvars.records; i++)
{
- THD *thd= ((st_store_variable_param *)data_ptr)->thd;
- String *buf= ((st_store_variable_param *)data_ptr)->buf;
+ sysvar_node_st *node= at(i);
+
+ if (!node->m_changed)
+ continue;
+
char val_buf[SHOW_VAR_FUNC_BUFF_SIZE];
SHOW_VAR show;
CHARSET_INFO *charset;
@@ -760,7 +431,7 @@ my_bool Session_sysvars_tracker::store_variable(void *ptr, void *data_ptr)
if (!*node->test_load)
{
mysql_mutex_unlock(&LOCK_plugin);
- return false;
+ continue;
}
sys_var *svar= node->m_svar;
bool is_plugin= svar->cast_pluginvar();
@@ -805,11 +476,6 @@ my_bool Session_sysvars_tracker::store_variable(void *ptr, void *data_ptr)
return false;
}
-bool Session_sysvars_tracker::vars_list::store(THD *thd, String *buf)
-{
- st_store_variable_param data= {thd, buf};
- return my_hash_iterate(&m_registered_sysvars, &store_variable, &data);
-}
/**
Store the data for changed system variables in the specified buffer.
@@ -825,13 +491,13 @@ bool Session_sysvars_tracker::vars_list::store(THD *thd, String *buf)
bool Session_sysvars_tracker::store(THD *thd, String *buf)
{
- if (!orig_list->is_enabled())
+ if (!orig_list.is_enabled())
return false;
- if (orig_list->store(thd, buf))
+ if (orig_list.store(thd, buf))
return true;
- reset();
+ orig_list.reset();
return false;
}
@@ -846,14 +512,27 @@ bool Session_sysvars_tracker::store(THD *thd, String *buf)
void Session_sysvars_tracker::mark_as_changed(THD *thd,
LEX_CSTRING *var)
{
- sysvar_node_st *node= NULL;
+ sysvar_node_st *node;
sys_var *svar= (sys_var *)var;
+
+ if (!m_parsed)
+ {
+ DBUG_ASSERT(thd->variables.session_track_system_variables);
+ LEX_STRING tmp= { thd->variables.session_track_system_variables,
+ strlen(thd->variables.session_track_system_variables) };
+ if (orig_list.parse_var_list(thd, tmp, true, thd->charset()))
+ {
+ orig_list.reinit();
+ return;
+ }
+ m_parsed= true;
+ }
+
/*
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= (sysvar_node_st *) (orig_list->insert_or_search(node, svar))))
+ if (orig_list.is_enabled() && (node= orig_list.insert_or_search(svar)))
{
node->m_changed= true;
State_tracker::mark_as_changed(thd, var);
@@ -880,63 +559,41 @@ uchar *Session_sysvars_tracker::sysvars_get_key(const char *entry,
}
-/* Function to support resetting hash nodes for the variables */
-
-my_bool Session_sysvars_tracker::reset_variable(void *ptr,
- void *data_ptr)
-{
- ((Session_sysvars_tracker::sysvar_node_st *)ptr)->m_changed= false;
- return false;
-}
-
void Session_sysvars_tracker::vars_list::reset()
{
- my_hash_iterate(&m_registered_sysvars, &reset_variable, NULL);
+ for (ulong i= 0; i < m_registered_sysvars.records; i++)
+ at(i)->m_changed= false;
}
-/**
- Prepare/reset the m_registered_sysvars hash for next statement.
-*/
-void Session_sysvars_tracker::reset()
+bool sysvartrack_global_update(THD *thd, char *str, size_t len)
{
-
- orig_list->reset();
- m_changed= false;
+ LEX_STRING tmp= { str, len };
+ Session_sysvars_tracker::vars_list dummy;
+ if (!dummy.parse_var_list(thd, tmp, false, system_charset_info))
+ {
+ dummy.construct_var_list(str, len + 1);
+ return false;
+ }
+ return true;
}
-static Session_sysvars_tracker* sysvar_tracker(THD *thd)
-{
- return (Session_sysvars_tracker*)
- thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER);
-}
-bool sysvartrack_validate_value(THD *thd, const char *str, size_t len)
-{
- LEX_STRING tmp= {(char *)str, len};
- return Session_sysvars_tracker::server_init_check(thd, system_charset_info,
- tmp);
-}
-bool sysvartrack_reprint_value(THD *thd, char *str, size_t len)
+int session_tracker_init()
{
- LEX_STRING tmp= {str, len};
- return Session_sysvars_tracker::server_init_process(thd,
- system_charset_info,
- tmp);
-}
-bool sysvartrack_update(THD *thd, set_var *var)
-{
- return sysvar_tracker(thd)->update(thd, var);
-}
-size_t sysvartrack_value_len(THD *thd)
-{
- return sysvar_tracker(thd)->get_buffer_length();
-}
-bool sysvartrack_value_construct(THD *thd, char *val, size_t len)
-{
- return sysvar_tracker(thd)->construct_var_list(val, len);
+ DBUG_ASSERT(global_system_variables.session_track_system_variables);
+ if (sysvartrack_validate_value(0,
+ global_system_variables.session_track_system_variables,
+ strlen(global_system_variables.session_track_system_variables)))
+ {
+ sql_print_error("The variable session_track_system_variables has "
+ "invalid values.");
+ return 1;
+ }
+ return 0;
}
+
///////////////////////////////////////////////////////////////////////////////
/**
@@ -974,7 +631,7 @@ bool Current_schema_tracker::store(THD *thd, String *buf)
It saves length of database name and name of database name +
length of saved length of database length.
*/
- length= db_length= thd->db_length;
+ length= db_length= thd->db.length;
length += net_length_size(length);
compile_time_assert(SESSION_TRACK_SCHEMA < 251);
@@ -991,39 +648,14 @@ bool Current_schema_tracker::store(THD *thd, String *buf)
buf->q_net_store_length(length);
/* Length and current schema name */
- buf->q_net_store_data((const uchar *)thd->db, thd->db_length);
-
- reset();
+ buf->q_net_store_data((const uchar *)thd->db.str, thd->db.length);
return false;
}
-/**
- Reset the m_changed flag for next statement.
-
- @return void
-*/
-
-void Current_schema_tracker::reset()
-{
- m_changed= false;
-}
-
-
///////////////////////////////////////////////////////////////////////////////
-
-Transaction_state_tracker::Transaction_state_tracker()
-{
- m_enabled = false;
- tx_changed = TX_CHG_NONE;
- tx_curr_state =
- tx_reported_state= TX_EMPTY;
- tx_read_flags = TX_READ_INHERIT;
- tx_isol_level = TX_ISOL_INHERIT;
-}
-
/**
Enable/disable the tracker based on @@session_track_transaction_info.
@@ -1185,7 +817,7 @@ bool Transaction_state_tracker::store(THD *thd, String *buf)
statement even for a transaction that isn't the first in an
ongoing chain. Consider
- SET TRANSACTION ISOLATION LEVEL READ UNCOMMITED;
+ SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION READ ONLY, WITH CONSISTENT SNAPSHOT;
# work
COMMIT AND CHAIN;
@@ -1193,7 +825,7 @@ bool Transaction_state_tracker::store(THD *thd, String *buf)
If we switch away at this point, the replay in the new session
needs to be
- SET TRANSACTION ISOLATION LEVEL READ UNCOMMITED;
+ SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION READ ONLY;
When a transaction ends (COMMIT/ROLLBACK sans CHAIN), all
@@ -1216,7 +848,7 @@ bool Transaction_state_tracker::store(THD *thd, String *buf)
tx_isolation_typelib as it hyphenates its items.
*/
buf->append(STRING_WITH_LEN("SET TRANSACTION ISOLATION LEVEL "));
- buf->append(isol[tx_isol_level - 1].str, isol[tx_isol_level - 1].length);
+ buf->append(&isol[tx_isol_level - 1]);
buf->append(STRING_WITH_LEN("; "));
}
@@ -1333,25 +965,14 @@ bool Transaction_state_tracker::store(THD *thd, String *buf)
}
}
- reset();
+ tx_reported_state= tx_curr_state;
+ tx_changed= TX_CHG_NONE;
return false;
}
/**
- Reset the m_changed flag for next statement.
-*/
-
-void Transaction_state_tracker::reset()
-{
- m_changed= false;
- tx_reported_state= tx_curr_state;
- tx_changed= TX_CHG_NONE;
-}
-
-
-/**
Helper function: turn table info into table access flag.
Accepts table lock type and engine type flag (transactional/
non-transactional), and returns the corresponding access flag
@@ -1520,11 +1141,6 @@ void Transaction_state_tracker::set_isol_level(THD *thd,
///////////////////////////////////////////////////////////////////////////////
-Session_state_change_tracker::Session_state_change_tracker()
-{
- m_changed= false;
-}
-
/**
@Enable/disable the tracker based on @@session_track_state_change value.
@@ -1562,106 +1178,15 @@ bool Session_state_change_tracker::store(THD *thd, String *buf)
/* Length of the overall entity (1 byte) */
buf->q_append('\1');
- DBUG_ASSERT(is_state_changed(thd));
+ DBUG_ASSERT(is_changed());
buf->q_append('1');
- reset();
-
return false;
}
-
-/**
- Reset the m_changed flag for next statement.
-*/
-
-void Session_state_change_tracker::reset()
-{
- m_changed= false;
-}
-
-
-/**
- Find if there is a session state change.
-*/
-
-bool Session_state_change_tracker::is_state_changed(THD *)
-{
- return m_changed;
-}
-
///////////////////////////////////////////////////////////////////////////////
/**
- @brief Initialize session tracker objects.
-*/
-
-Session_tracker::Session_tracker()
-{
- /* track data ID fit into one byte in net coding */
- compile_time_assert(SESSION_TRACK_always_at_the_end < 251);
- /* one tracker could serv several tracking data */
- compile_time_assert((uint)SESSION_TRACK_always_at_the_end >=
- (uint)SESSION_TRACKER_END);
-
- for (int i= 0; i < SESSION_TRACKER_END; i++)
- m_trackers[i]= NULL;
-}
-
-
-/**
- @brief Enables the tracker objects.
-
- @param thd [IN] The thread handle.
-
- @return void
-*/
-
-void Session_tracker::enable(THD *thd)
-{
- /*
- Originally and correctly this allocation was in the constructor and
- deallocation in the destructor, but in this case memory counting
- system works incorrectly (for example in INSERT DELAYED thread)
- */
- deinit();
- m_trackers[SESSION_SYSVARS_TRACKER]=
- new (std::nothrow) Session_sysvars_tracker();
- m_trackers[CURRENT_SCHEMA_TRACKER]=
- new (std::nothrow) Current_schema_tracker;
- m_trackers[SESSION_STATE_CHANGE_TRACKER]=
- new (std::nothrow) Session_state_change_tracker;
- m_trackers[SESSION_GTIDS_TRACKER]=
- new (std::nothrow) Not_implemented_tracker;
- m_trackers[TRANSACTION_INFO_TRACKER]=
- new (std::nothrow) Transaction_state_tracker;
-
- for (int i= 0; i < SESSION_TRACKER_END; i++)
- m_trackers[i]->enable(thd);
-}
-
-
-/**
- Method called during the server startup to verify the contents
- of @@session_track_system_variables.
-
- @retval false Success
- @retval true Failure
-*/
-
-bool Session_tracker::server_boot_verify(CHARSET_INFO *char_set)
-{
- bool result;
- LEX_STRING tmp;
- tmp.str= global_system_variables.session_track_system_variables;
- tmp.length= safe_strlen(tmp.str);
- result=
- Session_sysvars_tracker::server_init_check(NULL, char_set, tmp);
- return result;
-}
-
-
-/**
@brief Store all change information in the specified buffer.
@param thd [IN] The thd handle.
@@ -1673,6 +1198,12 @@ void Session_tracker::store(THD *thd, String *buf)
{
size_t start;
+ /* track data ID fit into one byte in net coding */
+ compile_time_assert(SESSION_TRACK_always_at_the_end < 251);
+ /* one tracker could serv several tracking data */
+ compile_time_assert((uint) SESSION_TRACK_always_at_the_end >=
+ (uint) SESSION_TRACKER_END);
+
/*
Probably most track result will fit in 251 byte so lets made it at
least efficient. We allocate 1 byte for length and then will move
@@ -1684,11 +1215,15 @@ void Session_tracker::store(THD *thd, String *buf)
/* Get total length. */
for (int i= 0; i < SESSION_TRACKER_END; i++)
{
- if (m_trackers[i]->is_changed() &&
- m_trackers[i]->store(thd, buf))
+ if (m_trackers[i]->is_changed())
{
- buf->length(start); // it is safer to have 0-length block in case of error
- return;
+ if (m_trackers[i]->store(thd, buf))
+ {
+ // it is safer to have 0-length block in case of error
+ buf->length(start);
+ return;
+ }
+ m_trackers[i]->reset_changed();
}
}
@@ -1717,5 +1252,3 @@ void Session_tracker::store(THD *thd, String *buf)
net_store_length(data - 1, length);
}
-
-#endif //EMBEDDED_LIBRARY
diff --git a/sql/session_tracker.h b/sql/session_tracker.h
index 684692aae0c..226b026d590 100644
--- a/sql/session_tracker.h
+++ b/sql/session_tracker.h
@@ -32,7 +32,6 @@ enum enum_session_tracker
SESSION_SYSVARS_TRACKER, /* Session system variables */
CURRENT_SCHEMA_TRACKER, /* Current schema */
SESSION_STATE_CHANGE_TRACKER,
- SESSION_GTIDS_TRACKER, /* Tracks GTIDs */
TRANSACTION_INFO_TRACKER, /* Transaction state */
SESSION_TRACKER_END /* must be the last */
};
@@ -67,17 +66,12 @@ protected:
*/
bool m_enabled;
+private:
/** Has the session state type changed ? */
bool m_changed;
public:
- /** Constructor */
- State_tracker() : m_enabled(false), m_changed(false)
- {}
-
- /** Destructor */
- virtual ~State_tracker()
- {}
+ virtual ~State_tracker() {}
/** Getters */
bool is_enabled() const
@@ -86,8 +80,20 @@ public:
bool is_changed() const
{ return m_changed; }
- /** Called in the constructor of THD*/
- virtual bool enable(THD *thd)= 0;
+ void reset_changed() { m_changed= false; }
+
+ /**
+ Called by THD::init() when new connection is being created
+
+ We may inherit m_changed from previous connection served by this THD if
+ connection was broken or client didn't have session tracking capability.
+ Thus we have to reset it here.
+ */
+ virtual bool enable(THD *thd)
+ {
+ reset_changed();
+ return update(thd, 0);
+ }
/** To be invoked when the tracker's system variable is updated (ON_UPDATE).*/
virtual bool update(THD *thd, set_var *var)= 0;
@@ -99,74 +105,156 @@ public:
virtual void mark_as_changed(THD *thd, LEX_CSTRING *name);
};
-bool sysvartrack_validate_value(THD *thd, const char *str, size_t len);
-bool sysvartrack_reprint_value(THD *thd, char *str, size_t len);
-bool sysvartrack_update(THD *thd, set_var *var);
-size_t sysvartrack_value_len(THD *thd);
-bool sysvartrack_value_construct(THD *thd, char *val, size_t len);
-
/**
- Session_tracker
+ Session_sysvars_tracker
- This class holds an object each for all tracker classes and provides
- methods necessary for systematic detection and generation of session
- state change information.
+ This is a tracker class that enables & manages the tracking of session
+ system variables. It internally maintains a hash of user supplied variable
+ references and a boolean field to store if the variable was changed by the
+ last statement.
*/
-class Session_tracker
+class Session_sysvars_tracker: public State_tracker
{
-private:
- State_tracker *m_trackers[SESSION_TRACKER_END];
+ struct sysvar_node_st {
+ sys_var *m_svar;
+ bool *test_load;
+ bool m_changed;
+ };
- /* The following two functions are private to disable copying. */
- Session_tracker(Session_tracker const &other)
- {
- DBUG_ASSERT(FALSE);
- }
- Session_tracker& operator= (Session_tracker const &rhs)
+ class vars_list
{
- DBUG_ASSERT(FALSE);
- return *this;
- }
+ /**
+ Registered system variables. (@@session_track_system_variables)
+ A hash to store the name of all the system variables specified by the
+ user.
+ */
+ HASH m_registered_sysvars;
+ /**
+ If TRUE then we want to check all session variable.
+ */
+ 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));
+ }
+ void free_hash()
+ {
+ DBUG_ASSERT(my_hash_inited(&m_registered_sysvars));
+ my_hash_free(&m_registered_sysvars);
+ }
-public:
+ sysvar_node_st *search(const sys_var *svar)
+ {
+ return reinterpret_cast<sysvar_node_st*>(
+ my_hash_search(&m_registered_sysvars,
+ reinterpret_cast<const uchar*>(&svar),
+ sizeof(sys_var*)));
+ }
- Session_tracker();
- ~Session_tracker()
- {
- deinit();
- }
+ sysvar_node_st *at(ulong i)
+ {
+ DBUG_ASSERT(i < m_registered_sysvars.records);
+ return reinterpret_cast<sysvar_node_st*>(
+ my_hash_element(&m_registered_sysvars, i));
+ }
+ public:
+ vars_list(): track_all(false) { init(); }
+ ~vars_list() { if (my_hash_inited(&m_registered_sysvars)) free_hash(); }
+ void deinit() { free_hash(); }
- /* trick to make happy memory accounting system */
- void deinit()
- {
- for (int i= 0; i < SESSION_TRACKER_END; i++)
+ sysvar_node_st *insert_or_search(const sys_var *svar)
{
- if (m_trackers[i])
- delete m_trackers[i];
- m_trackers[i]= NULL;
+ sysvar_node_st *res= search(svar);
+ if (!res)
+ {
+ if (track_all)
+ {
+ insert(svar);
+ return search(svar);
+ }
+ }
+ return res;
}
- }
- void enable(THD *thd);
- static bool server_boot_verify(CHARSET_INFO *char_set);
+ bool insert(const sys_var *svar);
+ void reinit();
+ void reset();
+ inline bool is_enabled()
+ {
+ return track_all || m_registered_sysvars.records;
+ }
+ void copy(vars_list* from, THD *thd);
+ bool parse_var_list(THD *thd, LEX_STRING var_list, bool throw_error,
+ CHARSET_INFO *char_set);
+ bool construct_var_list(char *buf, size_t buf_len);
+ bool store(THD *thd, String *buf);
+ };
+ /**
+ Two objects of vars_list type are maintained to manage
+ various operations.
+ */
+ vars_list orig_list;
+ bool m_parsed;
- /** Returns the pointer to the tracker object for the specified tracker. */
- inline State_tracker *get_tracker(enum_session_tracker tracker) const
- {
- return m_trackers[tracker];
- }
+public:
+ void init(THD *thd);
+ void deinit(THD *thd);
+ 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 deinit() { orig_list.deinit(); }
+ /* callback */
+ static uchar *sysvars_get_key(const char *entry, size_t *length,
+ my_bool not_used __attribute__((unused)));
- 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);
- }
+ friend bool sysvartrack_global_update(THD *thd, char *str, size_t len);
+};
- void store(THD *thd, String *main_buf);
+bool sysvartrack_validate_value(THD *thd, const char *str, size_t len);
+bool sysvartrack_global_update(THD *thd, char *str, size_t len);
+
+
+/**
+ Current_schema_tracker,
+
+ This is a tracker class that enables & manages the tracking of current
+ schema for a particular connection.
+*/
+
+class Current_schema_tracker: public State_tracker
+{
+public:
+ bool update(THD *thd, set_var *var);
+ bool store(THD *thd, String *buf);
+};
+
+
+/*
+ Session_state_change_tracker
+
+ This is a boolean tracker class that will monitor any change that contributes
+ to a session state change.
+ Attributes that contribute to session state change include:
+ - Successful change to System variables
+ - User defined variables assignments
+ - temporary tables created, altered or deleted
+ - prepared statements added or removed
+ - change in current database
+ - change of current role
+*/
+
+class Session_state_change_tracker: public State_tracker
+{
+public:
+ bool update(THD *thd, set_var *var);
+ bool store(THD *thd, String *buf);
};
@@ -231,14 +319,21 @@ enum enum_session_track_transaction_info {
class Transaction_state_tracker : public State_tracker
{
-private:
/** Helper function: turn table info into table access flag */
enum_tx_state calc_trx_state(THD *thd, thr_lock_type l, bool has_trx);
public:
- /** Constructor */
- Transaction_state_tracker();
+
bool enable(THD *thd)
- { return update(thd, NULL); }
+ {
+ m_enabled= false;
+ tx_changed= TX_CHG_NONE;
+ tx_curr_state= TX_EMPTY;
+ tx_reported_state= TX_EMPTY;
+ tx_read_flags= TX_READ_INHERIT;
+ tx_isol_level= TX_ISOL_INHERIT;
+ return State_tracker::enable(thd);
+ }
+
bool update(THD *thd, set_var *var);
bool store(THD *thd, String *buf);
@@ -276,8 +371,6 @@ private:
/** isolation level */
enum enum_tx_isol_level tx_isol_level;
- void reset();
-
inline void update_change_flags(THD *thd)
{
tx_changed &= uint(~TX_CHG_STATE);
@@ -289,11 +382,67 @@ private:
#define TRANSACT_TRACKER(X) \
do { if (thd->variables.session_track_transaction_info > TX_TRACK_NONE) \
- {((Transaction_state_tracker *) \
- thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER)) \
- ->X; } } while(0)
+ thd->session_tracker.transaction_info.X; } while(0)
#define SESSION_TRACKER_CHANGED(A,B,C) \
thd->session_tracker.mark_as_changed(A,B,C)
+
+
+/**
+ Session_tracker
+
+ This class holds an object each for all tracker classes and provides
+ methods necessary for systematic detection and generation of session
+ state change information.
+*/
+
+class Session_tracker
+{
+ State_tracker *m_trackers[SESSION_TRACKER_END];
+
+ /* The following two functions are private to disable copying. */
+ Session_tracker(Session_tracker const &other)
+ {
+ DBUG_ASSERT(FALSE);
+ }
+ Session_tracker& operator= (Session_tracker const &rhs)
+ {
+ DBUG_ASSERT(FALSE);
+ return *this;
+ }
+
+public:
+ Current_schema_tracker current_schema;
+ Session_state_change_tracker state_change;
+ Transaction_state_tracker transaction_info;
+ Session_sysvars_tracker sysvars;
+
+ Session_tracker()
+ {
+ m_trackers[SESSION_SYSVARS_TRACKER]= &sysvars;
+ m_trackers[CURRENT_SCHEMA_TRACKER]= &current_schema;
+ m_trackers[SESSION_STATE_CHANGE_TRACKER]= &state_change;
+ m_trackers[TRANSACTION_INFO_TRACKER]= &transaction_info;
+ }
+
+ void enable(THD *thd)
+ {
+ for (int i= 0; i < SESSION_TRACKER_END; i++)
+ 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);
+};
+
+
+int session_tracker_init();
#else
#define TRANSACT_TRACKER(X) do{}while(0)
diff --git a/sql/set_var.cc b/sql/set_var.cc
index 569362fb7bd..eb2b9234db3 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -16,7 +16,7 @@
/* variable declarations are in sys_vars.cc now !!! */
-#include "sql_plugin.h" // Includes my_global.h
+#include "sql_plugin.h"
#include "sql_class.h" // set_var.h: session_var_ptr
#include "set_var.h"
#include "sql_priv.h"
@@ -233,22 +233,22 @@ bool sys_var::update(THD *thd, set_var *var)
}
}
-uchar *sys_var::session_value_ptr(THD *thd, const LEX_STRING *base)
+uchar *sys_var::session_value_ptr(THD *thd, const LEX_CSTRING *base)
{
return session_var_ptr(thd);
}
-uchar *sys_var::global_value_ptr(THD *thd, const LEX_STRING *base)
+uchar *sys_var::global_value_ptr(THD *thd, const LEX_CSTRING *base)
{
return global_var_ptr();
}
bool sys_var::check(THD *thd, set_var *var)
{
- if ((var->value && do_check(thd, var))
- || (on_check && on_check(this, thd, var)))
+ if (unlikely((var->value && do_check(thd, var)) ||
+ (on_check && on_check(this, thd, var))))
{
- if (!thd->is_error())
+ if (likely(!thd->is_error()))
{
char buff[STRING_BUFFER_USUAL_SIZE];
String str(buff, sizeof(buff), system_charset_info), *res;
@@ -271,7 +271,8 @@ bool sys_var::check(THD *thd, set_var *var)
return false;
}
-uchar *sys_var::value_ptr(THD *thd, enum_var_type type, const LEX_STRING *base)
+uchar *sys_var::value_ptr(THD *thd, enum_var_type type,
+ const LEX_CSTRING *base)
{
DBUG_ASSERT(base);
if (type == OPT_GLOBAL || scope() == GLOBAL)
@@ -323,13 +324,14 @@ do { \
sval.length= sval.str ? strlen(sval.str) : 0; \
break; \
case SHOW_LEX_STRING: \
- sval= *(LEX_STRING *) value; \
+ sval= *(LEX_CSTRING *) value; \
break
longlong sys_var::val_int(bool *is_null,
- THD *thd, enum_var_type type, const LEX_STRING *base)
+ THD *thd, enum_var_type type,
+ const LEX_CSTRING *base)
{
- LEX_STRING sval;
+ LEX_CSTRING sval;
AutoWLock lock(&PLock_global_system_variables);
const uchar *value= value_ptr(thd, type, base);
*is_null= false;
@@ -355,13 +357,13 @@ longlong sys_var::val_int(bool *is_null,
String *sys_var::val_str_nolock(String *str, THD *thd, const uchar *value)
{
- static LEX_STRING bools[]=
+ static LEX_CSTRING bools[]=
{
- { C_STRING_WITH_LEN("OFF") },
- { C_STRING_WITH_LEN("ON") }
+ { STRING_WITH_LEN("OFF") },
+ { STRING_WITH_LEN("ON") }
};
- LEX_STRING sval;
+ LEX_CSTRING sval;
switch (show_type())
{
case_get_string_as_lex_string;
@@ -382,7 +384,7 @@ String *sys_var::val_str_nolock(String *str, THD *thd, const uchar *value)
String *sys_var::val_str(String *str,
- THD *thd, enum_var_type type, const LEX_STRING *base)
+ THD *thd, enum_var_type type, const LEX_CSTRING *base)
{
AutoWLock lock(&PLock_global_system_variables);
const uchar *value= value_ptr(thd, type, base);
@@ -391,9 +393,9 @@ String *sys_var::val_str(String *str,
double sys_var::val_real(bool *is_null,
- THD *thd, enum_var_type type, const LEX_STRING *base)
+ THD *thd, enum_var_type type, const LEX_CSTRING *base)
{
- LEX_STRING sval;
+ LEX_CSTRING sval;
AutoWLock lock(&PLock_global_system_variables);
const uchar *value= value_ptr(thd, type, base);
*is_null= false;
@@ -683,7 +685,7 @@ SHOW_VAR* enumerate_sys_vars(THD *thd, bool sorted, enum enum_var_type scope)
0 Unknown variable (error message is given)
*/
-sys_var *intern_find_sys_var(const char *str, uint length)
+sys_var *intern_find_sys_var(const char *str, size_t length)
{
sys_var *var;
@@ -728,10 +730,10 @@ int sql_set_variables(THD *thd, List<set_var_base> *var_list, bool free)
set_var_base *var;
while ((var=it++))
{
- if ((error= var->check(thd)))
+ if (unlikely((error= var->check(thd))))
goto err;
}
- if (was_error || !(error= MY_TEST(thd->is_error())))
+ if (unlikely(was_error) || likely(!(error= MY_TEST(thd->is_error()))))
{
it.rewind();
while ((var= it++))
@@ -778,8 +780,7 @@ int set_var::check(THD *thd)
if (!value)
return 0;
- if ((!value->fixed &&
- value->fix_fields(thd, &value)) || value->check_cols(1))
+ if (value->fix_fields_if_needed_for_scalar(thd, &value))
return -1;
if (var->check_update_type(value))
{
@@ -807,14 +808,13 @@ int set_var::light_check(THD *thd)
if (var->check_type(type))
{
int err= type == OPT_GLOBAL ? ER_LOCAL_VARIABLE : ER_GLOBAL_VARIABLE;
- my_error(err, MYF(0), var->name);
+ my_error(err, MYF(0), var->name.str);
return -1;
}
if (type == OPT_GLOBAL && check_global_access(thd, SUPER_ACL))
return 1;
- if (value && ((!value->fixed && value->fix_fields(thd, &value)) ||
- value->check_cols(1)))
+ if (value && value->fix_fields_if_needed_for_scalar(thd, &value))
return -1;
return 0;
}
@@ -839,7 +839,7 @@ int set_var::update(THD *thd)
set_var::set_var(THD *thd, enum_var_type type_arg, sys_var *var_arg,
- const LEX_STRING *base_name_arg, Item *value_arg)
+ const LEX_CSTRING *base_name_arg, Item *value_arg)
:var(var_arg), type(type_arg), base(*base_name_arg)
{
/*
@@ -850,7 +850,9 @@ set_var::set_var(THD *thd, enum_var_type type_arg, sys_var *var_arg,
{
Item_field *item= (Item_field*) value_arg;
// names are utf8
- if (!(value= new (thd->mem_root) Item_string_sys(thd, item->field_name)))
+ if (!(value= new (thd->mem_root) Item_string_sys(thd,
+ item->field_name.str,
+ (uint)item->field_name.length)))
value=value_arg; /* Give error message later */
}
else
@@ -1024,13 +1026,13 @@ int set_var_collation_client::update(THD *thd)
/* Mark client collation variables as changed */
#ifndef EMBEDDED_LIBRARY
- if (thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER)->is_enabled())
+ if (thd->session_tracker.sysvars.is_enabled())
{
- thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER)->
+ thd->session_tracker.sysvars.
mark_as_changed(thd, (LEX_CSTRING*)Sys_character_set_client_ptr);
- thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER)->
+ thd->session_tracker.sysvars.
mark_as_changed(thd, (LEX_CSTRING*)Sys_character_set_results_ptr);
- thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER)->
+ 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);
@@ -1060,7 +1062,7 @@ static void store_var(Field *field, sys_var *var, enum_var_type scope,
return;
store_value_ptr(field, var, str,
- var->value_ptr(field->table->in_use, scope, &null_lex_str));
+ var->value_ptr(field->table->in_use, scope, &null_clex_str));
}
int fill_sysvars(THD *thd, TABLE_LIST *tables, COND *cond)
@@ -1157,6 +1159,7 @@ int fill_sysvars(THD *thd, TABLE_LIST *tables, COND *cond)
{ STRING_WITH_LEN("SET") }, // GET_SET 13
{ STRING_WITH_LEN("DOUBLE") }, // GET_DOUBLE 14
{ STRING_WITH_LEN("FLAGSET") }, // GET_FLAGSET 15
+ { STRING_WITH_LEN("BOOLEAN") }, // GET_BIT 16
};
const ulong vartype= (var->option.var_type & GET_TYPE_MASK);
const LEX_CSTRING *type= types + vartype;
@@ -1288,3 +1291,222 @@ enum sys_var::where get_sys_var_value_origin(void *ptr)
return sys_var::CONFIG;
}
+
+/*
+ Find the next item in string of comma-separated items.
+ END_POS points at the end of the string.
+ ITEM_START and ITEM_END return the limits of the next item.
+ Returns true while items are available, false at the end.
+*/
+static bool
+engine_list_next_item(const char **pos, const char *end_pos,
+ const char **item_start, const char **item_end)
+{
+ if (*pos >= end_pos)
+ return false;
+ *item_start= *pos;
+ while (*pos < end_pos && **pos != ',')
+ ++*pos;
+ *item_end= *pos;
+ ++*pos;
+ return true;
+}
+
+
+static bool
+resolve_engine_list_item(THD *thd, plugin_ref *list, uint32 *idx,
+ const char *pos, const char *pos_end,
+ bool error_on_unknown_engine, bool temp_copy)
+{
+ LEX_CSTRING item_str;
+ plugin_ref ref;
+ uint32 i;
+ THD *thd_or_null = (temp_copy ? thd : NULL);
+
+ item_str.str= pos;
+ item_str.length= pos_end-pos;
+ ref= ha_resolve_by_name(thd_or_null, &item_str, false);
+ if (!ref)
+ {
+ if (error_on_unknown_engine)
+ {
+ ErrConvString err(pos, pos_end-pos, system_charset_info);
+ my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), err.ptr());
+ return true;
+ }
+ return false;
+ }
+ /* Ignore duplicates, like --plugin-load does. */
+ for (i= 0; i < *idx; ++i)
+ {
+ if (plugin_hton(list[i]) == plugin_hton(ref))
+ {
+ if (!temp_copy)
+ plugin_unlock(NULL, ref);
+ return false;
+ }
+ }
+ list[*idx]= ref;
+ ++*idx;
+ return false;
+}
+
+
+/*
+ Helper for class Sys_var_pluginlist.
+ Resolve a comma-separated list of storage engine names to a null-terminated
+ array of plugin_ref.
+
+ If TEMP_COPY is true, a THD must be given as well. In this case, the
+ allocated memory and locked plugins are registered in the THD and will
+ be freed / unlocked automatically. If TEMP_COPY is true, THD can be
+ passed as NULL, and resources must be freed explicitly later with
+ free_engine_list().
+*/
+plugin_ref *
+resolve_engine_list(THD *thd, const char *str_arg, size_t str_arg_len,
+ bool error_on_unknown_engine, bool temp_copy)
+{
+ uint32 count, idx;
+ const char *pos, *item_start, *item_end;
+ const char *str_arg_end= str_arg + str_arg_len;
+ plugin_ref *res;
+
+ count= 0;
+ pos= str_arg;
+ for (;;)
+ {
+ if (!engine_list_next_item(&pos, str_arg_end, &item_start, &item_end))
+ break;
+ ++count;
+ }
+
+ 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));
+ if (!res)
+ {
+ my_error(ER_OUTOFMEMORY, MYF(0), (int)((count+1)*sizeof(*res)));
+ goto err;
+ }
+
+ idx= 0;
+ pos= str_arg;
+ for (;;)
+ {
+ if (!engine_list_next_item(&pos, str_arg_end, &item_start, &item_end))
+ break;
+ DBUG_ASSERT(idx < count);
+ if (idx >= count)
+ break;
+ if (resolve_engine_list_item(thd, res, &idx, item_start, item_end,
+ error_on_unknown_engine, temp_copy))
+ goto err;
+ }
+
+ return res;
+
+err:
+ if (!temp_copy)
+ free_engine_list(res);
+ return NULL;
+}
+
+
+void
+free_engine_list(plugin_ref *list)
+{
+ plugin_ref *p;
+
+ if (!list)
+ return;
+ for (p= list; *p; ++p)
+ plugin_unlock(NULL, *p);
+ my_free(list);
+}
+
+
+plugin_ref *
+copy_engine_list(plugin_ref *list)
+{
+ plugin_ref *p;
+ uint32 count, i;
+
+ for (p= list, count= 0; *p; ++p, ++count)
+ ;
+ p= (plugin_ref *)my_malloc((count+1)*sizeof(*p), MYF(0));
+ if (!p)
+ {
+ my_error(ER_OUTOFMEMORY, MYF(0), (int)((count+1)*sizeof(*p)));
+ return NULL;
+ }
+ for (i= 0; i < count; ++i)
+ p[i]= my_plugin_lock(NULL, list[i]);
+ p[i] = NULL;
+ return p;
+}
+
+
+/*
+ Create a temporary copy of an engine list. The memory will be freed
+ (and the plugins unlocked) automatically, on the passed THD.
+*/
+plugin_ref *
+temp_copy_engine_list(THD *thd, plugin_ref *list)
+{
+ plugin_ref *p;
+ uint32 count, i;
+
+ for (p= list, count= 0; *p; ++p, ++count)
+ ;
+ p= (plugin_ref *)thd->alloc((count+1)*sizeof(*p));
+ if (!p)
+ {
+ my_error(ER_OUTOFMEMORY, MYF(0), (int)((count+1)*sizeof(*p)));
+ return NULL;
+ }
+ for (i= 0; i < count; ++i)
+ p[i]= my_plugin_lock(thd, list[i]);
+ p[i] = NULL;
+ return p;
+}
+
+
+char *
+pretty_print_engine_list(THD *thd, plugin_ref *list)
+{
+ plugin_ref *p;
+ size_t size;
+ char *buf, *pos;
+
+ if (!list || !*list)
+ return thd->strmake("", 0);
+
+ size= 0;
+ for (p= list; *p; ++p)
+ size+= plugin_name(*p)->length + 1;
+ buf= static_cast<char *>(thd->alloc(size));
+ if (!buf)
+ return NULL;
+ pos= buf;
+ for (p= list; *p; ++p)
+ {
+ LEX_CSTRING *name;
+ size_t remain;
+
+ remain= buf + size - pos;
+ DBUG_ASSERT(remain > 0);
+ if (remain <= 1)
+ break;
+ if (pos != buf)
+ {
+ pos= strmake(pos, ",", remain-1);
+ --remain;
+ }
+ name= plugin_name(*p);
+ pos= strmake(pos, name->str, MY_MIN(name->length, remain-1));
+ }
+ *pos= '\0';
+ return buf;
+}
diff --git a/sql/set_var.h b/sql/set_var.h
index b43e8f96c59..12e025e4696 100644
--- a/sql/set_var.h
+++ b/sql/set_var.h
@@ -1,7 +1,7 @@
#ifndef SET_VAR_INCLUDED
#define SET_VAR_INCLUDED
/* Copyright (c) 2002, 2013, Oracle and/or its affiliates.
- Copyright (c) 2009, 2014, SkySQL Ab.
+ 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
@@ -112,7 +112,7 @@ public:
virtual sys_var_pluginvar *cast_pluginvar() { return 0; }
bool check(THD *thd, set_var *var);
- uchar *value_ptr(THD *thd, enum_var_type type, const LEX_STRING *base);
+ uchar *value_ptr(THD *thd, enum_var_type type, const LEX_CSTRING *base);
/**
Update the system variable with the default value from either
@@ -123,9 +123,9 @@ public:
bool update(THD *thd, set_var *var);
String *val_str_nolock(String *str, THD *thd, const uchar *value);
- longlong val_int(bool *is_null, THD *thd, enum_var_type type, const LEX_STRING *base);
- String *val_str(String *str, THD *thd, enum_var_type type, const LEX_STRING *base);
- double val_real(bool *is_null, THD *thd, enum_var_type type, const LEX_STRING *base);
+ longlong val_int(bool *is_null, THD *thd, enum_var_type type, const LEX_CSTRING *base);
+ String *val_str(String *str, THD *thd, enum_var_type type, const LEX_CSTRING *base);
+ double val_real(bool *is_null, THD *thd, enum_var_type type, const LEX_CSTRING *base);
SHOW_TYPE show_type() { return show_val_type; }
int scope() const { return flags & SCOPE_MASK; }
@@ -158,6 +158,7 @@ public:
case GET_BOOL:
case GET_SET:
case GET_FLAGSET:
+ case GET_BIT:
return type != STRING_RESULT && type != INT_RESULT;
case GET_DOUBLE:
return type != INT_RESULT && type != REAL_RESULT && type != DECIMAL_RESULT;
@@ -229,8 +230,8 @@ protected:
It must be of show_val_type type (my_bool for SHOW_MY_BOOL,
int for SHOW_INT, longlong for SHOW_LONGLONG, etc).
*/
- virtual uchar *session_value_ptr(THD *thd, const LEX_STRING *base);
- virtual uchar *global_value_ptr(THD *thd, const LEX_STRING *base);
+ virtual uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base);
+ virtual uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base);
/**
A pointer to a storage area of the variable, to the raw data.
@@ -292,14 +293,15 @@ public:
longlong longlong_value; ///< for signed integer
double double_value; ///< for Sys_var_double
plugin_ref plugin; ///< for Sys_var_plugin
+ plugin_ref *plugins; ///< for Sys_var_pluginlist
Time_zone *time_zone; ///< for Sys_var_tz
LEX_STRING string_value; ///< for Sys_var_charptr and others
const void *ptr; ///< for Sys_var_struct
} save_result;
- LEX_STRING base; /**< for structured variables, like keycache_name.variable_name */
+ LEX_CSTRING base; /**< for structured variables, like keycache_name.variable_name */
set_var(THD *thd, enum_var_type type_arg, sys_var *var_arg,
- const LEX_STRING *base_name_arg, Item *value_arg);
+ const LEX_CSTRING *base_name_arg, Item *value_arg);
virtual bool is_system() { return 1; }
int check(THD *thd);
int update(THD *thd);
@@ -336,10 +338,10 @@ public:
class set_var_role: public set_var_base
{
- LEX_STRING role;
+ LEX_CSTRING role;
ulonglong access;
public:
- set_var_role(LEX_STRING role_arg) : role(role_arg) {}
+ set_var_role(LEX_CSTRING role_arg) : role(role_arg) {}
int check(THD *thd);
int update(THD *thd);
};
@@ -349,10 +351,10 @@ public:
class set_var_default_role: public set_var_base
{
LEX_USER *user, *real_user;
- LEX_STRING role;
+ LEX_CSTRING role;
const char *real_role;
public:
- set_var_default_role(LEX_USER *user_arg, LEX_STRING role_arg) :
+ set_var_default_role(LEX_USER *user_arg, LEX_CSTRING role_arg) :
user(user_arg), role(role_arg) {}
int check(THD *thd);
int update(THD *thd);
@@ -397,7 +399,8 @@ extern SHOW_COMP_OPTION have_openssl;
SHOW_VAR* enumerate_sys_vars(THD *thd, bool sorted, enum enum_var_type type);
int fill_sysvars(THD *thd, TABLE_LIST *tables, COND *cond);
-sys_var *find_sys_var(THD *thd, const char *str, size_t length=0);
+sys_var *find_sys_var(THD *thd, const char *str, size_t length= 0,
+ bool throw_error= false);
int sql_set_variables(THD *thd, List<set_var_base> *var_list, bool free);
#define SYSVAR_AUTOSIZE(VAR,VAL) \
@@ -429,7 +432,8 @@ bool fix_delay_key_write(sys_var *self, THD *thd, enum_var_type type);
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_STRING *ls);
+bool sql_mode_string_representation(THD *thd, sql_mode_t sql_mode,
+ LEX_CSTRING *ls);
int default_regex_flags_pcre(const THD *thd);
extern sys_var *Sys_autocommit_ptr, *Sys_last_gtid_ptr,
@@ -443,6 +447,11 @@ uint sys_var_elements();
int sys_var_add_options(DYNAMIC_ARRAY *long_options, int parse_flags);
void sys_var_end(void);
bool check_has_super(sys_var *self, THD *thd, set_var *var);
+plugin_ref *resolve_engine_list(THD *thd, const char *str_arg, size_t str_arg_len,
+ bool error_on_unknown_engine, bool temp_copy);
+void free_engine_list(plugin_ref *list);
+plugin_ref *copy_engine_list(plugin_ref *list);
+plugin_ref *temp_copy_engine_list(THD *thd, plugin_ref *list);
+char *pretty_print_engine_list(THD *thd, plugin_ref *list);
#endif
-
diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
index a4465ae0083..dbc52697d51 100644
--- a/sql/share/errmsg-utf8.txt
+++ b/sql/share/errmsg-utf8.txt
@@ -952,10 +952,10 @@ 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"
- eng "Out of memory; check if mysqld or some other process uses all available memory; if not, you may have to use 'ulimit' to allow mysqld to use more memory or you can add more swap space"
+ 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"
- ger "Kein Speicher mehr vorhanden. Prüfen Sie, ob mysqld oder ein anderer Prozess den gesamten Speicher verbraucht. Wenn nicht, sollten Sie mit 'ulimit' dafür sorgen, dass mysqld mehr Speicher benutzen darf, oder mehr Swap-Speicher einrichten"
+ ger "Kein Speicher mehr vorhanden."
greek "Πρόβλημα με τη διαθέσιμη μνήμη (Out of thread space/memory)"
hun "Elfogyott a thread-memoria"
ita "Fine dello spazio/memoria per i thread"
@@ -964,14 +964,14 @@ ER_OUT_OF_RESOURCES
nor "Tomt for tråd plass/minne"
norwegian-ny "Tomt for tråd plass/minne"
pol "Zbyt mało miejsca/pamięci dla w?tku"
- por "Sem memória. Verifique se o mysqld ou algum outro processo está usando toda memória disponível. Se não, você pode ter que usar 'ulimit' para permitir ao mysqld usar mais memória ou você pode adicionar mais área de 'swap'"
- rum "Out of memory; Verifica daca mysqld sau vreun alt proces foloseste toate memoria disponbila. Altfel, trebuie sa folosesi 'ulimit' ca sa permiti lui memoria disponbila. Altfel, trebuie sa folosesi 'ulimit' ca sa permiti lui mysqld sa foloseasca mai multa memorie ori adauga mai mult spatiu pentru swap (swap space)"
- rus "Недостаточно памяти; удостоверьтесь, что mysqld или какой-либо другой процесс не занимает всю доступную память. Если нет, то вы можете использовать ulimit, чтобы выделить для mysqld больше памяти, или увеличить объем файла подкачки"
- serbian "Nema memorije; Proverite da li MariaDB server ili neki drugi proces koristi svu slobodnu memoriju. (UNIX: Ako ne, probajte da upotrebite 'ulimit' komandu da biste dozvolili daemon-u da koristi više memorije ili probajte da dodate više swap memorije)"
+ por "Sem memória."
+ rum "Out of memory."
+ rus "Недостаточно памяти."
+ serbian "Nema memorije."
slo "Málo miesta-pamäti pre vlákno"
spa "Memoria/espacio de tranpaso insuficiente"
- swe "Fick slut på minnet. Kontrollera om mysqld eller någon annan process använder allt tillgängligt minne. Om inte, försök använda 'ulimit' eller allokera mera swap"
- ukr "Брак пам'яті; Перевірте чи mysqld або якісь інші процеси використовують усю доступну пам'ять. Як ні, то ви можете скористатися 'ulimit', аби дозволити mysqld використовувати більше пам'яті або ви можете додати більше місця під свап"
+ swe "Fick slut på minnet."
+ ukr "Брак пам'яті."
ER_BAD_HOST_ERROR 08S01
cze "Nemohu zjistit jméno stroje pro Vaši adresu"
dan "Kan ikke få værtsnavn for din adresse"
@@ -2626,7 +2626,7 @@ ER_INVALID_GROUP_FUNC_USE
swe "Felaktig användning av SQL grupp function"
ukr "Хибне використання функції групування"
ER_UNSUPPORTED_EXTENSION 42000
- cze "Tabulka '%-.192s' používá rozšíření, které v této verzi MySQL není"
+ cze "Tabulka '%-.192s' používá rozšíření, které v této verzi MariaDB není"
dan "Tabellen '%-.192s' bruger et filtypenavn som ikke findes i denne MariaDB version"
nla "Tabel '%-.192s' gebruikt een extensie, die niet in deze MariaDB-versie voorkomt"
eng "Table '%-.192s' uses an extension that doesn't exist in this MariaDB version"
@@ -2637,7 +2637,7 @@ ER_UNSUPPORTED_EXTENSION 42000
hindi "टेबल '%-.192s' जिस इक्स्टेन्शन का उपयोग कर रहा है, वह इस MariaDB संस्करण में उपलब्ध नहीं है"
hun "A(z) '%-.192s' tabla olyan bovitest hasznal, amely nem letezik ebben a MariaDB versioban"
ita "La tabella '%-.192s' usa un'estensione che non esiste in questa versione di MariaDB"
- jpn "表 '%-.192s' は、このMySQLバージョンには無い機能を使用しています。"
+ jpn "表 '%-.192s' は、このMariaDBバージョンには無い機能を使用しています。"
kor "테이블 '%-.192s'는 확장명령을 이용하지만 현재의 MariaDB 버젼에서는 존재하지 않습니다."
nor "Table '%-.192s' uses a extension that doesn't exist in this MariaDB version"
norwegian-ny "Table '%-.192s' uses a extension that doesn't exist in this MariaDB version"
@@ -2717,7 +2717,7 @@ ER_UNKNOWN_CHARACTER_SET 42000
swe "Okänd teckenuppsättning: '%-.64s'"
ukr "Невідома кодова таблиця: '%-.64s'"
ER_TOO_MANY_TABLES
- cze "Příliš mnoho tabulek, MySQL jich může mít v joinu jen %d"
+ cze "Příliš mnoho tabulek, MariaDB jich může mít v joinu jen %d"
dan "For mange tabeller. MariaDB kan kun bruge %d tabeller i et join"
nla "Teveel tabellen. MariaDB kan slechts %d tabellen in een join bevatten"
eng "Too many tables; MariaDB can only use %d tables in a join"
@@ -2728,7 +2728,7 @@ ER_TOO_MANY_TABLES
hindi "बहुत अधिक टेबल्स, MariaDB एक JOIN में केवल %d टेबल्स का उपयोग कर सकता है"
hun "Tul sok tabla. A MariaDB csak %d tablat tud kezelni osszefuzeskor"
ita "Troppe tabelle. MariaDB puo` usare solo %d tabelle in una join"
- jpn "表が多すぎます。MySQLがJOINできる表は %d 個までです。"
+ jpn "表が多すぎます。MariaDBがJOINできる表は %d 個までです。"
kor "너무 많은 테이블이 Join되었습니다. MariaDB에서는 JOIN시 %d개의 테이블만 사용할 수 있습니다."
por "Tabelas demais. O MariaDB pode usar somente %d tabelas em uma junção (JOIN)"
rum "Prea multe tabele. MariaDB nu poate folosi mai mult de %d tabele intr-un join"
@@ -3000,7 +3000,7 @@ ER_HOST_IS_BLOCKED
swe "Denna dator, '%-.64s', är blockerad pga många felaktig paket. Gör 'mysqladmin flush-hosts' för att ta bort alla blockeringarna"
ukr "Хост '%-.64s' заблоковано з причини великої кількості помилок з'єднання. Для розблокування використовуйте 'mysqladmin flush-hosts'"
ER_HOST_NOT_PRIVILEGED
- cze "Stroj '%-.64s' nemá povoleno se k tomuto MySQL serveru připojit"
+ cze "Stroj '%-.64s' nemá povoleno se k tomuto MariaDB serveru připojit"
dan "Værten '%-.64s' kan ikke tilkoble denne MariaDB-server"
nla "Het is host '%-.64s' is niet toegestaan verbinding te maken met deze MariaDB server"
eng "Host '%-.64s' is not allowed to connect to this MariaDB server"
@@ -3011,7 +3011,7 @@ ER_HOST_NOT_PRIVILEGED
hindi "होस्ट '%-.64s' को इस MariaDB सर्वर से कनेक्ट करने के लिए अनुमति नहीं है"
hun "A '%-.64s' host szamara nem engedelyezett a kapcsolodas ehhez a MariaDB szerverhez"
ita "Al sistema '%-.64s' non e` consentita la connessione a questo server MariaDB"
- jpn "ホスト '%-.64s' からのこの MySQL server への接続は許可されていません。"
+ jpn "ホスト '%-.64s' からのこの MariaDB server への接続は許可されていません。"
kor "'%-.64s' 호스트는 이 MariaDB서버에 접속할 허가를 받지 못했습니다."
por "'Host' '%-.64s' não tem permissão para se conectar com este servidor MariaDB"
rum "Host-ul '%-.64s' nu este permis a se conecta la aceste server MariaDB"
@@ -3021,7 +3021,7 @@ ER_HOST_NOT_PRIVILEGED
swe "Denna dator, '%-.64s', har inte privileger att använda denna MariaDB server"
ukr "Хосту '%-.64s' не доволено зв'язуватись з цим сервером MariaDB"
ER_PASSWORD_ANONYMOUS_USER 42000
- cze "Používáte MySQL jako anonymní uživatel a anonymní uživatelé nemají povoleno měnit hesla"
+ cze "Používáte MariaDB jako anonymní uživatel a anonymní uživatelé nemají povoleno měnit hesla"
dan "Du bruger MariaDB som anonym bruger. Anonyme brugere må ikke ændre adgangskoder"
nla "U gebruikt MariaDB als anonieme gebruiker en deze mogen geen wachtwoorden wijzigen"
eng "You are using MariaDB as an anonymous user and anonymous users are not allowed to modify user settings"
@@ -3032,7 +3032,7 @@ ER_PASSWORD_ANONYMOUS_USER 42000
hindi "आप MariaDB का उपयोग एक बेनाम यूज़र की तरह कर रहे हैं; बेनाम यूज़र्स को 'यूज़र सेटिंग्स' बदलने की अनुमति नहीं है"
hun "Nevtelen (anonymous) felhasznalokent nem negedelyezett a jelszovaltoztatas"
ita "Impossibile cambiare la password usando MariaDB come utente anonimo"
- jpn "MySQL を匿名ユーザーで使用しているので、パスワードの変更はできません。"
+ jpn "MariaDB を匿名ユーザーで使用しているので、パスワードの変更はできません。"
kor "당신은 MariaDB서버에 익명의 사용자로 접속을 하셨습니다.익명의 사용자는 암호를 변경할 수 없습니다."
por "Você está usando o MariaDB como usuário anônimo e usuários anônimos não têm permissão para mudar senhas"
rum "Dumneavoastra folositi MariaDB ca un utilizator anonim si utilizatorii anonimi nu au voie sa schimbe setarile utilizatorilor"
@@ -3371,7 +3371,7 @@ ER_NONEXISTING_TABLE_GRANT 42000
swe "Det finns inget privilegium definierat för användare '%-.48s' på '%-.64s' för tabell '%-.192s'"
ukr "Повноважень не визначено для користувача '%-.48s' з хосту '%-.64s' для таблиці '%-.192s'"
ER_NOT_ALLOWED_COMMAND 42000
- cze "Použitý příkaz není v této verzi MySQL povolen"
+ cze "Použitý příkaz není v této verzi MariaDB povolen"
dan "Den brugte kommando er ikke tilladt med denne udgave af MariaDB"
nla "Het used commando is niet toegestaan in deze MariaDB versie"
eng "The used command is not allowed with this MariaDB version"
@@ -3381,7 +3381,7 @@ ER_NOT_ALLOWED_COMMAND 42000
hindi "यह कमांड इस MariaDB संस्करण के साथ इस्तेमाल नहीं किया जा सकता है"
hun "A hasznalt parancs nem engedelyezett ebben a MariaDB verzioban"
ita "Il comando utilizzato non e` supportato in questa versione di MariaDB"
- jpn "このMySQLバージョンでは利用できないコマンドです。"
+ jpn "このMariaDBバージョンでは利用できないコマンドです。"
kor "사용된 명령은 현재의 MariaDB 버젼에서는 이용되지 않습니다."
por "Comando usado não é permitido para esta versão do MariaDB"
rum "Comanda folosita nu este permisa pentru aceasta versiune de MariaDB"
@@ -3884,7 +3884,7 @@ ER_REQUIRES_PRIMARY_KEY 42000
swe "Denna tabelltyp kräver en PRIMARY KEY"
ukr "Цей тип таблиці потребує первинного ключа"
ER_NO_RAID_COMPILED
- cze "Tato verze MySQL není zkompilována s podporou RAID"
+ cze "Tato verze MariaDB není zkompilována s podporou RAID"
dan "Denne udgave af MariaDB er ikke oversat med understøttelse af RAID"
nla "Deze versie van MariaDB is niet gecompileerd met RAID ondersteuning"
eng "This version of MariaDB is not compiled with RAID support"
@@ -3894,7 +3894,7 @@ ER_NO_RAID_COMPILED
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"
- jpn "このバージョンのMySQLは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"
rus "Эта версия MariaDB скомпилирована без поддержки RAID"
@@ -4505,18 +4505,18 @@ ER_TABLE_CANT_HANDLE_FT
spa "El tipo de tabla usada (%s) no soporta índices FULLTEXT"
swe "Tabelltypen (%s) har inte hantering av FULLTEXT-index"
ukr "Використаний тип таблиці (%s) не підтримує FULLTEXT індексів"
-ER_CANNOT_ADD_FOREIGN
- nla "Kan foreign key beperking niet toevoegen"
- eng "Cannot add foreign key constraint"
- fre "Impossible d'ajouter des contraintes d'index externe"
- ger "Fremdschlüssel-Beschränkung kann nicht hinzugefügt werden"
- ita "Impossibile aggiungere il vincolo di integrita' referenziale (foreign key constraint)"
- jpn "外部キー制約を追加できません。"
- por "Não pode acrescentar uma restrição de chave estrangeira"
- rus "Невозможно добавить ограничения внешнего ключа"
- serbian "Ne mogu da dodam proveru spoljnog ključa"
- spa "No puede adicionar clave extranjera constraint"
- swe "Kan inte lägga till 'FOREIGN KEY constraint'"
+ER_CANNOT_ADD_FOREIGN
+ nla "Kan foreign key beperking niet toevoegen vor `%s`"
+ eng "Cannot add foreign key constraint for `%s`"
+ fre "Impossible d'ajouter des contraintes d'index externe à `%s`"
+ ger "Fremdschlüssel-Beschränkung kann nicht hinzugefügt werden für `%s`"
+ ita "Impossibile aggiungere il vincolo di integrita' referenziale (foreign key constraint) a `%s`"
+ jpn "`%s` 外部キー制約を追加できません。"
+ por "Não pode acrescentar uma restrição de chave estrangeira para `%s`"
+ rus "Невозможно добавить ограничения внешнего ключа для `%s`"
+ serbian "Ne mogu da dodam proveru spoljnog ključa na `%s`"
+ spa "No puede adicionar clave extranjera constraint para `%s`"
+ swe "Kan inte lägga till 'FOREIGN KEY constraint' för `%s`'"
ER_NO_REFERENCED_ROW 23000
nla "Kan onderliggende rij niet toevoegen: foreign key beperking gefaald"
eng "Cannot add or update a child row: a foreign key constraint fails"
@@ -5019,7 +5019,7 @@ ER_UNKNOWN_COLLATION
ER_SLAVE_IGNORED_SSL_PARAMS
eng "SSL parameters in CHANGE MASTER are ignored because this MariaDB slave was compiled without SSL support; they can be used later if MariaDB slave with SSL is started"
ger "SSL-Parameter in CHANGE MASTER werden ignoriert, weil dieser MariaDB-Slave ohne SSL-Unterstützung kompiliert wurde. Sie können aber später verwendet werden, wenn ein MariaDB-Slave mit SSL gestartet wird"
- jpn "このMySQLスレーブはSSLサポートを含めてコンパイルされていないので、CHANGE MASTER のSSLパラメータは無視されました。今後SSLサポートを持つMySQLスレーブを起動する際に利用されます。"
+ jpn "このMariaDBスレーブはSSLサポートを含めてコンパイルされていないので、CHANGE MASTER のSSLパラメータは無視されました。今後SSLサポートを持つMariaDBスレーブを起動する際に利用されます。"
por "SSL parâmetros em CHANGE MASTER são ignorados porque este escravo MariaDB foi compilado sem o SSL suporte. Os mesmos podem ser usados mais tarde quando o escravo MariaDB com SSL seja iniciado."
spa "Parametros SSL en CHANGE MASTER son ignorados porque este slave MariaDB fue compilado sin soporte SSL; pueden ser usados despues cuando el slave MariaDB con SSL sea inicializado"
ER_SERVER_IS_IN_SECURE_AUTH_MODE
@@ -5347,7 +5347,7 @@ ER_FRM_UNKNOWN_TYPE
rus "Файл '%-.192s' содержит неизвестный тип '%-.64s' в заголовке"
ukr "Файл '%-.192s' має невідомий тип '%-.64s' у заголовку"
ER_WRONG_OBJECT
- eng "'%-.192s.%-.192s' is not %s"
+ eng "'%-.192s.%-.192s' is not of type '%s'"
ger "'%-.192s.%-.192s' ist nicht %s"
rus "'%-.192s.%-.192s' - не %s"
ukr "'%-.192s.%-.192s' не є %s"
@@ -5402,9 +5402,9 @@ ER_SP_GOTO_IN_HNDLR
eng "GOTO is not allowed in a stored procedure handler"
ger "GOTO ist im Handler einer gespeicherten Prozedur nicht erlaubt"
ER_TRG_ALREADY_EXISTS
- eng "Trigger already exists"
- ger "Trigger existiert bereits"
- hindi "TRIGGER पहले से मौजूद है"
+ eng "Trigger '%s' already exists"
+ ger "Trigger '%s' existiert bereits"
+ hindi "TRIGGER '%s' पहले से मौजूद है"
ER_TRG_DOES_NOT_EXIST
eng "Trigger does not exist"
ger "Trigger existiert nicht"
@@ -5729,8 +5729,8 @@ ER_MAX_PREPARED_STMT_COUNT_REACHED 42000
eng "Can't create more than max_prepared_stmt_count statements (current value: %u)"
ger "Kann nicht mehr Anweisungen als max_prepared_stmt_count erzeugen (aktueller Wert: %u)"
ER_VIEW_RECURSIVE
- eng "`%-.192s`.`%-.192s` contains view recursion"
- ger "`%-.192s`.`%-.192s` enthält View-Rekursion"
+ eng "%`s.%`s contains view recursion"
+ ger "%`s.%`s enthält View-Rekursion"
ER_NON_GROUPING_FIELD_USED 42000
eng "Non-grouping field '%-.192s' is used in %-.64s clause"
ger "In der %-.192s-Klausel wird das die Nicht-Gruppierungsspalte '%-.64s' verwendet"
@@ -6226,37 +6226,37 @@ ER_BINLOG_LOGGING_IMPOSSIBLE
eng "Binary logging not possible. Message: %s"
ger "Binärlogging nicht möglich. Meldung: %s"
ER_VIEW_NO_CREATION_CTX
- eng "View `%-.64s`.`%-.64s` has no creation context"
- ger "View `%-.64s`.`%-.64s` hat keinen Erzeugungskontext"
+ eng "View %`s.%`s has no creation context"
+ ger "View %`s.%`s hat keinen Erzeugungskontext"
ER_VIEW_INVALID_CREATION_CTX
- eng "Creation context of view `%-.64s`.`%-.64s' is invalid"
- ger "Erzeugungskontext des Views`%-.64s`.`%-.64s' ist ungültig"
+ eng "Creation context of view %`s.%`s is invalid"
+ ger "Erzeugungskontext des Views%`s.%`s ist ungültig"
ER_SR_INVALID_CREATION_CTX
- eng "Creation context of stored routine `%-.64s`.`%-.64s` is invalid"
- ger "Erzeugungskontext der gespeicherten Routine`%-.64s`.`%-.64s` ist ungültig"
+ eng "Creation context of stored routine %`s.%`s is invalid"
+ ger "Erzeugungskontext der gespeicherten Routine%`s.%`s ist ungültig"
ER_TRG_CORRUPTED_FILE
- eng "Corrupted TRG file for table `%-.64s`.`%-.64s`"
- ger "Beschädigte TRG-Datei für Tabelle `%-.64s`.`%-.64s`"
+ eng "Corrupted TRG file for table %`s.%`s"
+ ger "Beschädigte TRG-Datei für Tabelle %`s.%`s"
ER_TRG_NO_CREATION_CTX
- eng "Triggers for table `%-.64s`.`%-.64s` have no creation context"
- ger "Trigger für Tabelle `%-.64s`.`%-.64s` haben keinen Erzeugungskontext"
+ eng "Triggers for table %`s.%`s have no creation context"
+ ger "Trigger für Tabelle %`s.%`s haben keinen Erzeugungskontext"
ER_TRG_INVALID_CREATION_CTX
- eng "Trigger creation context of table `%-.64s`.`%-.64s` is invalid"
- ger "Trigger-Erzeugungskontext der Tabelle `%-.64s`.`%-.64s` ist ungültig"
+ eng "Trigger creation context of table %`s.%`s is invalid"
+ ger "Trigger-Erzeugungskontext der Tabelle %`s.%`s ist ungültig"
ER_EVENT_INVALID_CREATION_CTX
- eng "Creation context of event `%-.64s`.`%-.64s` is invalid"
- ger "Erzeugungskontext des Events `%-.64s`.`%-.64s` ist ungültig"
+ eng "Creation context of event %`s.%`s is invalid"
+ ger "Erzeugungskontext des Events %`s.%`s ist ungültig"
ER_TRG_CANT_OPEN_TABLE
- eng "Cannot open table for trigger `%-.64s`.`%-.64s`"
- ger "Kann Tabelle für den Trigger `%-.64s`.`%-.64s` nicht öffnen"
+ eng "Cannot open table for trigger %`s.%`s"
+ ger "Kann Tabelle für den Trigger %`s.%`s nicht öffnen"
ER_CANT_CREATE_SROUTINE
- eng "Cannot create stored routine `%-.64s`. Check warnings"
- ger "Kann gespeicherte Routine `%-.64s` nicht erzeugen. Beachten Sie die Warnungen"
+ eng "Cannot create stored routine %`s. Check warnings"
+ ger "Kann gespeicherte Routine %`s nicht erzeugen. Beachten Sie die Warnungen"
ER_UNUSED_11
eng "You should never see it"
ER_NO_FORMAT_DESCRIPTION_EVENT_BEFORE_BINLOG_STATEMENT
- eng "The BINLOG statement of type `%s` was not preceded by a format description BINLOG statement"
- ger "Der BINLOG-Anweisung vom Typ `%s` ging keine BINLOG-Anweisung zur Formatbeschreibung voran"
+ eng "The BINLOG statement of type %s was not preceded by a format description BINLOG statement"
+ ger "Der BINLOG-Anweisung vom Typ %s ging keine BINLOG-Anweisung zur Formatbeschreibung voran"
ER_SLAVE_CORRUPT_EVENT
eng "Corrupted replication event was detected"
ger "Beschädigtes Replikationsereignis entdeckt"
@@ -6573,7 +6573,7 @@ ER_MULTI_UPDATE_KEY_CONFLICT
# When translating this error message make sure to include "ALTER TABLE" in the
# message as mysqlcheck parses the error message looking for ALTER TABLE.
ER_TABLE_NEEDS_REBUILD
- eng "Table rebuild required. Please do \"ALTER TABLE `%-.32T` FORCE\" or dump/reload to fix it!"
+ eng "Table rebuild required. Please do \"ALTER TABLE %`s FORCE\" or dump/reload to fix it!"
WARN_OPTION_BELOW_LIMIT
eng "The value of '%s' should be no less than the value of '%s'"
@@ -6644,7 +6644,7 @@ ER_CANNOT_LOAD_FROM_TABLE_V2
ger "Kann %s.%s nicht einlesen. Tabelle ist wahrscheinlich beschädigt"
ER_MASTER_DELAY_VALUE_OUT_OF_RANGE
- eng "The requested value %u for the master delay exceeds the maximum %u"
+ eng "The requested value %lu for the master delay exceeds the maximum %lu"
ER_ONLY_FD_AND_RBR_EVENTS_ALLOWED_IN_BINLOG_STATEMENT
eng "Only Format_description_log_event and row events are allowed in BINLOG statements (but %s was provided)"
@@ -6730,7 +6730,7 @@ ER_INSECURE_PLAIN_TEXT
eng "Sending passwords in plain text without SSL/TLS is extremely insecure"
ER_INSECURE_CHANGE_MASTER
- eng "Storing MySQL user name or password information in the master.info repository is not secure and is therefore not recommended. Please see the MySQL Manual for more about this issue and possible alternatives"
+ eng "Storing MariaDB user name or password information in the master.info repository is not secure and is therefore not recommended. Please see the MariaDB Manual for more about this issue and possible alternatives"
ER_FOREIGN_DUPLICATE_KEY_WITH_CHILD_INFO 23000 S1009
eng "Foreign key constraint for table '%.192s', record '%-.192s' would lead to a duplicate entry in table '%.192s', key '%.192s'"
@@ -6827,8 +6827,8 @@ ER_CANT_SET_GTID_NEXT_WHEN_OWNING_GTID
eng "GTID_NEXT cannot be changed by a client that owns a GTID. The client owns %s. Ownership is released on COMMIT or ROLLBACK"
ER_UNKNOWN_EXPLAIN_FORMAT
- eng "Unknown EXPLAIN format name: '%s'"
- rus "Неизвестное имя формата команды EXPLAIN: '%s'"
+ eng "Unknown %s format name: '%s'"
+ rus "Неизвестное имя формата команды %s: '%s'"
ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION 25006
eng "Cannot execute statement in a READ ONLY transaction"
@@ -6837,7 +6837,7 @@ ER_TOO_LONG_TABLE_PARTITION_COMMENT
eng "Comment for table partition '%-.64s' is too long (max = %lu)"
ER_SLAVE_CONFIGURATION
- eng "Slave is not configured or failed to initialize properly. You must at least set --server-id to enable either a master or a slave. Additional error messages can be found in the MySQL error log"
+ eng "Slave is not configured or failed to initialize properly. You must at least set --server-id to enable either a master or a slave. Additional error messages can be found in the MariaDB error log"
ER_INNODB_FT_LIMIT
eng "InnoDB presently supports one FULLTEXT index creation at a time"
@@ -6864,10 +6864,10 @@ ER_MTS_CHANGE_MASTER_CANT_RUN_WITH_GAPS
eng "CHANGE MASTER cannot be executed when the slave was stopped with an error or killed in MTS mode. Consider using RESET SLAVE or START SLAVE UNTIL"
ER_MTS_RECOVERY_FAILURE
- eng "Cannot recover after SLAVE errored out in parallel execution mode. Additional error messages can be found in the MySQL error log"
+ eng "Cannot recover after SLAVE errored out in parallel execution mode. Additional error messages can be found in the MariaDB error log"
ER_MTS_RESET_WORKERS
- eng "Cannot clean up worker info tables. Additional error messages can be found in the MySQL error log"
+ eng "Cannot clean up worker info tables. Additional error messages can be found in the MariaDB error log"
ER_COL_COUNT_DOESNT_MATCH_CORRUPTED_V2
eng "Column count of %s.%s is wrong. Expected %d, found %d. The table is probably corrupted"
@@ -6876,8 +6876,8 @@ ER_COL_COUNT_DOESNT_MATCH_CORRUPTED_V2
ER_SLAVE_SILENT_RETRY_TRANSACTION
eng "Slave must silently retry current transaction"
-ER_DISCARD_FK_CHECKS_RUNNING
- eng "There is a foreign key check running on table '%-.192s'. Cannot discard the table"
+ER_UNUSED_22
+ eng "You should never see it"
ER_TABLE_SCHEMA_MISMATCH
eng "Schema mismatch (%s)"
@@ -7011,7 +7011,7 @@ ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FK_RENAME
eng "Columns participating in a foreign key are renamed"
ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_COLUMN_TYPE
- eng "Cannot change column type INPLACE"
+ eng "Cannot change column type"
ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FK_CHECK
eng "Adding foreign keys needs foreign_key_checks=OFF"
@@ -7066,7 +7066,7 @@ ER_IDENT_CAUSES_TOO_LONG_PATH
eng "Long database name and identifier for object resulted in path length exceeding %d characters. Path: '%s'"
ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOT_NULL
- eng "cannot silently convert NULL values, as required in this SQL_MODE"
+ eng "cannot convert NULL to non-constant DEFAULT"
ER_MUST_CHANGE_PASSWORD_LOGIN
eng "Your password has expired. To log in you must change it using a client that supports expired passwords"
@@ -7377,8 +7377,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 MySQL %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 MySQL %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 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"
ER_WARN_TRIGGER_DOESNT_HAVE_CREATED
eng "Trigger %s.%s.%s does not have CREATED attribute."
@@ -7455,7 +7455,7 @@ ER_GIS_UNSUPPORTED_ARGUMENT
eng "Calling geometry function %s with unsupported types of arguments."
ER_GIS_UNKNOWN_ERROR
- eng "Unknown GIS error occured in function %s."
+ eng "Unknown GIS error occurred in function %s."
ER_GIS_UNKNOWN_EXCEPTION
eng "Unknown exception caught in GIS function %s."
@@ -7549,7 +7549,7 @@ ER_WITH_COL_WRONG_LIST
ER_TOO_MANY_DEFINITIONS_IN_WITH_CLAUSE
eng "Too many WITH elements in WITH clause"
ER_DUP_QUERY_NAME
- eng "Duplicate query name in WITH clause '%s'"
+ eng "Duplicate query name %`-.64s in WITH clause"
ER_RECURSIVE_WITHOUT_ANCHORS
eng "No anchors for recursive WITH element '%s'"
ER_UNACCEPTABLE_MUTUAL_RECURSION
@@ -7731,5 +7731,172 @@ ER_GEOJSON_EMPTY_COORDINATES
ER_MYROCKS_CANT_NOPAD_COLLATION
eng "MyRocks doesn't currently support collations with \"No pad\" attribute."
-ER_TRANSACTIONAL_ARIA_LOG_ENGINE
- eng "Only non-transactional Aria table can be used for logging"
+
+ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+ eng "Illegal parameter data types %s and %s for operation '%s'"
+ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
+ eng "Illegal parameter data type %s for operation '%s'"
+ER_WRONG_PARAMCOUNT_TO_CURSOR 42000
+ eng "Incorrect parameter count to cursor '%-.192s'"
+ rus "Некорректное количество параметров для курсора '%-.192s'"
+ER_UNKNOWN_STRUCTURED_VARIABLE
+ eng "Unknown structured system variable or ROW routine variable '%-.*s'"
+ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD
+ eng "Row variable '%-.192s' does not have a field '%-.192s'"
+ER_END_IDENTIFIER_DOES_NOT_MATCH
+ eng "END identifier '%-.192s' does not match '%-.192s'"
+ER_SEQUENCE_RUN_OUT
+ eng "Sequence '%-.64s.%-.64s' has run out"
+ER_SEQUENCE_INVALID_DATA
+ eng "Sequence '%-.64s.%-.64s' values are conflicting"
+ER_SEQUENCE_INVALID_TABLE_STRUCTURE
+ eng "Sequence '%-.64s.%-.64s' table structure is invalid (%s)"
+ER_SEQUENCE_ACCESS_ERROR
+ eng "Sequence '%-.64s.%-.64s' access error"
+ER_SEQUENCE_BINLOG_FORMAT
+ eng "Sequences requires binlog_format mixed or row"
+ER_NOT_SEQUENCE 42S02
+ eng "'%-.64s.%-.64s' is not a SEQUENCE"
+ER_NOT_SEQUENCE2 42S02
+ eng "'%-.192s' is not a SEQUENCE"
+ER_UNKNOWN_SEQUENCES 42S02
+ eng "Unknown SEQUENCE: '%-.300s'"
+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."
+ER_SP_STACK_TRACE
+ eng "At line %u in %s"
+ER_PACKAGE_ROUTINE_IN_SPEC_NOT_DEFINED_IN_BODY
+ eng "Subroutine '%-.192s' is declared in the package specification but is not defined in the package body"
+ER_PACKAGE_ROUTINE_FORWARD_DECLARATION_NOT_DEFINED
+ eng "Subroutine '%-.192s' has a forward declaration but is not defined"
+ER_COMPRESSED_COLUMN_USED_AS_KEY
+ eng "Compressed column '%-.192s' can't be used in key specification"
+ER_UNKNOWN_COMPRESSION_METHOD
+ eng "Unknown compression method: %s"
+ER_WRONG_NUMBER_OF_VALUES_IN_TVC
+ eng "The used table value constructor has a different number of values"
+ER_FIELD_REFERENCE_IN_TVC
+ eng "Field reference '%-.192s' can't be used in table value constructor"
+ER_WRONG_TYPE_FOR_PERCENTILE_FUNC
+ eng "Numeric datatype is required for %s function"
+ER_ARGUMENT_NOT_CONSTANT
+ eng "Argument to the %s function is not a constant for a partition"
+ER_ARGUMENT_OUT_OF_RANGE
+ eng "Argument to the %s function does not belong to the range [0,1]"
+ER_WRONG_TYPE_OF_ARGUMENT
+ eng "%s function only accepts arguments that can be converted to numerical types"
+ER_NOT_AGGREGATE_FUNCTION
+ eng "Non-aggregate function contains aggregate specific instructions: (FETCH GROUP NEXT ROW)"
+ER_INVALID_AGGREGATE_FUNCTION
+ eng "Aggregate specific instruction(FETCH GROUP NEXT ROW) missing from the aggregate function"
+ER_INVALID_VALUE_TO_LIMIT
+ eng "Limit only accepts integer values"
+ER_INVISIBLE_NOT_NULL_WITHOUT_DEFAULT
+ eng "Invisible column %`s must have a default value"
+
+
+# MariaDB error numbers related to System Versioning
+
+ER_UPDATE_INFO_WITH_SYSTEM_VERSIONING
+ eng "Rows matched: %ld Changed: %ld Inserted: %ld Warnings: %ld"
+
+ER_VERS_FIELD_WRONG_TYPE
+ eng "%`s must be of type %s for system-versioned table %`s"
+
+ER_VERS_ENGINE_UNSUPPORTED
+ eng "Transaction-precise system versioning for %`s is not supported"
+
+ER_UNUSED_23
+ eng "You should never see it"
+
+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"
+
+WARN_VERS_PARAMETERS
+ eng "Maybe missing parameters: %s"
+
+ER_VERS_DROP_PARTITION_INTERVAL
+ eng "Can only drop oldest partitions when rotating by INTERVAL"
+
+ER_UNUSED_25
+ eng "You should never see it"
+
+WARN_VERS_PART_NON_HISTORICAL
+ eng "Partition %`s contains non-historical data"
+
+ER_VERS_ALTER_NOT_ALLOWED
+ eng "Not allowed for system-versioned %`s.%`s. Change @@system_versioning_alter_history to proceed with ALTER."
+
+ER_VERS_ALTER_ENGINE_PROHIBITED
+ eng "Not allowed for system-versioned %`s.%`s. Change to/from native system versioning engine is not supported."
+
+ER_VERS_RANGE_PROHIBITED
+ eng "SYSTEM_TIME range selector is not allowed"
+
+ER_CONFLICTING_FOR_SYSTEM_TIME
+ eng "Conflicting FOR SYSTEM_TIME clauses in WITH RECURSIVE"
+
+ER_VERS_TABLE_MUST_HAVE_COLUMNS
+ eng "Table %`s must have at least one versioned column"
+
+ER_VERS_NOT_VERSIONED
+ eng "Table %`s is not system-versioned"
+
+ER_MISSING
+ eng "Wrong parameters for %`s: missing '%s'"
+
+ER_VERS_PERIOD_COLUMNS
+ eng "PERIOD FOR SYSTEM_TIME must use columns %`s and %`s"
+
+ER_PART_WRONG_VALUE
+ eng "Wrong parameters for partitioned %`s: wrong value for '%s'"
+
+ER_VERS_WRONG_PARTS
+ eng "Wrong partitions for %`s: must have at least one HISTORY and exactly one last CURRENT"
+
+ER_VERS_NO_TRX_ID
+ eng "TRX_ID %llu not found in `mysql.transaction_registry`"
+
+ER_VERS_ALTER_SYSTEM_FIELD
+ eng "Can not change system versioning field %`s"
+
+ER_DROP_VERSIONING_SYSTEM_TIME_PARTITION
+ eng "Can not DROP SYSTEM VERSIONING for table %`s partitioned BY SYSTEM_TIME"
+
+ER_VERS_DB_NOT_SUPPORTED
+ eng "System versioning tables in the %`s database are not supported"
+
+ER_VERS_TRT_IS_DISABLED
+ eng "Transaction registry is disabled"
+
+ER_VERS_DUPLICATE_ROW_START_END
+ eng "Duplicate ROW %s column %`s"
+
+ER_VERS_ALREADY_VERSIONED
+ eng "Table %`s is already system-versioned"
+
+ER_UNUSED_24
+ eng "You should never see it"
+
+ER_VERS_TEMPORARY
+ eng "TEMPORARY tables do not support system versioning"
+
+ER_VERS_TRX_PART_HISTORIC_ROW_NOT_SUPPORTED
+ eng "Transaction-precise system versioned tables do not support partitioning by ROW START or ROW END"
+ER_INDEX_FILE_FULL
+ eng "The index file for table '%-.192s' is full"
+ER_UPDATED_COLUMN_ONLY_ONCE
+ eng "The column %`s.%`s cannot be changed more than once in a single UPDATE statement"
+ER_EMPTY_ROW_IN_TVC
+ eng "Row with no elements is not allowed in table value constructor in this context"
+ER_VERS_QUERY_IN_PARTITION
+ eng "SYSTEM_TIME partitions in table %`s does not support historical query"
+ER_KEY_DOESNT_SUPPORT
+ eng "%s index %`s does not support this operation"
+ER_ALTER_OPERATION_TABLE_OPTIONS_NEED_REBUILD
+ eng "Changing table options requires the table to be rebuilt"
diff --git a/sql/signal_handler.cc b/sql/signal_handler.cc
index c913a0477d7..5439f13b3f4 100644
--- a/sql/signal_handler.cc
+++ b/sql/signal_handler.cc
@@ -14,7 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include "my_global.h"
+#include "mariadb.h"
#include <signal.h>
//#include "sys_vars.h"
@@ -129,7 +129,7 @@ extern "C" sig_handler handle_fatal_signal(int sig)
tm.tm_hour, tm.tm_min, tm.tm_sec);
if (opt_expect_abort
#ifdef _WIN32
- && sig == EXCEPTION_BREAKPOINT /* __debugbreak in my_sigabrt_hander() */
+ && sig == (int)EXCEPTION_BREAKPOINT /* __debugbreak in my_sigabrt_hander() */
#else
&& sig == SIGABRT
#endif
@@ -204,7 +204,7 @@ extern "C" sig_handler handle_fatal_signal(int sig)
"where mysqld died. If you see no messages after this, something went\n"
"terribly wrong...\n");
my_print_stacktrace(thd ? (uchar*) thd->thread_stack : NULL,
- (ulong)my_thread_stack_size);
+ (ulong)my_thread_stack_size, 0);
}
if (thd)
{
diff --git a/sql/slave.cc b/sql/slave.cc
index 87eacfcfd0a..44fb1d0804e 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -25,7 +25,7 @@
replication slave.
*/
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "slave.h"
#include "sql_parse.h" // execute_init_command
@@ -43,7 +43,6 @@
#include <ssl_compat.h>
#include "unireg.h"
#include <mysys_err.h>
-#include "rpl_handler.h"
#include <signal.h>
#include <mysql.h>
#include <myisam.h>
@@ -60,6 +59,8 @@
#include "rpl_tblmap.h"
#include "debug_sync.h"
#include "rpl_parallel.h"
+#include "sql_show.h"
+#include "semisync_slave.h"
#define FLAGSTR(V,F) ((V)&(F)?#F" ":"")
@@ -71,6 +72,9 @@
bool use_slave_mask = 0;
MY_BITMAP slave_error_mask;
char slave_skip_error_names[SHOW_VAR_FUNC_BUFF_SIZE];
+uint *slave_transaction_retry_errors;
+uint slave_transaction_retry_error_length= 0;
+char slave_transaction_retry_error_names[SHOW_VAR_FUNC_BUFF_SIZE];
char* slave_load_tmpdir = 0;
Master_info *active_mi= 0;
@@ -82,7 +86,7 @@ ulonglong opt_read_binlog_speed_limit = 0;
const char *relay_log_index= 0;
const char *relay_log_basename= 0;
-LEX_STRING default_master_connection_name= { (char*) "", 0 };
+LEX_CSTRING default_master_connection_name= { (char*) "", 0 };
/*
When slave thread exits, we need to remember the temporary tables so we
@@ -155,7 +159,8 @@ static bool wait_for_relay_log_space(Relay_log_info* rli);
static bool io_slave_killed(Master_info* mi);
static bool sql_slave_killed(rpl_group_info *rgi);
static int init_slave_thread(THD*, Master_info *, SLAVE_THD_TYPE);
-static void print_slave_skip_errors(void);
+static void make_slave_skip_errors_printable(void);
+static void make_slave_transaction_retry_errors_printable(void);
static int safe_connect(THD* thd, MYSQL* mysql, Master_info* mi);
static int safe_reconnect(THD*, MYSQL*, Master_info*, bool);
static int connect_to_master(THD*, MYSQL*, Master_info*, bool, bool);
@@ -279,15 +284,187 @@ static void init_slave_psi_keys(void)
#endif /* HAVE_PSI_INTERFACE */
+/*
+ Note: This definition needs to be kept in sync with the one in
+ mysql_system_tables.sql which is used by mysql_create_db.
+*/
+static const char gtid_pos_table_definition1[]=
+ "CREATE TABLE ";
+static const char gtid_pos_table_definition2[]=
+ " (domain_id INT UNSIGNED NOT NULL, "
+ "sub_id BIGINT UNSIGNED NOT NULL, "
+ "server_id INT UNSIGNED NOT NULL, "
+ "seq_no BIGINT UNSIGNED NOT NULL, "
+ "PRIMARY KEY (domain_id, sub_id)) CHARSET=latin1 "
+ "COMMENT='Replication slave GTID position' "
+ "ENGINE=";
+
+/*
+ Build a query string
+ CREATE TABLE mysql.gtid_slave_pos_<engine> ... ENGINE=<engine>
+*/
+static bool
+build_gtid_pos_create_query(THD *thd, String *query,
+ LEX_CSTRING *table_name,
+ LEX_CSTRING *engine_name)
+{
+ bool err= false;
+ err|= query->append(gtid_pos_table_definition1);
+ err|= append_identifier(thd, query, table_name);
+ err|= query->append(gtid_pos_table_definition2);
+ err|= append_identifier(thd, query, engine_name);
+ return err;
+}
+
+
+static int
+gtid_pos_table_creation(THD *thd, plugin_ref engine, LEX_CSTRING *table_name)
+{
+ int err;
+ StringBuffer<sizeof(gtid_pos_table_definition1) +
+ sizeof(gtid_pos_table_definition1) +
+ 2*FN_REFLEN> query;
+
+ if (build_gtid_pos_create_query(thd, &query, table_name, plugin_name(engine)))
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ return 1;
+ }
+
+ thd->set_db(&MYSQL_SCHEMA_NAME);
+ thd->clear_error();
+ ulonglong thd_saved_option= thd->variables.option_bits;
+ /* This query shuold not be binlogged. */
+ thd->variables.option_bits&= ~(ulonglong)OPTION_BIN_LOG;
+ thd->set_query_and_id(query.c_ptr(), query.length(), thd->charset(),
+ next_query_id());
+ Parser_state parser_state;
+ err= parser_state.init(thd, thd->query(), thd->query_length());
+ if (err)
+ goto end;
+ mysql_parse(thd, thd->query(), thd->query_length(), &parser_state,
+ FALSE, FALSE);
+ if (unlikely(thd->is_error()))
+ err= 1;
+ /* The warning is relevant to 10.3 and earlier. */
+ sql_print_warning("The automatically created table '%s' name may not be "
+ "entirely in lowercase. The table name will be converted "
+ "to lowercase to any future upgrade to 10.4.0 and later "
+ "version where it will be auto-created at once "
+ "in lowercase.",
+ table_name->str);
+end:
+ thd->variables.option_bits= thd_saved_option;
+ thd->reset_query();
+ return err;
+}
+
+
+static void
+handle_gtid_pos_auto_create_request(THD *thd, void *hton)
+{
+ int UNINIT_VAR(err);
+ plugin_ref engine= NULL, *auto_engines;
+ rpl_slave_state::gtid_pos_table *entry;
+ StringBuffer<FN_REFLEN> loc_table_name;
+ LEX_CSTRING table_name;
+
+ /*
+ Check that the plugin is still in @@gtid_pos_auto_engines, and lock
+ it.
+ */
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ engine= NULL;
+ for (auto_engines= opt_gtid_pos_auto_plugins;
+ auto_engines && *auto_engines;
+ ++auto_engines)
+ {
+ if (plugin_hton(*auto_engines) == hton)
+ {
+ engine= my_plugin_lock(NULL, *auto_engines);
+ break;
+ }
+ }
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ if (!engine)
+ {
+ /* The engine is gone from @@gtid_pos_auto_engines, so no action. */
+ goto end;
+ }
+
+ /* Find the entry for the table to auto-create. */
+ mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+ entry= (rpl_slave_state::gtid_pos_table *)
+ rpl_global_gtid_slave_state->gtid_pos_tables;
+ while (entry)
+ {
+ if (entry->table_hton == hton &&
+ entry->state == rpl_slave_state::GTID_POS_CREATE_REQUESTED)
+ break;
+ entry= entry->next;
+ }
+ if (entry)
+ {
+ entry->state = rpl_slave_state::GTID_POS_CREATE_IN_PROGRESS;
+ err= loc_table_name.append(entry->table_name.str, entry->table_name.length);
+ }
+ mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+ if (!entry)
+ goto end;
+ if (err)
+ {
+ sql_print_error("Out of memory while trying to auto-create GTID position table");
+ goto end;
+ }
+ table_name.str= loc_table_name.c_ptr_safe();
+ table_name.length= loc_table_name.length();
+
+ err= gtid_pos_table_creation(thd, engine, &table_name);
+ if (err)
+ {
+ sql_print_error("Error auto-creating GTID position table `mysql.%s`: %s Error_code: %d",
+ table_name.str, thd->get_stmt_da()->message(),
+ thd->get_stmt_da()->sql_errno());
+ thd->clear_error();
+ goto end;
+ }
+
+ /* Now enable the entry for the auto-created table. */
+ mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+ entry= (rpl_slave_state::gtid_pos_table *)
+ rpl_global_gtid_slave_state->gtid_pos_tables;
+ while (entry)
+ {
+ if (entry->table_hton == hton &&
+ entry->state == rpl_slave_state::GTID_POS_CREATE_IN_PROGRESS)
+ {
+ entry->state= rpl_slave_state::GTID_POS_AVAILABLE;
+ break;
+ }
+ entry= entry->next;
+ }
+ mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+
+end:
+ if (engine)
+ plugin_unlock(NULL, engine);
+}
+
+
static bool slave_background_thread_running;
static bool slave_background_thread_stop;
static bool slave_background_thread_gtid_loaded;
-struct slave_background_kill_t {
+static struct slave_background_kill_t {
slave_background_kill_t *next;
THD *to_kill;
} *slave_background_kill_list;
+static struct slave_background_gtid_pos_create_t {
+ slave_background_gtid_pos_create_t *next;
+ void *hton;
+} *slave_background_gtid_pos_create_list;
+
pthread_handler_t
handle_slave_background(void *arg __attribute__((unused)))
@@ -324,6 +501,7 @@ handle_slave_background(void *arg __attribute__((unused)))
do
{
slave_background_kill_t *kill_list;
+ slave_background_gtid_pos_create_t *create_list;
thd->ENTER_COND(&COND_slave_background, &LOCK_slave_background,
&stage_slave_background_wait_request,
@@ -332,12 +510,14 @@ handle_slave_background(void *arg __attribute__((unused)))
{
stop= abort_loop || thd->killed || slave_background_thread_stop;
kill_list= slave_background_kill_list;
- if (stop || kill_list)
+ create_list= slave_background_gtid_pos_create_list;
+ if (stop || kill_list || create_list)
break;
mysql_cond_wait(&COND_slave_background, &LOCK_slave_background);
}
slave_background_kill_list= NULL;
+ slave_background_gtid_pos_create_list= NULL;
thd->EXIT_COND(&old_stage);
while (kill_list)
@@ -346,9 +526,7 @@ handle_slave_background(void *arg __attribute__((unused)))
THD *to_kill= p->to_kill;
kill_list= p->next;
- mysql_mutex_lock(&to_kill->LOCK_thd_data);
to_kill->awake(KILL_CONNECTION);
- mysql_mutex_unlock(&to_kill->LOCK_thd_data);
mysql_mutex_lock(&to_kill->LOCK_wakeup_ready);
to_kill->rgi_slave->killed_for_retry=
rpl_group_info::RETRY_KILL_KILLED;
@@ -356,6 +534,16 @@ handle_slave_background(void *arg __attribute__((unused)))
mysql_mutex_unlock(&to_kill->LOCK_wakeup_ready);
my_free(p);
}
+
+ while (create_list)
+ {
+ slave_background_gtid_pos_create_t *next= create_list->next;
+ void *hton= create_list->hton;
+ handle_gtid_pos_auto_create_request(thd, hton);
+ my_free(create_list);
+ create_list= next;
+ }
+
mysql_mutex_lock(&LOCK_slave_background);
} while (!stop);
@@ -395,6 +583,41 @@ slave_background_kill_request(THD *to_kill)
/*
+ This function must only be called from a slave SQL thread (or worker thread),
+ to ensure that the table_entry will not go away before we can lock the
+ LOCK_slave_state.
+*/
+void
+slave_background_gtid_pos_create_request(
+ rpl_slave_state::gtid_pos_table *table_entry)
+{
+ slave_background_gtid_pos_create_t *p;
+
+ 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));
+ if (!p)
+ return;
+ mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+ if (table_entry->state != rpl_slave_state::GTID_POS_AUTO_CREATE)
+ {
+ my_free(p);
+ mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+ return;
+ }
+ table_entry->state= rpl_slave_state::GTID_POS_CREATE_REQUESTED;
+ mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+
+ p->hton= table_entry->table_hton;
+ mysql_mutex_lock(&LOCK_slave_background);
+ p->next= slave_background_gtid_pos_create_list;
+ slave_background_gtid_pos_create_list= p;
+ mysql_cond_signal(&COND_slave_background);
+ mysql_mutex_unlock(&LOCK_slave_background);
+}
+
+
+/*
Start the slave background thread.
This thread is currently used for two purposes:
@@ -424,7 +647,6 @@ start_slave_background_thread()
sql_print_error("Failed to create thread while initialising slave");
return 1;
}
-
mysql_mutex_lock(&LOCK_slave_background);
while (!slave_background_thread_gtid_loaded)
mysql_cond_wait(&COND_slave_background, &LOCK_slave_background);
@@ -496,15 +718,6 @@ int init_slave()
}
/*
- If --slave-skip-errors=... was not used, the string value for the
- system variable has not been set up yet. Do it now.
- */
- if (!use_slave_mask)
- {
- print_slave_skip_errors();
- }
-
- /*
If master_host is not specified, try to read it from the master_info file.
If master_host is specified, create the master_info file if it doesn't
exists.
@@ -536,7 +749,7 @@ int init_slave()
thd->reset_globals();
delete thd;
- if (error)
+ if (unlikely(error))
{
sql_print_error("Failed to create slave threads");
goto err;
@@ -601,12 +814,12 @@ int init_recovery(Master_info* mi, const char** errmsg)
DBUG_RETURN(0);
}
-
+
/**
Convert slave skip errors bitmap into a printable string.
*/
-static void print_slave_skip_errors(void)
+static void make_slave_skip_errors_printable(void)
{
/*
To be safe, we want 10 characters of room in the buffer for a number
@@ -615,7 +828,7 @@ static void print_slave_skip_errors(void)
plus a NUL terminator. That is a max 6 digit number.
*/
const size_t MIN_ROOM= 10;
- DBUG_ENTER("print_slave_skip_errors");
+ DBUG_ENTER("make_slave_skip_errors_printable");
DBUG_ASSERT(sizeof(slave_skip_error_names) > MIN_ROOM);
DBUG_ASSERT(MAX_SLAVE_ERROR <= 999999); // 6 digits
@@ -637,14 +850,14 @@ static void print_slave_skip_errors(void)
else
{
char *buff= slave_skip_error_names;
- char *bend= buff + sizeof(slave_skip_error_names);
+ char *bend= buff + sizeof(slave_skip_error_names) - MIN_ROOM;
int errnum;
for (errnum= 0; errnum < MAX_SLAVE_ERROR; errnum++)
{
if (bitmap_is_set(&slave_error_mask, errnum))
{
- if (buff + MIN_ROOM >= bend)
+ if (buff >= bend)
break; /* purecov: tested */
buff= int10_to_str(errnum, buff, 10);
*buff++= ',';
@@ -674,24 +887,24 @@ static void print_slave_skip_errors(void)
Called from get_options() in mysqld.cc on start-up
*/
-void init_slave_skip_errors(const char* arg)
+bool init_slave_skip_errors(const char* arg)
{
const char *p;
DBUG_ENTER("init_slave_skip_errors");
- if (my_bitmap_init(&slave_error_mask,0,MAX_SLAVE_ERROR,0))
- {
- fprintf(stderr, "Badly out of memory, please check your system status\n");
- exit(1);
- }
- use_slave_mask = 1;
+ if (!arg || !*arg) // No errors defined
+ goto end;
+
+ if (unlikely(my_bitmap_init(&slave_error_mask,0,MAX_SLAVE_ERROR,0)))
+ DBUG_RETURN(1);
+
+ 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))
{
bitmap_set_all(&slave_error_mask);
- print_slave_skip_errors();
- DBUG_VOID_RETURN;
+ goto end;
}
for (p= arg ; *p; )
{
@@ -703,11 +916,109 @@ void init_slave_skip_errors(const char* arg)
while (!my_isdigit(system_charset_info,*p) && *p)
p++;
}
- /* Convert slave skip errors bitmap into a printable string. */
- print_slave_skip_errors();
+
+end:
+ make_slave_skip_errors_printable();
+ DBUG_RETURN(0);
+}
+
+/**
+ Make printable version if slave_transaction_retry_errors
+ This is never empty as at least ER_LOCK_DEADLOCK and ER_LOCK_WAIT_TIMEOUT
+ will be there
+*/
+
+static void make_slave_transaction_retry_errors_printable(void)
+{
+ /*
+ To be safe, we want 10 characters of room in the buffer for a number
+ plus terminators. Also, we need some space for constant strings.
+ 10 characters must be sufficient for a number plus {',' | '...'}
+ plus a NUL terminator. That is a max 6 digit number.
+ */
+ const size_t MIN_ROOM= 10;
+ char *buff= slave_transaction_retry_error_names;
+ char *bend= buff + sizeof(slave_transaction_retry_error_names) - MIN_ROOM;
+ uint i;
+ DBUG_ENTER("make_slave_transaction_retry_errors_printable");
+ DBUG_ASSERT(sizeof(slave_transaction_retry_error_names) > MIN_ROOM);
+
+ /* Make @@slave_transaction_retry_errors show a human-readable value */
+ opt_slave_transaction_retry_errors= slave_transaction_retry_error_names;
+
+ for (i= 0; i < slave_transaction_retry_error_length && buff < bend; i++)
+ {
+ buff= int10_to_str(slave_transaction_retry_errors[i], buff, 10);
+ *buff++= ',';
+ }
+ if (buff != slave_transaction_retry_error_names)
+ buff--; // Remove last ','
+ if (i < slave_transaction_retry_error_length)
+ {
+ /* Couldn't show all errors */
+ buff= strmov(buff, "..."); /* purecov: tested */
+ }
+ *buff=0;
+ DBUG_PRINT("exit", ("error_names: '%s'",
+ slave_transaction_retry_error_names));
DBUG_VOID_RETURN;
}
+
+bool init_slave_transaction_retry_errors(const char* arg)
+{
+ const char *p;
+ long err_code;
+ uint i;
+ DBUG_ENTER("init_slave_transaction_retry_errors");
+
+ /* Handle empty strings */
+ if (!arg)
+ arg= "";
+
+ slave_transaction_retry_error_length= 2;
+ for (;my_isspace(system_charset_info,*arg);++arg)
+ /* empty */;
+ for (p= arg; *p; )
+ {
+ if (!(p= str2int(p, 10, 0, LONG_MAX, &err_code)))
+ break;
+ slave_transaction_retry_error_length++;
+ while (!my_isdigit(system_charset_info,*p) && *p)
+ p++;
+ }
+
+ if (unlikely(!(slave_transaction_retry_errors=
+ (uint *) my_once_alloc(sizeof(int) *
+ slave_transaction_retry_error_length,
+ MYF(MY_WME)))))
+ DBUG_RETURN(1);
+
+ /*
+ Temporary error codes:
+ currently, InnoDB deadlock detected by InnoDB or lock
+ wait timeout (innodb_lock_wait_timeout exceeded
+ */
+ slave_transaction_retry_errors[0]= ER_LOCK_DEADLOCK;
+ slave_transaction_retry_errors[1]= ER_LOCK_WAIT_TIMEOUT;
+
+ /* Add user codes after this */
+ for (p= arg, i= 2; *p; )
+ {
+ if (!(p= str2int(p, 10, 0, LONG_MAX, &err_code)))
+ break;
+ if (err_code > 0 && err_code < ER_ERROR_LAST)
+ slave_transaction_retry_errors[i++]= (uint) err_code;
+ while (!my_isdigit(system_charset_info,*p) && *p)
+ p++;
+ }
+ slave_transaction_retry_error_length= i;
+
+ make_slave_transaction_retry_errors_printable();
+ DBUG_RETURN(0);
+}
+
+
int terminate_slave_threads(Master_info* mi,int thread_mask,bool skip_lock)
{
DBUG_ENTER("terminate_slave_threads");
@@ -729,11 +1040,12 @@ int terminate_slave_threads(Master_info* mi,int thread_mask,bool skip_lock)
}
else
mi->rli.abort_slave=1;
- if ((error=terminate_slave_thread(mi->rli.sql_driver_thd, sql_lock,
- &mi->rli.stop_cond,
- &mi->rli.slave_running,
- skip_lock)) &&
- !force_all)
+ if (unlikely((error= terminate_slave_thread(mi->rli.sql_driver_thd,
+ sql_lock,
+ &mi->rli.stop_cond,
+ &mi->rli.slave_running,
+ skip_lock))) &&
+ !force_all)
DBUG_RETURN(error);
retval= error;
@@ -751,11 +1063,11 @@ int terminate_slave_threads(Master_info* mi,int thread_mask,bool skip_lock)
{
DBUG_PRINT("info",("Terminating IO thread"));
mi->abort_slave=1;
- if ((error=terminate_slave_thread(mi->io_thd, io_lock,
- &mi->stop_cond,
- &mi->slave_running,
- skip_lock)) &&
- !force_all)
+ if (unlikely((error= terminate_slave_thread(mi->io_thd, io_lock,
+ &mi->stop_cond,
+ &mi->slave_running,
+ skip_lock))) &&
+ !force_all)
DBUG_RETURN(error);
if (!retval)
retval= error;
@@ -776,7 +1088,7 @@ int terminate_slave_threads(Master_info* mi,int thread_mask,bool skip_lock)
mysql_mutex_unlock(log_lock);
}
- DBUG_RETURN(retval);
+ DBUG_RETURN(retval);
}
@@ -859,7 +1171,7 @@ terminate_slave_thread(THD *thd,
int error __attribute__((unused));
DBUG_PRINT("loop", ("killing slave thread"));
- mysql_mutex_lock(&thd->LOCK_thd_data);
+ mysql_mutex_lock(&thd->LOCK_thd_kill);
#ifndef DONT_USE_THR_ALARM
/*
Error codes from pthread_kill are:
@@ -869,9 +1181,9 @@ terminate_slave_thread(THD *thd,
int err __attribute__((unused))= pthread_kill(thd->real_id, thr_client_alarm);
DBUG_ASSERT(err != EINVAL);
#endif
- thd->awake(NOT_KILLED);
+ thd->awake_no_mutex(NOT_KILLED);
- mysql_mutex_unlock(&thd->LOCK_thd_data);
+ mysql_mutex_unlock(&thd->LOCK_thd_kill);
/*
There is a small chance that slave thread might miss the first
@@ -931,8 +1243,9 @@ int start_slave_thread(
}
start_id= *slave_run_id;
DBUG_PRINT("info",("Creating new slave thread"));
- if ((error = mysql_thread_create(thread_key,
- &th, &connection_attrib, h_func, (void*)mi)))
+ if (unlikely((error= mysql_thread_create(thread_key,
+ &th, &connection_attrib, h_func,
+ (void*)mi))))
{
sql_print_error("Can't create slave thread (errno= %d).", error);
if (start_lock)
@@ -1045,7 +1358,7 @@ int start_slave_threads(THD *thd,
mi->rli.restart_gtid_pos.reset();
}
- if (!error && (thread_mask & SLAVE_IO))
+ if (likely(!error) && likely((thread_mask & SLAVE_IO)))
error= start_slave_thread(
#ifdef HAVE_PSI_INTERFACE
key_thread_slave_io,
@@ -1054,7 +1367,7 @@ int start_slave_threads(THD *thd,
cond_io,
&mi->slave_running, &mi->slave_run_id,
mi);
- if (!error && (thread_mask & SLAVE_SQL))
+ if (likely(!error) && likely(thread_mask & SLAVE_SQL))
{
error= start_slave_thread(
#ifdef HAVE_PSI_INTERFACE
@@ -1064,7 +1377,7 @@ int start_slave_threads(THD *thd,
cond_sql,
&mi->rli.slave_running, &mi->rli.slave_run_id,
mi);
- if (error)
+ if (unlikely(error))
terminate_slave_threads(mi, thread_mask & SLAVE_IO, !need_slave_mutex);
}
DBUG_RETURN(error);
@@ -1289,7 +1602,7 @@ const char *print_slave_db_safe(const char* db)
int init_strvar_from_file(char *var, int max_size, IO_CACHE *f,
const char *default_val)
{
- uint length;
+ size_t length;
DBUG_ENTER("init_strvar_from_file");
if ((length=my_b_gets(f,var, max_size)))
@@ -1899,7 +2212,7 @@ when it try to get the value of TIME_ZONE global variable from master.";
if (++dbug_count < 3)
goto heartbeat_network_error;
});
- if (mysql_real_query(mysql, query, strlen(query)))
+ if (mysql_real_query(mysql, query, (ulong)strlen(query)))
{
if (check_io_slave_killed(mi, NULL))
goto slave_killed_err;
@@ -1947,7 +2260,7 @@ when it try to get the value of TIME_ZONE global variable from master.";
Once the first FD will be received its alg descriptor will replace
the being queried one.
*/
- rc= mysql_real_query(mysql, query, strlen(query));
+ rc= mysql_real_query(mysql, query,(ulong)strlen(query));
if (rc != 0)
{
if (check_io_slave_killed(mi, NULL))
@@ -2036,7 +2349,8 @@ past_checksum:
*/
if (opt_replicate_events_marked_for_skip == RPL_SKIP_FILTER_ON_MASTER)
{
- if (mysql_real_query(mysql, STRING_WITH_LEN("SET skip_replication=1")))
+ if (unlikely(mysql_real_query(mysql,
+ STRING_WITH_LEN("SET skip_replication=1"))))
{
err_code= mysql_errno(mysql);
if (is_network_error(err_code))
@@ -2080,7 +2394,7 @@ past_checksum:
STRINGIFY_ARG(MARIA_SLAVE_CAPABILITY_ANNOTATE))),
mysql_real_query(mysql, STRING_WITH_LEN("SET @mariadb_slave_capability="
STRINGIFY_ARG(MARIA_SLAVE_CAPABILITY_MINE))));
- if (rc)
+ if (unlikely(rc))
{
err_code= mysql_errno(mysql);
if (is_network_error(err_code))
@@ -2156,7 +2470,7 @@ after_set_capability:
query_str.append(STRING_WITH_LEN("'"), system_charset_info);
rc= mysql_real_query(mysql, query_str.ptr(), query_str.length());
- if (rc)
+ if (unlikely(rc))
{
err_code= mysql_errno(mysql);
if (is_network_error(err_code))
@@ -2189,7 +2503,7 @@ after_set_capability:
}
rc= mysql_real_query(mysql, query_str.ptr(), query_str.length());
- if (rc)
+ if (unlikely(rc))
{
err_code= mysql_errno(mysql);
if (is_network_error(err_code))
@@ -2222,7 +2536,7 @@ after_set_capability:
}
rc= mysql_real_query(mysql, query_str.ptr(), query_str.length());
- if (rc)
+ if (unlikely(rc))
{
err_code= mysql_errno(mysql);
if (is_network_error(err_code))
@@ -2258,7 +2572,7 @@ after_set_capability:
query_str.append(STRING_WITH_LEN("'"), system_charset_info);
rc= mysql_real_query(mysql, query_str.ptr(), query_str.length());
- if (rc)
+ if (unlikely(rc))
{
err_code= mysql_errno(mysql);
if (is_network_error(err_code))
@@ -2525,7 +2839,7 @@ int register_slave_on_master(MYSQL* mysql, Master_info *mi,
bool *suppress_warnings)
{
uchar buf[1024], *pos= buf;
- uint report_host_len=0, report_user_len=0, report_password_len=0;
+ size_t report_host_len=0, report_user_len=0, report_password_len=0;
DBUG_ENTER("register_slave_on_master");
*suppress_warnings= FALSE;
@@ -2533,7 +2847,7 @@ int register_slave_on_master(MYSQL* mysql, Master_info *mi,
report_host_len= strlen(report_host);
if (report_host_len > HOSTNAME_LENGTH)
{
- sql_print_warning("The length of report_host is %d. "
+ sql_print_warning("The length of report_host is %zu. "
"It is larger than the max length(%d), so this "
"slave cannot be registered to the master.",
report_host_len, HOSTNAME_LENGTH);
@@ -2544,7 +2858,7 @@ int register_slave_on_master(MYSQL* mysql, Master_info *mi,
report_user_len= strlen(report_user);
if (report_user_len > USERNAME_LENGTH)
{
- sql_print_warning("The length of report_user is %d. "
+ sql_print_warning("The length of report_user is %zu. "
"It is larger than the max length(%d), so this "
"slave cannot be registered to the master.",
report_user_len, USERNAME_LENGTH);
@@ -2555,7 +2869,7 @@ int register_slave_on_master(MYSQL* mysql, Master_info *mi,
report_password_len= strlen(report_password);
if (report_password_len > MAX_PASSWORD_LENGTH)
{
- sql_print_warning("The length of report_password is %d. "
+ sql_print_warning("The length of report_password is %zu. "
"It is larger than the max length(%d), so this "
"slave cannot be registered to the master.",
report_password_len, MAX_PASSWORD_LENGTH);
@@ -2576,7 +2890,7 @@ int register_slave_on_master(MYSQL* mysql, Master_info *mi,
/* The master will fill in master_id */
int4store(pos, 0); pos+= 4;
- if (simple_command(mysql, COM_REGISTER_SLAVE, buf, (size_t) (pos- buf), 0))
+ if (simple_command(mysql, COM_REGISTER_SLAVE, buf, (ulong) (pos- buf), 0))
{
if (mysql_errno(mysql) == ER_NET_READ_INTERRUPTED)
{
@@ -2820,6 +3134,19 @@ void show_master_info_get_fields(THD *thd, List<Item> *field_list,
field_list->push_back(new (mem_root)
Item_empty_string(thd, "Slave_SQL_Running_State",
20));
+ field_list->push_back(new (mem_root)
+ Item_return_int(thd, "Slave_DDL_Groups", 20,
+ MYSQL_TYPE_LONGLONG),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_return_int(thd, "Slave_Non_Transactional_Groups", 20,
+ MYSQL_TYPE_LONGLONG),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_return_int(thd, "Slave_Transactional_Groups", 20,
+ MYSQL_TYPE_LONGLONG),
+ mem_root);
+
if (full)
{
field_list->push_back(new (mem_root)
@@ -2843,7 +3170,7 @@ void show_master_info_get_fields(THD *thd, List<Item> *field_list,
mem_root);
field_list->push_back(new (mem_root)
Item_empty_string(thd, "Gtid_Slave_Pos",
- gtid_pos_length),
+ (uint)gtid_pos_length),
mem_root);
}
DBUG_VOID_RETURN;
@@ -3050,6 +3377,17 @@ static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full,
// Slave_SQL_Running_State
protocol->store(slave_sql_running_state, &my_charset_bin);
+ uint64 events;
+ events= (uint64)my_atomic_load64_explicit((volatile int64 *)
+ &mi->total_ddl_groups, MY_MEMORY_ORDER_RELAXED);
+ protocol->store(events);
+ events= (uint64)my_atomic_load64_explicit((volatile int64 *)
+ &mi->total_non_trans_groups, MY_MEMORY_ORDER_RELAXED);
+ protocol->store(events);
+ events= (uint64)my_atomic_load64_explicit((volatile int64 *)
+ &mi->total_trans_groups, MY_MEMORY_ORDER_RELAXED);
+ protocol->store(events);
+
if (full)
{
protocol->store((uint32) mi->rli.retried_trans);
@@ -3167,6 +3505,13 @@ void set_slave_thread_options(THD* thd)
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;
+
+ thd->variables.sql_log_slow= !MY_TEST(thd->variables.log_slow_disabled_statements &
+ LOG_SLOW_DISABLE_SLAVE);
DBUG_VOID_RETURN;
}
@@ -3213,8 +3558,7 @@ static int init_slave_thread(THD* thd, Master_info *mi,
thd->security_ctx->skip_grants();
thd->slave_thread= 1;
thd->connection_name= mi->connection_name;
- thd->variables.sql_log_slow= opt_log_slow_slave_statements;
- thd->variables.log_slow_filter= global_system_variables.log_slow_filter;
+ thd->variables.sql_log_slow= !MY_TEST(thd->variables.log_slow_disabled_statements & LOG_SLOW_DISABLE_SLAVE);
set_slave_thread_options(thd);
if (thd_type == SLAVE_THD_SQL)
@@ -3279,11 +3623,9 @@ static int request_dump(THD *thd, MYSQL* mysql, Master_info* mi,
if (opt_log_slave_updates && opt_replicate_annotate_row_events)
binlog_flags|= BINLOG_SEND_ANNOTATE_ROWS_EVENT;
- if (RUN_HOOK(binlog_relay_io,
- before_request_transmit,
- (thd, mi, binlog_flags)))
+ if (repl_semisync_slave.request_transmit(mi))
DBUG_RETURN(1);
-
+
// TODO if big log files: Change next to int8store()
int4store(buf, (ulong) mi->master_log_pos);
int2store(buf + 4, binlog_flags);
@@ -3346,7 +3688,7 @@ static ulong read_event(MYSQL* mysql, Master_info *mi, bool* suppress_warnings,
#endif
len = cli_safe_read_reallen(mysql, network_read_len);
- if (len == packet_error || (long) len < 1)
+ if (unlikely(len == packet_error || (long) len < 1))
{
if (mysql_errno(mysql) == ER_NET_READ_INTERRUPTED)
{
@@ -3355,7 +3697,8 @@ static ulong read_event(MYSQL* mysql, Master_info *mi, bool* suppress_warnings,
we suppress prints to .err file as long as the reconnect
happens without problems
*/
- *suppress_warnings= TRUE;
+ *suppress_warnings=
+ global_system_variables.log_warnings < 2 ? TRUE : FALSE;
}
else
{
@@ -3382,14 +3725,20 @@ static ulong read_event(MYSQL* mysql, Master_info *mi, bool* suppress_warnings,
DBUG_RETURN(len - 1);
}
-/*
+
+/**
Check if the current error is of temporary nature of not.
Some errors are temporary in nature, such as
ER_LOCK_DEADLOCK and ER_LOCK_WAIT_TIMEOUT.
+
+ @retval 0 if fatal error
+ @retval 1 temporary error, do retry
*/
+
int
has_temporary_error(THD *thd)
{
+ uint current_errno;
DBUG_ENTER("has_temporary_error");
DBUG_EXECUTE_IF("all_errors_are_temporary_errors",
@@ -3404,17 +3753,15 @@ has_temporary_error(THD *thd)
error or not. This is currently the case for Incident_log_event,
which sets no message. Return FALSE.
*/
- if (!thd->is_error())
+ if (!likely(thd->is_error()))
DBUG_RETURN(0);
- /*
- Temporary error codes:
- currently, InnoDB deadlock detected by InnoDB or lock
- wait timeout (innodb_lock_wait_timeout exceeded
- */
- if (thd->get_stmt_da()->sql_errno() == ER_LOCK_DEADLOCK ||
- thd->get_stmt_da()->sql_errno() == ER_LOCK_WAIT_TIMEOUT)
- DBUG_RETURN(1);
+ current_errno= thd->get_stmt_da()->sql_errno();
+ for (uint i= 0; i < slave_transaction_retry_error_length; i++)
+ {
+ if (current_errno == slave_transaction_retry_errors[i])
+ DBUG_RETURN(1);
+ }
DBUG_RETURN(0);
}
@@ -3641,7 +3988,7 @@ apply_event_and_update_pos_apply(Log_event* ev, THD* thd, rpl_group_info *rgi,
TODO: Replace this with a decent error message when merged
with BUG#24954 (which adds several new error message).
*/
- if (error)
+ if (unlikely(error))
{
rli->report(ERROR_LEVEL, ER_UNKNOWN_ERROR, rgi->gtid_info(),
"It was not possible to update the positions"
@@ -3746,10 +4093,7 @@ int
apply_event_and_update_pos_for_parallel(Log_event* ev, THD* thd,
rpl_group_info *rgi)
{
-#ifndef DBUG_OFF
- Relay_log_info* rli= rgi->rli;
-#endif
- mysql_mutex_assert_not_owner(&rli->data_lock);
+ mysql_mutex_assert_not_owner(&rgi->rli->data_lock);
int reason= apply_event_and_update_pos_setup(ev, thd, rgi);
/*
In parallel replication, sql_slave_skip_counter is handled in the SQL
@@ -4044,7 +4388,7 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli,
update_log_pos failed: this should not happen, so we don't
retry.
*/
- if (exec_res == 2)
+ if (unlikely(exec_res == 2))
DBUG_RETURN(1);
#ifdef WITH_WSREP
@@ -4056,7 +4400,7 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli,
if (slave_trans_retries)
{
int UNINIT_VAR(temp_err);
- if (exec_res && (temp_err= has_temporary_error(thd)))
+ if (unlikely(exec_res) && (temp_err= has_temporary_error(thd)))
{
const char *errmsg;
rli->clear_error();
@@ -4091,8 +4435,9 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli,
exec_res= 0;
serial_rgi->cleanup_context(thd, 1);
/* chance for concurrent connection to get more locks */
- slave_sleep(thd, MY_MIN(serial_rgi->trans_retries,
+ slave_sleep(thd, MY_MAX(MY_MIN(serial_rgi->trans_retries,
MAX_SLAVE_RETRY_PAUSE),
+ slave_trans_retry_interval),
sql_slave_killed, serial_rgi);
serial_rgi->trans_retries++;
mysql_mutex_lock(&rli->data_lock); // because of SHOW STATUS
@@ -4177,7 +4522,7 @@ static bool check_io_slave_killed(Master_info *mi, const char *info)
@param[in] mysql MySQL connection.
@param[in] mi Master connection information.
@param[in,out] retry_count Number of attempts to reconnect.
- @param[in] suppress_warnings TRUE when a normal net read timeout
+ @param[in] suppress_warnings TRUE when a normal net read timeout
has caused to reconnecting.
@param[in] messages Messages to print/log, see
reconnect_messages[] array.
@@ -4306,6 +4651,7 @@ pthread_handler_t handle_slave_io(void *arg)
mi->abort_slave = 0;
mysql_mutex_unlock(&mi->run_lock);
mysql_cond_broadcast(&mi->start_cond);
+ mi->rows_event_tracker.reset();
DBUG_PRINT("master_info",("log_file_name: '%s' position: %llu",
mi->master_log_name, mi->master_log_pos));
@@ -4329,10 +4675,12 @@ pthread_handler_t handle_slave_io(void *arg)
goto err;
}
+
#ifdef WITH_WSREP
thd->variables.wsrep_on= 0;
#endif
- if (RUN_HOOK(binlog_relay_io, thread_start, (thd, mi)))
+ if (DBUG_EVALUATE_IF("failed_slave_start", 1, 0)
+ || repl_semisync_slave.slave_start(mi))
{
mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, NULL,
ER_THD(thd, ER_SLAVE_FATAL_ERROR),
@@ -4390,6 +4738,10 @@ connected:
*/
mi->gtid_reconnect_event_skip_count= mi->events_queued_since_last_gtid;
mi->gtid_event_seen= false;
+ /*
+ Reset stale state of the rows-event group tracker at reconnect.
+ */
+ mi->rows_event_tracker.reset();
}
#ifdef ENABLED_DEBUG_SYNC
@@ -4489,7 +4841,7 @@ connected:
if (check_io_slave_killed(mi, NullS))
goto err;
- if (event_len == packet_error)
+ if (unlikely(event_len == packet_error))
{
uint mysql_error_number= mysql_errno(mysql);
switch (mysql_error_number) {
@@ -4523,9 +4875,10 @@ Stopping slave I/O thread due to out-of-memory error from master");
retry_count=0; // ok event, reset retry counter
THD_STAGE_INFO(thd, stage_queueing_master_event_to_the_relay_log);
event_buf= (const char*)mysql->net.read_pos + 1;
- if (RUN_HOOK(binlog_relay_io, after_read_event,
- (thd, mi,(const char*)mysql->net.read_pos + 1,
- event_len, &event_buf, &event_len)))
+ mi->semi_ack= 0;
+ if (repl_semisync_slave.
+ slave_read_sync_header((const char*)mysql->net.read_pos + 1, event_len,
+ &(mi->semi_ack), &event_buf, &event_len))
{
mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, NULL,
ER_THD(thd, ER_SLAVE_FATAL_ERROR),
@@ -4574,9 +4927,6 @@ Stopping slave I/O thread due to out-of-memory error from master");
tokenamount -= network_read_len;
}
- /* XXX: 'synced' should be updated by queue_event to indicate
- whether event has been synced to disk */
- bool synced= 0;
if (queue_event(mi, event_buf, event_len))
{
mi->report(ERROR_LEVEL, ER_SLAVE_RELAY_LOG_WRITE_FAILURE, NULL,
@@ -4585,17 +4935,27 @@ Stopping slave I/O thread due to out-of-memory error from master");
goto err;
}
- if (RUN_HOOK(binlog_relay_io, after_queue_event,
- (thd, mi, event_buf, event_len, synced)))
+ if (rpl_semi_sync_slave_status && (mi->semi_ack & SEMI_SYNC_NEED_ACK))
{
- mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, NULL,
- ER_THD(thd, ER_SLAVE_FATAL_ERROR),
- "Failed to run 'after_queue_event' hook");
- goto err;
+ /*
+ We deliberately ignore the error in slave_reply, such error should
+ not cause the slave IO thread to stop, and the error messages are
+ already reported.
+ */
+ (void)repl_semisync_slave.slave_reply(mi);
}
if (mi->using_gtid == Master_info::USE_GTID_NO &&
- flush_master_info(mi, TRUE, TRUE))
+ /*
+ If rpl_semi_sync_slave_delay_master is enabled, we will flush
+ master info only when ack is needed. This may lead to at least one
+ group transaction delay but affords better performance improvement.
+ */
+ (!repl_semisync_slave.get_slave_enabled() ||
+ (!(mi->semi_ack & SEMI_SYNC_SLAVE_DELAY_SYNC) ||
+ (mi->semi_ack & (SEMI_SYNC_NEED_ACK)))) &&
+ (DBUG_EVALUATE_IF("failed_flush_master_info", 1, 0) ||
+ flush_master_info(mi, TRUE, TRUE)))
{
sql_print_error("Failed to flush master info file");
goto err;
@@ -4649,9 +5009,9 @@ err:
IO_RPL_LOG_NAME, mi->master_log_pos,
tmp.c_ptr_safe());
}
- RUN_HOOK(binlog_relay_io, thread_stop, (thd, mi));
+ repl_semisync_slave.slave_stop(mi);
thd->reset_query();
- thd->reset_db(NULL, 0);
+ thd->reset_db(&null_clex_str);
if (mysql)
{
/*
@@ -4683,9 +5043,7 @@ err_during_init:
// TODO: make rpl_status part of Master_info
change_rpl_status(RPL_ACTIVE_SLAVE,RPL_IDLE_SLAVE);
- mysql_mutex_lock(&LOCK_thread_count);
- thd->unlink();
- mysql_mutex_unlock(&LOCK_thread_count);
+ thd->assert_not_linked();
delete thd;
thread_safe_decrement32(&service_thread_count);
signal_thd_deleted();
@@ -4787,7 +5145,7 @@ slave_output_error_info(rpl_group_info *rgi, THD *thd)
Relay_log_info *rli= rgi->rli;
uint32 const last_errno= rli->last_error().number;
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
{
char const *const errmsg= thd->get_stmt_da()->message();
@@ -4831,7 +5189,7 @@ slave_output_error_info(rpl_group_info *rgi, THD *thd)
udf_error = true;
sql_print_warning("Slave: %s Error_code: %d", err->get_message_text(), err->get_sql_errno());
}
- if (udf_error)
+ if (unlikely(udf_error))
{
StringBuffer<100> tmp;
if (rli->mi->using_gtid != Master_info::USE_GTID_NO)
@@ -4959,6 +5317,10 @@ pthread_handler_t handle_slave_sql(void *arg)
applied. In all other cases it must be FALSE.
*/
thd->variables.binlog_annotate_row_events= 0;
+
+ /* Ensure that slave can exeute any alter table it gets from master */
+ thd->variables.alter_algorithm= (ulong) Alter_info::ALTER_TABLE_ALGORITHM_DEFAULT;
+
add_to_active_threads(thd);
/*
We are going to set slave_running to 1. Assuming slave I/O thread is
@@ -5099,12 +5461,20 @@ pthread_handler_t handle_slave_sql(void *arg)
if (mi->using_gtid != Master_info::USE_GTID_NO || opt_gtid_strict_mode)
goto err;
}
+ /* Re-load the set of mysql.gtid_slave_posXXX tables available. */
+ if (find_gtid_slave_pos_tables(thd))
+ {
+ rli->report(ERROR_LEVEL, thd->get_stmt_da()->sql_errno(), NULL,
+ "Error processing replication GTID position tables: %s",
+ thd->get_stmt_da()->message());
+ goto err;
+ }
/* execute init_slave variable */
if (opt_init_slave.length)
{
execute_init_command(thd, &opt_init_slave, &LOCK_sys_init_slave);
- if (thd->is_slave_error)
+ if (unlikely(thd->is_slave_error))
{
rli->report(ERROR_LEVEL, thd->get_stmt_da()->sql_errno(), NULL,
"Slave SQL thread aborted. Can't execute init_slave query");
@@ -5255,7 +5625,7 @@ pthread_handler_t handle_slave_sql(void *arg)
*/
thd->catalog= 0;
thd->reset_query();
- thd->reset_db(NULL, 0);
+ thd->reset_db(&null_clex_str);
if (rli->mi->using_gtid != Master_info::USE_GTID_NO)
{
ulong domain_count;
@@ -5380,11 +5750,7 @@ err_during_init:
rpl_parallel_resize_pool_if_no_slaves();
- /* TODO: Check if this lock is needed */
- mysql_mutex_lock(&LOCK_thread_count);
delete serial_rgi;
- mysql_mutex_unlock(&LOCK_thread_count);
-
delete thd;
thread_safe_decrement32(&service_thread_count);
signal_thd_deleted();
@@ -5797,7 +6163,7 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
{
int error= 0;
StringBuffer<1024> error_msg;
- ulonglong inc_pos;
+ ulonglong inc_pos= 0;
ulonglong event_pos;
Relay_log_info *rli= &mi->rli;
mysql_mutex_t *log_lock= rli->relay_log.get_log_lock();
@@ -5811,7 +6177,7 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
char* new_buf = NULL;
char new_buf_arr[4096];
bool is_malloc = false;
-
+ bool is_rows_event= false;
/*
FD_q must have been prepared for the first R_a event
inside get_master_version_and_clock()
@@ -5896,6 +6262,7 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
DBUG_ASSERT(debug_sync_service);
DBUG_ASSERT(!debug_sync_set_action(current_thd,
STRING_WITH_LEN(act)));
+ dbug_rows_event_count = 0;
};);
#endif
mysql_mutex_lock(&mi->data_lock);
@@ -5993,7 +6360,7 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
mysql_mutex_unlock(log_lock);
goto err;
}
- rli->relay_log.signal_update();
+ rli->relay_log.signal_relay_log_update();
mysql_mutex_unlock(log_lock);
mi->gtid_reconnect_event_skip_count= 0;
@@ -6245,11 +6612,11 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
got_gtid_event= true;
if (mi->using_gtid == Master_info::USE_GTID_NO)
goto default_action;
- if (unlikely(!mi->gtid_event_seen))
+ if (unlikely(mi->gtid_reconnect_event_skip_count))
{
- mi->gtid_event_seen= true;
- if (mi->gtid_reconnect_event_skip_count)
+ if (likely(!mi->gtid_event_seen))
{
+ mi->gtid_event_seen= true;
/*
If we are reconnecting, and we need to skip a partial event group
already queued to the relay log before the reconnect, then we check
@@ -6278,13 +6645,45 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
rpl_slave_state_tostring_helper(&error_msg, &event_gtid, &first);
goto err;
}
+ if (global_system_variables.log_warnings > 1)
+ {
+ bool first= true;
+ StringBuffer<1024> gtid_text;
+ rpl_slave_state_tostring_helper(&gtid_text, &mi->last_queued_gtid,
+ &first);
+ sql_print_information("Slave IO thread is reconnected to "
+ "receive Gtid_log_event %s. It is to skip %llu "
+ "already received events including the gtid one",
+ gtid_text.ptr(),
+ mi->events_queued_since_last_gtid);
+ }
+ goto default_action;
}
- }
+ else
+ {
+ bool first;
+ StringBuffer<1024> gtid_text;
- if (unlikely(mi->gtid_reconnect_event_skip_count))
- {
- goto default_action;
+ gtid_text.append(STRING_WITH_LEN("Last received gtid: "));
+ first= true;
+ rpl_slave_state_tostring_helper(&gtid_text, &mi->last_queued_gtid,
+ &first);
+ gtid_text.append(STRING_WITH_LEN(", currently received: "));
+ first= true;
+ rpl_slave_state_tostring_helper(&gtid_text, &event_gtid, &first);
+
+ error= ER_SLAVE_RELAY_LOG_WRITE_FAILURE;
+ sql_print_error("Slave IO thread has received a new Gtid_log_event "
+ "while skipping already logged events "
+ "after reconnect. %s. %llu remains to be skipped. "
+ "The number of originally read events was %llu",
+ gtid_text.ptr(),
+ mi->gtid_reconnect_event_skip_count,
+ mi->events_queued_since_last_gtid);
+ goto err;
+ }
}
+ mi->gtid_event_seen= true;
/*
We have successfully queued to relay log everything before this GTID, so
@@ -6351,8 +6750,34 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
goto err;
}
}
- buf = new_buf;
is_compress_event = true;
+ buf = new_buf;
+ /*
+ As we are uncertain about compressed V2 rows events, we don't track
+ them
+ */
+ if (LOG_EVENT_IS_ROW_V2((Log_event_type) buf[EVENT_TYPE_OFFSET]))
+ goto default_action;
+ /* fall through */
+ case WRITE_ROWS_EVENT_V1:
+ case UPDATE_ROWS_EVENT_V1:
+ case DELETE_ROWS_EVENT_V1:
+ case WRITE_ROWS_EVENT:
+ case UPDATE_ROWS_EVENT:
+ case DELETE_ROWS_EVENT:
+ {
+ is_rows_event= true;
+ mi->rows_event_tracker.update(mi->master_log_name,
+ mi->master_log_pos,
+ buf,
+ mi->rli.relay_log.
+ description_event_for_queue);
+
+ DBUG_EXECUTE_IF("simulate_stmt_end_rows_event_loss",
+ {
+ mi->rows_event_tracker.stmt_end_seen= false;
+ });
+ }
goto default_action;
#ifndef DBUG_OFF
@@ -6422,6 +6847,21 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
}
/*
+ Integrity of Rows- event group check.
+ A sequence of Rows- events must end with STMT_END_F flagged one.
+ Even when Heartbeat event interrupts Rows- events flow this must indicate a
+ malfunction e.g logging on the master.
+ */
+ if (((uchar) buf[EVENT_TYPE_OFFSET] != HEARTBEAT_LOG_EVENT) &&
+ !is_rows_event &&
+ mi->rows_event_tracker.check_and_report(mi->master_log_name,
+ mi->master_log_pos))
+ {
+ error= ER_SLAVE_RELAY_LOG_WRITE_FAILURE;
+ goto err;
+ }
+
+ /*
If we filter events master-side (eg. @@skip_replication), we will see holes
in the event positions from the master. If we see such a hole, adjust
mi->master_log_pos accordingly so we maintain the correct position (for
@@ -6549,7 +6989,8 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
if (got_gtid_event)
rli->ign_gtids.update(&event_gtid);
}
- rli->relay_log.signal_update(); // the slave SQL thread needs to re-check
+ // the slave SQL thread needs to re-check
+ rli->relay_log.signal_relay_log_update();
DBUG_PRINT("info", ("master_log_pos: %lu, event originating from %u server, ignored",
(ulong) mi->master_log_pos, uint4korr(buf + SERVER_ID_OFFSET)));
}
@@ -6573,7 +7014,7 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
}
mysql_mutex_unlock(log_lock);
- if (!error &&
+ if (likely(!error) &&
mi->using_gtid != Master_info::USE_GTID_NO &&
mi->events_queued_since_last_gtid > 0 &&
( (mi->last_queued_gtid_standalone &&
@@ -6589,6 +7030,21 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
The whole of the current event group is queued. So in case of
reconnect we can start from after the current GTID.
*/
+ if (mi->gtid_reconnect_event_skip_count)
+ {
+ bool first= true;
+ StringBuffer<1024> gtid_text;
+
+ rpl_slave_state_tostring_helper(&gtid_text, &mi->last_queued_gtid,
+ &first);
+ sql_print_error("Slave IO thread received a terminal event from "
+ "group %s whose retrieval was interrupted "
+ "with reconnect. We still had %llu events to read. "
+ "The number of originally read events was %llu",
+ gtid_text.ptr(),
+ mi->gtid_reconnect_event_skip_count,
+ mi->events_queued_since_last_gtid);
+ }
mi->gtid_current_pos.update(&mi->last_queued_gtid);
mi->events_queued_since_last_gtid= 0;
@@ -6607,11 +7063,11 @@ err:
Do not print ER_SLAVE_RELAY_LOG_WRITE_FAILURE error here, as the caller
handle_slave_io() prints it on return.
*/
- if (error && error != ER_SLAVE_RELAY_LOG_WRITE_FAILURE)
+ if (unlikely(error) && error != ER_SLAVE_RELAY_LOG_WRITE_FAILURE)
mi->report(ERROR_LEVEL, error, NULL, ER_DEFAULT(error),
error_msg.ptr());
- if(is_malloc)
+ if (unlikely(is_malloc))
my_free((void *)new_buf);
DBUG_RETURN(error);
@@ -7059,7 +7515,7 @@ static Log_event* next_event(rpl_group_info *rgi, ulonglong *event_size)
MYSQL_BIN_LOG::open() will write the buffered description event.
*/
old_pos= rli->event_relay_log_pos;
- if ((ev= Log_event::read_log_event(cur_log,0,
+ if ((ev= Log_event::read_log_event(cur_log,
rli->relay_log.description_event_for_exec,
opt_slave_sql_verify_checksum)))
@@ -7078,7 +7534,7 @@ static Log_event* next_event(rpl_group_info *rgi, ulonglong *event_size)
}
if (opt_reckless_slave) // For mysql-test
cur_log->error = 0;
- if (cur_log->error < 0)
+ if (unlikely(cur_log->error < 0))
{
errmsg = "slave SQL thread aborted because of I/O error";
if (hot_log)
@@ -7620,6 +8076,92 @@ bool rpl_master_erroneous_autoinc(THD *thd)
return FALSE;
}
+
+static bool get_row_event_stmt_end(const char* buf,
+ const Format_description_log_event *fdle)
+{
+ uint8 const common_header_len= fdle->common_header_len;
+ Log_event_type event_type= (Log_event_type)(uchar)buf[EVENT_TYPE_OFFSET];
+
+ uint8 const post_header_len= fdle->post_header_len[event_type-1];
+ const char *flag_start= buf + common_header_len;
+ /*
+ The term 4 below signifies that master is of 'an intermediate source', see
+ Rows_log_event::Rows_log_event.
+ */
+ flag_start += RW_MAPID_OFFSET + ((post_header_len == 6) ? 4 : RW_FLAGS_OFFSET);
+
+ return (uint2korr(flag_start) & Rows_log_event::STMT_END_F) != 0;
+}
+
+
+/*
+ Reset log event tracking data.
+*/
+
+void Rows_event_tracker::reset()
+{
+ binlog_file_name[0]= 0;
+ first_seen= last_seen= 0;
+ stmt_end_seen= false;
+}
+
+
+/*
+ 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.
+*/
+
+void Rows_event_tracker::update(const char* file_name, my_off_t pos,
+ const char* buf,
+ const Format_description_log_event *fdle)
+{
+ if (!first_seen)
+ {
+ first_seen= pos;
+ strmake(binlog_file_name, file_name, sizeof(binlog_file_name) - 1);
+ }
+ last_seen= pos;
+ DBUG_ASSERT(stmt_end_seen == 0); // We can only have one
+ stmt_end_seen= get_row_event_stmt_end(buf, fdle);
+};
+
+
+/**
+ The function is called at next event reading
+ after a sequence of Rows- log-events. It checks the end-of-statement status
+ of the past sequence to report on any isssue.
+ In the positive case the tracker gets reset.
+
+ @return true when the Rows- event group integrity found compromised,
+ false otherwise.
+*/
+bool Rows_event_tracker::check_and_report(const char* file_name,
+ my_off_t pos)
+{
+ if (last_seen)
+ {
+ // there was at least one "block" event previously
+ if (!stmt_end_seen)
+ {
+ sql_print_error("Slave IO thread did not receive an expected "
+ "Rows-log end-of-statement for event starting "
+ "at log '%s' position %llu "
+ "whose last block was seen at log '%s' position %llu. "
+ "The end-of-statement should have been delivered "
+ "before the current one at log '%s' position %llu",
+ binlog_file_name, first_seen,
+ binlog_file_name, last_seen, file_name, pos);
+ return true;
+ }
+ reset();
+ }
+
+ return false;
+}
+
/**
@} (end of group Replication)
*/
diff --git a/sql/slave.h b/sql/slave.h
index 4b2e6d95b55..97fd4823a07 100644
--- a/sql/slave.h
+++ b/sql/slave.h
@@ -48,6 +48,7 @@
#include "my_list.h"
#include "rpl_filter.h"
#include "rpl_tblmap.h"
+#include "rpl_gtid.h"
#define SLAVE_NET_TIMEOUT 60
@@ -131,6 +132,9 @@ extern ulong master_retry_count;
extern MY_BITMAP slave_error_mask;
extern char slave_skip_error_names[];
extern bool use_slave_mask;
+extern char slave_transaction_retry_error_names[];
+extern uint *slave_transaction_retry_errors;
+extern uint slave_transaction_retry_error_length;
extern char *slave_load_tmpdir;
extern char *master_info_file;
extern MYSQL_PLUGIN_IMPORT char *relay_log_info_file;
@@ -138,6 +142,7 @@ extern char *opt_relay_logname, *opt_relaylog_index_name;
extern my_bool opt_skip_slave_start, opt_reckless_slave;
extern my_bool opt_log_slave_updates;
extern char *opt_slave_skip_errors;
+extern char *opt_slave_transaction_retry_errors;
extern my_bool opt_replicate_annotate_row_events;
extern ulonglong relay_log_space_limit;
extern ulonglong opt_read_binlog_speed_limit;
@@ -183,7 +188,8 @@ extern const char *relay_log_basename;
int init_slave();
int init_recovery(Master_info* mi, const char** errmsg);
-void init_slave_skip_errors(const char* arg);
+bool init_slave_skip_errors(const char* arg);
+bool init_slave_transaction_retry_errors(const char* arg);
int register_slave_on_master(MYSQL* mysql);
int terminate_slave_threads(Master_info* mi, int thread_mask,
bool skip_lock = 0);
@@ -268,12 +274,14 @@ void slave_output_error_info(rpl_group_info *rgi, THD *thd);
pthread_handler_t handle_slave_sql(void *arg);
bool net_request_file(NET* net, const char* fname);
void slave_background_kill_request(THD *to_kill);
+void slave_background_gtid_pos_create_request
+ (rpl_slave_state::gtid_pos_table *table_entry);
extern bool volatile abort_loop;
extern Master_info *active_mi; /* active_mi for multi-master */
extern Master_info *default_master_info; /* To replace active_mi */
extern Master_info_index *master_info_index;
-extern LEX_STRING default_master_connection_name;
+extern LEX_CSTRING default_master_connection_name;
extern my_bool replicate_same_server_id;
extern int disconnect_slave_event_count, abort_slave_event_count ;
diff --git a/sql/sp.cc b/sql/sp.cc
index b9757887e7a..93c1f13e0d6 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -1,6 +1,6 @@
/*
Copyright (c) 2002, 2018, 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
@@ -15,11 +15,12 @@
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>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#include "sp.h"
#include "sql_base.h" // close_thread_tables
+#include "sql_lex.h" // empty_clex_str
#include "sql_parse.h" // parse_sql
#include "key.h" // key_copy
#include "sql_show.h" // append_definer, append_identifier
@@ -34,96 +35,163 @@
#include <my_user.h>
-/* Used in error handling only */
-#define SP_TYPE_STRING(type) \
- (type == TYPE_ENUM_FUNCTION ? "FUNCTION" : "PROCEDURE")
-
-static int
-db_load_routine(THD *thd, stored_procedure_type type, sp_name *name,
- sp_head **sphp,
- sql_mode_t sql_mode, const char *params, const char *returns,
- const char *body, st_sp_chistics &chistics,
- LEX_STRING *definer_user_name, LEX_STRING *definer_host_name,
- longlong created, longlong modified,
- Stored_program_creation_ctx *creation_ctx);
+sp_cache **Sp_handler_procedure::get_cache(THD *thd) const
+{
+ return &thd->sp_proc_cache;
+}
+
+sp_cache **Sp_handler_function::get_cache(THD *thd) const
+{
+ return &thd->sp_func_cache;
+}
+
+sp_cache **Sp_handler_package_spec::get_cache(THD *thd) const
+{
+ return &thd->sp_package_spec_cache;
+}
+
+sp_cache **Sp_handler_package_body::get_cache(THD *thd) const
+{
+ return &thd->sp_package_body_cache;
+}
+
+
+ulong Sp_handler_procedure::recursion_depth(THD *thd) const
+{
+ return thd->variables.max_sp_recursion_depth;
+}
+
+
+bool Sp_handler::add_instr_freturn(THD *thd, sp_head *sp,
+ sp_pcontext *spcont,
+ Item *item, LEX *lex) const
+{
+ my_error(ER_SP_BADRETURN, MYF(0));
+ return true;
+}
+
+
+bool Sp_handler::add_instr_preturn(THD *thd, sp_head *sp,
+ sp_pcontext *spcont) const
+{
+ thd->parse_error();
+ return true;
+}
+
+
+bool Sp_handler_function::add_instr_freturn(THD *thd, sp_head *sp,
+ sp_pcontext *spcont,
+ Item *item, LEX *lex) const
+{
+ return sp->add_instr_freturn(thd, spcont, item, lex);
+}
+
+
+bool Sp_handler_procedure::add_instr_preturn(THD *thd, sp_head *sp,
+ sp_pcontext *spcont) const
+{
+ return sp->add_instr_preturn(thd, spcont);
+}
+
+
+Sp_handler_procedure sp_handler_procedure;
+Sp_handler_function sp_handler_function;
+Sp_handler_package_spec sp_handler_package_spec;
+Sp_handler_package_body sp_handler_package_body;
+Sp_handler_trigger sp_handler_trigger;
+Sp_handler_package_procedure sp_handler_package_procedure;
+Sp_handler_package_function sp_handler_package_function;
+
+
+const Sp_handler *Sp_handler_procedure::package_routine_handler() const
+{
+ return &sp_handler_package_procedure;
+}
+
+
+const Sp_handler *Sp_handler_function::package_routine_handler() const
+{
+ return &sp_handler_package_function;
+}
+
static const
TABLE_FIELD_TYPE proc_table_fields[MYSQL_PROC_FIELD_COUNT] =
{
{
- { C_STRING_WITH_LEN("db") },
- { C_STRING_WITH_LEN("char(64)") },
- { C_STRING_WITH_LEN("utf8") }
+ { STRING_WITH_LEN("db") },
+ { STRING_WITH_LEN("char(64)") },
+ { STRING_WITH_LEN("utf8") }
},
{
- { C_STRING_WITH_LEN("name") },
- { C_STRING_WITH_LEN("char(64)") },
- { C_STRING_WITH_LEN("utf8") }
+ { STRING_WITH_LEN("name") },
+ { STRING_WITH_LEN("char(64)") },
+ { STRING_WITH_LEN("utf8") }
},
{
- { C_STRING_WITH_LEN("type") },
- { C_STRING_WITH_LEN("enum('FUNCTION','PROCEDURE')") },
+ { STRING_WITH_LEN("type") },
+ { STRING_WITH_LEN("enum('FUNCTION','PROCEDURE')") },
{ NULL, 0 }
},
{
- { C_STRING_WITH_LEN("specific_name") },
- { C_STRING_WITH_LEN("char(64)") },
- { C_STRING_WITH_LEN("utf8") }
+ { STRING_WITH_LEN("specific_name") },
+ { STRING_WITH_LEN("char(64)") },
+ { STRING_WITH_LEN("utf8") }
},
{
- { C_STRING_WITH_LEN("language") },
- { C_STRING_WITH_LEN("enum('SQL')") },
+ { STRING_WITH_LEN("language") },
+ { STRING_WITH_LEN("enum('SQL')") },
{ NULL, 0 }
},
{
- { C_STRING_WITH_LEN("sql_data_access") },
- { C_STRING_WITH_LEN("enum('CONTAINS_SQL','NO_SQL','READS_SQL_DATA','MODIFIES_SQL_DATA')") },
+ { STRING_WITH_LEN("sql_data_access") },
+ { STRING_WITH_LEN("enum('CONTAINS_SQL','NO_SQL','READS_SQL_DATA','MODIFIES_SQL_DATA')") },
{ NULL, 0 }
},
{
- { C_STRING_WITH_LEN("is_deterministic") },
- { C_STRING_WITH_LEN("enum('YES','NO')") },
+ { STRING_WITH_LEN("is_deterministic") },
+ { STRING_WITH_LEN("enum('YES','NO')") },
{ NULL, 0 }
},
{
- { C_STRING_WITH_LEN("security_type") },
- { C_STRING_WITH_LEN("enum('INVOKER','DEFINER')") },
+ { STRING_WITH_LEN("security_type") },
+ { STRING_WITH_LEN("enum('INVOKER','DEFINER')") },
{ NULL, 0 }
},
{
- { C_STRING_WITH_LEN("param_list") },
- { C_STRING_WITH_LEN("blob") },
+ { STRING_WITH_LEN("param_list") },
+ { STRING_WITH_LEN("blob") },
{ NULL, 0 }
},
{
- { C_STRING_WITH_LEN("returns") },
- { C_STRING_WITH_LEN("longblob") },
+ { STRING_WITH_LEN("returns") },
+ { STRING_WITH_LEN("longblob") },
{ NULL, 0 }
},
{
- { C_STRING_WITH_LEN("body") },
- { C_STRING_WITH_LEN("longblob") },
+ { STRING_WITH_LEN("body") },
+ { STRING_WITH_LEN("longblob") },
{ NULL, 0 }
},
{
- { C_STRING_WITH_LEN("definer") },
- { C_STRING_WITH_LEN("char(") },
- { C_STRING_WITH_LEN("utf8") }
+ { STRING_WITH_LEN("definer") },
+ { STRING_WITH_LEN("char(") },
+ { STRING_WITH_LEN("utf8") }
},
{
- { C_STRING_WITH_LEN("created") },
- { C_STRING_WITH_LEN("timestamp") },
+ { STRING_WITH_LEN("created") },
+ { STRING_WITH_LEN("timestamp") },
{ NULL, 0 }
},
{
- { C_STRING_WITH_LEN("modified") },
- { C_STRING_WITH_LEN("timestamp") },
+ { STRING_WITH_LEN("modified") },
+ { STRING_WITH_LEN("timestamp") },
{ NULL, 0 }
},
{
- { C_STRING_WITH_LEN("sql_mode") },
- { C_STRING_WITH_LEN("set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES',"
+ { STRING_WITH_LEN("sql_mode") },
+ { STRING_WITH_LEN("set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES',"
"'IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY',"
"'NO_UNSIGNED_SUBTRACTION',"
"'NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB',"
@@ -131,32 +199,38 @@ TABLE_FIELD_TYPE proc_table_fields[MYSQL_PROC_FIELD_COUNT] =
"'ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES',"
"'STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES',"
"'ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER',"
- "'HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH')") },
+ "'HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH',"
+ "'EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT')") },
{ NULL, 0 }
},
{
- { C_STRING_WITH_LEN("comment") },
- { C_STRING_WITH_LEN("text") },
- { C_STRING_WITH_LEN("utf8") }
+ { STRING_WITH_LEN("comment") },
+ { STRING_WITH_LEN("text") },
+ { STRING_WITH_LEN("utf8") }
+ },
+ {
+ { STRING_WITH_LEN("character_set_client") },
+ { STRING_WITH_LEN("char(32)") },
+ { STRING_WITH_LEN("utf8") }
},
{
- { C_STRING_WITH_LEN("character_set_client") },
- { C_STRING_WITH_LEN("char(32)") },
- { C_STRING_WITH_LEN("utf8") }
+ { STRING_WITH_LEN("collation_connection") },
+ { STRING_WITH_LEN("char(32)") },
+ { STRING_WITH_LEN("utf8") }
},
{
- { C_STRING_WITH_LEN("collation_connection") },
- { C_STRING_WITH_LEN("char(32)") },
- { C_STRING_WITH_LEN("utf8") }
+ { STRING_WITH_LEN("db_collation") },
+ { STRING_WITH_LEN("char(32)") },
+ { STRING_WITH_LEN("utf8") }
},
{
- { C_STRING_WITH_LEN("db_collation") },
- { C_STRING_WITH_LEN("char(32)") },
- { C_STRING_WITH_LEN("utf8") }
+ { STRING_WITH_LEN("body_utf8") },
+ { STRING_WITH_LEN("longblob") },
+ { NULL, 0 }
},
{
- { C_STRING_WITH_LEN("body_utf8") },
- { C_STRING_WITH_LEN("longblob") },
+ { STRING_WITH_LEN("aggregate") },
+ { STRING_WITH_LEN("enum('NONE','GROUP')") },
{ NULL, 0 }
}
};
@@ -176,7 +250,7 @@ class Stored_routine_creation_ctx : public Stored_program_creation_ctx,
{
public:
static Stored_routine_creation_ctx *
- load_from_db(THD *thd, const sp_name *name, TABLE *proc_tbl);
+ load_from_db(THD *thd, const Database_qualified_name *name, TABLE *proc_tbl);
public:
virtual Stored_program_creation_ctx *clone(MEM_ROOT *mem_root)
@@ -214,15 +288,16 @@ bool load_charset(MEM_ROOT *mem_root,
CHARSET_INFO *dflt_cs,
CHARSET_INFO **cs)
{
- String cs_name;
+ LEX_CSTRING cs_name;
- if (get_field(mem_root, field, &cs_name))
+ if (field->val_str_nopad(mem_root, &cs_name))
{
*cs= dflt_cs;
return TRUE;
}
- *cs= get_charset_by_csname(cs_name.c_ptr(), MY_CS_PRIMARY, MYF(0));
+ DBUG_ASSERT(cs_name.str[cs_name.length] == 0);
+ *cs= get_charset_by_csname(cs_name.str, MY_CS_PRIMARY, MYF(0));
if (*cs == NULL)
{
@@ -240,15 +315,16 @@ bool load_collation(MEM_ROOT *mem_root,
CHARSET_INFO *dflt_cl,
CHARSET_INFO **cl)
{
- String cl_name;
+ LEX_CSTRING cl_name;
- if (get_field(mem_root, field, &cl_name))
+ if (field->val_str_nopad(mem_root, &cl_name))
{
*cl= dflt_cl;
return TRUE;
}
- *cl= get_charset_by_name(cl_name.c_ptr(), MYF(0));
+ DBUG_ASSERT(cl_name.str[cl_name.length] == 0);
+ *cl= get_charset_by_name(cl_name.str, MYF(0));
if (*cl == NULL)
{
@@ -263,8 +339,8 @@ bool load_collation(MEM_ROOT *mem_root,
Stored_routine_creation_ctx *
Stored_routine_creation_ctx::load_from_db(THD *thd,
- const sp_name *name,
- TABLE *proc_tbl)
+ const Database_qualified_name *name,
+ TABLE *proc_tbl)
{
/* Load character set/collation attributes. */
@@ -405,7 +481,7 @@ TABLE *open_proc_table_for_read(THD *thd, Open_tables_backup *backup)
DBUG_ENTER("open_proc_table_for_read");
- table.init_one_table("mysql", 5, "proc", 4, "proc", TL_READ);
+ table.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PROC_NAME, NULL, TL_READ);
if (open_system_tables_for_read(thd, &table, backup))
DBUG_RETURN(NULL);
@@ -440,7 +516,7 @@ 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", 5, "proc", 4, "proc", TL_WRITE);
+ 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);
@@ -459,7 +535,6 @@ static TABLE *open_proc_table_for_update(THD *thd)
Find row in open mysql.proc table representing stored routine.
@param thd Thread context
- @param type Type of routine to find (function or procedure)
@param name Name of routine
@param table TABLE object for open mysql.proc table.
@@ -469,14 +544,16 @@ static TABLE *open_proc_table_for_update(THD *thd)
SP_KEY_NOT_FOUND No routine with given name
*/
-static int
-db_find_routine_aux(THD *thd, stored_procedure_type type, sp_name *name,
- TABLE *table)
+int
+Sp_handler::db_find_routine_aux(THD *thd,
+ const Database_qualified_name *name,
+ TABLE *table) const
{
uchar key[MAX_KEY_LENGTH]; // db, name, optional key length type
DBUG_ENTER("db_find_routine_aux");
- DBUG_PRINT("enter", ("type: %d name: %.*s",
- type, (int) name->m_name.length, name->m_name.str));
+ DBUG_PRINT("enter", ("type: %s name: %.*s",
+ type_str(),
+ (int) name->m_name.length, name->m_name.str));
/*
Create key to find row. We have to use field->store() to be able to
@@ -487,10 +564,9 @@ db_find_routine_aux(THD *thd, stored_procedure_type type, sp_name *name,
*/
if (name->m_name.length > table->field[1]->field_length)
DBUG_RETURN(SP_KEY_NOT_FOUND);
- table->field[0]->store(name->m_db.str, name->m_db.length, &my_charset_bin);
- table->field[1]->store(name->m_name.str, name->m_name.length,
- &my_charset_bin);
- table->field[2]->store((longlong) type, TRUE);
+ table->field[0]->store(name->m_db, &my_charset_bin);
+ table->field[1]->store(name->m_name, &my_charset_bin);
+ table->field[2]->store((longlong) type(), true);
key_copy(key, table->record[0], table->key_info,
table->key_info->key_length);
@@ -503,12 +579,83 @@ db_find_routine_aux(THD *thd, stored_procedure_type type, sp_name *name,
}
+bool st_sp_chistics::read_from_mysql_proc_row(THD *thd, TABLE *table)
+{
+ LEX_CSTRING str;
+
+ if (table->field[MYSQL_PROC_FIELD_ACCESS]->val_str_nopad(thd->mem_root,
+ &str))
+ return true;
+
+ switch (str.str[0]) {
+ case 'N':
+ daccess= SP_NO_SQL;
+ break;
+ case 'C':
+ daccess= SP_CONTAINS_SQL;
+ break;
+ case 'R':
+ daccess= SP_READS_SQL_DATA;
+ break;
+ case 'M':
+ daccess= SP_MODIFIES_SQL_DATA;
+ break;
+ default:
+ daccess= SP_DEFAULT_ACCESS_MAPPING;
+ }
+
+ if (table->field[MYSQL_PROC_FIELD_DETERMINISTIC]->val_str_nopad(thd->mem_root,
+ &str))
+ return true;
+ detistic= str.str[0] == 'N' ? false : true;
+
+ if (table->field[MYSQL_PROC_FIELD_SECURITY_TYPE]->val_str_nopad(thd->mem_root,
+ &str))
+ return true;
+ suid= str.str[0] == 'I' ? SP_IS_NOT_SUID : SP_IS_SUID;
+
+ if (table->field[MYSQL_PROC_FIELD_AGGREGATE]->val_str_nopad(thd->mem_root,
+ &str))
+ return true;
+
+ switch (str.str[0]) {
+ case 'N':
+ agg_type= NOT_AGGREGATE;
+ break;
+ case 'G':
+ agg_type= GROUP_AGGREGATE;
+ break;
+ default:
+ agg_type= DEFAULT_AGGREGATE;
+ }
+
+
+ if (table->field[MYSQL_PROC_FIELD_COMMENT]->val_str_nopad(thd->mem_root,
+ &comment))
+ return true;
+
+ return false;
+}
+
+
+bool AUTHID::read_from_mysql_proc_row(THD *thd, TABLE *table)
+{
+ LEX_CSTRING str;
+ if (table->field[MYSQL_PROC_FIELD_DEFINER]->val_str_nopad(thd->mem_root,
+ &str))
+ return true;
+ parse(str.str, str.length);
+ if (user.str[user.length])
+ ((char *) user.str)[user.length]= '\0'; // 0-terminate if was truncated
+ return false;
+}
+
+
/**
Find routine definition in mysql.proc table and create corresponding
sp_head object for it.
@param thd Thread context
- @param type Type of routine (TYPE_ENUM_PROCEDURE/...)
@param name Name of routine
@param sphp Out parameter in which pointer to created sp_head
object is returned (0 in case of error).
@@ -523,33 +670,27 @@ db_find_routine_aux(THD *thd, stored_procedure_type type, sp_name *name,
non-0 Error (may be one of special codes like SP_KEY_NOT_FOUND)
*/
-static int
-db_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
- sp_head **sphp)
+int
+Sp_handler::db_find_routine(THD *thd,
+ const Database_qualified_name *name,
+ sp_head **sphp) const
{
TABLE *table;
- const char *params, *returns, *body;
+ LEX_CSTRING params, returns, body;
int ret;
- const char *definer;
longlong created;
longlong modified;
- st_sp_chistics chistics;
- char *ptr;
- uint length;
- char buff[65];
- String str(buff, sizeof(buff), &my_charset_bin);
+ 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;
Stored_program_creation_ctx *creation_ctx;
- char definer_user_name_holder[USERNAME_LENGTH + 1];
- LEX_STRING definer_user_name= { definer_user_name_holder, USERNAME_LENGTH };
- char definer_host_name_holder[HOSTNAME_LENGTH + 1];
- LEX_STRING definer_host_name= { definer_host_name_holder, HOSTNAME_LENGTH };
+ AUTHID definer;
DBUG_ENTER("db_find_routine");
- DBUG_PRINT("enter", ("type: %d name: %.*s",
- type, (int) name->m_name.length, name->m_name.str));
+ 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)))
@@ -558,7 +699,7 @@ db_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
/* Reset sql_mode during data dictionary operations. */
thd->variables.sql_mode= 0;
- if ((ret= db_find_routine_aux(thd, type, name, table)) != SP_OK)
+ if ((ret= db_find_routine_aux(thd, name, table)) != SP_OK)
goto done;
if (table->s->fields < MYSQL_PROC_FIELD_COUNT)
@@ -567,107 +708,44 @@ db_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
goto done;
}
- bzero((char *)&chistics, sizeof(chistics));
- if ((ptr= get_field(thd->mem_root,
- table->field[MYSQL_PROC_FIELD_ACCESS])) == NULL)
- {
- ret= SP_GET_FIELD_FAILED;
- goto done;
- }
- switch (ptr[0]) {
- case 'N':
- chistics.daccess= SP_NO_SQL;
- break;
- case 'C':
- chistics.daccess= SP_CONTAINS_SQL;
- break;
- case 'R':
- chistics.daccess= SP_READS_SQL_DATA;
- break;
- case 'M':
- chistics.daccess= SP_MODIFIES_SQL_DATA;
- break;
- default:
- chistics.daccess= SP_DEFAULT_ACCESS_MAPPING;
- }
-
- if ((ptr= get_field(thd->mem_root,
- table->field[MYSQL_PROC_FIELD_DETERMINISTIC])) == NULL)
- {
- ret= SP_GET_FIELD_FAILED;
- goto done;
- }
- chistics.detistic= (ptr[0] == 'N' ? FALSE : TRUE);
-
- if ((ptr= get_field(thd->mem_root,
- table->field[MYSQL_PROC_FIELD_SECURITY_TYPE])) == NULL)
+ if (chistics.read_from_mysql_proc_row(thd, table) ||
+ definer.read_from_mysql_proc_row(thd, table))
{
ret= SP_GET_FIELD_FAILED;
goto done;
}
- chistics.suid= (ptr[0] == 'I' ? SP_IS_NOT_SUID : SP_IS_SUID);
- if ((params= get_field(thd->mem_root,
- table->field[MYSQL_PROC_FIELD_PARAM_LIST])) == NULL)
- {
- params= "";
- }
-
- if (type == TYPE_ENUM_PROCEDURE)
- returns= "";
- else if ((returns= get_field(thd->mem_root,
- table->field[MYSQL_PROC_FIELD_RETURNS])) == NULL)
+ table->field[MYSQL_PROC_FIELD_PARAM_LIST]->val_str_nopad(thd->mem_root,
+ &params);
+ if (type() != TYPE_ENUM_FUNCTION)
+ returns= empty_clex_str;
+ else if (table->field[MYSQL_PROC_FIELD_RETURNS]->val_str_nopad(thd->mem_root,
+ &returns))
{
ret= SP_GET_FIELD_FAILED;
goto done;
}
- if ((body= get_field(thd->mem_root,
- table->field[MYSQL_PROC_FIELD_BODY])) == NULL)
+ if (table->field[MYSQL_PROC_FIELD_BODY]->val_str_nopad(thd->mem_root,
+ &body))
{
ret= SP_GET_FIELD_FAILED;
goto done;
}
// Get additional information
- if ((definer= get_field(thd->mem_root,
- table->field[MYSQL_PROC_FIELD_DEFINER])) == NULL)
- {
- ret= SP_GET_FIELD_FAILED;
- goto done;
- }
-
modified= table->field[MYSQL_PROC_FIELD_MODIFIED]->val_int();
created= table->field[MYSQL_PROC_FIELD_CREATED]->val_int();
-
- sql_mode= (ulong) table->field[MYSQL_PROC_FIELD_SQL_MODE]->val_int();
-
- table->field[MYSQL_PROC_FIELD_COMMENT]->val_str(&str, &str);
-
- ptr= 0;
- if ((length= str.length()))
- ptr= thd->strmake(str.ptr(), length);
- chistics.comment.str= ptr;
- chistics.comment.length= length;
+ sql_mode= (sql_mode_t) table->field[MYSQL_PROC_FIELD_SQL_MODE]->val_int();
creation_ctx= Stored_routine_creation_ctx::load_from_db(thd, name, table);
close_system_tables(thd, &open_tables_state_backup);
table= 0;
- if (parse_user(definer, strlen(definer),
- definer_user_name.str, &definer_user_name.length,
- definer_host_name.str, &definer_host_name.length) &&
- definer_user_name.length && !definer_host_name.length)
- {
- // 'user@' -> 'user@%'
- definer_host_name= host_not_specified;
- }
-
- ret= db_load_routine(thd, type, name, sphp,
- sql_mode, params, returns, body, chistics,
- &definer_user_name, &definer_host_name,
- created, modified, creation_ctx);
+ ret= db_load_routine(thd, name, sphp,
+ sql_mode, params, returns, body, chistics, definer,
+ created, modified, NULL, creation_ctx);
done:
/*
Restore the time zone flag as the timezone usage in proc table
@@ -681,10 +759,28 @@ db_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
}
+int
+Sp_handler::db_find_and_cache_routine(THD *thd,
+ const Database_qualified_name *name,
+ sp_head **sp) const
+{
+ int rc= db_find_routine(thd, name, sp);
+ if (rc == SP_OK)
+ {
+ sp_cache_insert(get_cache(thd), *sp);
+ DBUG_PRINT("info", ("added new: %p, level: %lu, flags %x",
+ sp[0], sp[0]->m_recursion_level,
+ sp[0]->m_flags));
+ }
+ return rc;
+}
+
+
/**
Silence DEPRECATED SYNTAX warnings when loading a stored procedure
into the cache.
*/
+
struct Silence_deprecated_warning : public Internal_error_handler
{
public:
@@ -720,6 +816,8 @@ Silence_deprecated_warning::handle_condition(
@param[in] thd Thread handler
@param[in] defstr CREATE... string
@param[in] sql_mode SQL mode
+ @param[in] parent The owner package for package routines,
+ or NULL for standalone routines.
@param[in] creation_ctx Creation context of stored routines
@return Pointer on sp_head struct
@@ -728,6 +826,7 @@ Silence_deprecated_warning::handle_condition(
*/
static sp_head *sp_compile(THD *thd, String *defstr, sql_mode_t sql_mode,
+ sp_package *parent,
Stored_program_creation_ctx *creation_ctx)
{
sp_head *sp;
@@ -748,6 +847,7 @@ static sp_head *sp_compile(THD *thd, String *defstr, sql_mode_t sql_mode,
}
lex_start(thd);
+ thd->lex->sphead= parent;
thd->push_internal_handler(&warning_handler);
thd->spcont= 0;
@@ -808,14 +908,18 @@ Bad_db_error_handler::handle_condition(THD *thd,
}
-static int
-db_load_routine(THD *thd, stored_procedure_type type,
- sp_name *name, sp_head **sphp,
- sql_mode_t sql_mode, const char *params, const char *returns,
- const char *body, st_sp_chistics &chistics,
- LEX_STRING *definer_user_name, LEX_STRING *definer_host_name,
- longlong created, longlong modified,
- Stored_program_creation_ctx *creation_ctx)
+int
+Sp_handler::db_load_routine(THD *thd, const Database_qualified_name *name,
+ sp_head **sphp,
+ sql_mode_t sql_mode,
+ const LEX_CSTRING &params,
+ const LEX_CSTRING &returns,
+ const LEX_CSTRING &body,
+ const st_sp_chistics &chistics,
+ const AUTHID &definer,
+ longlong created, longlong modified,
+ sp_package *parent,
+ Stored_program_creation_ctx *creation_ctx) const
{
LEX *old_lex= thd->lex, newlex;
String defstr;
@@ -829,8 +933,6 @@ db_load_routine(THD *thd, stored_procedure_type type,
thd->lex= &newlex;
newlex.current_select= NULL;
- // Resetting REPLACE and EXIST flags in create_info, for show_create_sp()
- newlex.create_info.DDL_options_st::init();
defstr.set_charset(creation_ctx->get_client_cs());
@@ -840,15 +942,10 @@ db_load_routine(THD *thd, stored_procedure_type type,
definition for SHOW CREATE PROCEDURE later.
*/
- if (!show_create_sp(thd, &defstr,
- type,
- NULL, 0,
- name->m_name.str, name->m_name.length,
- params, strlen(params),
- returns, strlen(returns),
- body, strlen(body),
- &chistics, definer_user_name, definer_host_name,
- sql_mode))
+ if (show_create_sp(thd, &defstr,
+ null_clex_str, name->m_name,
+ params, returns, body,
+ chistics, definer, DDL_options(), sql_mode))
{
ret= SP_INTERNAL_ERROR;
goto end;
@@ -878,14 +975,16 @@ db_load_routine(THD *thd, stored_procedure_type type,
}
{
- *sphp= sp_compile(thd, &defstr, sql_mode, creation_ctx);
+ *sphp= sp_compile(thd, &defstr, sql_mode, parent, creation_ctx);
/*
Force switching back to the saved current database (if changed),
because it may be NULL. In this case, mysql_change_db() would
generate an error.
*/
- if (cur_db_changed && mysql_change_db(thd, &saved_cur_db_name, TRUE))
+ if (cur_db_changed && mysql_change_db(thd,
+ (LEX_CSTRING*) &saved_cur_db_name,
+ TRUE))
{
ret= SP_INTERNAL_ERROR;
goto end;
@@ -897,10 +996,26 @@ db_load_routine(THD *thd, stored_procedure_type type,
goto end;
}
- (*sphp)->set_definer(definer_user_name, definer_host_name);
- (*sphp)->set_info(created, modified, &chistics, sql_mode);
+ (*sphp)->set_definer(&definer.user, &definer.host);
+ (*sphp)->set_info(created, modified, chistics, sql_mode);
(*sphp)->set_creation_ctx(creation_ctx);
(*sphp)->optimize();
+
+ if (type() == TYPE_ENUM_PACKAGE_BODY)
+ {
+ sp_package *package= (*sphp)->get_package();
+ List_iterator<LEX> it(package->m_routine_implementations);
+ for (LEX *lex; (lex= it++); )
+ {
+ DBUG_ASSERT(lex->sphead);
+ lex->sphead->set_definer(&definer.user, &definer.host);
+ lex->sphead->set_suid(package->suid());
+ lex->sphead->m_sql_mode= sql_mode;
+ lex->sphead->set_creation_ctx(creation_ctx);
+ lex->sphead->optimize();
+ }
+ }
+
/*
Not strictly necessary to invoke this method here, since we know
that we've parsed CREATE PROCEDURE/FUNCTION and not an
@@ -921,7 +1036,7 @@ end:
void
-sp_returns_type(THD *thd, String &result, sp_head *sp)
+sp_returns_type(THD *thd, String &result, const sp_head *sp)
{
TABLE table;
TABLE_SHARE share;
@@ -957,7 +1072,6 @@ sp_returns_type(THD *thd, String &result, sp_head *sp)
and invalidates the stored-routine cache.
@param thd Thread context.
- @param type Stored routine type (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION)
@param name Stored routine name.
@param table A pointer to the opened mysql.proc table
@@ -965,9 +1079,11 @@ sp_returns_type(THD *thd, String &result, sp_head *sp)
@return SP_OK on success, or SP_DELETE_ROW_FAILED on error.
used to indicate about errors.
*/
-static int
-sp_drop_routine_internal(THD *thd, stored_procedure_type type,
- sp_name *name, TABLE *table)
+
+int
+Sp_handler::sp_drop_routine_internal(THD *thd,
+ const Database_qualified_name *name,
+ TABLE *table) const
{
DBUG_ENTER("sp_drop_routine_internal");
@@ -984,15 +1100,52 @@ sp_drop_routine_internal(THD *thd, stored_procedure_type type,
local cache.
*/
sp_head *sp;
- sp_cache **spc= (type == TYPE_ENUM_FUNCTION ?
- &thd->sp_func_cache : &thd->sp_proc_cache);
- sp= sp_cache_lookup(spc, name);
- if (sp)
+ sp_cache **spc= get_cache(thd);
+ DBUG_ASSERT(spc);
+ if ((sp= sp_cache_lookup(spc, name)))
sp_cache_flush_obsolete(spc, &sp);
DBUG_RETURN(SP_OK);
}
+int
+Sp_handler::sp_find_and_drop_routine(THD *thd, TABLE *table,
+ const Database_qualified_name *name) const
+{
+ int ret;
+ if ((ret= db_find_routine_aux(thd, name, table)) != SP_OK)
+ return ret;
+ return sp_drop_routine_internal(thd, name, table);
+}
+
+
+int
+Sp_handler_package_spec::
+ sp_find_and_drop_routine(THD *thd, TABLE *table,
+ const Database_qualified_name *name) const
+{
+ int ret;
+ if ((ret= db_find_routine_aux(thd, name, table)) != SP_OK)
+ return ret;
+ /*
+ When we do "DROP PACKAGE pkg", we should also perform
+ "DROP PACKAGE BODY pkg" automatically.
+ */
+ ret= sp_handler_package_body.sp_find_and_drop_routine(thd, table, name);
+ if (ret != SP_KEY_NOT_FOUND && ret != SP_OK)
+ {
+ /*
+ - SP_KEY_NOT_FOUND means that "CREATE PACKAGE pkg" did not
+ have a correspoinding "CREATE PACKAGE BODY pkg" yet.
+ - SP_OK means that "CREATE PACKAGE pkg" had a correspoinding
+ "CREATE PACKAGE BODY pkg", which was successfully dropped.
+ */
+ return ret; // Other codes mean an unexpecte error
+ }
+ return Sp_handler::sp_find_and_drop_routine(thd, table, name);
+}
+
+
/**
Write stored-routine object into mysql.proc.
@@ -1000,8 +1153,6 @@ sp_drop_routine_internal(THD *thd, stored_procedure_type type,
the mysql.proc.
@param thd Thread context.
- @param type Stored routine type
- (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION).
@param sp Stored routine object to store.
@note Opens and closes the thread tables. Therefore assumes
@@ -1018,16 +1169,14 @@ sp_drop_routine_internal(THD *thd, stored_procedure_type type,
*/
bool
-sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
+Sp_handler::sp_create_routine(THD *thd, const sp_head *sp) const
{
LEX *lex= thd->lex;
bool ret= TRUE;
TABLE *table;
char definer_buf[USER_HOST_BUFF_SIZE];
- LEX_STRING definer;
+ LEX_CSTRING definer;
sql_mode_t saved_mode= thd->variables.sql_mode;
- MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ?
- MDL_key::FUNCTION : MDL_key::PROCEDURE;
CHARSET_INFO *db_cs= get_default_db_collation(thd, sp->m_db.str);
@@ -1035,15 +1184,15 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
bool store_failed= FALSE;
DBUG_ENTER("sp_create_routine");
- DBUG_PRINT("enter", ("type: %d name: %.*s", (int) type,
+ DBUG_PRINT("enter", ("type: %s name: %.*s",
+ type_str(),
(int) sp->m_name.length,
sp->m_name.str));
+ MDL_key::enum_mdl_namespace mdl_type= get_mdl_type();
+ LEX_CSTRING returns= empty_clex_str;
String retstr(64);
retstr.set_charset(system_charset_info);
- DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE ||
- type == TYPE_ENUM_FUNCTION);
-
/* Grab an exclusive MDL lock. */
if (lock_object_name(thd, mdl_type, sp->m_db.str, sp->m_name.str))
{
@@ -1071,38 +1220,54 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
if (!(table= open_proc_table_for_update(thd)))
{
- my_error(ER_SP_STORE_FAILED, MYF(0), SP_TYPE_STRING(type),sp->m_name.str);
+ my_error(ER_SP_STORE_FAILED, MYF(0), type_str(), sp->m_name.str);
goto done;
}
else
{
/* Checking if the routine already exists */
- if (db_find_routine_aux(thd, type, lex->spname, table) == SP_OK)
+ if (db_find_routine_aux(thd, sp, table) == SP_OK)
{
if (lex->create_info.or_replace())
{
- if (sp_drop_routine_internal(thd, type, lex->spname, table))
- goto done;
+ switch (type()) {
+ case TYPE_ENUM_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:
+ if (sp_drop_routine_internal(thd, sp, table))
+ goto done;
+ break;
+ case TYPE_ENUM_TRIGGER:
+ case TYPE_ENUM_PROXY:
+ DBUG_ASSERT(0);
+ ret= SP_OK;
+ }
}
else if (lex->create_info.if_not_exists())
{
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_SP_ALREADY_EXISTS,
ER_THD(thd, ER_SP_ALREADY_EXISTS),
- SP_TYPE_STRING(type),
- lex->spname->m_name.str);
+ type_str(), sp->m_name.str);
ret= FALSE;
// Setting retstr as it is used for logging.
- if (sp->m_type == TYPE_ENUM_FUNCTION)
+ if (type() == TYPE_ENUM_FUNCTION)
+ {
sp_returns_type(thd, retstr, sp);
+ returns= retstr.lex_cstring();
+ }
goto log;
}
else
{
- my_error(ER_SP_ALREADY_EXISTS, MYF(0),
- SP_TYPE_STRING(type), sp->m_name.str);
+ my_error(ER_SP_ALREADY_EXISTS, MYF(0), type_str(), sp->m_name.str);
goto done;
}
}
@@ -1114,8 +1279,7 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
if (table->s->fields < MYSQL_PROC_FIELD_COUNT)
{
- my_error(ER_SP_STORE_FAILED, MYF(0),
- SP_TYPE_STRING(type), sp->m_name.str);
+ my_error(ER_SP_STORE_FAILED, MYF(0), type_str(), sp->m_name.str);
goto done;
}
@@ -1135,45 +1299,53 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
store_failed=
table->field[MYSQL_PROC_FIELD_DB]->
- store(sp->m_db.str, sp->m_db.length, system_charset_info);
+ store(sp->m_db, system_charset_info);
store_failed= store_failed ||
table->field[MYSQL_PROC_FIELD_NAME]->
- store(sp->m_name.str, sp->m_name.length, system_charset_info);
+ store(sp->m_name, system_charset_info);
+
+ if (sp->agg_type() != DEFAULT_AGGREGATE)
+ {
+ store_failed= store_failed ||
+ table->field[MYSQL_PROC_FIELD_AGGREGATE]->
+ store((longlong)sp->agg_type(),TRUE);
+ }
store_failed= store_failed ||
table->field[MYSQL_PROC_MYSQL_TYPE]->
- store((longlong)type, TRUE);
+ store((longlong) type(), true);
store_failed= store_failed ||
table->field[MYSQL_PROC_FIELD_SPECIFIC_NAME]->
- store(sp->m_name.str, sp->m_name.length, system_charset_info);
+ store(sp->m_name, system_charset_info);
- if (sp->m_chistics->daccess != SP_DEFAULT_ACCESS)
+ if (sp->daccess() != SP_DEFAULT_ACCESS)
{
store_failed= store_failed ||
table->field[MYSQL_PROC_FIELD_ACCESS]->
- store((longlong)sp->m_chistics->daccess, TRUE);
+ store((longlong)sp->daccess(), TRUE);
}
store_failed= store_failed ||
table->field[MYSQL_PROC_FIELD_DETERMINISTIC]->
- store((longlong)(sp->m_chistics->detistic ? 1 : 2), TRUE);
+ store((longlong)(sp->detistic() ? 1 : 2), TRUE);
- if (sp->m_chistics->suid != SP_IS_DEFAULT_SUID)
+ if (sp->suid() != SP_IS_DEFAULT_SUID)
{
store_failed= store_failed ||
table->field[MYSQL_PROC_FIELD_SECURITY_TYPE]->
- store((longlong)sp->m_chistics->suid, TRUE);
+ store((longlong)sp->suid(), TRUE);
}
store_failed= store_failed ||
table->field[MYSQL_PROC_FIELD_PARAM_LIST]->
- store(sp->m_params.str, sp->m_params.length, system_charset_info);
+ store(sp->m_params, system_charset_info);
- if (sp->m_type == TYPE_ENUM_FUNCTION)
+ if (type() == TYPE_ENUM_FUNCTION)
{
sp_returns_type(thd, retstr, sp);
+ returns= retstr.lex_cstring();
store_failed= store_failed ||
table->field[MYSQL_PROC_FIELD_RETURNS]->
@@ -1182,11 +1354,11 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
store_failed= store_failed ||
table->field[MYSQL_PROC_FIELD_BODY]->
- store(sp->m_body.str, sp->m_body.length, system_charset_info);
+ store(sp->m_body, system_charset_info);
store_failed= store_failed ||
table->field[MYSQL_PROC_FIELD_DEFINER]->
- store(definer.str, definer.length, system_charset_info);
+ 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();
@@ -1195,26 +1367,25 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
table->field[MYSQL_PROC_FIELD_SQL_MODE]->
store((longlong)saved_mode, TRUE);
- if (sp->m_chistics->comment.str)
+ if (sp->comment().str)
{
store_failed= store_failed ||
table->field[MYSQL_PROC_FIELD_COMMENT]->
- store(sp->m_chistics->comment.str, sp->m_chistics->comment.length,
- system_charset_info);
+ store(sp->comment(), system_charset_info);
}
- if ((sp->m_type == TYPE_ENUM_FUNCTION) &&
+ if (type() == TYPE_ENUM_FUNCTION &&
!trust_function_creators && mysql_bin_log.is_open())
{
- if (!sp->m_chistics->detistic)
+ if (!sp->detistic())
{
/*
Note that this test is not perfect; one could use
a non-deterministic read-only function in an update statement.
*/
enum enum_sp_data_access access=
- (sp->m_chistics->daccess == SP_DEFAULT_ACCESS) ?
- SP_DEFAULT_ACCESS_MAPPING : sp->m_chistics->daccess;
+ (sp->daccess() == SP_DEFAULT_ACCESS) ?
+ SP_DEFAULT_ACCESS_MAPPING : sp->daccess();
if (access == SP_CONTAINS_SQL ||
access == SP_MODIFIES_SQL_DATA)
{
@@ -1251,7 +1422,7 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
table->field[MYSQL_PROC_FIELD_BODY_UTF8]->set_notnull();
store_failed= store_failed ||
table->field[MYSQL_PROC_FIELD_BODY_UTF8]->store(
- sp->m_body_utf8.str, sp->m_body_utf8.length, system_charset_info);
+ sp->m_body_utf8, system_charset_info);
if (store_failed)
{
@@ -1261,8 +1432,7 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
if (table->file->ha_write_row(table->record[0]))
{
- my_error(ER_SP_ALREADY_EXISTS, MYF(0),
- SP_TYPE_STRING(type), sp->m_name.str);
+ my_error(ER_SP_ALREADY_EXISTS, MYF(0), type_str(), sp->m_name.str);
goto done;
}
/* Make change permanent and avoid 'table is marked as crashed' errors */
@@ -1279,16 +1449,13 @@ log:
StringBuffer<128> log_query(thd->variables.character_set_client);
DBUG_ASSERT(log_query.charset()->mbminlen == 1);
- if (!show_create_sp(thd, &log_query,
- sp->m_type,
- (sp->m_explicit_name ? sp->m_db.str : NULL),
- (sp->m_explicit_name ? sp->m_db.length : 0),
- sp->m_name.str, sp->m_name.length,
- sp->m_params.str, sp->m_params.length,
- retstr.ptr(), retstr.length(),
- sp->m_body.str, sp->m_body.length,
- sp->m_chistics, &(thd->lex->definer->user),
- &(thd->lex->definer->host),
+ if (show_create_sp(thd, &log_query,
+ sp->m_explicit_name ? sp->m_db : null_clex_str,
+ sp->m_name,
+ sp->m_params, returns, sp->m_body,
+ sp->chistics(),
+ thd->lex->definer[0],
+ thd->lex->create_info,
saved_mode))
{
my_error(ER_OUT_OF_RESOURCES, MYF(0));
@@ -1299,7 +1466,7 @@ log:
/* Such a statement can always go directly to binlog, no trans cache */
if (thd->binlog_query(THD::STMT_QUERY_TYPE,
log_query.ptr(), log_query.length(),
- FALSE, FALSE, FALSE, 0))
+ FALSE, FALSE, FALSE, 0) > 0)
{
my_error(ER_ERROR_ON_WRITE, MYF(MY_WME), "binary log", -1);
goto done;
@@ -1316,6 +1483,69 @@ done:
}
+static bool
+append_suid(String *buf, enum_sp_suid_behaviour suid)
+{
+ return suid == SP_IS_NOT_SUID &&
+ buf->append(STRING_WITH_LEN(" SQL SECURITY INVOKER\n"));
+}
+
+
+static bool
+append_comment(String *buf, const LEX_CSTRING &comment)
+{
+ if (!comment.length)
+ return false;
+ if (buf->append(STRING_WITH_LEN(" COMMENT ")))
+ return true;
+ append_unescaped(buf, comment.str, comment.length);
+ return buf->append('\n');
+}
+
+
+static bool
+append_package_chistics(String *buf, const st_sp_chistics &chistics)
+{
+ return append_suid(buf, chistics.suid) ||
+ append_comment(buf, chistics.comment);
+}
+
+
+bool
+Sp_handler_package::show_create_sp(THD *thd, String *buf,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &name,
+ const LEX_CSTRING &params,
+ const LEX_CSTRING &returns,
+ const LEX_CSTRING &body,
+ const st_sp_chistics &chistics,
+ const AUTHID &definer,
+ 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;
+ bool rc=
+ buf->append(STRING_WITH_LEN("CREATE ")) ||
+ (ddl_options.or_replace() &&
+ buf->append(STRING_WITH_LEN("OR REPLACE "))) ||
+ append_definer(thd, buf, &definer.user, &definer.host) ||
+ buf->append(type_lex_cstring()) ||
+ buf->append(" ", 1) ||
+ (ddl_options.if_not_exists() &&
+ buf->append(STRING_WITH_LEN("IF NOT EXISTS "))) ||
+ (db.length > 0 &&
+ (append_identifier(thd, buf, db.str, db.length) ||
+ buf->append('.'))) ||
+ append_identifier(thd, buf, name.str, name.length) ||
+ append_package_chistics(buf, chistics) ||
+ buf->append(" ", 1) ||
+ buf->append(body.str, body.length);
+ thd->variables.sql_mode= old_sql_mode;
+ return rc;
+}
+
+
/**
Delete the record for the stored routine object from mysql.proc
and do binary logging.
@@ -1324,8 +1554,6 @@ done:
from the mysql.proc table and invalidates the stored-routine cache.
@param thd Thread context.
- @param type Stored routine type
- (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION)
@param name Stored routine name.
@return Error code. SP_OK is returned on success. Other SP_ constants are
@@ -1333,18 +1561,16 @@ done:
*/
int
-sp_drop_routine(THD *thd, stored_procedure_type type, sp_name *name)
+Sp_handler::sp_drop_routine(THD *thd,
+ const Database_qualified_name *name) const
{
TABLE *table;
int ret;
- MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ?
- MDL_key::FUNCTION : MDL_key::PROCEDURE;
DBUG_ENTER("sp_drop_routine");
- DBUG_PRINT("enter", ("type: %d name: %.*s",
- type, (int) name->m_name.length, name->m_name.str));
-
- DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE ||
- type == TYPE_ENUM_FUNCTION);
+ DBUG_PRINT("enter", ("type: %s name: %.*s",
+ type_str(),
+ (int) name->m_name.length, name->m_name.str));
+ MDL_key::enum_mdl_namespace mdl_type= get_mdl_type();
/* Grab an exclusive MDL lock. */
if (lock_object_name(thd, mdl_type, name->m_db.str, name->m_name.str))
@@ -1353,10 +1579,7 @@ sp_drop_routine(THD *thd, stored_procedure_type type, sp_name *name)
if (!(table= open_proc_table_for_update(thd)))
DBUG_RETURN(SP_OPEN_TABLE_FAILED);
- if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK)
- ret= sp_drop_routine_internal(thd, type, name, table);
-
- if (ret == SP_OK &&
+ if ((ret= sp_find_and_drop_routine(thd, table, name)) == SP_OK &&
write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
ret= SP_INTERNAL_ERROR;
/*
@@ -1377,8 +1600,6 @@ sp_drop_routine(THD *thd, stored_procedure_type type, sp_name *name)
successful update, the cache is invalidated.
@param thd Thread context.
- @param type Stored routine type
- (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION)
@param name Stored routine name.
@param chistics New values of stored routine attributes to write.
@@ -1387,20 +1608,16 @@ sp_drop_routine(THD *thd, stored_procedure_type type, sp_name *name)
*/
int
-sp_update_routine(THD *thd, stored_procedure_type type, sp_name *name,
- st_sp_chistics *chistics)
+Sp_handler::sp_update_routine(THD *thd, const Database_qualified_name *name,
+ const st_sp_chistics *chistics) const
{
TABLE *table;
int ret;
- MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ?
- MDL_key::FUNCTION : MDL_key::PROCEDURE;
DBUG_ENTER("sp_update_routine");
- DBUG_PRINT("enter", ("type: %d name: %.*s",
- (int) type,
+ DBUG_PRINT("enter", ("type: %s name: %.*s",
+ type_str(),
(int) name->m_name.length, name->m_name.str));
-
- DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE ||
- type == TYPE_ENUM_FUNCTION);
+ MDL_key::enum_mdl_namespace mdl_type= get_mdl_type();
/* Grab an exclusive MDL lock. */
if (lock_object_name(thd, mdl_type, name->m_db.str, name->m_name.str))
@@ -1409,9 +1626,9 @@ sp_update_routine(THD *thd, stored_procedure_type type, sp_name *name,
if (!(table= open_proc_table_for_update(thd)))
DBUG_RETURN(SP_OPEN_TABLE_FAILED);
- if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK)
+ if ((ret= db_find_routine_aux(thd, name, table)) == SP_OK)
{
- if (type == TYPE_ENUM_FUNCTION && ! trust_function_creators &&
+ if (type() == TYPE_ENUM_FUNCTION && ! trust_function_creators &&
mysql_bin_log.is_open() &&
(chistics->daccess == SP_CONTAINS_SQL ||
chistics->daccess == SP_MODIFIES_SQL_DATA))
@@ -1444,9 +1661,11 @@ sp_update_routine(THD *thd, stored_procedure_type type, sp_name *name,
table->field[MYSQL_PROC_FIELD_ACCESS]->
store((longlong)chistics->daccess, TRUE);
if (chistics->comment.str)
- table->field[MYSQL_PROC_FIELD_COMMENT]->store(chistics->comment.str,
- chistics->comment.length,
+ table->field[MYSQL_PROC_FIELD_COMMENT]->store(chistics->comment,
system_charset_info);
+ if (chistics->agg_type != DEFAULT_AGGREGATE)
+ table->field[MYSQL_PROC_FIELD_AGGREGATE]->
+ store((longlong)chistics->agg_type, TRUE);
if ((ret= table->file->ha_update_row(table->record[1],table->record[0])) &&
ret != HA_ERR_RECORD_IS_THE_SAME)
ret= SP_WRITE_ROW_FAILED;
@@ -1502,7 +1721,7 @@ public:
cases.
*/
-bool lock_db_routines(THD *thd, char *db)
+bool lock_db_routines(THD *thd, const char *db)
{
TABLE *table;
uint key_len;
@@ -1512,7 +1731,7 @@ bool lock_db_routines(THD *thd, char *db)
uchar keybuf[MAX_KEY_LENGTH];
DBUG_ENTER("lock_db_routines");
- DBUG_ASSERT(ok_for_lower_case_names(db));
+ DBUG_SLOW_ASSERT(ok_for_lower_case_names(db));
/*
mysql.proc will be re-opened during deletion, so we can ignore
@@ -1555,9 +1774,12 @@ bool lock_db_routines(THD *thd, char *db)
longlong sp_type= table->field[MYSQL_PROC_MYSQL_TYPE]->val_int();
MDL_request *mdl_request= new (thd->mem_root) MDL_request;
- mdl_request->init(sp_type == TYPE_ENUM_FUNCTION ?
- MDL_key::FUNCTION : MDL_key::PROCEDURE,
- db, sp_name, MDL_EXCLUSIVE, MDL_TRANSACTION);
+ const Sp_handler *sph= Sp_handler::handler((stored_procedure_type)
+ sp_type);
+ if (!sph)
+ sph= &sp_handler_procedure;
+ mdl_request->init(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)));
}
@@ -1588,7 +1810,7 @@ bool lock_db_routines(THD *thd, char *db)
*/
int
-sp_drop_db_routines(THD *thd, char *db)
+sp_drop_db_routines(THD *thd, const char *db)
{
TABLE *table;
int ret;
@@ -1661,8 +1883,6 @@ err:
calls sp_head::show_create_routine() for the object.
@param thd Thread context.
- @param type Stored routine type
- (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION)
@param name Stored routine name.
@return Error status.
@@ -1671,18 +1891,16 @@ err:
*/
bool
-sp_show_create_routine(THD *thd, stored_procedure_type type, sp_name *name)
+Sp_handler::sp_show_create_routine(THD *thd,
+ const Database_qualified_name *name) const
{
sp_head *sp;
DBUG_ENTER("sp_show_create_routine");
- DBUG_PRINT("enter", ("name: %.*s",
+ DBUG_PRINT("enter", ("type: %s name: %.*s",
+ type_str(),
(int) name->m_name.length,
name->m_name.str));
-
- DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE ||
- type == TYPE_ENUM_FUNCTION);
-
/*
@todo: Consider using prelocking for this code as well. Currently
SHOW CREATE PROCEDURE/FUNCTION is a dirty read of the data
@@ -1690,18 +1908,16 @@ sp_show_create_routine(THD *thd, stored_procedure_type type, sp_name *name)
It is "safe" to do as long as it doesn't affect the results
of the binary log or the query cache, which currently it does not.
*/
- if (sp_cache_routine(thd, type, name, FALSE, &sp))
+ if (sp_cache_routine(thd, name, false, &sp))
DBUG_RETURN(TRUE);
- if (sp == NULL || sp->show_create_routine(thd, type))
+ if (sp == NULL || sp->show_create_routine(thd, this))
{
/*
If we have insufficient privileges, pretend the routine
does not exist.
*/
- my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
- type == TYPE_ENUM_FUNCTION ? "FUNCTION" : "PROCEDURE",
- name->m_name.str);
+ my_error(ER_SP_DOES_NOT_EXIST, MYF(0), type_str(), name->m_name.str);
DBUG_RETURN(TRUE);
}
@@ -1709,12 +1925,178 @@ sp_show_create_routine(THD *thd, stored_procedure_type type, sp_name *name)
}
+/*
+ A helper class to split package name from a dot-qualified name
+ and return it as a 0-terminated string
+ 'pkg.name' -> 'pkg\0'
+*/
+
+class Prefix_name_buf: public LEX_CSTRING
+{
+ char m_buf[SAFE_NAME_LEN + 1];
+public:
+ Prefix_name_buf(const THD *thd, const LEX_CSTRING &name)
+ {
+ const char *end;
+ if (!(end= strrchr(name.str, '.')))
+ {
+ static_cast<LEX_CSTRING*>(this)[0]= null_clex_str;
+ }
+ else
+ {
+ str= m_buf;
+ length= end - name.str;
+ set_if_smaller(length, sizeof(m_buf) - 1);
+ memcpy(m_buf, name.str, length);
+ m_buf[length]= '\0';
+ }
+ }
+};
+
+
+/*
+ In case of recursions, we create multiple copies of the same SP.
+ This methods checks the current recursion depth.
+ In case if the recursion limit exceeded, it throws an error
+ and returns NULL.
+ Otherwise, depending on the current recursion level, it:
+ - either returns the original SP,
+ - or makes and returns a new clone of SP
+*/
+
+sp_head *
+Sp_handler::sp_clone_and_link_routine(THD *thd,
+ const Database_qualified_name *name,
+ sp_head *sp) const
+{
+ DBUG_ENTER("sp_link_routine");
+ int rc;
+ ulong level;
+ sp_head *new_sp;
+ LEX_CSTRING returns= empty_clex_str;
+ Database_qualified_name lname(name->m_db, name->m_name);
+#ifndef DBUG_OFF
+ uint parent_subroutine_count=
+ !sp->m_parent ? 0 :
+ sp->m_parent->m_routine_declarations.elements +
+ sp->m_parent->m_routine_implementations.elements;
+#endif
+
+ /*
+ String buffer for RETURNS data type must have system charset;
+ 64 -- size of "returns" column of mysql.proc.
+ */
+ String retstr(64);
+ retstr.set_charset(sp->get_creation_ctx()->get_client_cs());
+
+ DBUG_PRINT("info", ("found: %p", sp));
+ if (sp->m_first_free_instance)
+ {
+ DBUG_PRINT("info", ("first free: %p level: %lu flags %x",
+ sp->m_first_free_instance,
+ sp->m_first_free_instance->m_recursion_level,
+ sp->m_first_free_instance->m_flags));
+ DBUG_ASSERT(!(sp->m_first_free_instance->m_flags & sp_head::IS_INVOKED));
+ if (sp->m_first_free_instance->m_recursion_level > recursion_depth(thd))
+ {
+ recursion_level_error(thd, sp);
+ DBUG_RETURN(0);
+ }
+ DBUG_RETURN(sp->m_first_free_instance);
+ }
+ /*
+ Actually depth could be +1 than the actual value in case a SP calls
+ SHOW CREATE PROCEDURE. Hence, the linked list could hold up to one more
+ instance.
+ */
+
+ level= sp->m_last_cached_sp->m_recursion_level + 1;
+ if (level > recursion_depth(thd))
+ {
+ recursion_level_error(thd, sp);
+ DBUG_RETURN(0);
+ }
+
+ if (type() == TYPE_ENUM_FUNCTION)
+ {
+ sp_returns_type(thd, retstr, sp);
+ returns= retstr.lex_cstring();
+ }
+
+ if (sp->m_parent)
+ {
+ /*
+ If we're cloning a recursively called package routine,
+ we need to take some special measures:
+ 1. Cut the package name prefix from the routine name: 'pkg1.p1' -> 'p1',
+ to have db_load_routine() generate and parse a query like this:
+ CREATE PROCEDURE p1 ...;
+ rather than:
+ CREATE PROCEDURE pkg1.p1 ...;
+ The latter would be misinterpreted by the parser as a standalone
+ routine 'p1' in the database 'pkg1', which is not what we need.
+ 2. We pass m_parent to db_load_routine() to have it set
+ thd->lex->sphead to sp->m_parent before calling parse_sql().
+ These two measures allow to parse a package subroutine using
+ the grammar for standalone routines, e.g.:
+ CREATE PROCEDURE p1 ... END;
+ instead of going through a more complex query, e.g.:
+ CREATE PACKAGE BODY pkg1 AS
+ PROCEDURE p1 ... END;
+ END;
+ */
+ size_t prefix_length= sp->m_parent->m_name.length + 1;
+ DBUG_ASSERT(prefix_length < lname.m_name.length);
+ DBUG_ASSERT(lname.m_name.str[sp->m_parent->m_name.length] == '.');
+ lname.m_name.str+= prefix_length;
+ lname.m_name.length-= prefix_length;
+ sp->m_parent->m_is_cloning_routine= true;
+ }
+
+
+ rc= db_load_routine(thd, &lname, &new_sp,
+ sp->m_sql_mode, sp->m_params, returns,
+ sp->m_body, sp->chistics(),
+ sp->m_definer,
+ sp->m_created, sp->m_modified,
+ sp->m_parent,
+ sp->get_creation_ctx());
+ if (sp->m_parent)
+ sp->m_parent->m_is_cloning_routine= false;
+
+ if (rc == SP_OK)
+ {
+#ifndef DBUG_OFF
+ /*
+ We've just called the parser to clone the routine.
+ In case of a package routine, make sure that the parser
+ has not added any new subroutines directly to the parent package.
+ The cloned subroutine instances get linked below to the first instance,
+ they must have no direct links from the parent package.
+ */
+ DBUG_ASSERT(!sp->m_parent ||
+ parent_subroutine_count ==
+ sp->m_parent->m_routine_declarations.elements +
+ sp->m_parent->m_routine_implementations.elements);
+#endif
+ sp->m_last_cached_sp->m_next_cached_sp= new_sp;
+ new_sp->m_recursion_level= level;
+ new_sp->m_first_instance= sp;
+ sp->m_last_cached_sp= sp->m_first_free_instance= new_sp;
+ DBUG_PRINT("info", ("added level: %p, level: %lu, flags %x",
+ new_sp, new_sp->m_recursion_level,
+ new_sp->m_flags));
+ DBUG_RETURN(new_sp);
+ }
+ DBUG_RETURN(0);
+}
+
+
/**
Obtain object representing stored procedure/function by its name from
stored procedures cache and looking into mysql.proc if needed.
@param thd thread context
- @param type type of object (TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE)
@param name name of procedure
@param cp hash to look routine in
@param cache_only if true perform cache-only lookup
@@ -1727,94 +2109,92 @@ sp_show_create_routine(THD *thd, stored_procedure_type type, sp_name *name)
*/
sp_head *
-sp_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
- sp_cache **cp, bool cache_only)
+Sp_handler::sp_find_routine(THD *thd, const Database_qualified_name *name,
+ bool cache_only) const
{
- sp_head *sp;
- ulong depth= (type == TYPE_ENUM_PROCEDURE ?
- thd->variables.max_sp_recursion_depth :
- 0);
- DBUG_ENTER("sp_find_routine");
- DBUG_PRINT("enter", ("name: %.*s.%.*s type: %d cache only %d",
+ DBUG_ENTER("Sp_handler::sp_find_routine");
+ DBUG_PRINT("enter", ("name: %.*s.%.*s type: %s cache only %d",
(int) name->m_db.length, name->m_db.str,
(int) name->m_name.length, name->m_name.str,
- type, cache_only));
+ type_str(), cache_only));
+ sp_cache **cp= get_cache(thd);
+ sp_head *sp;
if ((sp= sp_cache_lookup(cp, name)))
- {
- ulong level;
- sp_head *new_sp;
- const char *returns= "";
-
- /*
- String buffer for RETURNS data type must have system charset;
- 64 -- size of "returns" column of mysql.proc.
- */
- String retstr(64);
- retstr.set_charset(sp->get_creation_ctx()->get_client_cs());
+ DBUG_RETURN(sp_clone_and_link_routine(thd, name, sp));
+ if (!cache_only)
+ db_find_and_cache_routine(thd, name, &sp);
+ DBUG_RETURN(sp);
+}
- DBUG_PRINT("info", ("found:%p", sp));
- if (sp->m_first_free_instance)
- {
- DBUG_PRINT("info", ("first free:%p level: %lu flags %x",
- sp->m_first_free_instance,
- sp->m_first_free_instance->m_recursion_level,
- sp->m_first_free_instance->m_flags));
- DBUG_ASSERT(!(sp->m_first_free_instance->m_flags & sp_head::IS_INVOKED));
- if (sp->m_first_free_instance->m_recursion_level > depth)
- {
- sp->recursion_level_error(thd);
- DBUG_RETURN(0);
- }
- DBUG_RETURN(sp->m_first_free_instance);
- }
- /*
- Actually depth could be +1 than the actual value in case a SP calls
- SHOW CREATE PROCEDURE. Hence, the linked list could hold up to one more
- instance.
- */
- level= sp->m_last_cached_sp->m_recursion_level + 1;
- if (level > depth)
- {
- sp->recursion_level_error(thd);
- DBUG_RETURN(0);
- }
+/**
+ Find a package routine.
+ See sp_cache_routine() for more information on parameters and return value.
+
+ @param thd - current THD
+ @param pkgname_str - package name
+ @param name - a mixed qualified name, with:
+ * name->m_db set to the database, e.g. "dbname"
+ * name->m_name set to a package-qualified name,
+ e.g. "pkgname.spname".
+ @param cache_only - don't load mysql.proc if not cached
+ @retval non-NULL - a pointer to an sp_head object
+ @retval NULL - an error happened.
+*/
- if (type == TYPE_ENUM_FUNCTION)
- {
- sp_returns_type(thd, retstr, sp);
- returns= retstr.ptr();
- }
- if (db_load_routine(thd, type, name, &new_sp,
- sp->m_sql_mode, sp->m_params.str, returns,
- sp->m_body.str, *sp->m_chistics,
- &sp->m_definer_user, &sp->m_definer_host,
- sp->m_created, sp->m_modified,
- sp->get_creation_ctx()) == SP_OK)
- {
- sp->m_last_cached_sp->m_next_cached_sp= new_sp;
- new_sp->m_recursion_level= level;
- new_sp->m_first_instance= sp;
- sp->m_last_cached_sp= sp->m_first_free_instance= new_sp;
- DBUG_PRINT("info", ("added level:%p, level: %lu, flags %x",
- new_sp, new_sp->m_recursion_level,
- new_sp->m_flags));
- DBUG_RETURN(new_sp);
- }
- DBUG_RETURN(0);
- }
- if (!cache_only)
- {
- if (db_find_routine(thd, type, name, &sp) == SP_OK)
- {
- sp_cache_insert(cp, sp);
- DBUG_PRINT("info", ("added new:%p, level: %lu, flags %x",
- sp, sp->m_recursion_level,
- sp->m_flags));
- }
+sp_head *
+Sp_handler::sp_find_package_routine(THD *thd,
+ const LEX_CSTRING pkgname_str,
+ const Database_qualified_name *name,
+ bool cache_only) const
+{
+ DBUG_ENTER("sp_find_package_routine");
+ Database_qualified_name pkgname(&name->m_db, &pkgname_str);
+ sp_head *ph= sp_cache_lookup(&thd->sp_package_body_cache, &pkgname);
+ if (!ph && !cache_only)
+ sp_handler_package_body.db_find_and_cache_routine(thd, &pkgname, &ph);
+ if (ph)
+ {
+ LEX_CSTRING tmp= name->m_name;
+ const char *dot= strrchr(tmp.str, '.');
+ size_t prefix_length= dot ? dot - tmp.str + 1 : 0;
+ sp_package *pkg= ph->get_package();
+ tmp.str+= prefix_length;
+ tmp.length-= prefix_length;
+ LEX *plex= pkg ? pkg->m_routine_implementations.find(tmp, type()) : NULL;
+ sp_head *sp= plex ? plex->sphead : NULL;
+ if (sp)
+ DBUG_RETURN(sp_clone_and_link_routine(thd, name, sp));
}
- DBUG_RETURN(sp);
+ DBUG_RETURN(NULL);
+}
+
+
+/**
+ Find a package routine.
+ See sp_cache_routine() for more information on parameters and return value.
+
+ @param thd - current THD
+ @param name - Qualified name with the following format:
+ * name->m_db is set to the database name, e.g. "dbname"
+ * name->m_name is set to a package-qualified name,
+ e.g. "pkgname.spname", as a single string with a
+ dot character as a separator.
+ @param cache_only - don't load mysql.proc if not cached
+ @retval non-NULL - a pointer to an sp_head object
+ @retval NULL - an error happened
+*/
+
+sp_head *
+Sp_handler::sp_find_package_routine(THD *thd,
+ const Database_qualified_name *name,
+ bool cache_only) const
+{
+ DBUG_ENTER("Sp_handler::sp_find_package_routine");
+ Prefix_name_buf pkgname(thd, name->m_name);
+ DBUG_ASSERT(pkgname.length);
+ DBUG_RETURN(sp_find_package_routine(thd, pkgname, name, cache_only));
}
@@ -1824,8 +2204,6 @@ sp_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
@param thd Thread handler
@param routines List of needles in the hay stack
- @param is_proc Indicates whether routines in the list are procedures
- or functions.
@return
@retval FALSE Found.
@@ -1833,7 +2211,7 @@ sp_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
*/
bool
-sp_exist_routines(THD *thd, TABLE_LIST *routines, bool is_proc)
+Sp_handler::sp_exist_routines(THD *thd, TABLE_LIST *routines) const
{
TABLE_LIST *routine;
bool sp_object_found;
@@ -1841,25 +2219,18 @@ sp_exist_routines(THD *thd, TABLE_LIST *routines, bool is_proc)
for (routine= routines; routine; routine= routine->next_global)
{
sp_name *name;
- LEX_STRING lex_db;
- LEX_STRING lex_name;
- lex_db.length= strlen(routine->db);
- lex_name.length= strlen(routine->table_name);
- lex_db.str= thd->strmake(routine->db, lex_db.length);
- lex_name.str= thd->strmake(routine->table_name, lex_name.length);
- name= new sp_name(lex_db, lex_name, true);
- name->init_qname(thd);
- sp_object_found= is_proc ? sp_find_routine(thd, TYPE_ENUM_PROCEDURE,
- name, &thd->sp_proc_cache,
- FALSE) != NULL :
- sp_find_routine(thd, TYPE_ENUM_FUNCTION,
- name, &thd->sp_func_cache,
- FALSE) != NULL;
+ LEX_CSTRING lex_db;
+ LEX_CSTRING lex_name;
+ thd->make_lex_string(&lex_db, routine->db.str, routine->db.length);
+ thd->make_lex_string(&lex_name, routine->table_name.str,
+ routine->table_name.length);
+ name= new sp_name(&lex_db, &lex_name, true);
+ sp_object_found= sp_find_routine(thd, name, false) != NULL;
thd->get_stmt_da()->clear_warning_info(thd->query_id);
if (! sp_object_found)
{
my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION or PROCEDURE",
- routine->table_name);
+ routine->table_name.str);
DBUG_RETURN(TRUE);
}
}
@@ -1910,7 +2281,9 @@ extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen,
*/
bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena,
- const MDL_key *key, TABLE_LIST *belong_to_view)
+ const MDL_key *key,
+ const Sp_handler *handler,
+ TABLE_LIST *belong_to_view)
{
my_hash_init_opt(&prelocking_ctx->sroutines, system_charset_info,
Query_tables_list::START_SROUTINES_HASH_SIZE,
@@ -1920,13 +2293,14 @@ bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena,
{
Sroutine_hash_entry *rn=
(Sroutine_hash_entry *)arena->alloc(sizeof(Sroutine_hash_entry));
- if (!rn) // OOM. Error will be reported using fatal_error().
+ if (unlikely(!rn)) // OOM. Error will be reported using fatal_error().
return FALSE;
rn->mdl_request.init(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);
rn->belong_to_view= belong_to_view;
+ rn->m_handler= handler;
rn->m_sp_cache_version= 0;
return TRUE;
}
@@ -1934,6 +2308,275 @@ bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena,
}
+/*
+ Find and cache a routine in a parser-safe reentrant mode.
+
+ If sp_head is not in the cache,
+ its loaded from mysql.proc, parsed using parse_sql(), and cached.
+ Note, as it is called from inside parse_sql() itself,
+ we need to preserve and restore the parser state.
+
+ It's used during parsing of CREATE PACKAGE BODY,
+ to load the corresponding CREATE PACKAGE.
+*/
+
+int
+Sp_handler::sp_cache_routine_reentrant(THD *thd,
+ const Database_qualified_name *name,
+ sp_head **sp) const
+{
+ int ret;
+ Parser_state *oldps= thd->m_parser_state;
+ thd->m_parser_state= NULL;
+ ret= sp_cache_routine(thd, name, false, sp);
+ thd->m_parser_state= oldps;
+ return ret;
+}
+
+
+/**
+ Check if a routine has a declaration in the CREATE PACKAGE statement,
+ by looking up in thd->sp_package_spec_cache, and by loading from mysql.proc
+ if needed.
+
+ @param thd current thd
+ @param db the database name
+ @param package the package name
+ @param name the routine name
+ @param type the routine type
+ @retval true, if the routine has a declaration
+ @retval false, if the routine does not have a declaration
+
+ This function can be called in arbitrary context:
+ - inside a package routine
+ - inside a standalone routine
+ - inside a anonymous block
+ - outside of any routines
+
+ The state of the package specification (i.e. the CREATE PACKAGE statement)
+ for "package" before the call of this function is not known:
+ it can be cached, or not cached.
+ After the call of this function, the package specification is always cached,
+ unless a fatal error happens.
+*/
+
+static bool
+is_package_public_routine(THD *thd,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &package,
+ const LEX_CSTRING &routine,
+ stored_procedure_type type)
+{
+ sp_head *sp= NULL;
+ Database_qualified_name tmp(db, package);
+ bool ret= sp_handler_package_spec.
+ sp_cache_routine_reentrant(thd, &tmp, &sp);
+ sp_package *spec= (!ret && sp) ? sp->get_package() : NULL;
+ return spec && spec->m_routine_declarations.find(routine, type);
+}
+
+
+/**
+ Check if a routine has a declaration in the CREATE PACKAGE statement
+ by looking up in sp_package_spec_cache.
+
+ @param thd current thd
+ @param db the database name
+ @param pkgname the package name
+ @param name the routine name
+ @param type the routine type
+ @retval true, if the routine has a declaration
+ @retval false, if the routine does not have a declaration
+
+ This function is called in the middle of CREATE PACKAGE BODY parsing,
+ to lookup the current package routines.
+ The package specification (i.e. the CREATE PACKAGE statement) for
+ the current package body must already be loaded and cached at this point.
+*/
+
+static bool
+is_package_public_routine_quick(THD *thd,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &pkgname,
+ const LEX_CSTRING &name,
+ stored_procedure_type type)
+{
+ Database_qualified_name tmp(db, pkgname);
+ sp_head *sp= sp_cache_lookup(&thd->sp_package_spec_cache, &tmp);
+ sp_package *pkg= sp ? sp->get_package() : NULL;
+ DBUG_ASSERT(pkg); // Must already be cached
+ return pkg && pkg->m_routine_declarations.find(name, type);
+}
+
+
+/**
+ Check if a qualified name, e.g. "CALL name1.name2",
+ refers to a known routine in the package body "pkg".
+*/
+
+static bool
+is_package_body_routine(THD *thd, sp_package *pkg,
+ const LEX_CSTRING &name1,
+ const LEX_CSTRING &name2,
+ stored_procedure_type type)
+{
+ return Sp_handler::eq_routine_name(pkg->m_name, name1) &&
+ (pkg->m_routine_declarations.find(name2, type) ||
+ pkg->m_routine_implementations.find(name2, type));
+}
+
+
+/**
+ Resolve a qualified routine reference xxx.yyy(), between:
+ - A standalone routine: xxx.yyy
+ - A package routine: current_database.xxx.yyy
+*/
+
+bool Sp_handler::
+ sp_resolve_package_routine_explicit(THD *thd,
+ sp_head *caller,
+ sp_name *name,
+ const Sp_handler **pkg_routine_handler,
+ Database_qualified_name *pkgname) const
+{
+ sp_package *pkg;
+
+ /*
+ If a qualified routine name was used, e.g. xxx.yyy(),
+ we possibly have a call to a package routine.
+ Rewrite name if name->m_db (xxx) is a known package,
+ and name->m_name (yyy) is a known routine in this package.
+ */
+ LEX_CSTRING tmpdb= thd->db;
+ if (is_package_public_routine(thd, tmpdb, name->m_db, name->m_name, type()) ||
+ // Check if a package routine calls a private routine
+ (caller && caller->m_parent &&
+ is_package_body_routine(thd, caller->m_parent,
+ name->m_db, name->m_name, type())) ||
+ // Check if a package initialization sections calls a private routine
+ (caller && (pkg= caller->get_package()) &&
+ is_package_body_routine(thd, pkg, name->m_db, name->m_name, type())))
+ {
+ pkgname->m_db= tmpdb;
+ pkgname->m_name= name->m_db;
+ *pkg_routine_handler= package_routine_handler();
+ return name->make_package_routine_name(thd->mem_root, tmpdb,
+ name->m_db, name->m_name);
+ }
+ return false;
+}
+
+
+/**
+ Resolve a non-qualified routine reference yyy(), between:
+ - A standalone routine: current_database.yyy
+ - A package routine: current_database.current_package.yyy
+*/
+
+bool Sp_handler::
+ sp_resolve_package_routine_implicit(THD *thd,
+ sp_head *caller,
+ sp_name *name,
+ const Sp_handler **pkg_routine_handler,
+ Database_qualified_name *pkgname) const
+{
+ sp_package *pkg;
+
+ if (!caller || !caller->m_name.length)
+ {
+ /*
+ We are either in a an anonymous block,
+ or not in a routine at all.
+ */
+ return false; // A standalone routine is called
+ }
+
+ if (caller->m_parent)
+ {
+ // A package routine calls a non-qualified routine
+ int ret= SP_OK;
+ Prefix_name_buf pkgstr(thd, caller->m_name);
+ DBUG_ASSERT(pkgstr.length);
+ LEX_CSTRING tmpname; // Non-qualified m_name
+ tmpname.str= caller->m_name.str + pkgstr.length + 1;
+ tmpname.length= caller->m_name.length - pkgstr.length - 1;
+
+ /*
+ We're here if a package routine calls another non-qualified
+ function or procedure, e.g. yyy().
+ We need to distinguish two cases:
+ - yyy() is another routine from the same package
+ - yyy() is a standalone routine from the same database
+ To detect if yyy() is a package (rather than a standalone) routine,
+ we check if:
+ - yyy() recursively calls itself
+ - yyy() is earlier implemented in the current CREATE PACKAGE BODY
+ - yyy() has a forward declaration
+ - yyy() is declared in the corresponding CREATE PACKAGE
+ */
+ if (eq_routine_name(tmpname, name->m_name) ||
+ caller->m_parent->m_routine_implementations.find(name->m_name, type()) ||
+ caller->m_parent->m_routine_declarations.find(name->m_name, type()) ||
+ is_package_public_routine_quick(thd, caller->m_db,
+ pkgstr, name->m_name, type()))
+ {
+ DBUG_ASSERT(ret == SP_OK);
+ pkgname->copy(thd->mem_root, caller->m_db, pkgstr);
+ *pkg_routine_handler= package_routine_handler();
+ if (name->make_package_routine_name(thd->mem_root, pkgstr, name->m_name))
+ return true;
+ }
+ return ret != SP_OK;
+ }
+
+ if ((pkg= caller->get_package()) &&
+ pkg->m_routine_implementations.find(name->m_name, type()))
+ {
+ pkgname->m_db= caller->m_db;
+ pkgname->m_name= caller->m_name;
+ // Package initialization section is calling a non-qualified routine
+ *pkg_routine_handler= package_routine_handler();
+ return name->make_package_routine_name(thd->mem_root,
+ caller->m_name, name->m_name);
+ }
+
+ return false; // A standalone routine is called
+
+}
+
+
+/**
+ Detect cases when a package routine (rather than a standalone routine)
+ is called, and rewrite sp_name accordingly.
+
+ @param thd Current thd
+ @param caller The caller routine (or NULL if outside of a routine)
+ @param [IN/OUT] name The called routine name
+ @param [OUT] pkgname If the routine is found to be a package routine,
+ pkgname is populated with the package name.
+ Otherwise, it's not touched.
+ @retval false on success
+ @retval true on error (e.g. EOM, could not read CREATE PACKAGE)
+*/
+
+bool
+Sp_handler::sp_resolve_package_routine(THD *thd,
+ sp_head *caller,
+ sp_name *name,
+ const Sp_handler **pkg_routine_handler,
+ Database_qualified_name *pkgname) const
+{
+ if (!thd->db.length || !(thd->variables.sql_mode & MODE_ORACLE))
+ return false;
+
+ return name->m_explicit_name ?
+ sp_resolve_package_routine_explicit(thd, caller, name,
+ pkg_routine_handler, pkgname) :
+ sp_resolve_package_routine_implicit(thd, caller, name,
+ pkg_routine_handler, pkgname);
+}
+
+
/**
Add routine which is explicitly used by statement to the set of stored
routines used by this statement.
@@ -1945,20 +2588,18 @@ bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena,
@param arena Arena in which memory for new element of the set
will be allocated
@param rt Routine name
- @param rt_type Routine type (one of TYPE_ENUM_PROCEDURE/...)
@note
Will also add element to end of 'Query_tables_list::sroutines_list' list
(and will take into account that this is an explicitly used routine).
*/
-void sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena,
- sp_name *rt, enum stored_procedure_type rt_type)
+void Sp_handler::add_used_routine(Query_tables_list *prelocking_ctx,
+ Query_arena *arena,
+ const Database_qualified_name *rt) const
{
- MDL_key key((rt_type == TYPE_ENUM_FUNCTION) ? MDL_key::FUNCTION :
- MDL_key::PROCEDURE,
- rt->m_db.str, rt->m_name.str);
- (void)sp_add_used_routine(prelocking_ctx, arena, &key, 0);
+ MDL_key key(get_mdl_type(), rt->m_db.str, rt->m_name.str);
+ (void) sp_add_used_routine(prelocking_ctx, arena, &key, this, 0);
prelocking_ctx->sroutines_list_own_last= prelocking_ctx->sroutines_list.next;
prelocking_ctx->sroutines_list_own_elements=
prelocking_ctx->sroutines_list.elements;
@@ -2051,7 +2692,8 @@ sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx,
{
Sroutine_hash_entry *rt= (Sroutine_hash_entry *)my_hash_element(src, i);
(void)sp_add_used_routine(prelocking_ctx, thd->stmt_arena,
- &rt->mdl_request.key, belong_to_view);
+ &rt->mdl_request.key, rt->m_handler,
+ belong_to_view);
}
}
@@ -2076,7 +2718,8 @@ void sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx,
{
for (Sroutine_hash_entry *rt= src->first; rt; rt= rt->next)
(void)sp_add_used_routine(prelocking_ctx, thd->stmt_arena,
- &rt->mdl_request.key, belong_to_view);
+ &rt->mdl_request.key, rt->m_handler,
+ belong_to_view);
}
@@ -2085,24 +2728,21 @@ void sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx,
prelocking until 'sp_name' is eradicated as a class.
*/
-int sp_cache_routine(THD *thd, Sroutine_hash_entry *rt,
- bool lookup_only, sp_head **sp)
+int Sroutine_hash_entry::sp_cache_routine(THD *thd,
+ bool lookup_only,
+ sp_head **sp) const
{
char qname_buff[NAME_LEN*2+1+1];
- sp_name name(&rt->mdl_request.key, qname_buff);
- MDL_key::enum_mdl_namespace mdl_type= rt->mdl_request.key.mdl_namespace();
- stored_procedure_type type= ((mdl_type == MDL_key::FUNCTION) ?
- TYPE_ENUM_FUNCTION : TYPE_ENUM_PROCEDURE);
-
+ sp_name name(&mdl_request.key, qname_buff);
/*
Check that we have an MDL lock on this routine, unless it's a top-level
CALL. The assert below should be unambiguous: the first element
in sroutines_list has an MDL lock unless it's a top-level call, or a
trigger, but triggers can't occur here (see the preceding assert).
*/
- DBUG_ASSERT(rt->mdl_request.ticket || rt == thd->lex->sroutines_list.first);
+ DBUG_ASSERT(mdl_request.ticket || this == thd->lex->sroutines_list.first);
- return sp_cache_routine(thd, type, &name, lookup_only, sp);
+ return m_handler->sp_cache_routine(thd, &name, lookup_only, sp);
}
@@ -2113,7 +2753,6 @@ int sp_cache_routine(THD *thd, Sroutine_hash_entry *rt,
loading.
@param[in] thd Thread context.
- @param[in] type Type of object (TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE).
@param[in] name Name of routine.
@param[in] lookup_only Only check that the routine is in the cache.
If it's not, don't try to load. If it is present,
@@ -2126,16 +2765,17 @@ int sp_cache_routine(THD *thd, Sroutine_hash_entry *rt,
@retval non-0 Error while loading routine from mysql,proc table.
*/
-int sp_cache_routine(THD *thd, enum stored_procedure_type type, sp_name *name,
- bool lookup_only, sp_head **sp)
+int Sp_handler::sp_cache_routine(THD *thd,
+ const Database_qualified_name *name,
+ bool lookup_only,
+ sp_head **sp) const
{
int ret= 0;
- sp_cache **spc= (type == TYPE_ENUM_FUNCTION ?
- &thd->sp_func_cache : &thd->sp_proc_cache);
+ sp_cache **spc= get_cache(thd);
- DBUG_ENTER("sp_cache_routine");
+ DBUG_ENTER("Sp_handler::sp_cache_routine");
- DBUG_ASSERT(type == TYPE_ENUM_FUNCTION || type == TYPE_ENUM_PROCEDURE);
+ DBUG_ASSERT(spc);
*sp= sp_cache_lookup(spc, name);
@@ -2149,10 +2789,9 @@ int sp_cache_routine(THD *thd, enum stored_procedure_type type, sp_name *name,
DBUG_RETURN(SP_OK);
}
- switch ((ret= db_find_routine(thd, type, name, sp)))
+ switch ((ret= db_find_and_cache_routine(thd, name, sp)))
{
case SP_OK:
- sp_cache_insert(spc, *sp);
break;
case SP_KEY_NOT_FOUND:
ret= SP_OK;
@@ -2173,22 +2812,10 @@ int sp_cache_routine(THD *thd, enum stored_procedure_type type, sp_name *name,
an error with it's return value without calling my_error(), we
set the generic "mysql.proc table corrupt" error here.
*/
- if (! thd->is_error())
+ if (!thd->is_error())
{
- /*
- SP allows full NAME_LEN chars thus he have to allocate enough
- size in bytes. Otherwise there is stack overrun could happen
- if multibyte sequence is `name`. `db` is still safe because the
- rest of the server checks agains NAME_LEN bytes and not chars.
- Hence, the overrun happens only if the name is in length > 32 and
- uses multibyte (cyrillic, greek, etc.)
- */
- char n[NAME_LEN*2+2];
-
- /* m_qname.str is not always \0 terminated */
- memcpy(n, name->m_qname.str, name->m_qname.length);
- n[name->m_qname.length]= '\0';
- my_error(ER_SP_PROC_TABLE_CORRUPT, MYF(0), n, ret);
+ my_error(ER_SP_PROC_TABLE_CORRUPT, MYF(0),
+ ErrConvDQName(name).ptr(), ret);
}
break;
}
@@ -2197,59 +2824,138 @@ int sp_cache_routine(THD *thd, enum stored_procedure_type type, sp_name *name,
/**
+ Cache a package routine using its package name and a qualified name.
+ See sp_cache_routine() for more information on parameters and return values.
+
+ @param thd - current THD
+ @param pkgname_str - package name, e.g. "pkgname"
+ @param name - name with the following format:
+ * name->m_db is a database name, e.g. "dbname"
+ * name->m_name is a package-qualified name,
+ e.g. "pkgname.spname"
+ @param lookup_only - don't load mysql.proc if not cached
+ @param [OUT] sp - the result is returned here.
+ @retval false - loaded or does not exists
+ @retval true - error while loading mysql.proc
+*/
+
+int
+Sp_handler::sp_cache_package_routine(THD *thd,
+ const LEX_CSTRING &pkgname_cstr,
+ const Database_qualified_name *name,
+ bool lookup_only, sp_head **sp) const
+{
+ DBUG_ENTER("sp_cache_package_routine");
+ DBUG_ASSERT(type() == TYPE_ENUM_FUNCTION || type() == TYPE_ENUM_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,
+ lookup_only,
+ &ph);
+ if (!ret)
+ {
+ sp_package *pkg= ph ? ph->get_package() : NULL;
+ LEX_CSTRING tmp= name->m_name;
+ const char *dot= strrchr(tmp.str, '.');
+ size_t prefix_length= dot ? dot - tmp.str + 1 : 0;
+ tmp.str+= prefix_length;
+ tmp.length-= prefix_length;
+ LEX *rlex= pkg ? pkg->m_routine_implementations.find(tmp, type()) : NULL;
+ *sp= rlex ? rlex->sphead : NULL;
+ }
+
+ DBUG_RETURN(ret);
+}
+
+
+/**
+ Cache a package routine by its fully qualified name.
+ See sp_cache_routine() for more information on parameters and return values.
+
+ @param thd - current THD
+ @param name - name with the following format:
+ * name->m_db is a database name, e.g. "dbname"
+ * name->m_name is a package-qualified name,
+ e.g. "pkgname.spname"
+ @param lookup_only - don't load mysql.proc if not cached
+ @param [OUT] sp - the result is returned here
+ @retval false - loaded or does not exists
+ @retval true - error while loading mysql.proc
+*/
+
+int Sp_handler::sp_cache_package_routine(THD *thd,
+ const Database_qualified_name *name,
+ bool lookup_only, sp_head **sp) const
+{
+ DBUG_ENTER("Sp_handler::sp_cache_package_routine");
+ Prefix_name_buf pkgname(thd, name->m_name);
+ DBUG_ASSERT(pkgname.length);
+ DBUG_RETURN(sp_cache_package_routine(thd, pkgname, name, lookup_only, sp));
+}
+
+
+/**
Generates the CREATE... string from the table information.
@return
- Returns TRUE on success, FALSE on (alloc) failure.
+ Returns false on success, true on (alloc) failure.
*/
+
bool
-show_create_sp(THD *thd, String *buf,
- stored_procedure_type type,
- const char *db, ulong dblen,
- const char *name, ulong namelen,
- const char *params, ulong paramslen,
- const char *returns, ulong returnslen,
- const char *body, ulong bodylen,
- st_sp_chistics *chistics,
- const LEX_STRING *definer_user,
- const LEX_STRING *definer_host,
- sql_mode_t sql_mode)
+Sp_handler::show_create_sp(THD *thd, String *buf,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &name,
+ const LEX_CSTRING &params,
+ const LEX_CSTRING &returns,
+ const LEX_CSTRING &body,
+ const st_sp_chistics &chistics,
+ const AUTHID &definer,
+ 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;
+
/* Make some room to begin with */
- if (buf->alloc(100 + dblen + 1 + namelen + paramslen + returnslen + bodylen +
- chistics->comment.length + 10 /* length of " DEFINER= "*/ +
- USER_HOST_BUFF_SIZE))
- return FALSE;
+ if (buf->alloc(100 + db.length + 1 + name.length +
+ params.length + returns.length +
+ chistics.comment.length + 10 /* length of " DEFINER= "*/ +
+ agglen + USER_HOST_BUFF_SIZE))
+ return true;
thd->variables.sql_mode= sql_mode;
buf->append(STRING_WITH_LEN("CREATE "));
- if (thd->lex->create_info.or_replace())
+ if (ddl_options.or_replace())
buf->append(STRING_WITH_LEN("OR REPLACE "));
- append_definer(thd, buf, definer_user, definer_host);
- if (type == TYPE_ENUM_FUNCTION)
- buf->append(STRING_WITH_LEN("FUNCTION "));
- else
- buf->append(STRING_WITH_LEN("PROCEDURE "));
- if (thd->lex->create_info.if_not_exists())
+ append_definer(thd, buf, &definer.user, &definer.host);
+ if (chistics.agg_type == GROUP_AGGREGATE)
+ buf->append(STRING_WITH_LEN("AGGREGATE "));
+ tmp= type_lex_cstring();
+ buf->append(&tmp);
+ buf->append(STRING_WITH_LEN(" "));
+ if (ddl_options.if_not_exists())
buf->append(STRING_WITH_LEN("IF NOT EXISTS "));
- if (dblen > 0)
+ if (db.length > 0)
{
- append_identifier(thd, buf, db, dblen);
+ append_identifier(thd, buf, &db);
buf->append('.');
}
- append_identifier(thd, buf, name, namelen);
+ append_identifier(thd, buf, &name);
buf->append('(');
- buf->append(params, paramslen);
+ buf->append(&params);
buf->append(')');
- if (type == TYPE_ENUM_FUNCTION)
+ if (type() == TYPE_ENUM_FUNCTION)
{
- buf->append(STRING_WITH_LEN(" RETURNS "));
- buf->append(returns, returnslen);
+ if (sql_mode & MODE_ORACLE)
+ buf->append(STRING_WITH_LEN(" RETURN "));
+ else
+ buf->append(STRING_WITH_LEN(" RETURNS "));
+ buf->append(&returns);
}
buf->append('\n');
- switch (chistics->daccess) {
+ switch (chistics.daccess) {
case SP_NO_SQL:
buf->append(STRING_WITH_LEN(" NO SQL\n"));
break;
@@ -2264,19 +2970,13 @@ show_create_sp(THD *thd, String *buf,
/* Do nothing */
break;
}
- if (chistics->detistic)
+ if (chistics.detistic)
buf->append(STRING_WITH_LEN(" DETERMINISTIC\n"));
- if (chistics->suid == SP_IS_NOT_SUID)
- buf->append(STRING_WITH_LEN(" SQL SECURITY INVOKER\n"));
- if (chistics->comment.length)
- {
- buf->append(STRING_WITH_LEN(" COMMENT "));
- append_unescaped(buf, chistics->comment.str, chistics->comment.length);
- buf->append('\n');
- }
- buf->append(body, bodylen);
+ 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 TRUE;
+ return false;
}
@@ -2301,28 +3001,19 @@ show_create_sp(THD *thd, String *buf,
*/
sp_head *
-sp_load_for_information_schema(THD *thd, TABLE *proc_table, String *db,
- String *name, sql_mode_t sql_mode,
- stored_procedure_type type,
- const char *returns, const char *params,
- bool *free_sp_head)
+Sp_handler::sp_load_for_information_schema(THD *thd, TABLE *proc_table,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &name,
+ const LEX_CSTRING &params,
+ const LEX_CSTRING &returns,
+ sql_mode_t sql_mode,
+ bool *free_sp_head) const
{
- const char *sp_body;
String defstr;
- struct st_sp_chistics sp_chistics;
- const LEX_STRING definer_user= {(char*)STRING_WITH_LEN("")};
- const LEX_STRING definer_host= {(char*)STRING_WITH_LEN("")};
- LEX_STRING sp_db_str;
- LEX_STRING sp_name_str;
+ const AUTHID definer= {{STRING_WITH_LEN("")}, {STRING_WITH_LEN("")}};
sp_head *sp;
- sp_cache **spc= ((type == TYPE_ENUM_PROCEDURE) ?
- &thd->sp_proc_cache : &thd->sp_func_cache);
- sp_db_str.str= db->c_ptr();
- sp_db_str.length= db->length();
- sp_name_str.str= name->c_ptr();
- sp_name_str.length= name->length();
- sp_name sp_name_obj(sp_db_str, sp_name_str, true);
- sp_name_obj.init_qname(thd);
+ sp_cache **spc= get_cache(thd);
+ sp_name sp_name_obj(&db, &name, true); // This can change "name"
*free_sp_head= 0;
if ((sp= sp_cache_lookup(spc, &sp_name_obj)))
{
@@ -2332,21 +3023,16 @@ sp_load_for_information_schema(THD *thd, TABLE *proc_table, String *db,
LEX *old_lex= thd->lex, newlex;
Stored_program_creation_ctx *creation_ctx=
Stored_routine_creation_ctx::load_from_db(thd, &sp_name_obj, proc_table);
- sp_body= (type == TYPE_ENUM_FUNCTION ? "RETURN NULL" : "BEGIN END");
- bzero((char*) &sp_chistics, sizeof(sp_chistics));
defstr.set_charset(creation_ctx->get_client_cs());
- if (!show_create_sp(thd, &defstr, type,
- sp_db_str.str, sp_db_str.length,
- sp_name_obj.m_name.str, sp_name_obj.m_name.length,
- params, strlen(params),
- returns, strlen(returns),
- sp_body, strlen(sp_body),
- &sp_chistics, &definer_user, &definer_host, sql_mode))
+ if (show_create_sp(thd, &defstr,
+ sp_name_obj.m_db, sp_name_obj.m_name,
+ params, returns, empty_body_lex_cstring(sql_mode),
+ Sp_chistics(), definer, DDL_options(), sql_mode))
return 0;
thd->lex= &newlex;
newlex.current_select= NULL;
- sp= sp_compile(thd, &defstr, sql_mode, creation_ctx);
+ sp= sp_compile(thd, &defstr, sql_mode, NULL, creation_ctx);
*free_sp_head= 1;
thd->lex->sphead= NULL;
lex_end(thd->lex);
@@ -2354,3 +3040,18 @@ sp_load_for_information_schema(THD *thd, TABLE *proc_table, String *db,
return sp;
}
+
+LEX_CSTRING Sp_handler_procedure::empty_body_lex_cstring(sql_mode_t mode) const
+{
+ static LEX_CSTRING m_empty_body_std= {STRING_WITH_LEN("BEGIN END")};
+ static LEX_CSTRING m_empty_body_ora= {STRING_WITH_LEN("AS BEGIN NULL; END")};
+ return mode & MODE_ORACLE ? m_empty_body_ora : m_empty_body_std;
+}
+
+
+LEX_CSTRING Sp_handler_function::empty_body_lex_cstring(sql_mode_t mode) const
+{
+ static LEX_CSTRING m_empty_body_std= {STRING_WITH_LEN("RETURN NULL")};
+ static LEX_CSTRING m_empty_body_ora= {STRING_WITH_LEN("AS BEGIN RETURN NULL; END")};
+ return mode & MODE_ORACLE ? m_empty_body_ora : m_empty_body_std;
+}
diff --git a/sql/sp.h b/sql/sp.h
index 3b979d96413..852fbccafdf 100644
--- a/sql/sp.h
+++ b/sql/sp.h
@@ -17,7 +17,10 @@
#ifndef _SP_H_
#define _SP_H_
+#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_string.h" // LEX_STRING
+#include "sql_cmd.h"
+#include "mdl.h"
class Field;
class Open_tables_backup;
@@ -28,8 +31,12 @@ class Sroutine_hash_entry;
class THD;
class sp_cache;
class sp_head;
+class sp_package;
+class sp_pcontext;
class sp_name;
+class Database_qualified_name;
struct st_sp_chistics;
+class Stored_program_creation_ctx;
struct LEX;
struct TABLE;
struct TABLE_LIST;
@@ -44,10 +51,491 @@ enum stored_procedure_type
{
TYPE_ENUM_FUNCTION=1,
TYPE_ENUM_PROCEDURE=2,
- TYPE_ENUM_TRIGGER=3,
- TYPE_ENUM_PROXY=4
+ TYPE_ENUM_PACKAGE=3,
+ TYPE_ENUM_PACKAGE_BODY=4,
+ TYPE_ENUM_TRIGGER=5,
+ TYPE_ENUM_PROXY=6
};
+
+class Sp_handler
+{
+ bool sp_resolve_package_routine_explicit(THD *thd,
+ sp_head *caller,
+ sp_name *name,
+ const Sp_handler **pkg_routine_hndlr,
+ Database_qualified_name *pkgname)
+ const;
+ bool sp_resolve_package_routine_implicit(THD *thd,
+ sp_head *caller,
+ sp_name *name,
+ const Sp_handler **pkg_routine_hndlr,
+ Database_qualified_name *pkgname)
+ const;
+protected:
+ int db_find_routine_aux(THD *thd, const Database_qualified_name *name,
+ TABLE *table) const;
+ int db_find_routine(THD *thd, const Database_qualified_name *name,
+ sp_head **sphp) const;
+ int db_find_and_cache_routine(THD *thd,
+ const Database_qualified_name *name,
+ sp_head **sp) const;
+ int db_load_routine(THD *thd, const Database_qualified_name *name,
+ sp_head **sphp,
+ sql_mode_t sql_mode,
+ const LEX_CSTRING &params,
+ const LEX_CSTRING &returns,
+ const LEX_CSTRING &body,
+ const st_sp_chistics &chistics,
+ const AUTHID &definer,
+ longlong created, longlong modified,
+ sp_package *parent,
+ Stored_program_creation_ctx *creation_ctx) const;
+ int sp_drop_routine_internal(THD *thd,
+ const Database_qualified_name *name,
+ TABLE *table) const;
+
+ sp_head *sp_clone_and_link_routine(THD *thd,
+ const Database_qualified_name *name,
+ sp_head *sp) const;
+ int sp_cache_package_routine(THD *thd,
+ const LEX_CSTRING &pkgname_cstr,
+ const Database_qualified_name *name,
+ bool lookup_only, sp_head **sp) const;
+ int sp_cache_package_routine(THD *thd,
+ const Database_qualified_name *name,
+ bool lookup_only, sp_head **sp) const;
+ sp_head *sp_find_package_routine(THD *thd,
+ const LEX_CSTRING pkgname_str,
+ const Database_qualified_name *name,
+ bool cache_only) const;
+ sp_head *sp_find_package_routine(THD *thd,
+ const Database_qualified_name *name,
+ bool cache_only) const;
+public: // TODO: make it private or protected
+ virtual int sp_find_and_drop_routine(THD *thd, TABLE *table,
+ const Database_qualified_name *name)
+ const;
+
+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(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)
+ {
+ const Sp_handler *sph= handler(type);
+ return sph ? sph->sp_handler_mysql_proc() : NULL;
+ }
+
+ 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;
+ }
+ const char *type_str() const { return type_lex_cstring().str; }
+ virtual const char *show_create_routine_col1_caption() const
+ {
+ DBUG_ASSERT(0);
+ return "";
+ }
+ virtual const char *show_create_routine_col3_caption() const
+ {
+ DBUG_ASSERT(0);
+ return "";
+ }
+ virtual const Sp_handler *package_routine_handler() const
+ {
+ return this;
+ }
+ virtual stored_procedure_type type() const= 0;
+ virtual LEX_CSTRING type_lex_cstring() const= 0;
+ virtual LEX_CSTRING empty_body_lex_cstring(sql_mode_t mode) const
+ {
+ static LEX_CSTRING m_empty_body= {STRING_WITH_LEN("???")};
+ DBUG_ASSERT(0);
+ return m_empty_body;
+ }
+ virtual MDL_key::enum_mdl_namespace get_mdl_type() const= 0;
+ virtual const Sp_handler *sp_handler_mysql_proc() const { return this; }
+ virtual sp_cache **get_cache(THD *) const { return NULL; }
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ virtual HASH *get_priv_hash() const { return NULL; }
+#endif
+ virtual ulong recursion_depth(THD *thd) const { return 0; }
+ /**
+ Return appropriate error about recursion limit reaching
+
+ @param thd Thread handle
+ @param sp SP routine
+
+ @remark For functions and triggers we return error about
+ prohibited recursion. For stored procedures we
+ return about reaching recursion limit.
+ */
+ virtual void recursion_level_error(THD *thd, const sp_head *sp) const
+ {
+ my_error(ER_SP_NO_RECURSION, MYF(0));
+ }
+ virtual bool add_instr_freturn(THD *thd, sp_head *sp,
+ sp_pcontext *spcont,
+ Item *item, LEX *lex) const;
+ virtual bool add_instr_preturn(THD *thd, sp_head *sp,
+ sp_pcontext *spcont) const;
+
+ void add_used_routine(Query_tables_list *prelocking_ctx,
+ Query_arena *arena,
+ const Database_qualified_name *name) const;
+
+ bool sp_resolve_package_routine(THD *thd,
+ sp_head *caller,
+ sp_name *name,
+ const Sp_handler **pkg_routine_handler,
+ Database_qualified_name *pkgname) const;
+ virtual sp_head *sp_find_routine(THD *thd,
+ const Database_qualified_name *name,
+ bool cache_only) const;
+ virtual int sp_cache_routine(THD *thd, const Database_qualified_name *name,
+ bool lookup_only, sp_head **sp) const;
+
+ int sp_cache_routine_reentrant(THD *thd,
+ const Database_qualified_name *nm,
+ sp_head **sp) const;
+
+ bool sp_exist_routines(THD *thd, TABLE_LIST *procs) const;
+ bool sp_show_create_routine(THD *thd,
+ const Database_qualified_name *name) const;
+
+ bool sp_create_routine(THD *thd, const sp_head *sp) const;
+
+ int sp_update_routine(THD *thd, const Database_qualified_name *name,
+ const st_sp_chistics *chistics) const;
+
+ int sp_drop_routine(THD *thd, const Database_qualified_name *name) const;
+
+ sp_head *sp_load_for_information_schema(THD *thd, TABLE *proc_table,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &name,
+ const LEX_CSTRING &params,
+ const LEX_CSTRING &returns,
+ sql_mode_t sql_mode,
+ bool *free_sp_head) const;
+
+ /*
+ Make a SHOW CREATE statement.
+ @retval true on error
+ @retval false on success
+ */
+ virtual bool show_create_sp(THD *thd, String *buf,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &name,
+ const LEX_CSTRING &params,
+ const LEX_CSTRING &returns,
+ const LEX_CSTRING &body,
+ const st_sp_chistics &chistics,
+ const AUTHID &definer,
+ const DDL_options_st ddl_options,
+ sql_mode_t sql_mode) const;
+
+};
+
+
+class Sp_handler_procedure: public Sp_handler
+{
+public:
+ stored_procedure_type type() const { return TYPE_ENUM_PROCEDURE; }
+ LEX_CSTRING type_lex_cstring() const
+ {
+ static LEX_CSTRING m_type_str= { STRING_WITH_LEN("PROCEDURE")};
+ return m_type_str;
+ }
+ LEX_CSTRING empty_body_lex_cstring(sql_mode_t mode) const;
+ const char *show_create_routine_col1_caption() const
+ {
+ return "Procedure";
+ }
+ const char *show_create_routine_col3_caption() const
+ {
+ return "Create Procedure";
+ }
+ MDL_key::enum_mdl_namespace get_mdl_type() const
+ {
+ return MDL_key::PROCEDURE;
+ }
+ const Sp_handler *package_routine_handler() const;
+ sp_cache **get_cache(THD *) const;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ HASH *get_priv_hash() const;
+#endif
+ ulong recursion_depth(THD *thd) const;
+ void recursion_level_error(THD *thd, const sp_head *sp) const;
+ bool add_instr_preturn(THD *thd, sp_head *sp, sp_pcontext *spcont) const;
+};
+
+
+class Sp_handler_package_procedure: public Sp_handler_procedure
+{
+public:
+ int sp_cache_routine(THD *thd, const Database_qualified_name *name,
+ bool lookup_only, sp_head **sp) const
+ {
+ return sp_cache_package_routine(thd, name, lookup_only, sp);
+ }
+ sp_head *sp_find_routine(THD *thd,
+ const Database_qualified_name *name,
+ bool cache_only) const
+ {
+ return sp_find_package_routine(thd, name, cache_only);
+ }
+};
+
+
+class Sp_handler_function: public Sp_handler
+{
+public:
+ stored_procedure_type type() const { return TYPE_ENUM_FUNCTION; }
+ LEX_CSTRING type_lex_cstring() const
+ {
+ static LEX_CSTRING m_type_str= { STRING_WITH_LEN("FUNCTION")};
+ return m_type_str;
+ }
+ LEX_CSTRING empty_body_lex_cstring(sql_mode_t mode) const;
+ const char *show_create_routine_col1_caption() const
+ {
+ return "Function";
+ }
+ const char *show_create_routine_col3_caption() const
+ {
+ return "Create Function";
+ }
+ MDL_key::enum_mdl_namespace get_mdl_type() const
+ {
+ return MDL_key::FUNCTION;
+ }
+ const Sp_handler *package_routine_handler() const;
+ sp_cache **get_cache(THD *) const;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ HASH *get_priv_hash() const;
+#endif
+ bool add_instr_freturn(THD *thd, sp_head *sp, sp_pcontext *spcont,
+ Item *item, LEX *lex) const;
+};
+
+
+class Sp_handler_package_function: public Sp_handler_function
+{
+public:
+ int sp_cache_routine(THD *thd, const Database_qualified_name *name,
+ bool lookup_only, sp_head **sp) const
+ {
+ return sp_cache_package_routine(thd, name, lookup_only, sp);
+ }
+ sp_head *sp_find_routine(THD *thd,
+ const Database_qualified_name *name,
+ bool cache_only) const
+ {
+ return sp_find_package_routine(thd, name, cache_only);
+ }
+};
+
+
+class Sp_handler_package: public Sp_handler
+{
+public:
+ bool show_create_sp(THD *thd, String *buf,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &name,
+ const LEX_CSTRING &params,
+ const LEX_CSTRING &returns,
+ const LEX_CSTRING &body,
+ const st_sp_chistics &chistics,
+ const AUTHID &definer,
+ const DDL_options_st ddl_options,
+ sql_mode_t sql_mode) const;
+};
+
+
+class Sp_handler_package_spec: public Sp_handler_package
+{
+public: // TODO: make it private or protected
+ int sp_find_and_drop_routine(THD *thd, TABLE *table,
+ const Database_qualified_name *name)
+ const;
+public:
+ stored_procedure_type type() const { return TYPE_ENUM_PACKAGE; }
+ LEX_CSTRING type_lex_cstring() const
+ {
+ static LEX_CSTRING m_type_str= {STRING_WITH_LEN("PACKAGE")};
+ return m_type_str;
+ }
+ LEX_CSTRING empty_body_lex_cstring(sql_mode_t mode) const
+ {
+ static LEX_CSTRING m_empty_body= {STRING_WITH_LEN("BEGIN END")};
+ return m_empty_body;
+ }
+ const char *show_create_routine_col1_caption() const
+ {
+ return "Package";
+ }
+ const char *show_create_routine_col3_caption() const
+ {
+ return "Create Package";
+ }
+ MDL_key::enum_mdl_namespace get_mdl_type() const
+ {
+ return MDL_key::PACKAGE_BODY;
+ }
+ sp_cache **get_cache(THD *) const;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ HASH *get_priv_hash() const;
+#endif
+};
+
+
+class Sp_handler_package_body: public Sp_handler_package
+{
+public:
+ stored_procedure_type type() const { return TYPE_ENUM_PACKAGE_BODY; }
+ LEX_CSTRING type_lex_cstring() const
+ {
+ static LEX_CSTRING m_type_str= {STRING_WITH_LEN("PACKAGE BODY")};
+ return m_type_str;
+ }
+ LEX_CSTRING empty_body_lex_cstring(sql_mode_t mode) const
+ {
+ static LEX_CSTRING m_empty_body= {STRING_WITH_LEN("BEGIN END")};
+ return m_empty_body;
+ }
+ const char *show_create_routine_col1_caption() const
+ {
+ return "Package body";
+ }
+ const char *show_create_routine_col3_caption() const
+ {
+ return "Create Package Body";
+ }
+ MDL_key::enum_mdl_namespace get_mdl_type() const
+ {
+ return MDL_key::PACKAGE_BODY;
+ }
+ sp_cache **get_cache(THD *) const;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ HASH *get_priv_hash() const;
+#endif
+};
+
+
+class Sp_handler_trigger: public Sp_handler
+{
+public:
+ stored_procedure_type type() const { return TYPE_ENUM_TRIGGER; }
+ LEX_CSTRING type_lex_cstring() const
+ {
+ static LEX_CSTRING m_type_str= { STRING_WITH_LEN("TRIGGER")};
+ return m_type_str;
+ }
+ MDL_key::enum_mdl_namespace get_mdl_type() const
+ {
+ DBUG_ASSERT(0);
+ return MDL_key::TRIGGER;
+ }
+ const Sp_handler *sp_handler_mysql_proc() const { return NULL; }
+};
+
+
+extern MYSQL_PLUGIN_IMPORT Sp_handler_function sp_handler_function;
+extern MYSQL_PLUGIN_IMPORT Sp_handler_procedure sp_handler_procedure;
+extern MYSQL_PLUGIN_IMPORT Sp_handler_package_spec sp_handler_package_spec;
+extern MYSQL_PLUGIN_IMPORT Sp_handler_package_body sp_handler_package_body;
+extern MYSQL_PLUGIN_IMPORT Sp_handler_package_function sp_handler_package_function;
+extern MYSQL_PLUGIN_IMPORT Sp_handler_package_procedure sp_handler_package_procedure;
+extern MYSQL_PLUGIN_IMPORT Sp_handler_trigger sp_handler_trigger;
+
+
+inline const Sp_handler *Sp_handler::handler(enum_sql_command cmd)
+{
+ switch (cmd) {
+ case SQLCOM_CREATE_PROCEDURE:
+ case SQLCOM_ALTER_PROCEDURE:
+ case SQLCOM_DROP_PROCEDURE:
+ case SQLCOM_SHOW_PROC_CODE:
+ case SQLCOM_SHOW_CREATE_PROC:
+ case SQLCOM_SHOW_STATUS_PROC:
+ return &sp_handler_procedure;
+ case SQLCOM_CREATE_SPFUNCTION:
+ case SQLCOM_ALTER_FUNCTION:
+ case SQLCOM_DROP_FUNCTION:
+ case SQLCOM_SHOW_FUNC_CODE:
+ case SQLCOM_SHOW_CREATE_FUNC:
+ case SQLCOM_SHOW_STATUS_FUNC:
+ return &sp_handler_function;
+ case SQLCOM_CREATE_PACKAGE:
+ case SQLCOM_DROP_PACKAGE:
+ case SQLCOM_SHOW_CREATE_PACKAGE:
+ case SQLCOM_SHOW_STATUS_PACKAGE:
+ return &sp_handler_package_spec;
+ case SQLCOM_CREATE_PACKAGE_BODY:
+ case SQLCOM_DROP_PACKAGE_BODY:
+ case SQLCOM_SHOW_CREATE_PACKAGE_BODY:
+ case SQLCOM_SHOW_STATUS_PACKAGE_BODY:
+ case SQLCOM_SHOW_PACKAGE_BODY_CODE:
+ return &sp_handler_package_body;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+
+inline const Sp_handler *Sp_handler::handler(stored_procedure_type type)
+{
+ switch (type) {
+ case TYPE_ENUM_PROCEDURE:
+ return &sp_handler_procedure;
+ case TYPE_ENUM_FUNCTION:
+ return &sp_handler_function;
+ case TYPE_ENUM_PACKAGE:
+ return &sp_handler_package_spec;
+ case TYPE_ENUM_PACKAGE_BODY:
+ return &sp_handler_package_body;
+ case TYPE_ENUM_TRIGGER:
+ return &sp_handler_trigger;
+ case TYPE_ENUM_PROXY:
+ break;
+ }
+ return NULL;
+}
+
+
+inline const Sp_handler *Sp_handler::handler(MDL_key::enum_mdl_namespace type)
+{
+ switch (type) {
+ case MDL_key::FUNCTION:
+ return &sp_handler_function;
+ case MDL_key::PROCEDURE:
+ return &sp_handler_procedure;
+ case MDL_key::PACKAGE_BODY:
+ return &sp_handler_package_body;
+ case MDL_key::GLOBAL:
+ case MDL_key::SCHEMA:
+ case MDL_key::TABLE:
+ case MDL_key::TRIGGER:
+ case MDL_key::EVENT:
+ case MDL_key::COMMIT:
+ case MDL_key::USER_LOCK:
+ case MDL_key::NAMESPACE_END:
+ break;
+ }
+ return NULL;
+}
+
+
/* Tells what SP_DEFAULT_ACCESS should be mapped to */
#define SP_DEFAULT_ACCESS_MAPPING SP_CONTAINS_SQL
@@ -88,12 +576,13 @@ enum
MYSQL_PROC_FIELD_COLLATION_CONNECTION,
MYSQL_PROC_FIELD_DB_COLLATION,
MYSQL_PROC_FIELD_BODY_UTF8,
+ MYSQL_PROC_FIELD_AGGREGATE,
MYSQL_PROC_FIELD_COUNT
};
/* Drop all routines in database 'db' */
int
-sp_drop_db_routines(THD *thd, char *db);
+sp_drop_db_routines(THD *thd, const char *db);
/**
Acquires exclusive metadata lock on all stored routines in the
@@ -105,37 +594,7 @@ sp_drop_db_routines(THD *thd, char *db);
@retval false Success
@retval true Failure
*/
-bool lock_db_routines(THD *thd, char *db);
-
-sp_head *
-sp_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
- sp_cache **cp, bool cache_only);
-
-int
-sp_cache_routine(THD *thd, Sroutine_hash_entry *rt,
- bool lookup_only, sp_head **sp);
-
-
-int
-sp_cache_routine(THD *thd, stored_procedure_type type, sp_name *name,
- bool lookup_only, sp_head **sp);
-
-bool
-sp_exist_routines(THD *thd, TABLE_LIST *procs, bool is_proc);
-
-bool
-sp_show_create_routine(THD *thd, stored_procedure_type type, sp_name *name);
-
-bool
-sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp);
-
-int
-sp_update_routine(THD *thd, stored_procedure_type type, sp_name *name,
- st_sp_chistics *chistics);
-
-int
-sp_drop_routine(THD *thd, stored_procedure_type type, sp_name *name);
-
+bool lock_db_routines(THD *thd, const char *db);
/**
Structure that represents element in the set of stored routines
@@ -171,16 +630,17 @@ public:
changes.
*/
ulong m_sp_cache_version;
+
+ const Sp_handler *m_handler;
+
+ int sp_cache_routine(THD *thd, bool lookup_only, sp_head **sp) const;
};
-/*
- Procedures for handling sets of stored routines used by statement or routine.
-*/
-void sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena,
- sp_name *rt, stored_procedure_type rt_type);
bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena,
- const MDL_key *key, TABLE_LIST *belong_to_view);
+ const MDL_key *key,
+ const Sp_handler *handler,
+ TABLE_LIST *belong_to_view);
void sp_remove_not_own_routines(Query_tables_list *prelocking_ctx);
bool sp_update_sp_used_routines(HASH *dst, HASH *src);
void sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx,
@@ -198,13 +658,6 @@ extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen,
*/
TABLE *open_proc_table_for_read(THD *thd, Open_tables_backup *backup);
-sp_head *
-sp_load_for_information_schema(THD *thd, TABLE *proc_table, String *db,
- String *name, sql_mode_t sql_mode,
- stored_procedure_type type,
- const char *returns, const char *params,
- bool *free_sp_head);
-
bool load_charset(MEM_ROOT *mem_root,
Field *field,
CHARSET_INFO *dflt_cs,
@@ -217,17 +670,6 @@ bool load_collation(MEM_ROOT *mem_root,
void sp_returns_type(THD *thd,
String &result,
- sp_head *sp);
-
-bool show_create_sp(THD *thd, String *buf,
- stored_procedure_type type,
- const char *db, ulong dblen,
- const char *name, ulong namelen,
- const char *params, ulong paramslen,
- const char *returns, ulong returnslen,
- const char *body, ulong bodylen,
- st_sp_chistics *chistics,
- const LEX_STRING *definer_user,
- const LEX_STRING *definer_host,
- sql_mode_t sql_mode);
+ const sp_head *sp);
+
#endif /* _SP_H_ */
diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc
index bc91634eb32..e4ffbdcb155 100644
--- a/sql/sp_cache.cc
+++ b/sql/sp_cache.cc
@@ -13,7 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#ifdef USE_PRAGMA_IMPLEMENTATION
@@ -23,7 +23,7 @@
#include "sp_head.h"
static mysql_mutex_t Cversion_lock;
-static ulong volatile Cversion= 0;
+static ulong volatile Cversion= 1;
/*
@@ -48,7 +48,7 @@ public:
return my_hash_insert(&m_hashtable, (const uchar *)sp);
}
- inline sp_head *lookup(char *name, uint namelen)
+ inline sp_head *lookup(char *name, size_t namelen)
{
return (sp_head *) my_hash_search(&m_hashtable, (const uchar *)name,
namelen);
@@ -167,8 +167,7 @@ void sp_cache_insert(sp_cache **cp, sp_head *sp)
}
/* Reading a ulong variable with no lock. */
sp->set_sp_cache_version(Cversion);
- DBUG_PRINT("info",("sp_cache: inserting: %.*s", (int) sp->m_qname.length,
- sp->m_qname.str));
+ DBUG_PRINT("info",("sp_cache: inserting: %s", ErrConvDQName(sp).ptr()));
c->insert(sp);
*cp= c; // Update *cp if it was NULL
}
@@ -190,12 +189,13 @@ void sp_cache_insert(sp_cache **cp, sp_head *sp)
NULL if the routine not found.
*/
-sp_head *sp_cache_lookup(sp_cache **cp, sp_name *name)
+sp_head *sp_cache_lookup(sp_cache **cp, const Database_qualified_name *name)
{
+ char buf[NAME_LEN * 2 + 2];
sp_cache *c= *cp;
if (! c)
return NULL;
- return c->lookup(name->m_qname.str, name->m_qname.length);
+ return c->lookup(buf, name->make_qname(buf, sizeof(buf)));
}
@@ -238,7 +238,6 @@ void sp_cache_flush_obsolete(sp_cache **cp, sp_head **sp)
}
}
-
/**
Return the current global version of the cache.
*/
diff --git a/sql/sp_cache.h b/sql/sp_cache.h
index 607495666fc..7506edff814 100644
--- a/sql/sp_cache.h
+++ b/sql/sp_cache.h
@@ -21,8 +21,6 @@
#pragma interface /* gcc class implementation */
#endif
-#include "my_global.h" /* ulong */
-
/*
Stored procedures/functions cache. This is used as follows:
* Each thread has its own cache.
@@ -32,7 +30,7 @@
class sp_head;
class sp_cache;
-class sp_name;
+class Database_qualified_name;
/*
Cache usage scenarios:
@@ -59,7 +57,7 @@ void sp_cache_init();
void sp_cache_end();
void sp_cache_clear(sp_cache **cp);
void sp_cache_insert(sp_cache **cp, sp_head *sp);
-sp_head *sp_cache_lookup(sp_cache **cp, sp_name *name);
+sp_head *sp_cache_lookup(sp_cache **cp, const Database_qualified_name *name);
void sp_cache_invalidate();
void sp_cache_flush_obsolete(sp_cache **cp, sp_head **sp);
ulong sp_cache_version();
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 6a650183fb8..59a304deb99 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -15,7 +15,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
#include "unireg.h"
#include "sql_prepare.h"
@@ -23,13 +23,12 @@
#include "probes_mysql.h"
#include "sql_show.h" // append_identifier
#include "sql_db.h" // mysql_opt_change_db, mysql_change_db
-#include "sql_table.h" // sp_prepare_create_field,
- // prepare_create_field
#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
#include "sql_cte.h"
+#include "sql_select.h" // Virtual_tmp_table
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation
@@ -76,8 +75,12 @@ static void reset_start_time_for_sp(THD *thd)
Item::Type
-sp_map_item_type(enum enum_field_types type)
+sp_map_item_type(const Type_handler *handler)
{
+ if (handler == &type_handler_row)
+ return Item::ROW_ITEM;
+ enum_field_types type= real_type_to_type(handler->real_field_type());
+
switch (type) {
case MYSQL_TYPE_BIT:
case MYSQL_TYPE_TINY:
@@ -98,68 +101,47 @@ sp_map_item_type(enum enum_field_types type)
}
-/**
- Return a string representation of the Item value.
-
- @param thd thread handle
- @param str string buffer for representation of the value
+bool Item_splocal::append_for_log(THD *thd, String *str)
+{
+ if (fix_fields_if_needed(thd, NULL))
+ return true;
- @note
- If the item has a string result type, the string is escaped
- according to its character set.
+ if (limit_clause_param)
+ return str->append_ulonglong(val_uint());
- @retval
- NULL on error
- @retval
- non-NULL a pointer to valid a valid string on success
-*/
+ /*
+ ROW variables are currently not allowed in select_list, e.g.:
+ SELECT row_variable;
+ ROW variables can appear in query parts where name is not important, e.g.:
+ SELECT ROW(1,2)=row_variable FROM t1;
+ So we can skip using NAME_CONST() and use ROW() constants directly.
+ */
+ if (type_handler() == &type_handler_row)
+ return append_value_for_log(thd, str);
-static String *
-sp_get_item_value(THD *thd, Item *item, String *str)
-{
- switch (item->result_type()) {
- case REAL_RESULT:
- case INT_RESULT:
- case DECIMAL_RESULT:
- if (item->field_type() != MYSQL_TYPE_BIT)
- return item->val_str(str);
- /* fall through */
- case STRING_RESULT:
- {
- String *result= item->val_str(str);
+ if (str->append(STRING_WITH_LEN(" NAME_CONST('")) ||
+ str->append(&m_name) ||
+ str->append(STRING_WITH_LEN("',")))
+ return true;
+ return append_value_for_log(thd, str) || str->append(')');
+}
- if (!result)
- return NULL;
- {
- StringBuffer<STRING_BUFFER_USUAL_SIZE> buf(result->charset());
- CHARSET_INFO *cs= thd->variables.character_set_client;
-
- buf.append('_');
- buf.append(result->charset()->csname);
- if (cs->escape_with_backslash_is_dangerous)
- buf.append(' ');
- append_query_string(cs, &buf, result->ptr(), result->length(),
- thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES);
- buf.append(" COLLATE '");
- buf.append(item->collation.collation->name);
- buf.append('\'');
- str->copy(buf);
-
- return str;
- }
- }
-
- case ROW_RESULT:
- default:
- return NULL;
- }
+bool Item_splocal::append_value_for_log(THD *thd, String *str)
+{
+ StringBuffer<STRING_BUFFER_USUAL_SIZE> str_value_holder(&my_charset_latin1);
+ Item *item= this_item();
+ String *str_value= item->type_handler()->print_item_value(thd, item,
+ &str_value_holder);
+ return str_value ?
+ str->append(*str_value) :
+ str->append(STRING_WITH_LEN("NULL"));
}
-bool Item_splocal::append_for_log(THD *thd, String *str)
+bool Item_splocal_row_field::append_for_log(THD *thd, String *str)
{
- if (fix_fields(thd, NULL))
+ if (fix_fields_if_needed(thd, NULL))
return true;
if (limit_clause_param)
@@ -167,15 +149,11 @@ bool Item_splocal::append_for_log(THD *thd, String *str)
if (str->append(STRING_WITH_LEN(" NAME_CONST('")) ||
str->append(&m_name) ||
+ str->append(".") ||
+ str->append(&m_field_name) ||
str->append(STRING_WITH_LEN("',")))
return true;
-
- StringBuffer<STRING_BUFFER_USUAL_SIZE> str_value_holder(&my_charset_latin1);
- String *str_value= sp_get_item_value(thd, this_item(), &str_value_holder);
- if (str_value)
- return str->append(*str_value) || str->append(')');
- else
- return str->append(STRING_WITH_LEN("NULL)"));
+ return append_value_for_log(thd, str) || str->append(')');
}
@@ -218,6 +196,8 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_SHOW_CREATE_DB:
case SQLCOM_SHOW_CREATE_FUNC:
case SQLCOM_SHOW_CREATE_PROC:
+ case SQLCOM_SHOW_CREATE_PACKAGE:
+ case SQLCOM_SHOW_CREATE_PACKAGE_BODY:
case SQLCOM_SHOW_CREATE_EVENT:
case SQLCOM_SHOW_CREATE_TRIGGER:
case SQLCOM_SHOW_CREATE_USER:
@@ -238,11 +218,14 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_SHOW_PRIVILEGES:
case SQLCOM_SHOW_PROCESSLIST:
case SQLCOM_SHOW_PROC_CODE:
+ case SQLCOM_SHOW_PACKAGE_BODY_CODE:
case SQLCOM_SHOW_SLAVE_HOSTS:
case SQLCOM_SHOW_SLAVE_STAT:
case SQLCOM_SHOW_STATUS:
case SQLCOM_SHOW_STATUS_FUNC:
case SQLCOM_SHOW_STATUS_PROC:
+ case SQLCOM_SHOW_STATUS_PACKAGE:
+ case SQLCOM_SHOW_STATUS_PACKAGE_BODY:
case SQLCOM_SHOW_STORAGE_ENGINES:
case SQLCOM_SHOW_TABLES:
case SQLCOM_SHOW_TABLE_STATUS:
@@ -266,12 +249,14 @@ sp_get_flags_for_command(LEX *lex)
flags= sp_head::CONTAINS_DYNAMIC_SQL;
break;
case SQLCOM_CREATE_TABLE:
+ case SQLCOM_CREATE_SEQUENCE:
if (lex->tmp_table())
flags= 0;
else
flags= sp_head::HAS_COMMIT_OR_ROLLBACK;
break;
case SQLCOM_DROP_TABLE:
+ case SQLCOM_DROP_SEQUENCE:
if (lex->tmp_table())
flags= 0;
else
@@ -285,11 +270,14 @@ sp_get_flags_for_command(LEX *lex)
break;
case SQLCOM_CREATE_INDEX:
case SQLCOM_CREATE_DB:
+ case SQLCOM_CREATE_PACKAGE:
+ case SQLCOM_CREATE_PACKAGE_BODY:
case SQLCOM_CREATE_VIEW:
case SQLCOM_CREATE_TRIGGER:
case SQLCOM_CREATE_USER:
case SQLCOM_CREATE_ROLE:
case SQLCOM_ALTER_TABLE:
+ case SQLCOM_ALTER_SEQUENCE:
case SQLCOM_ALTER_USER:
case SQLCOM_GRANT:
case SQLCOM_GRANT_ROLE:
@@ -300,6 +288,8 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_RENAME_USER:
case SQLCOM_DROP_INDEX:
case SQLCOM_DROP_DB:
+ case SQLCOM_DROP_PACKAGE:
+ case SQLCOM_DROP_PACKAGE_BODY:
case SQLCOM_REVOKE_ALL:
case SQLCOM_DROP_USER:
case SQLCOM_DROP_ROLE:
@@ -362,8 +352,8 @@ sp_get_flags_for_command(LEX *lex)
/**
Prepare an Item for evaluation (call of fix_fields).
- @param thd thread handler
@param it_addr pointer on item refernce
+ @param cols expected number of elements (1 for scalar, >=1 for ROWs)
@retval
NULL error
@@ -371,15 +361,31 @@ sp_get_flags_for_command(LEX *lex)
non-NULL prepared item
*/
-Item *
-sp_prepare_func_item(THD* thd, Item **it_addr)
+Item *THD::sp_prepare_func_item(Item **it_addr, uint cols)
+{
+ DBUG_ENTER("THD::sp_prepare_func_item");
+ Item *res= sp_fix_func_item(it_addr);
+ if (res && res->check_cols(cols))
+ DBUG_RETURN(NULL);
+ DBUG_RETURN(res);
+}
+
+
+/**
+ Fix an Item for evaluation for SP.
+*/
+
+Item *THD::sp_fix_func_item(Item **it_addr)
{
- DBUG_ENTER("sp_prepare_func_item");
- it_addr= (*it_addr)->this_item_addr(thd, it_addr);
+ DBUG_ENTER("THD::sp_fix_func_item");
+ if ((*it_addr)->fix_fields_if_needed(this, it_addr))
+ {
+ DBUG_PRINT("info", ("fix_fields() failed"));
+ DBUG_RETURN(NULL);
+ }
+ it_addr= (*it_addr)->this_item_addr(this, it_addr);
- if (!(*it_addr)->fixed &&
- ((*it_addr)->fix_fields(thd, it_addr) ||
- (*it_addr)->check_cols(1)))
+ if ((*it_addr)->fix_fields_if_needed(this, it_addr))
{
DBUG_PRINT("info", ("fix_fields() failed"));
DBUG_RETURN(NULL);
@@ -391,7 +397,6 @@ sp_prepare_func_item(THD* thd, Item **it_addr)
/**
Evaluate an expression and store the result in the field.
- @param thd current thread object
@param result_field the field to store the result
@param expr_item_ptr the root item of the expression
@@ -401,54 +406,13 @@ sp_prepare_func_item(THD* thd, Item **it_addr)
TRUE on error
*/
-bool
-sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr)
+bool THD::sp_eval_expr(Field *result_field, Item **expr_item_ptr)
{
- Item *expr_item;
- enum_check_fields save_count_cuted_fields= thd->count_cuted_fields;
- bool save_abort_on_warning= thd->abort_on_warning;
- bool save_stmt_modified_non_trans_table=
- thd->transaction.stmt.modified_non_trans_table;
-
- DBUG_ENTER("sp_eval_expr");
-
- if (!*expr_item_ptr)
- goto error;
-
- if (!(expr_item= sp_prepare_func_item(thd, expr_item_ptr)))
- goto error;
-
- /*
- Set THD flags to emit warnings/errors in case of overflow/type errors
- during saving the item into the field.
-
- Save original values and restore them after save.
- */
-
- thd->count_cuted_fields= CHECK_FIELD_ERROR_FOR_NULL;
- thd->abort_on_warning= thd->is_strict_mode();
- thd->transaction.stmt.modified_non_trans_table= FALSE;
-
+ DBUG_ENTER("THD::sp_eval_expr");
+ DBUG_ASSERT(*expr_item_ptr);
+ Sp_eval_expr_state state(this);
/* Save the value in the field. Convert the value if needed. */
-
- expr_item->save_in_field(result_field, 0);
-
- thd->count_cuted_fields= save_count_cuted_fields;
- thd->abort_on_warning= save_abort_on_warning;
- thd->transaction.stmt.modified_non_trans_table= save_stmt_modified_non_trans_table;
-
- if (!thd->is_error())
- DBUG_RETURN(FALSE);
-
-error:
- /*
- In case of error during evaluation, leave the result field set to NULL.
- Sic: we can't do it in the beginning of the function because the
- result field might be needed for its own re-evaluation, e.g. case of
- set x = x + 1;
- */
- result_field->set_null();
- DBUG_RETURN (TRUE);
+ DBUG_RETURN(result_field->sp_prepare_and_store_item(this, expr_item_ptr));
}
@@ -465,42 +429,14 @@ error:
*/
sp_name::sp_name(const MDL_key *key, char *qname_buff)
+ :Database_qualified_name(key->db_name(), key->db_name_length(),
+ key->name(), key->name_length()),
+ m_explicit_name(false)
{
- m_db.str= (char*)key->db_name();
- m_db.length= key->db_name_length();
- m_name.str= (char*)key->name();
- m_name.length= key->name_length();
- m_qname.str= qname_buff;
if (m_db.length)
- {
strxmov(qname_buff, m_db.str, ".", m_name.str, NullS);
- m_qname.length= m_db.length + 1 + m_name.length;
- }
else
- {
strmov(qname_buff, m_name.str);
- m_qname.length= m_name.length;
- }
- m_explicit_name= false;
-}
-
-
-/**
- Init the qualified name from the db and name.
-*/
-void
-sp_name::init_qname(THD *thd)
-{
- const uint dot= !!m_db.length;
- /* m_qname format: [database + dot] + name + '\0' */
- m_qname.length= m_db.length + dot + m_name.length;
- if (!(m_qname.str= (char*) thd->alloc(m_qname.length + 1)))
- return;
- sprintf(m_qname.str, "%.*s%.*s%.*s",
- (int) m_db.length, (m_db.length ? m_db.str : ""),
- dot, ".",
- (int) m_name.length, m_name.str);
- DBUG_ASSERT(ok_for_lower_case_names(m_db.str));
}
@@ -518,7 +454,7 @@ sp_name::init_qname(THD *thd)
*/
bool
-check_routine_name(LEX_STRING *ident)
+check_routine_name(const LEX_CSTRING *ident)
{
DBUG_ASSERT(ident);
DBUG_ASSERT(ident->str);
@@ -535,12 +471,19 @@ check_routine_name(LEX_STRING *ident)
}
-sp_head* sp_head::create()
+/*
+ *
+ * sp_head
+ *
+ */
+
+sp_head *sp_head::create(sp_package *parent, const Sp_handler *handler)
{
MEM_ROOT own_root;
- init_sql_alloc(&own_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC, MYF(0));
+ init_sql_alloc(&own_root, "sp_head", MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC,
+ MYF(0));
sp_head *sp;
- if (!(sp= new (&own_root) sp_head(&own_root)))
+ if (!(sp= new (&own_root) sp_head(&own_root, parent, handler)))
free_root(&own_root, MYF(0));
return sp;
@@ -554,8 +497,10 @@ void sp_head::destroy(sp_head *sp)
/* Make a copy of main_mem_root as free_root will free the sp */
MEM_ROOT own_root= sp->main_mem_root;
DBUG_PRINT("info", ("mem_root %p moved to %p",
- &sp->main_mem_root, &own_root));
+ &sp->mem_root, &own_root));
delete sp;
+
+
free_root(&own_root, MYF(0));
}
}
@@ -566,15 +511,39 @@ void sp_head::destroy(sp_head *sp)
*
*/
-sp_head::sp_head(MEM_ROOT *mem_root_arg)
+sp_head::sp_head(MEM_ROOT *mem_root_arg, sp_package *parent,
+ const Sp_handler *sph)
:Query_arena(NULL, STMT_INITIALIZED_FOR_SP),
- main_mem_root(*mem_root_arg), // todo: std::move operator.
+ Database_qualified_name(&null_clex_str, &null_clex_str),
+ main_mem_root(*mem_root_arg),
+ m_parent(parent),
+ m_handler(sph),
m_flags(0),
+ m_tmp_query(NULL),
+ m_explicit_name(false),
+ /*
+ FIXME: the only use case when name is NULL is events, and it should
+ be rewritten soon. Remove the else part and replace 'if' with
+ an assert when this is done.
+ */
+ m_qname(null_clex_str),
+ m_params(null_clex_str),
+ m_body(null_clex_str),
+ m_body_utf8(null_clex_str),
+ m_defstr(null_clex_str),
m_sp_cache_version(0),
m_creation_ctx(0),
unsafe_flags(0),
+ m_created(0),
+ m_modified(0),
m_recursion_level(0),
m_next_cached_sp(0),
+ m_param_begin(NULL),
+ m_param_end(NULL),
+ m_body_begin(NULL),
+ m_thd_root(NULL),
+ m_thd(NULL),
+ m_pcont(new (&main_mem_root) sp_pcontext()),
m_cont_level(0)
{
mem_root= &main_mem_root;
@@ -584,80 +553,218 @@ sp_head::sp_head(MEM_ROOT *mem_root_arg)
m_last_cached_sp= this;
m_return_field_def.charset = NULL;
- /*
- FIXME: the only use case when name is NULL is events, and it should
- be rewritten soon. Remove the else part and replace 'if' with
- an assert when this is done.
- */
- m_db= m_name= m_qname= null_lex_str;
DBUG_ENTER("sp_head::sp_head");
m_security_ctx.init();
m_backpatch.empty();
+ 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_body_utf8.str= NULL;
- m_body_utf8.length= 0;
-
DBUG_VOID_RETURN;
}
-void
-sp_head::init(LEX *lex)
+sp_package *sp_package::create(LEX *top_level_lex, const sp_name *name,
+ const Sp_handler *sph)
{
- DBUG_ENTER("sp_head::init");
+ MEM_ROOT own_root;
+ init_sql_alloc(&own_root, "sp_package", 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)))
+ free_root(&own_root, MYF(0));
+
+ return sp;
+}
- lex->spcont= m_pcont= new sp_pcontext();
- if (!lex->spcont)
- DBUG_VOID_RETURN;
+sp_package::sp_package(MEM_ROOT *mem_root_arg,
+ LEX *top_level_lex,
+ const sp_name *name,
+ const Sp_handler *sph)
+ :sp_head(mem_root_arg, NULL, sph),
+ m_current_routine(NULL),
+ m_top_level_lex(top_level_lex),
+ m_rcontext(NULL),
+ m_invoked_subroutine_count(0),
+ m_is_instantiated(false),
+ m_is_cloning_routine(false)
+{
+ init_sp_name(name);
+}
+
+
+sp_package::~sp_package()
+{
+ m_routine_implementations.cleanup();
+ m_routine_declarations.cleanup();
+ m_body= null_clex_str;
+ if (m_current_routine)
+ sp_head::destroy(m_current_routine->sphead);
+ delete m_rcontext;
+}
+
+
+/*
+ Test if two routines have equal specifications
+*/
+
+bool sp_head::eq_routine_spec(const sp_head *sp) const
+{
+ // TODO: Add tests for equal return data types (in case of FUNCTION)
+ // TODO: Add tests for equal argument data types
+ return
+ m_handler->type() == sp->m_handler->type() &&
+ m_pcont->context_var_count() == sp->m_pcont->context_var_count();
+}
+
+bool sp_package::validate_after_parser(THD *thd)
+{
+ if (m_handler->type() != TYPE_ENUM_PACKAGE_BODY)
+ return false;
+ sp_head *sp= sp_cache_lookup(&thd->sp_package_spec_cache, this);
+ sp_package *spec= sp ? sp->get_package() : NULL;
+ DBUG_ASSERT(spec); // CREATE PACKAGE must already be cached
+ return validate_public_routines(thd, spec) ||
+ validate_private_routines(thd);
+}
+
+
+bool sp_package::validate_public_routines(THD *thd, sp_package *spec)
+{
/*
- Altough trg_table_fields list is used only in triggers we init for all
- types of stored procedures to simplify reset_lex()/restore_lex() code.
+ Check that all routines declared in CREATE PACKAGE
+ have implementations in CREATE PACKAGE BODY.
*/
- lex->trg_table_fields.empty();
- my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8, MYF(0));
+ List_iterator<LEX> it(spec->m_routine_declarations);
+ for (LEX *lex; (lex= it++); )
+ {
+ bool found= false;
+ DBUG_ASSERT(lex->sphead);
+ List_iterator<LEX> it2(m_routine_implementations);
+ for (LEX *lex2; (lex2= it2++); )
+ {
+ DBUG_ASSERT(lex2->sphead);
+ if (Sp_handler::eq_routine_name(lex2->sphead->m_name,
+ lex->sphead->m_name) &&
+ lex2->sphead->eq_routine_spec(lex->sphead))
+ {
+ found= true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ my_error(ER_PACKAGE_ROUTINE_IN_SPEC_NOT_DEFINED_IN_BODY, MYF(0),
+ ErrConvDQName(lex->sphead).ptr());
+ return true;
+ }
+ }
+ return false;
+}
- m_param_begin= NULL;
- m_param_end= NULL;
- m_body_begin= NULL ;
+bool sp_package::validate_private_routines(THD *thd)
+{
+ /*
+ Check that all forwad declarations in
+ CREATE PACKAGE BODY have implementations.
+ */
+ List_iterator<LEX> it(m_routine_declarations);
+ for (LEX *lex; (lex= it++); )
+ {
+ bool found= false;
+ DBUG_ASSERT(lex->sphead);
+ List_iterator<LEX> it2(m_routine_implementations);
+ for (LEX *lex2; (lex2= it2++); )
+ {
+ DBUG_ASSERT(lex2->sphead);
+ if (Sp_handler::eq_routine_name(lex2->sphead->m_name,
+ lex->sphead->m_name) &&
+ lex2->sphead->eq_routine_spec(lex->sphead))
+ {
+ found= true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ my_error(ER_PACKAGE_ROUTINE_FORWARD_DECLARATION_NOT_DEFINED, MYF(0),
+ ErrConvDQName(lex->sphead).ptr());
+ return true;
+ }
+ }
+ return false;
+}
+
- m_qname.str= NULL;
- m_qname.length= 0;
+LEX *sp_package::LexList::find(const LEX_CSTRING &name,
+ stored_procedure_type type)
+{
+ List_iterator<LEX> it(*this);
+ for (LEX *lex; (lex= it++); )
+ {
+ DBUG_ASSERT(lex->sphead);
+ const char *dot;
+ if (lex->sphead->m_handler->type() == type &&
+ (dot= strrchr(lex->sphead->m_name.str, '.')))
+ {
+ size_t ofs= dot + 1 - lex->sphead->m_name.str;
+ LEX_CSTRING non_qualified_sphead_name= lex->sphead->m_name;
+ non_qualified_sphead_name.str+= ofs;
+ non_qualified_sphead_name.length-= ofs;
+ if (Sp_handler::eq_routine_name(non_qualified_sphead_name, name))
+ return lex;
+ }
+ }
+ return NULL;
+}
- m_explicit_name= false;
- m_db.str= NULL;
- m_db.length= 0;
+LEX *sp_package::LexList::find_qualified(const LEX_CSTRING &name,
+ stored_procedure_type type)
+{
+ List_iterator<LEX> it(*this);
+ for (LEX *lex; (lex= it++); )
+ {
+ DBUG_ASSERT(lex->sphead);
+ if (lex->sphead->m_handler->type() == type &&
+ Sp_handler::eq_routine_name(lex->sphead->m_name, name))
+ return lex;
+ }
+ return NULL;
+}
- m_name.str= NULL;
- m_name.length= 0;
- m_params.str= NULL;
- m_params.length= 0;
+void
+sp_head::init(LEX *lex)
+{
+ DBUG_ENTER("sp_head::init");
- m_body.str= NULL;
- m_body.length= 0;
+ lex->spcont= m_pcont;
- m_defstr.str= NULL;
- m_defstr.length= 0;
+ if (!lex->spcont)
+ DBUG_VOID_RETURN;
- m_return_field_def.charset= NULL;
+ /*
+ Altough trg_table_fields list is used only in triggers we init for all
+ types of stored procedures to simplify reset_lex()/restore_lex() code.
+ */
+ lex->trg_table_fields.empty();
DBUG_VOID_RETURN;
}
void
-sp_head::init_sp_name(THD *thd, sp_name *spname)
+sp_head::init_sp_name(const sp_name *spname)
{
DBUG_ENTER("sp_head::init_sp_name");
@@ -666,24 +773,8 @@ sp_head::init_sp_name(THD *thd, sp_name *spname)
DBUG_ASSERT(spname && spname->m_db.str && spname->m_db.length);
/* We have to copy strings to get them into the right memroot. */
-
- m_db.length= spname->m_db.length;
- m_db.str= strmake_root(thd->mem_root, spname->m_db.str, spname->m_db.length);
-
- m_name.length= spname->m_name.length;
- m_name.str= strmake_root(thd->mem_root, spname->m_name.str,
- spname->m_name.length);
-
+ Database_qualified_name::copy(&main_mem_root, spname->m_db, spname->m_name);
m_explicit_name= spname->m_explicit_name;
-
- if (spname->m_qname.length == 0)
- spname->init_qname(thd);
-
- m_qname.length= spname->m_qname.length;
- m_qname.str= (char*) memdup_root(thd->mem_root,
- spname->m_qname.str,
- spname->m_qname.length + 1);
-
DBUG_VOID_RETURN;
}
@@ -701,7 +792,6 @@ sp_head::set_stmt_end(THD *thd)
{
Lex_input_stream *lip= & thd->m_parser_state->m_lip; /* shortcut */
const char *end_ptr= lip->get_cpp_ptr(); /* shortcut */
- uint not_used;
/* Make the string of parameters. */
@@ -719,7 +809,7 @@ sp_head::set_stmt_end(THD *thd)
m_body.length= end_ptr - m_body_begin;
m_body.str= thd->strmake(m_body_begin, m_body.length);
- trim_whitespace(thd->charset(), &m_body, &not_used);
+ trim_whitespace(thd->charset(), &m_body);
/* Make the string of UTF-body. */
@@ -727,7 +817,7 @@ sp_head::set_stmt_end(THD *thd)
m_body_utf8.length= lip->get_body_utf8_length();
m_body_utf8.str= thd->strmake(lip->get_body_utf8_str(), m_body_utf8.length);
- trim_whitespace(thd->charset(), &m_body_utf8, &not_used);
+ trim_whitespace(thd->charset(), &m_body_utf8);
/*
Make the string of whole stored-program-definition query (in the
@@ -736,59 +826,7 @@ sp_head::set_stmt_end(THD *thd)
m_defstr.length= end_ptr - lip->get_cpp_buf();
m_defstr.str= thd->strmake(lip->get_cpp_buf(), m_defstr.length);
- trim_whitespace(thd->charset(), &m_defstr, &not_used);
-}
-
-
-static TYPELIB *
-create_typelib(MEM_ROOT *mem_root, Column_definition *field_def, List<String> *src)
-{
- TYPELIB *result= NULL;
- CHARSET_INFO *cs= field_def->charset;
- DBUG_ENTER("create_typelib");
-
- if (src->elements)
- {
- result= (TYPELIB*) alloc_root(mem_root, sizeof(TYPELIB));
- result->count= src->elements;
- result->name= "";
- if (!(result->type_names=(const char **)
- alloc_root(mem_root,(sizeof(char *)+sizeof(int))*(result->count+1))))
- DBUG_RETURN(0);
- result->type_lengths= (uint*)(result->type_names + result->count+1);
- List_iterator<String> it(*src);
- String conv;
- for (uint i=0; i < result->count; i++)
- {
- uint32 dummy;
- uint length;
- String *tmp= it++;
-
- if (String::needs_conversion(tmp->length(), tmp->charset(),
- cs, &dummy))
- {
- uint cnv_errs;
- conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), cs, &cnv_errs);
-
- length= conv.length();
- result->type_names[i]= (char*) strmake_root(mem_root, conv.ptr(),
- length);
- }
- else
- {
- length= tmp->length();
- result->type_names[i]= strmake_root(mem_root, tmp->ptr(), length);
- }
-
- // Strip trailing spaces.
- length= cs->cset->lengthsp(cs, result->type_names[i], length);
- result->type_lengths[i]= length;
- ((uchar *)result->type_names[i])[length]= '\0';
- }
- result->type_names[result->count]= 0;
- result->type_lengths[result->count]= 0;
- }
- DBUG_RETURN(result);
+ trim_whitespace(thd->charset(), &m_defstr);
}
@@ -831,16 +869,28 @@ sp_head::~sp_head()
}
+void sp_package::LexList::cleanup()
+{
+ List_iterator<LEX> it(*this);
+ for (LEX *lex; (lex= it++); )
+ {
+ lex_end(lex);
+ delete lex;
+ }
+}
+
+
/**
This is only used for result fields from functions (both during
fix_length_and_dec() and evaluation).
*/
Field *
-sp_head::create_result_field(uint field_max_length, const char *field_name,
- TABLE *table)
+sp_head::create_result_field(uint field_max_length, const LEX_CSTRING *field_name,
+ TABLE *table) const
{
Field *field;
+ LEX_CSTRING name;
DBUG_ENTER("sp_head::create_result_field");
@@ -876,18 +926,25 @@ sp_head::create_result_field(uint field_max_length, const char *field_name,
Perhaps we should refactor prepare_create_field() to set
Create_field::length to maximum octet length for BLOBs,
instead of packed length).
+
+ Note, for integer data types, field_max_length can be bigger
+ than the user specified length, e.g. a field of the INT(1) data type
+ is translated to the item with max_length=11.
*/
DBUG_ASSERT(field_max_length <= m_return_field_def.length ||
+ m_return_field_def.type_handler()->cmp_type() == INT_RESULT ||
(current_thd->stmt_arena->is_stmt_execute() &&
m_return_field_def.length == 8 &&
(m_return_field_def.pack_flag &
(FIELDFLAG_BLOB|FIELDFLAG_GEOM))));
+ if (field_name)
+ name= *field_name;
+ else
+ name= m_name;
field= m_return_field_def.make_field(table->s, /* TABLE_SHARE ptr */
table->in_use->mem_root,
- field_name ?
- field_name :
- (const char *) m_name.str);
+ &name);
field->vcol_info= m_return_field_def.vcol_info;
if (field)
@@ -1021,7 +1078,7 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str)
rewritables.sort(cmp_rqp_locations);
- thd->query_name_consts= rewritables.elements();
+ thd->query_name_consts= (uint)rewritables.elements();
for (Rewritable_query_parameter **rqp= rewritables.front();
rqp <= rewritables.back(); rqp++)
@@ -1044,14 +1101,14 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str)
<db_name> Name of current database
<flags> Flags struct
*/
- int buf_len= (qbuf.length() + 1 + QUERY_CACHE_DB_LENGTH_SIZE +
- thd->db_length + QUERY_CACHE_FLAGS_SIZE + 1);
+ size_t buf_len= (qbuf.length() + 1 + QUERY_CACHE_DB_LENGTH_SIZE +
+ thd->db.length + QUERY_CACHE_FLAGS_SIZE + 1);
if ((pbuf= (char *) alloc_root(thd->mem_root, buf_len)))
{
char *ptr= pbuf + qbuf.length();
memcpy(pbuf, qbuf.ptr(), qbuf.length());
*ptr= 0;
- int2store(ptr+1, thd->db_length);
+ int2store(ptr+1, thd->db.length);
}
else
DBUG_RETURN(TRUE);
@@ -1062,30 +1119,15 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str)
}
-/**
- Return appropriate error about recursion limit reaching
-
- @param thd Thread handle
-
- @remark For functions and triggers we return error about
- prohibited recursion. For stored procedures we
- return about reaching recursion limit.
-*/
-
-void sp_head::recursion_level_error(THD *thd)
+void Sp_handler_procedure::recursion_level_error(THD *thd,
+ const sp_head *sp) const
{
- if (m_type == TYPE_ENUM_PROCEDURE)
- {
- my_error(ER_SP_RECURSION_LIMIT, MYF(0),
- static_cast<int>(thd->variables.max_sp_recursion_depth),
- m_name.str);
- }
- else
- my_error(ER_SP_NO_RECURSION, MYF(0));
+ my_error(ER_SP_RECURSION_LIMIT, MYF(0),
+ static_cast<int>(thd->variables.max_sp_recursion_depth),
+ sp->m_name.str);
}
-
/**
Execute the routine. The main instruction jump loop is there.
Assume the parameters already set.
@@ -1117,7 +1159,13 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
sp_rcontext *ctx= thd->spcont;
bool err_status= FALSE;
uint ip= 0;
- ulonglong save_sql_mode;
+ sql_mode_t save_sql_mode;
+
+ // TODO(cvicentiu) See if you can drop this bit. This is used to resume
+ // execution from where we left off.
+ if (m_chistics.agg_type == GROUP_AGGREGATE)
+ ip= thd->spcont->instr_ptr;
+
bool save_abort_on_warning;
Query_arena *old_arena;
/* per-instruction arena */
@@ -1144,10 +1192,13 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
DBUG_RETURN(TRUE);
/* init per-instruction memroot */
- init_sql_alloc(&execute_mem_root, MEM_ROOT_BLOCK_SIZE, 0, MYF(0));
+ init_sql_alloc(&execute_mem_root, "per_instruction_memroot",
+ MEM_ROOT_BLOCK_SIZE, 0, MYF(0));
DBUG_ASSERT(!(m_flags & IS_INVOKED));
m_flags|= IS_INVOKED;
+ if (m_parent)
+ m_parent->m_invoked_subroutine_count++;
m_first_instance->m_first_free_instance= m_next_cached_sp;
if (m_next_cached_sp)
{
@@ -1265,11 +1316,10 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
/* Discard the initial part of executing routines. */
thd->profiling.discard_current_query();
#endif
+ sp_instr *i;
DEBUG_SYNC(thd, "sp_head_execute_before_loop");
do
{
- sp_instr *i;
-
#if defined(ENABLED_PROFILING)
/*
Treat each "instr" of a routine as discrete unit that could be profiled.
@@ -1287,6 +1337,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
#if defined(ENABLED_PROFILING)
thd->profiling.discard_current_query();
#endif
+ thd->spcont->quit_func= TRUE;
break;
}
@@ -1325,7 +1376,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
err_status= i->execute(thd, &ip);
#ifdef WITH_WSREP
- if (m_type == TYPE_ENUM_PROCEDURE)
+ if (m_handler->type() == TYPE_ENUM_PROCEDURE)
{
mysql_mutex_lock(&thd->LOCK_thd_data);
if (thd->wsrep_conflict_state == MUST_REPLAY)
@@ -1435,7 +1486,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
errors are not catchable by SQL handlers) or the connection has been
killed during execution.
*/
- if (!thd->is_fatal_error && !thd->killed_errno() &&
+ if (likely(!thd->is_fatal_error) && likely(!thd->killed_errno()) &&
ctx->handle_sql_condition(thd, &ip, i))
{
err_status= FALSE;
@@ -1444,7 +1495,9 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
/* Reset sp_rcontext::end_partial_result_set flag. */
ctx->end_partial_result_set= FALSE;
- } while (!err_status && !thd->killed && !thd->is_fatal_error);
+ } while (!err_status && likely(!thd->killed) &&
+ likely(!thd->is_fatal_error) &&
+ !thd->spcont->pause_state);
#if defined(ENABLED_PROFILING)
thd->profiling.finish_current_query();
@@ -1460,9 +1513,14 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
thd->restore_active_arena(&execute_arena, &backup_arena);
- thd->spcont->pop_all_cursors(); // To avoid memory leaks after an error
+ /* Only pop cursors when we're done with group aggregate running. */
+ if (m_chistics.agg_type != GROUP_AGGREGATE ||
+ (m_chistics.agg_type == GROUP_AGGREGATE && thd->spcont->quit_func))
+ thd->spcont->pop_all_cursors(thd); // To avoid memory leaks after an error
/* Restore all saved */
+ if (m_chistics.agg_type == GROUP_AGGREGATE)
+ thd->spcont->instr_ptr= ip;
thd->server_status= (thd->server_status & ~status_backup_mask) | old_server_status;
old_packet.swap(thd->packet);
DBUG_ASSERT(thd->Item_change_list::is_empty());
@@ -1518,6 +1576,13 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
da->opt_clear_warning_info(thd->query_id);
da->copy_sql_conditions_from_wi(thd, &sp_wi);
da->remove_marked_sql_conditions();
+ if (i != NULL)
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_SP_STACK_TRACE,
+ ER_THD(thd, ER_SP_STACK_TRACE),
+ i->m_lineno,
+ m_qname.str != NULL ? m_qname.str :
+ "anonymous block");
}
}
@@ -1539,9 +1604,11 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
NULL. In this case, mysql_change_db() would generate an error.
*/
- err_status|= mysql_change_db(thd, &saved_cur_db_name, TRUE);
+ err_status|= mysql_change_db(thd, (LEX_CSTRING*)&saved_cur_db_name, TRUE) != 0;
}
m_flags&= ~IS_INVOKED;
+ if (m_parent)
+ m_parent->m_invoked_subroutine_count--;
DBUG_PRINT("info",
("first free for %p --: %p->%p, level: %lu, flags %x",
m_first_instance,
@@ -1580,7 +1647,6 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
@param thd thread handle
@param sp stored routine to change the context for
- @param is_proc TRUE is procedure, FALSE if function
@param save_ctx pointer to an old security context
@todo
@@ -1595,13 +1661,12 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
*/
bool
-set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc,
- Security_context **save_ctx)
+set_routine_security_ctx(THD *thd, sp_head *sp, Security_context **save_ctx)
{
*save_ctx= 0;
- if (sp->m_chistics->suid != SP_IS_NOT_SUID &&
- sp->m_security_ctx.change_security_context(thd, &sp->m_definer_user,
- &sp->m_definer_host,
+ if (sp->suid() != SP_IS_NOT_SUID &&
+ sp->m_security_ctx.change_security_context(thd, &sp->m_definer.user,
+ &sp->m_definer.host,
&sp->m_db,
save_ctx))
return TRUE;
@@ -1617,8 +1682,7 @@ set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc,
statement that 'may' affect this.
*/
if (*save_ctx &&
- check_routine_access(thd, EXECUTE_ACL,
- sp->m_db.str, sp->m_name.str, is_proc, FALSE))
+ sp->check_execute_access(thd))
{
sp->m_security_ctx.restore_security_context(thd, *save_ctx);
*save_ctx= 0;
@@ -1630,6 +1694,75 @@ set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc,
#endif // ! NO_EMBEDDED_ACCESS_CHECKS
+bool sp_head::check_execute_access(THD *thd) const
+{
+ return m_parent ? m_parent->check_execute_access(thd) :
+ check_routine_access(thd, EXECUTE_ACL,
+ &m_db, &m_name,
+ m_handler, false);
+}
+
+
+/**
+ Create rcontext optionally using the routine security.
+ This is important for sql_mode=ORACLE to make sure that the invoker has
+ access to the tables mentioned in the %TYPE references.
+
+ In non-Oracle sql_modes we do not need access to any tables,
+ so we can omit the security context switch for performance purposes.
+
+ @param thd
+ @param ret_value
+ @retval NULL - error (access denided or EOM)
+ @retval !NULL - success (the invoker has rights to all %TYPE tables)
+*/
+
+sp_rcontext *sp_head::rcontext_create(THD *thd, Field *ret_value,
+ Row_definition_list *defs,
+ bool switch_security_ctx)
+{
+ if (!(m_flags & HAS_COLUMN_TYPE_REFS))
+ return sp_rcontext::create(thd, this, m_pcont, ret_value, *defs);
+ sp_rcontext *res= NULL;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ Security_context *save_security_ctx;
+ if (switch_security_ctx &&
+ set_routine_security_ctx(thd, this, &save_security_ctx))
+ return NULL;
+#endif
+ if (!defs->resolve_type_refs(thd))
+ res= sp_rcontext::create(thd, this, m_pcont, ret_value, *defs);
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (switch_security_ctx)
+ m_security_ctx.restore_security_context(thd, save_security_ctx);
+#endif
+ return res;
+}
+
+
+sp_rcontext *sp_head::rcontext_create(THD *thd, Field *ret_value,
+ List<Item> *args)
+{
+ DBUG_ASSERT(args);
+ Row_definition_list defs;
+ m_pcont->retrieve_field_definitions(&defs);
+ if (defs.adjust_formal_params_to_actual_params(thd, args))
+ return NULL;
+ return rcontext_create(thd, ret_value, &defs, true);
+}
+
+
+sp_rcontext *sp_head::rcontext_create(THD *thd, Field *ret_value,
+ Item **args, uint arg_count)
+{
+ Row_definition_list defs;
+ m_pcont->retrieve_field_definitions(&defs);
+ if (defs.adjust_formal_params_to_actual_params(thd, args, arg_count))
+ return NULL;
+ return rcontext_create(thd, ret_value, &defs, true);
+}
+
+
/**
Execute trigger stored program.
@@ -1658,8 +1791,8 @@ set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc,
bool
sp_head::execute_trigger(THD *thd,
- const LEX_STRING *db_name,
- const LEX_STRING *table_name,
+ const LEX_CSTRING *db_name,
+ const LEX_CSTRING *table_name,
GRANT_INFO *grant_info)
{
sp_rcontext *octx = thd->spcont;
@@ -1668,7 +1801,6 @@ sp_head::execute_trigger(THD *thd,
MEM_ROOT call_mem_root;
Query_arena call_arena(&call_mem_root, Query_arena::STMT_INITIALIZED_FOR_SP);
Query_arena backup_arena;
-
DBUG_ENTER("sp_head::execute_trigger");
DBUG_PRINT("info", ("trigger %s", m_name.str));
@@ -1676,10 +1808,10 @@ sp_head::execute_trigger(THD *thd,
Security_context *save_ctx= NULL;
- if (m_chistics->suid != SP_IS_NOT_SUID &&
+ if (suid() != SP_IS_NOT_SUID &&
m_security_ctx.change_security_context(thd,
- &m_definer_user,
- &m_definer_host,
+ &m_definer.user,
+ &m_definer.host,
&m_db,
&save_ctx))
DBUG_RETURN(TRUE);
@@ -1724,19 +1856,18 @@ sp_head::execute_trigger(THD *thd,
TODO: we should create sp_rcontext once per command and reuse it
on subsequent executions of a trigger.
*/
- init_sql_alloc(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0, MYF(0));
+ init_sql_alloc(&call_mem_root, "execute_trigger", MEM_ROOT_BLOCK_SIZE, 0,
+ MYF(0));
thd->set_n_backup_active_arena(&call_arena, &backup_arena);
- if (!(nctx= sp_rcontext::create(thd, m_pcont, NULL)))
+ Row_definition_list defs;
+ m_pcont->retrieve_field_definitions(&defs);
+ if (!(nctx= rcontext_create(thd, NULL, &defs, false)))
{
err_status= TRUE;
goto err_with_cleanup;
}
-#ifndef DBUG_OFF
- nctx->sp= this;
-#endif
-
thd->spcont= nctx;
err_status= execute(thd, FALSE);
@@ -1760,6 +1891,43 @@ err_with_cleanup:
}
+/*
+ Execute the package initialization section.
+*/
+
+bool sp_package::instantiate_if_needed(THD *thd)
+{
+ List<Item> args;
+ if (m_is_instantiated)
+ return false;
+ /*
+ Set m_is_instantiated to true early, to avoid recursion in case if
+ the package initialization section calls routines from the same package.
+ */
+ m_is_instantiated= true;
+ /*
+ Check that the initialization section doesn't contain Dynamic SQL
+ and doesn't return result sets: such stored procedures can't
+ be called from a function or trigger.
+ */
+ if (thd->in_sub_stmt)
+ {
+ const char *where= (thd->in_sub_stmt & SUB_STMT_TRIGGER ?
+ "trigger" : "function");
+ if (is_not_allowed_in_function(where))
+ goto err;
+ }
+
+ args.elements= 0;
+ if (execute_procedure(thd, &args))
+ goto err;
+ return false;
+err:
+ m_is_instantiated= false;
+ return true;
+}
+
+
/**
Execute a function.
@@ -1795,22 +1963,23 @@ err_with_cleanup:
bool
sp_head::execute_function(THD *thd, Item **argp, uint argcount,
- Field *return_value_fld)
+ Field *return_value_fld, sp_rcontext **func_ctx,
+ Query_arena *call_arena)
{
ulonglong UNINIT_VAR(binlog_save_options);
bool need_binlog_call= FALSE;
uint arg_no;
sp_rcontext *octx = thd->spcont;
- sp_rcontext *nctx = NULL;
char buf[STRING_BUFFER_USUAL_SIZE];
String binlog_buf(buf, sizeof(buf), &my_charset_bin);
bool err_status= FALSE;
- MEM_ROOT call_mem_root;
- Query_arena call_arena(&call_mem_root, Query_arena::STMT_INITIALIZED_FOR_SP);
Query_arena backup_arena;
DBUG_ENTER("sp_head::execute_function");
DBUG_PRINT("info", ("function %s", m_name.str));
+ if (m_parent && m_parent->instantiate_if_needed(thd))
+ DBUG_RETURN(true);
+
/*
Check that the function is called with all specified arguments.
@@ -1824,7 +1993,8 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
invoking query properly.
*/
my_error(ER_SP_WRONG_NO_OF_ARGS, MYF(0),
- "FUNCTION", m_qname.str, m_pcont->context_var_count(), argcount);
+ "FUNCTION", ErrConvDQName(this).ptr(),
+ m_pcont->context_var_count(), argcount);
DBUG_RETURN(TRUE);
}
/*
@@ -1838,27 +2008,25 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
TODO: we should create sp_rcontext once per command and reuse
it on subsequent executions of a function/trigger.
*/
- init_sql_alloc(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0, MYF(0));
- thd->set_n_backup_active_arena(&call_arena, &backup_arena);
-
- if (!(nctx= sp_rcontext::create(thd, m_pcont, return_value_fld)))
+ if (!(*func_ctx))
{
- thd->restore_active_arena(&call_arena, &backup_arena);
- err_status= TRUE;
- goto err_with_cleanup;
- }
+ thd->set_n_backup_active_arena(call_arena, &backup_arena);
- /*
- We have to switch temporarily back to callers arena/memroot.
- Function arguments belong to the caller and so the may reference
- memory which they will allocate during calculation long after
- this function call will be finished (e.g. in Item::cleanup()).
- */
- thd->restore_active_arena(&call_arena, &backup_arena);
+ if (!(*func_ctx= rcontext_create(thd, return_value_fld, argp, argcount)))
+ {
+ thd->restore_active_arena(call_arena, &backup_arena);
+ err_status= TRUE;
+ goto err_with_cleanup;
+ }
-#ifndef DBUG_OFF
- nctx->sp= this;
-#endif
+ /*
+ We have to switch temporarily back to callers arena/memroot.
+ Function arguments belong to the caller and so the may reference
+ memory which they will allocate during calculation long after
+ this function call will be finished (e.g. in Item::cleanup()).
+ */
+ thd->restore_active_arena(call_arena, &backup_arena);
+ }
/* Pass arguments. */
for (arg_no= 0; arg_no < argcount; arg_no++)
@@ -1866,7 +2034,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
/* Arguments must be fixed in Item_func_sp::fix_fields */
DBUG_ASSERT(argp[arg_no]->fixed);
- if ((err_status= nctx->set_variable(thd, arg_no, &(argp[arg_no]))))
+ if ((err_status= (*func_ctx)->set_parameter(thd, arg_no, &(argp[arg_no]))))
goto err_with_cleanup;
}
@@ -1886,9 +2054,9 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
{
binlog_buf.length(0);
binlog_buf.append(STRING_WITH_LEN("SELECT "));
- append_identifier(thd, &binlog_buf, m_db.str, m_db.length);
+ append_identifier(thd, &binlog_buf, &m_db);
binlog_buf.append('.');
- append_identifier(thd, &binlog_buf, m_name.str, m_name.length);
+ append_identifier(thd, &binlog_buf, &m_name);
binlog_buf.append('(');
for (arg_no= 0; arg_no < argcount; arg_no++)
{
@@ -1898,9 +2066,9 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
if (arg_no)
binlog_buf.append(',');
- str_value= sp_get_item_value(thd, nctx->get_item(arg_no),
- &str_value_holder);
-
+ Item_field *item= (*func_ctx)->get_parameter(arg_no);
+ str_value= item->type_handler()->print_item_value(thd, item,
+ &str_value_holder);
if (str_value)
binlog_buf.append(*str_value);
else
@@ -1908,11 +2076,11 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
}
binlog_buf.append(')');
}
- thd->spcont= nctx;
+ thd->spcont= *func_ctx;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
Security_context *save_security_ctx;
- if (set_routine_security_ctx(thd, this, FALSE, &save_security_ctx))
+ if (set_routine_security_ctx(thd, this, &save_security_ctx))
{
err_status= TRUE;
goto err_with_cleanup;
@@ -1949,11 +2117,11 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
sp_rcontext and allocate all these objects (and sp_rcontext
itself) on it directly rather than juggle with arenas.
*/
- thd->set_n_backup_active_arena(&call_arena, &backup_arena);
+ thd->set_n_backup_active_arena(call_arena, &backup_arena);
err_status= execute(thd, TRUE);
- thd->restore_active_arena(&call_arena, &backup_arena);
+ thd->restore_active_arena(call_arena, &backup_arena);
if (need_binlog_call)
{
@@ -1979,11 +2147,11 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
}
}
- if (!err_status)
+ if (!err_status && thd->spcont->quit_func)
{
/* We need result only in function but not in trigger */
- if (!nctx->is_return_value_set())
+ if (!(*func_ctx)->is_return_value_set())
{
my_error(ER_SP_NORETURNEND, MYF(0), m_name.str);
err_status= TRUE;
@@ -1995,9 +2163,6 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
#endif
err_with_cleanup:
- delete nctx;
- call_arena.free_items();
- free_root(&call_mem_root, MYF(0));
thd->spcont= octx;
/*
@@ -2042,13 +2207,17 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
sp_rcontext *nctx = NULL;
bool save_enable_slow_log;
bool save_log_general= false;
+ sp_package *pkg= get_package();
DBUG_ENTER("sp_head::execute_procedure");
DBUG_PRINT("info", ("procedure %s", m_name.str));
+ if (m_parent && m_parent->instantiate_if_needed(thd))
+ DBUG_RETURN(true);
+
if (args->elements != params)
{
my_error(ER_SP_WRONG_NO_OF_ARGS, MYF(0), "PROCEDURE",
- m_qname.str, params, args->elements);
+ ErrConvDQName(this).ptr(), params, args->elements);
DBUG_RETURN(TRUE);
}
@@ -2056,30 +2225,45 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
if (! octx)
{
/* Create a temporary old context. */
- if (!(octx= sp_rcontext::create(thd, m_pcont, NULL)))
+ if (!(octx= rcontext_create(thd, NULL, args)))
{
DBUG_PRINT("error", ("Could not create octx"));
DBUG_RETURN(TRUE);
}
-#ifndef DBUG_OFF
- octx->sp= 0;
-#endif
thd->spcont= octx;
/* set callers_arena to thd, for upper-level function to work */
thd->spcont->callers_arena= thd;
}
- if (!(nctx= sp_rcontext::create(thd, m_pcont, NULL)))
+ if (!pkg)
{
- delete nctx; /* Delete nctx if it was init() that failed. */
- thd->spcont= save_spcont;
- DBUG_RETURN(TRUE);
+ if (!(nctx= rcontext_create(thd, NULL, args)))
+ {
+ delete nctx; /* Delete nctx if it was init() that failed. */
+ thd->spcont= save_spcont;
+ DBUG_RETURN(TRUE);
+ }
+ }
+ else
+ {
+ if (!pkg->m_rcontext)
+ {
+ Query_arena backup_arena;
+ thd->set_n_backup_active_arena(this, &backup_arena);
+ nctx= pkg->rcontext_create(thd, NULL, args);
+ thd->restore_active_arena(this, &backup_arena);
+ if (!nctx)
+ {
+ thd->spcont= save_spcont;
+ DBUG_RETURN(TRUE);
+ }
+ pkg->m_rcontext= nctx;
+ }
+ else
+ nctx= pkg->m_rcontext;
}
-#ifndef DBUG_OFF
- nctx->sp= this;
-#endif
if (params > 0)
{
@@ -2106,7 +2290,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
if (!srp)
{
- my_error(ER_SP_NOT_VAR_ARG, MYF(0), i+1, m_qname.str);
+ my_error(ER_SP_NOT_VAR_ARG, MYF(0), i+1, ErrConvDQName(this).ptr());
err_status= TRUE;
break;
}
@@ -2120,7 +2304,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
Item *tmp_item= null_item;
if (!null_item ||
- nctx->set_variable(thd, i, &tmp_item))
+ nctx->set_parameter(thd, i, &tmp_item))
{
DBUG_PRINT("error", ("set variable failed"));
err_status= TRUE;
@@ -2129,7 +2313,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
}
else
{
- if (nctx->set_variable(thd, i, it_args.ref()))
+ if (nctx->set_parameter(thd, i, it_args.ref()))
{
DBUG_PRINT("error", ("set variable 2 failed"));
err_status= TRUE;
@@ -2154,7 +2338,6 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
thd->get_stmt_da()->set_overwrite_status(false);
}
- thd_proc_info(thd, "closing tables");
close_thread_tables(thd);
thd_proc_info(thd, 0);
@@ -2176,13 +2359,32 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
DBUG_PRINT("info",(" %.*s: eval args done", (int) m_name.length,
m_name.str));
}
+
save_enable_slow_log= thd->enable_slow_log;
- if (!(m_flags & LOG_SLOW_STATEMENTS) && save_enable_slow_log)
+
+ /*
+ Disable slow log if:
+ - Slow logging is enabled (no change needed)
+ - This is a normal SP (not event log)
+ - If we have not explicitely disabled logging of SP
+ */
+ if (save_enable_slow_log &&
+ ((!(m_flags & LOG_SLOW_STATEMENTS) &&
+ (thd->variables.log_slow_disabled_statements & LOG_SLOW_DISABLE_SP))))
{
DBUG_PRINT("info", ("Disabling slow log for the execution"));
thd->enable_slow_log= FALSE;
}
- if (!(m_flags & LOG_GENERAL_LOG) && !(thd->variables.option_bits & OPTION_LOG_OFF))
+
+ /*
+ Disable general log if:
+ - If general log is enabled (no change needed)
+ - This is a normal SP (not event log)
+ - If we have not explicitely disabled logging of SP
+ */
+ if (!(thd->variables.option_bits & OPTION_LOG_OFF) &&
+ (!(m_flags & LOG_GENERAL_LOG) &&
+ (thd->variables.log_disabled_statements & LOG_DISABLE_SP)))
{
DBUG_PRINT("info", ("Disabling general log for the execution"));
save_log_general= true;
@@ -2194,7 +2396,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
#ifndef NO_EMBEDDED_ACCESS_CHECKS
Security_context *save_security_ctx= 0;
if (!err_status)
- err_status= set_routine_security_ctx(thd, this, TRUE, &save_security_ctx);
+ err_status= set_routine_security_ctx(thd, this, &save_security_ctx);
#endif
if (!err_status)
@@ -2205,6 +2407,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
if (save_log_general)
thd->variables.option_bits &= ~OPTION_LOG_OFF;
thd->enable_slow_log= save_enable_slow_log;
+
/*
In the case when we weren't able to employ reuse mechanism for
OUT/INOUT paranmeters, we should reallocate memory. This
@@ -2238,7 +2441,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
DBUG_ASSERT(srp);
- if (srp->set_value(thd, octx, nctx->get_item_addr(i)))
+ if (srp->set_value(thd, octx, nctx->get_variable_addr(i)))
{
DBUG_PRINT("error", ("set value failed"));
err_status= TRUE;
@@ -2246,12 +2449,12 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
}
Send_field *out_param_info= new (thd->mem_root) Send_field();
- nctx->get_item(i)->make_field(thd, out_param_info);
+ 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;
- out_param_info->col_name= spvar->name.str;
- out_param_info->org_col_name= spvar->name.str;
+ out_param_info->col_name= spvar->name;
+ out_param_info->org_col_name= spvar->name;
srp->set_out_param_info(out_param_info);
}
@@ -2265,7 +2468,8 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
if (!save_spcont)
delete octx;
- delete nctx;
+ if (!pkg)
+ delete nctx;
thd->spcont= save_spcont;
thd->utime_after_lock= utime_before_sp_exec;
@@ -2295,34 +2499,23 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
*/
bool
-sp_head::reset_lex(THD *thd)
+sp_head::reset_lex(THD *thd, sp_lex_local *sublex)
{
DBUG_ENTER("sp_head::reset_lex");
- LEX *sublex;
LEX *oldlex= thd->lex;
- sublex= new (thd->mem_root)st_lex_local;
- if (sublex == 0)
- DBUG_RETURN(TRUE);
-
- thd->lex= sublex;
- (void)m_lex.push_front(oldlex);
+ thd->set_local_lex(sublex);
- /* Reset most stuff. */
- lex_start(thd);
-
- /* And keep the SP stuff too */
- sublex->sphead= oldlex->sphead;
- sublex->spcont= oldlex->spcont;
- /* And trigger related stuff too */
- sublex->trg_chistics= oldlex->trg_chistics;
- sublex->trg_table_fields.empty();
- sublex->sp_lex_in_use= FALSE;
+ DBUG_RETURN(m_lex.push_front(oldlex));
+}
- /* Reset part of parser state which needs this. */
- thd->m_parser_state->m_yacc.reset_before_substatement();
- DBUG_RETURN(FALSE);
+bool
+sp_head::reset_lex(THD *thd)
+{
+ DBUG_ENTER("sp_head::reset_lex");
+ sp_lex_local *sublex= new (thd->mem_root) sp_lex_local(thd, thd->lex);
+ DBUG_RETURN(sublex ? reset_lex(thd, sublex) : true);
}
@@ -2330,6 +2523,8 @@ sp_head::reset_lex(THD *thd)
Restore lex during parsing, after we have parsed a sub statement.
@param thd Thread handle
+ @param oldlex The upper level lex we're near to restore to
+ @param sublex The local lex we're near to restore from
@return
@retval TRUE failure
@@ -2337,23 +2532,17 @@ sp_head::reset_lex(THD *thd)
*/
bool
-sp_head::restore_lex(THD *thd)
+sp_head::merge_lex(THD *thd, LEX *oldlex, LEX *sublex)
{
- DBUG_ENTER("sp_head::restore_lex");
- LEX *sublex= thd->lex;
- LEX *oldlex;
+ DBUG_ENTER("sp_head::merge_lex");
sublex->set_trg_event_type_for_tables();
- oldlex= (LEX *)m_lex.pop();
- if (! oldlex)
- DBUG_RETURN(FALSE); // Nothing to restore
-
oldlex->trg_table_fields.push_back(&sublex->trg_table_fields);
/* If this substatement is unsafe, the entire routine is too. */
- DBUG_PRINT("info", ("lex->get_stmt_unsafe_flags: 0x%x",
- thd->lex->get_stmt_unsafe_flags()));
+ DBUG_PRINT("info", ("sublex->get_stmt_unsafe_flags: 0x%x",
+ sublex->get_stmt_unsafe_flags()));
unsafe_flags|= sublex->get_stmt_unsafe_flags();
/*
@@ -2375,21 +2564,16 @@ sp_head::restore_lex(THD *thd)
/* Merge lists of PS parameters. */
oldlex->param_list.append(&sublex->param_list);
- if (! sublex->sp_lex_in_use)
- {
- sublex->sphead= NULL;
- lex_end(sublex);
- delete sublex;
- }
- thd->lex= oldlex;
DBUG_RETURN(FALSE);
}
/**
Put the instruction on the backpatch list, associated with the label.
*/
+
int
-sp_head::push_backpatch(THD *thd, sp_instr *i, sp_label *lab)
+sp_head::push_backpatch(THD *thd, sp_instr *i, sp_label *lab,
+ List<bp_t> *list, backpatch_instr_type itype)
{
bp_t *bp= (bp_t *) thd->alloc(sizeof(bp_t));
@@ -2397,13 +2581,52 @@ sp_head::push_backpatch(THD *thd, sp_instr *i, sp_label *lab)
return 1;
bp->lab= lab;
bp->instr= i;
- return m_backpatch.push_front(bp);
+ bp->instr_type= itype;
+ return list->push_front(bp);
+}
+
+int
+sp_head::push_backpatch(THD *thd, sp_instr *i, sp_label *lab)
+{
+ return push_backpatch(thd, i, lab, &m_backpatch, GOTO);
+}
+
+int
+sp_head::push_backpatch_goto(THD *thd, sp_pcontext *ctx, sp_label *lab)
+{
+ uint ip= instructions();
+
+ /*
+ Add cpop/hpop : they will be removed or updated later if target is in
+ the same block or not
+ */
+ sp_instr_hpop *hpop= new (thd->mem_root) sp_instr_hpop(ip++, ctx, 0);
+ if (hpop == NULL || add_instr(hpop))
+ return true;
+ if (push_backpatch(thd, hpop, lab, &m_backpatch_goto, HPOP))
+ return true;
+
+ sp_instr_cpop *cpop= new (thd->mem_root) sp_instr_cpop(ip++, ctx, 0);
+ if (cpop == NULL || add_instr(cpop))
+ return true;
+ if (push_backpatch(thd, cpop, lab, &m_backpatch_goto, CPOP))
+ return true;
+
+ // Add jump with ip=0. IP will be updated when label is found.
+ sp_instr_jump *i= new (thd->mem_root) sp_instr_jump(ip, ctx);
+ if (i == NULL || add_instr(i))
+ return true;
+ if (push_backpatch(thd, i, lab, &m_backpatch_goto, GOTO))
+ return true;
+
+ return false;
}
/**
Update all instruction with this label in the backpatch list to
the current position.
*/
+
void
sp_head::backpatch(sp_label *lab)
{
@@ -2424,44 +2647,95 @@ sp_head::backpatch(sp_label *lab)
DBUG_VOID_RETURN;
}
-/**
- Prepare an instance of Column_definition for field creation
- (fill all necessary attributes).
-
- @param[in] thd Thread handle
- @param[in] lex Yacc parsing context
- @param[out] field_def An instance of create_field to be filled
+void
+sp_head::backpatch_goto(THD *thd, sp_label *lab,sp_label *lab_begin_block)
+{
+ bp_t *bp;
+ uint dest= instructions();
+ List_iterator<bp_t> li(m_backpatch_goto);
- @retval
- FALSE on success
- @retval
- TRUE on error
-*/
+ DBUG_ENTER("sp_head::backpatch_goto");
+ while ((bp= li++))
+ {
+ if (bp->instr->m_ip < lab_begin_block->ip || bp->instr->m_ip > lab->ip)
+ {
+ /*
+ Update only jump target from the beginning of the block where the
+ label is defined.
+ */
+ continue;
+ }
+ if (lex_string_cmp(system_charset_info, &bp->lab->name, &lab->name) == 0)
+ {
+ if (bp->instr_type == GOTO)
+ {
+ DBUG_PRINT("info",
+ ("backpatch_goto: (m_ip %d, label %p <%s>) to dest %d",
+ bp->instr->m_ip, lab, lab->name.str, dest));
+ bp->instr->backpatch(dest, lab->ctx);
+ // Jump resolved, remove from the list
+ li.remove();
+ continue;
+ }
+ if (bp->instr_type == CPOP)
+ {
+ uint n= bp->instr->m_ctx->diff_cursors(lab_begin_block->ctx, true);
+ if (n == 0)
+ {
+ // Remove cpop instr
+ replace_instr_to_nop(thd,bp->instr->m_ip);
+ }
+ else
+ {
+ // update count of cpop
+ static_cast<sp_instr_cpop*>(bp->instr)->update_count(n);
+ n= 1;
+ }
+ li.remove();
+ continue;
+ }
+ if (bp->instr_type == HPOP)
+ {
+ uint n= bp->instr->m_ctx->diff_handlers(lab_begin_block->ctx, true);
+ if (n == 0)
+ {
+ // Remove hpop instr
+ replace_instr_to_nop(thd,bp->instr->m_ip);
+ }
+ else
+ {
+ // update count of cpop
+ static_cast<sp_instr_hpop*>(bp->instr)->update_count(n);
+ n= 1;
+ }
+ li.remove();
+ continue;
+ }
+ }
+ }
+ DBUG_VOID_RETURN;
+}
bool
-sp_head::fill_field_definition(THD *thd, LEX *lex,
- Column_definition *field_def)
+sp_head::check_unresolved_goto()
{
- uint unused1= 0;
-
- if (field_def->check(thd))
- return TRUE;
-
- if (field_def->interval_list.elements)
- field_def->interval= create_typelib(mem_root, field_def,
- &field_def->interval_list);
-
- sp_prepare_create_field(thd, field_def);
-
- if (prepare_create_field(field_def, &unused1, HA_CAN_GEOMETRY))
+ DBUG_ENTER("sp_head::check_unresolved_goto");
+ bool has_unresolved_label=false;
+ if (m_backpatch_goto.elements > 0)
{
- return TRUE;
+ List_iterator_fast<bp_t> li(m_backpatch_goto);
+ while (bp_t* bp= li++)
+ {
+ if (bp->instr_type == GOTO)
+ {
+ my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "GOTO", bp->lab->name.str);
+ has_unresolved_label=true;
+ }
+ }
}
-
- return FALSE;
+ DBUG_RETURN(has_unresolved_label);
}
-
int
sp_head::new_cont_backpatch(sp_instr_opt_meta *i)
{
@@ -2497,53 +2771,43 @@ sp_head::do_cont_backpatch()
}
}
-void
-sp_head::set_info(longlong created, longlong modified,
- st_sp_chistics *chistics, sql_mode_t sql_mode)
-{
- m_created= created;
- m_modified= modified;
- m_chistics= (st_sp_chistics *) memdup_root(mem_root, (char*) chistics,
- sizeof(*chistics));
- if (m_chistics->comment.length == 0)
- m_chistics->comment.str= 0;
- else
- m_chistics->comment.str= strmake_root(mem_root,
- m_chistics->comment.str,
- m_chistics->comment.length);
- m_sql_mode= sql_mode;
-}
-
-void
-sp_head::set_definer(const char *definer, uint definerlen)
+bool
+sp_head::sp_add_instr_cpush_for_cursors(THD *thd, sp_pcontext *pcontext)
{
- char user_name_holder[USERNAME_LENGTH + 1];
- LEX_STRING user_name= { user_name_holder, USERNAME_LENGTH };
-
- char host_name_holder[HOSTNAME_LENGTH + 1];
- LEX_STRING host_name= { host_name_holder, HOSTNAME_LENGTH };
-
- if (parse_user(definer, definerlen, user_name.str, &user_name.length,
- host_name.str, &host_name.length) &&
- user_name.length && !host_name.length)
+ for (uint i= 0; i < pcontext->frame_cursor_count(); i++)
{
- // 'user@' -> 'user@%'
- host_name= host_not_specified;
+ const sp_pcursor *c= pcontext->get_cursor_by_local_frame_offset(i);
+ sp_instr_cpush *instr= new (thd->mem_root)
+ sp_instr_cpush(instructions(), pcontext, c->lex(),
+ pcontext->cursor_offset() + i);
+ if (instr == NULL || add_instr(instr))
+ return true;
}
-
- set_definer(&user_name, &host_name);
+ return false;
}
void
-sp_head::set_definer(const LEX_STRING *user_name, const LEX_STRING *host_name)
+sp_head::set_chistics(const st_sp_chistics &chistics)
{
- m_definer_user.str= strmake_root(mem_root, user_name->str, user_name->length);
- m_definer_user.length= user_name->length;
+ m_chistics.set(chistics);
+ if (m_chistics.comment.length == 0)
+ m_chistics.comment.str= 0;
+ else
+ m_chistics.comment.str= strmake_root(mem_root,
+ m_chistics.comment.str,
+ m_chistics.comment.length);
+}
- m_definer_host.str= strmake_root(mem_root, host_name->str, host_name->length);
- m_definer_host.length= host_name->length;
+void
+sp_head::set_info(longlong created, longlong modified,
+ const st_sp_chistics &chistics, sql_mode_t sql_mode)
+{
+ m_created= created;
+ m_modified= modified;
+ set_chistics(chistics);
+ m_sql_mode= sql_mode;
}
@@ -2610,26 +2874,28 @@ bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access)
{
TABLE_LIST tables;
bzero((char*) &tables,sizeof(tables));
- tables.db= (char*) "mysql";
- tables.table_name= tables.alias= (char*) "proc";
+ tables.db= MYSQL_SCHEMA_NAME;
+ tables.table_name= MYSQL_PROC_NAME;
+ tables.alias= MYSQL_PROC_NAME;
+
*full_access= ((!check_table_access(thd, SELECT_ACL, &tables, FALSE,
1, TRUE) &&
(tables.grant.privilege & SELECT_ACL) != 0) ||
/* Check if user owns the routine. */
- (!strcmp(sp->m_definer_user.str,
+ (!strcmp(sp->m_definer.user.str,
thd->security_ctx->priv_user) &&
- !strcmp(sp->m_definer_host.str,
+ !strcmp(sp->m_definer.host.str,
thd->security_ctx->priv_host)) ||
/* Check if current role or any of the sub-granted roles
own the routine. */
- (sp->m_definer_host.length == 0 &&
- (!strcmp(sp->m_definer_user.str,
+ (sp->m_definer.host.length == 0 &&
+ (!strcmp(sp->m_definer.user.str,
thd->security_ctx->priv_role) ||
check_role_is_granted(thd->security_ctx->priv_role, NULL,
- sp->m_definer_user.str))));
+ sp->m_definer.user.str))));
if (!*full_access)
return check_some_routine_access(thd, sp->m_db.str, sp->m_name.str,
- sp->m_type == TYPE_ENUM_PROCEDURE);
+ sp->m_handler);
return 0;
}
@@ -2638,9 +2904,8 @@ bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access)
Collect metadata for SHOW CREATE statement for stored routines.
@param thd Thread context.
- @param type Stored routine type
- @param type Stored routine type
- (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION)
+ @param sph Stored routine handler
+ @param fields Item list to populate
@return Error status.
@retval FALSE on success
@@ -2648,13 +2913,11 @@ bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access)
*/
void
-sp_head::show_create_routine_get_fields(THD *thd, int type, List<Item> *fields)
+sp_head::show_create_routine_get_fields(THD *thd, const Sp_handler *sph,
+ List<Item> *fields)
{
- const char *col1_caption= type == TYPE_ENUM_PROCEDURE ?
- "Procedure" : "Function";
-
- const char *col3_caption= type == TYPE_ENUM_PROCEDURE ?
- "Create Procedure" : "Create Function";
+ const char *col1_caption= sph->show_create_routine_col1_caption();
+ const char *col3_caption= sph->show_create_routine_col3_caption();
MEM_ROOT *mem_root= thd->mem_root;
@@ -2701,8 +2964,7 @@ sp_head::show_create_routine_get_fields(THD *thd, int type, List<Item> *fields)
Implement SHOW CREATE statement for stored routines.
@param thd Thread context.
- @param type Stored routine type
- (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION)
+ @param sph Stored routine handler
@return Error status.
@retval FALSE on success
@@ -2710,20 +2972,17 @@ sp_head::show_create_routine_get_fields(THD *thd, int type, List<Item> *fields)
*/
bool
-sp_head::show_create_routine(THD *thd, int type)
+sp_head::show_create_routine(THD *thd, const Sp_handler *sph)
{
- const char *col1_caption= type == TYPE_ENUM_PROCEDURE ?
- "Procedure" : "Function";
-
- const char *col3_caption= type == TYPE_ENUM_PROCEDURE ?
- "Create Procedure" : "Create Function";
+ const char *col1_caption= sph->show_create_routine_col1_caption();
+ const char *col3_caption= sph->show_create_routine_col3_caption();
bool err_status;
Protocol *protocol= thd->protocol;
List<Item> fields;
- LEX_STRING sql_mode;
+ LEX_CSTRING sql_mode;
bool full_access;
MEM_ROOT *mem_root= thd->mem_root;
@@ -2731,9 +2990,6 @@ sp_head::show_create_routine(THD *thd, int type)
DBUG_ENTER("sp_head::show_create_routine");
DBUG_PRINT("info", ("routine %s", m_name.str));
- DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE ||
- type == TYPE_ENUM_FUNCTION);
-
if (check_show_routine_access(thd, this, &full_access))
DBUG_RETURN(TRUE);
@@ -2745,7 +3001,7 @@ sp_head::show_create_routine(THD *thd, int type)
Item_empty_string(thd, col1_caption, NAME_CHAR_LEN),
thd->mem_root);
fields.push_back(new (mem_root)
- Item_empty_string(thd, "sql_mode", sql_mode.length),
+ Item_empty_string(thd, "sql_mode", (uint)sql_mode.length),
thd->mem_root);
{
@@ -2756,7 +3012,7 @@ sp_head::show_create_routine(THD *thd, int type)
Item_empty_string *stmt_fld=
new (mem_root) Item_empty_string(thd, col3_caption,
- MY_MAX(m_defstr.length, 1024));
+ (uint)MY_MAX(m_defstr.length, 1024));
stmt_fld->maybe_null= TRUE;
@@ -2828,10 +3084,90 @@ int sp_head::add_instr(sp_instr *instr)
entire stored procedure, as their life span is equal.
*/
instr->mem_root= &main_mem_root;
+ instr->m_lineno= m_thd->m_parser_state->m_lip.yylineno;
return insert_dynamic(&m_instr, (uchar*)&instr);
}
+bool sp_head::add_instr_jump(THD *thd, sp_pcontext *spcont)
+{
+ sp_instr_jump *i= new (thd->mem_root) sp_instr_jump(instructions(), spcont);
+ return i == NULL || add_instr(i);
+}
+
+
+bool sp_head::add_instr_jump(THD *thd, sp_pcontext *spcont, uint dest)
+{
+ sp_instr_jump *i= new (thd->mem_root) sp_instr_jump(instructions(),
+ spcont, dest);
+ return i == NULL || add_instr(i);
+}
+
+
+bool sp_head::add_instr_jump_forward_with_backpatch(THD *thd,
+ sp_pcontext *spcont,
+ sp_label *lab)
+{
+ sp_instr_jump *i= new (thd->mem_root) sp_instr_jump(instructions(), spcont);
+ if (i == NULL || add_instr(i))
+ return true;
+ push_backpatch(thd, i, lab);
+ return false;
+}
+
+
+bool sp_head::add_instr_freturn(THD *thd, sp_pcontext *spcont,
+ Item *item, LEX *lex)
+{
+ sp_instr_freturn *i= new (thd->mem_root)
+ sp_instr_freturn(instructions(), spcont, item,
+ m_return_field_def.type_handler(), thd->lex);
+ if (i == NULL || add_instr(i))
+ return true;
+ m_flags|= sp_head::HAS_RETURN;
+ return false;
+}
+
+
+bool sp_head::add_instr_preturn(THD *thd, sp_pcontext *spcont)
+{
+ sp_instr_preturn *i= new (thd->mem_root)
+ sp_instr_preturn(instructions(), spcont);
+ if (i == NULL || add_instr(i))
+ return true;
+ return false;
+}
+
+
+/*
+ Replace an instruction at position to "no operation".
+
+ @param thd - use mem_root of this THD for "new".
+ @param ip - position of the operation
+ @returns - true on error, false on success
+
+ When we need to remove an instruction that during compilation
+ appeared to be useless (typically as useless jump), we replace
+ it to a jump to exactly the next instruction.
+ Such jumps are later removed during sp_head::optimize().
+
+ QQ: Perhaps we need a dedicated sp_instr_nop for this purpose.
+*/
+
+bool sp_head::replace_instr_to_nop(THD *thd, uint ip)
+{
+ sp_instr *instr= get_instr(ip);
+ sp_instr_jump *nop= new (thd->mem_root) sp_instr_jump(instr->m_ip,
+ instr->m_ctx,
+ instr->m_ip + 1);
+ if (!nop)
+ return true;
+ delete instr;
+ set_dynamic(&m_instr, (uchar *) &nop, ip);
+ return false;
+}
+
+
/**
Do some minimal optimization of the code:
-# Mark used instructions
@@ -2854,6 +3190,8 @@ void sp_head::optimize()
sp_instr *i;
uint src, dst;
+ DBUG_EXECUTE_IF("sp_head_optimize_disable", return; );
+
opt_mark();
bp.empty();
@@ -2942,6 +3280,7 @@ sp_head::opt_mark()
@return
0 if ok, !=0 on error.
*/
+
int
sp_head::show_routine_code(THD *thd)
{
@@ -2989,7 +3328,7 @@ sp_head::show_routine_code(THD *thd)
push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, tmp);
}
protocol->prepare_for_resend();
- protocol->store((longlong)ip);
+ protocol->store_long(ip);
buffer.set("", 0, system_charset_info);
i->print(&buffer);
@@ -3090,7 +3429,7 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
res= check_dependencies_in_with_clauses(m_lex->with_clauses_list) ||
instr->exec_open_and_lock_tables(thd, m_lex->query_tables);
- if (!res)
+ if (likely(!res))
{
res= instr->exec_core(thd, nextp);
DBUG_PRINT("info",("exec_core returned: %d", res));
@@ -3110,7 +3449,6 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
thd->get_stmt_da()->set_overwrite_status(false);
}
- thd_proc_info(thd, "closing tables");
close_thread_tables(thd);
thd_proc_info(thd, 0);
@@ -3151,11 +3489,7 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
Update the state of the active arena if no errors on
open_tables stage.
*/
- if (!res || !thd->is_error() ||
- (thd->get_stmt_da()->sql_errno() != ER_CANT_REOPEN_TABLE &&
- thd->get_stmt_da()->sql_errno() != ER_NO_SUCH_TABLE &&
- thd->get_stmt_da()->sql_errno() != ER_NO_SUCH_TABLE_IN_ENGINE &&
- thd->get_stmt_da()->sql_errno() != ER_UPDATE_TABLE_USED))
+ if (likely(!res) || likely(!thd->is_error()))
thd->stmt_arena->state= Query_arena::STMT_EXECUTED;
/*
@@ -3180,6 +3514,25 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
}
+int sp_lex_keeper::cursor_reset_lex_and_exec_core(THD *thd, uint *nextp,
+ bool open_tables,
+ sp_instr *instr)
+{
+ Query_arena *old_arena= thd->stmt_arena;
+ /*
+ Get the Query_arena from the cursor statement LEX, which contains
+ the free_list of the query, so new items (if any) are stored in
+ the right free_list, and we can cleanup after each cursor operation,
+ e.g. open or cursor_copy_struct (for cursor%ROWTYPE variables).
+ */
+ thd->stmt_arena= m_lex->query_arena();
+ int res= reset_lex_and_exec_core(thd, nextp, open_tables, instr);
+ cleanup_items(thd->stmt_arena->free_list);
+ thd->stmt_arena= old_arena;
+ return res;
+}
+
+
/*
sp_instr class functions
*/
@@ -3225,14 +3578,20 @@ int
sp_instr_stmt::execute(THD *thd, uint *nextp)
{
int res;
+ bool save_enable_slow_log;
+ const CSET_STRING query_backup= thd->query_string;
+ Sub_statement_state backup_state;
DBUG_ENTER("sp_instr_stmt::execute");
DBUG_PRINT("info", ("command: %d", m_lex_keeper.sql_command()));
- const CSET_STRING query_backup= thd->query_string;
#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);
#endif
+
+ save_enable_slow_log= thd->enable_slow_log;
+ thd->store_slow_query_state(&backup_state);
+
if (!(res= alloc_query(thd, m_query.str, m_query.length)) &&
!(res=subst_spvars(thd, this, &m_query)))
{
@@ -3245,6 +3604,7 @@ sp_instr_stmt::execute(THD *thd, uint *nextp)
if (query_cache_send_result_to_client(thd, thd->query(),
thd->query_length()) <= 0)
{
+ thd->reset_slow_query_state();
res= m_lex_keeper.reset_lex_and_exec_core(thd, nextp, FALSE, this);
bool log_slow= !res && thd->enable_slow_log;
@@ -3264,6 +3624,15 @@ sp_instr_stmt::execute(THD *thd, uint *nextp)
if (log_slow)
log_slow_statement(thd);
+
+ /*
+ Restore enable_slow_log, that can be changed by a admin or call
+ command
+ */
+ thd->enable_slow_log= save_enable_slow_log;
+
+ /* Add the number of rows to thd for the 'call' statistics */
+ thd->add_slow_query_state(&backup_state);
}
else
{
@@ -3278,12 +3647,13 @@ sp_instr_stmt::execute(THD *thd, uint *nextp)
thd->set_query(query_backup);
thd->query_name_consts= 0;
- if (!thd->is_error())
+ if (likely(!thd->is_error()))
{
res= 0;
thd->get_stmt_da()->reset_diagnostics_area();
}
}
+
DBUG_RETURN(res || thd->is_error());
}
@@ -3291,7 +3661,7 @@ sp_instr_stmt::execute(THD *thd, uint *nextp)
void
sp_instr_stmt::print(String *str)
{
- uint i, len;
+ size_t i, len;
/* stmt CMD "..." */
if (str->reserve(SP_STMT_PRINT_MAXLEN+SP_INSTR_UINT_MAXLEN+8))
@@ -3325,7 +3695,7 @@ sp_instr_stmt::exec_core(THD *thd, uint *nextp)
{
MYSQL_QUERY_EXEC_START(thd->query(),
thd->thread_id,
- (char *) (thd->db ? thd->db : ""),
+ thd->get_db(),
&thd->security_ctx->priv_user[0],
(char *)thd->security_ctx->host_or_ip,
3);
@@ -3350,23 +3720,17 @@ sp_instr_set::execute(THD *thd, uint *nextp)
}
-int
-sp_instr_set::exec_core(THD *thd, uint *nextp)
+sp_rcontext *sp_instr_set::get_rcontext(THD *thd) const
{
- int res= thd->spcont->set_variable(thd, m_offset, &m_value);
+ return m_rcontext_handler->get_rcontext(thd->spcont);
+}
- if (res)
- {
- /* Failed to evaluate the value. Reset the variable to NULL. */
- if (thd->spcont->set_variable(thd, m_offset, 0))
- {
- /* If this also failed, let's abort. */
- my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
- }
- }
+int
+sp_instr_set::exec_core(THD *thd, uint *nextp)
+{
+ int res= get_rcontext(thd)->set_variable(thd, m_offset, &m_value);
delete_explain_query(thd->lex);
-
*nextp = m_ip+1;
return res;
}
@@ -3375,18 +3739,20 @@ void
sp_instr_set::print(String *str)
{
/* set name@offset ... */
- int rsrv = SP_INSTR_UINT_MAXLEN+6;
+ size_t rsrv = SP_INSTR_UINT_MAXLEN+6;
sp_variable *var = m_ctx->find_variable(m_offset);
+ const LEX_CSTRING *prefix= m_rcontext_handler->get_name_prefix();
/* 'var' should always be non-null, but just in case... */
if (var)
- rsrv+= var->name.length;
+ rsrv+= var->name.length + prefix->length;
if (str->reserve(rsrv))
return;
str->qs_append(STRING_WITH_LEN("set "));
+ str->qs_append(prefix->str, prefix->length);
if (var)
{
- str->qs_append(var->name.str, var->name.length);
+ str->qs_append(&var->name);
str->qs_append('@');
}
str->qs_append(m_offset);
@@ -3397,6 +3763,100 @@ sp_instr_set::print(String *str)
/*
+ sp_instr_set_field class functions
+*/
+
+int
+sp_instr_set_row_field::exec_core(THD *thd, uint *nextp)
+{
+ int res= get_rcontext(thd)->set_variable_row_field(thd, m_offset,
+ m_field_offset,
+ &m_value);
+ delete_explain_query(thd->lex);
+ *nextp= m_ip + 1;
+ return res;
+}
+
+
+void
+sp_instr_set_row_field::print(String *str)
+{
+ /* set name@offset[field_offset] ... */
+ size_t rsrv= SP_INSTR_UINT_MAXLEN + 6 + 6 + 3;
+ sp_variable *var= m_ctx->find_variable(m_offset);
+ const LEX_CSTRING *prefix= m_rcontext_handler->get_name_prefix();
+ DBUG_ASSERT(var);
+ DBUG_ASSERT(var->field_def.is_row());
+ const Column_definition *def=
+ var->field_def.row_field_definitions()->elem(m_field_offset);
+ DBUG_ASSERT(def);
+
+ rsrv+= var->name.length + def->field_name.length + prefix->length;
+ if (str->reserve(rsrv))
+ return;
+ str->qs_append(STRING_WITH_LEN("set "));
+ str->qs_append(prefix);
+ str->qs_append(&var->name);
+ str->qs_append('.');
+ str->qs_append(&def->field_name);
+ str->qs_append('@');
+ str->qs_append(m_offset);
+ str->qs_append('[');
+ str->qs_append(m_field_offset);
+ str->qs_append(']');
+ str->qs_append(' ');
+ m_value->print(str, enum_query_type(QT_ORDINARY |
+ QT_ITEM_ORIGINAL_FUNC_NULLIF));
+}
+
+
+/*
+ sp_instr_set_field_by_name class functions
+*/
+
+int
+sp_instr_set_row_field_by_name::exec_core(THD *thd, uint *nextp)
+{
+ int res= get_rcontext(thd)->set_variable_row_field_by_name(thd, m_offset,
+ m_field_name,
+ &m_value);
+ delete_explain_query(thd->lex);
+ *nextp= m_ip + 1;
+ return res;
+}
+
+
+void
+sp_instr_set_row_field_by_name::print(String *str)
+{
+ /* set name.field@offset["field"] ... */
+ size_t rsrv= SP_INSTR_UINT_MAXLEN + 6 + 6 + 3 + 2;
+ sp_variable *var= m_ctx->find_variable(m_offset);
+ const LEX_CSTRING *prefix= m_rcontext_handler->get_name_prefix();
+ DBUG_ASSERT(var);
+ DBUG_ASSERT(var->field_def.is_table_rowtype_ref() ||
+ var->field_def.is_cursor_rowtype_ref());
+
+ rsrv+= var->name.length + 2 * m_field_name.length + prefix->length;
+ if (str->reserve(rsrv))
+ return;
+ str->qs_append(STRING_WITH_LEN("set "));
+ str->qs_append(prefix);
+ str->qs_append(&var->name);
+ str->qs_append('.');
+ str->qs_append(&m_field_name);
+ str->qs_append('@');
+ str->qs_append(m_offset);
+ str->qs_append("[\"",2);
+ str->qs_append(&m_field_name);
+ str->qs_append("\"]",2);
+ str->qs_append(' ');
+ m_value->print(str, enum_query_type(QT_ORDINARY |
+ QT_ITEM_ORIGINAL_FUNC_NULLIF));
+}
+
+
+/*
sp_instr_set_trigger_field class functions
*/
@@ -3525,7 +3985,7 @@ sp_instr_jump_if_not::exec_core(THD *thd, uint *nextp)
Item *it;
int res;
- it= sp_prepare_func_item(thd, &m_expr);
+ it= thd->sp_prepare_func_item(&m_expr);
if (! it)
{
res= -1;
@@ -3621,8 +4081,21 @@ sp_instr_freturn::exec_core(THD *thd, uint *nextp)
That means, Diagnostics Area should be clean before its execution.
*/
- Diagnostics_area *da= thd->get_stmt_da();
- da->clear_warning_info(da->warning_info_id());
+ if (!(thd->variables.sql_mode & MODE_ORACLE))
+ {
+ /*
+ Don't clean warnings in ORACLE mode,
+ as they are needed for SQLCODE and SQLERRM:
+ BEGIN
+ SELECT a INTO a FROM t1;
+ RETURN 'No exception ' || SQLCODE || ' ' || SQLERRM;
+ EXCEPTION WHEN NO_DATA_FOUND THEN
+ RETURN 'Exception ' || SQLCODE || ' ' || SQLERRM;
+ END;
+ */
+ Diagnostics_area *da= thd->get_stmt_da();
+ da->clear_warning_info(da->warning_info_id());
+ }
/*
Change <next instruction pointer>, so that this will be the last
@@ -3649,7 +4122,7 @@ sp_instr_freturn::print(String *str)
if (str->reserve(1024+8+32)) // Add some for the expr. too
return;
str->qs_append(STRING_WITH_LEN("freturn "));
- str->qs_append((uint)m_type);
+ str->qs_append(m_type_handler->name().ptr());
str->qs_append(' ');
m_value->print(str, enum_query_type(QT_ORDINARY |
QT_ITEM_ORIGINAL_FUNC_NULLIF));
@@ -3664,7 +4137,7 @@ sp_instr_hpush_jump::execute(THD *thd, uint *nextp)
{
DBUG_ENTER("sp_instr_hpush_jump::execute");
- int ret= thd->spcont->push_handler(m_handler, m_ip + 1);
+ int ret= thd->spcont->push_handler(this);
*nextp= m_dest;
@@ -3823,21 +4296,23 @@ sp_instr_cpush::execute(THD *thd, uint *nextp)
{
DBUG_ENTER("sp_instr_cpush::execute");
- int ret= thd->spcont->push_cursor(thd, &m_lex_keeper, this);
+ sp_cursor::reset(thd, &m_lex_keeper);
+ m_lex_keeper.disable_query_cache();
+ thd->spcont->push_cursor(this);
*nextp= m_ip+1;
- DBUG_RETURN(ret);
+ DBUG_RETURN(false);
}
void
sp_instr_cpush::print(String *str)
{
- const LEX_STRING *cursor_name= m_ctx->find_cursor(m_cursor);
+ const LEX_CSTRING *cursor_name= m_ctx->find_cursor(m_cursor);
/* cpush name@offset */
- uint rsrv= SP_INSTR_UINT_MAXLEN+7;
+ size_t rsrv= SP_INSTR_UINT_MAXLEN+7;
if (cursor_name)
rsrv+= cursor_name->length;
@@ -3861,7 +4336,7 @@ int
sp_instr_cpop::execute(THD *thd, uint *nextp)
{
DBUG_ENTER("sp_instr_cpop::execute");
- thd->spcont->pop_cursors(m_count);
+ thd->spcont->pop_cursors(thd, m_count);
*nextp= m_ip+1;
DBUG_RETURN(0);
}
@@ -3903,19 +4378,7 @@ sp_instr_copen::execute(THD *thd, uint *nextp)
else
{
sp_lex_keeper *lex_keeper= c->get_lex_keeper();
- Query_arena *old_arena= thd->stmt_arena;
-
- /*
- Get the Query_arena from the cpush instruction, which contains
- the free_list of the query, so new items (if any) are stored in
- the right free_list, and we can cleanup after each open.
- */
- thd->stmt_arena= c->get_instr();
- res= lex_keeper->reset_lex_and_exec_core(thd, nextp, FALSE, this);
- /* Cleanup the query's items */
- if (thd->stmt_arena->free_list)
- cleanup_items(thd->stmt_arena->free_list);
- thd->stmt_arena= old_arena;
+ res= lex_keeper->cursor_reset_lex_and_exec_core(thd, nextp, FALSE, this);
/* TODO: Assert here that we either have an error or a cursor */
}
DBUG_RETURN(res);
@@ -3934,10 +4397,10 @@ sp_instr_copen::exec_core(THD *thd, uint *nextp)
void
sp_instr_copen::print(String *str)
{
- const LEX_STRING *cursor_name= m_ctx->find_cursor(m_cursor);
+ const LEX_CSTRING *cursor_name= m_ctx->find_cursor(m_cursor);
/* copen name@offset */
- uint rsrv= SP_INSTR_UINT_MAXLEN+7;
+ size_t rsrv= SP_INSTR_UINT_MAXLEN+7;
if (cursor_name)
rsrv+= cursor_name->length;
@@ -3976,10 +4439,10 @@ sp_instr_cclose::execute(THD *thd, uint *nextp)
void
sp_instr_cclose::print(String *str)
{
- const LEX_STRING *cursor_name= m_ctx->find_cursor(m_cursor);
+ const LEX_CSTRING *cursor_name= m_ctx->find_cursor(m_cursor);
/* cclose name@offset */
- uint rsrv= SP_INSTR_UINT_MAXLEN+8;
+ size_t rsrv= SP_INSTR_UINT_MAXLEN+8;
if (cursor_name)
rsrv+= cursor_name->length;
@@ -4007,7 +4470,7 @@ sp_instr_cfetch::execute(THD *thd, uint *nextp)
Query_arena backup_arena;
DBUG_ENTER("sp_instr_cfetch::execute");
- res= c ? c->fetch(thd, &m_varlist) : -1;
+ res= c ? c->fetch(thd, &m_varlist, m_error_on_no_data) : -1;
*nextp= m_ip+1;
DBUG_RETURN(res);
@@ -4019,10 +4482,10 @@ sp_instr_cfetch::print(String *str)
{
List_iterator_fast<sp_variable> li(m_varlist);
sp_variable *pv;
- const LEX_STRING *cursor_name= m_ctx->find_cursor(m_cursor);
+ const LEX_CSTRING *cursor_name= m_ctx->find_cursor(m_cursor);
/* cfetch name@offset vars... */
- uint rsrv= SP_INSTR_UINT_MAXLEN+8;
+ size_t rsrv= SP_INSTR_UINT_MAXLEN+8;
if (cursor_name)
rsrv+= cursor_name->length;
@@ -4040,12 +4503,131 @@ sp_instr_cfetch::print(String *str)
if (str->reserve(pv->name.length+SP_INSTR_UINT_MAXLEN+2))
return;
str->qs_append(' ');
- str->qs_append(pv->name.str, pv->name.length);
+ str->qs_append(&pv->name);
str->qs_append('@');
str->qs_append(pv->offset);
}
}
+int
+sp_instr_agg_cfetch::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_agg_cfetch::execute");
+ int res= 0;
+ if (!thd->spcont->instr_ptr)
+ {
+ *nextp= m_ip+1;
+ thd->spcont->instr_ptr= m_ip + 1;
+ }
+ else if (!thd->spcont->pause_state)
+ thd->spcont->pause_state= TRUE;
+ else
+ {
+ thd->spcont->pause_state= FALSE;
+ if (thd->server_status & SERVER_STATUS_LAST_ROW_SENT)
+ {
+ my_message(ER_SP_FETCH_NO_DATA,
+ ER_THD(thd, ER_SP_FETCH_NO_DATA), MYF(0));
+ res= -1;
+ thd->spcont->quit_func= TRUE;
+ }
+ else
+ *nextp= m_ip + 1;
+ }
+ DBUG_RETURN(res);
+}
+
+void
+sp_instr_agg_cfetch::print(String *str)
+{
+
+ uint rsrv= SP_INSTR_UINT_MAXLEN+11;
+
+ if (str->reserve(rsrv))
+ return;
+ str->qs_append(STRING_WITH_LEN("agg_cfetch"));
+}
+
+/*
+ sp_instr_cursor_copy_struct class functions
+*/
+
+/**
+ This methods processes cursor %ROWTYPE declarations, e.g.:
+ CURSOR cur IS SELECT * FROM t1;
+ rec cur%ROWTYPE;
+ and does the following:
+ - opens the cursor without copying data (materialization).
+ - copies the cursor structure to the associated %ROWTYPE variable.
+*/
+
+int
+sp_instr_cursor_copy_struct::exec_core(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_cursor_copy_struct::exec_core");
+ int ret= 0;
+ Item_field_row *row= (Item_field_row*) thd->spcont->get_variable(m_var);
+ DBUG_ASSERT(row->type_handler() == &type_handler_row);
+
+ /*
+ Copy structure only once. If the cursor%ROWTYPE variable is declared
+ inside a LOOP block, it gets its structure on the first loop interation
+ and remembers the structure for all consequent loop iterations.
+ It we recreated the structure on every iteration, we would get
+ potential memory leaks, and it would be less efficient.
+ */
+ if (!row->arguments())
+ {
+ sp_cursor tmp(thd, &m_lex_keeper, true);
+ // Open the cursor without copying data
+ if (!(ret= tmp.open(thd)))
+ {
+ Row_definition_list defs;
+ /*
+ Create row elements on the caller arena.
+ It's the same arena that was used during sp_rcontext::create().
+ This puts cursor%ROWTYPE elements on the same mem_root
+ where explicit ROW elements and table%ROWTYPE reside:
+ - tmp.export_structure() allocates new Spvar_definition instances
+ and their components (such as TYPELIBs).
+ - row->row_create_items() creates new Item_field instances.
+ They all are created on the same mem_root.
+ */
+ Query_arena current_arena;
+ thd->set_n_backup_active_arena(thd->spcont->callers_arena, &current_arena);
+ if (!(ret= tmp.export_structure(thd, &defs)))
+ row->row_create_items(thd, &defs);
+ thd->restore_active_arena(thd->spcont->callers_arena, &current_arena);
+ tmp.close(thd);
+ }
+ }
+ *nextp= m_ip + 1;
+ DBUG_RETURN(ret);
+}
+
+
+int
+sp_instr_cursor_copy_struct::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_cursor_copy_struct::execute");
+ int ret= m_lex_keeper.cursor_reset_lex_and_exec_core(thd, nextp, FALSE, this);
+ DBUG_RETURN(ret);
+}
+
+
+void
+sp_instr_cursor_copy_struct::print(String *str)
+{
+ sp_variable *var= m_ctx->find_variable(m_var);
+ const LEX_CSTRING *name= m_ctx->find_cursor(m_cursor);
+ str->append(STRING_WITH_LEN("cursor_copy_struct "));
+ str->append(name);
+ str->append(' ');
+ str->append(&var->name);
+ str->append('@');
+ str->append_ulonglong(m_var);
+}
+
/*
sp_instr_error class functions
@@ -4170,12 +4752,13 @@ typedef struct st_sp_table
db_name\0table_name\0 - for temporary tables
*/
LEX_STRING qname;
- uint db_length, table_name_length;
+ size_t db_length, table_name_length;
bool temp; /* true if corresponds to a temporary table */
thr_lock_type lock_type; /* lock type used for prelocking */
uint lock_count;
uint query_lock_count;
uint8 trg_event_map;
+ my_bool for_insert_data;
} SP_TABLE;
@@ -4211,7 +4794,8 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check)
{
SP_TABLE *tab;
- if (lex_for_tmp_check->sql_command == SQLCOM_DROP_TABLE &&
+ if ((lex_for_tmp_check->sql_command == SQLCOM_DROP_TABLE ||
+ lex_for_tmp_check->sql_command == SQLCOM_DROP_SEQUENCE) &&
lex_for_tmp_check->tmp_table())
return TRUE;
@@ -4237,12 +4821,12 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check)
uint temp_table_key_length;
tname.length(0);
- tname.append(table->db, table->db_length);
+ tname.append(&table->db);
tname.append('\0');
- tname.append(table->table_name, table->table_name_length);
+ tname.append(&table->table_name);
tname.append('\0');
temp_table_key_length= tname.length();
- tname.append(table->alias);
+ tname.append(&table->alias);
tname.append('\0');
/*
@@ -4270,12 +4854,14 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check)
if (tab->query_lock_count > tab->lock_count)
tab->lock_count++;
tab->trg_event_map|= table->trg_event_map;
+ tab->for_insert_data|= table->for_insert_data;
}
else
{
if (!(tab= (SP_TABLE *)thd->calloc(sizeof(SP_TABLE))))
return FALSE;
- if (lex_for_tmp_check->sql_command == SQLCOM_CREATE_TABLE &&
+ if ((lex_for_tmp_check->sql_command == SQLCOM_CREATE_TABLE ||
+ lex_for_tmp_check->sql_command == SQLCOM_CREATE_SEQUENCE) &&
lex_for_tmp_check->query_tables == table &&
lex_for_tmp_check->tmp_table())
{
@@ -4287,11 +4873,12 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check)
tab->qname.str= (char*) thd->memdup(tname.ptr(), tab->qname.length);
if (!tab->qname.str)
return FALSE;
- tab->table_name_length= table->table_name_length;
- tab->db_length= table->db_length;
+ tab->table_name_length= table->table_name.length;
+ tab->db_length= table->db.length;
tab->lock_type= table->lock_type;
tab->lock_count= tab->query_lock_count= 1;
tab->trg_event_map= table->trg_event_map;
+ tab->for_insert_data= table->for_insert_data;
if (my_hash_insert(&m_sptabs, (uchar *)tab))
return FALSE;
}
@@ -4345,8 +4932,8 @@ sp_head::add_used_tables_to_table_list(THD *thd,
for (i=0 ; i < m_sptabs.records ; i++)
{
char *tab_buff, *key_buff;
- TABLE_LIST *table;
SP_TABLE *stab= (SP_TABLE*) my_hash_element(&m_sptabs, i);
+ LEX_CSTRING db_name;
if (stab->temp)
continue;
@@ -4356,15 +4943,27 @@ sp_head::add_used_tables_to_table_list(THD *thd,
stab->qname.length)))
DBUG_RETURN(FALSE);
+ db_name.str= key_buff;
+ db_name.length= stab->db_length;
+
+
for (uint j= 0; j < stab->lock_count; j++)
{
- table= (TABLE_LIST *)tab_buff;
- table->init_one_table_for_prelocking(key_buff, stab->db_length,
- key_buff + stab->db_length + 1, stab->table_name_length,
- key_buff + stab->db_length + stab->table_name_length + 2,
- stab->lock_type, true, belong_to_view, stab->trg_event_map,
- query_tables_last_ptr);
-
+ TABLE_LIST *table= (TABLE_LIST *)tab_buff;
+ LEX_CSTRING table_name= { key_buff + stab->db_length + 1,
+ stab->table_name_length };
+ LEX_CSTRING alias= { table_name.str + table_name.length + 1,
+ strlen(table_name.str + table_name.length + 1) };
+
+ table->init_one_table_for_prelocking(&db_name,
+ &table_name,
+ &alias,
+ stab->lock_type,
+ TABLE_LIST::PRELOCK_ROUTINE,
+ belong_to_view,
+ stab->trg_event_map,
+ query_tables_last_ptr,
+ stab->for_insert_data);
tab_buff+= ALIGN_SIZE(sizeof(TABLE_LIST));
result= TRUE;
}
@@ -4384,7 +4983,7 @@ sp_head::add_used_tables_to_table_list(THD *thd,
TABLE_LIST *
sp_add_to_query_tables(THD *thd, LEX *lex,
- const char *db, const char *name,
+ const LEX_CSTRING *db, const LEX_CSTRING *name,
thr_lock_type locktype,
enum_mdl_type mdl_type)
{
@@ -4392,17 +4991,280 @@ sp_add_to_query_tables(THD *thd, LEX *lex,
if (!(table= (TABLE_LIST *)thd->calloc(sizeof(TABLE_LIST))))
return NULL;
- table->db_length= strlen(db);
- table->db= thd->strmake(db, table->db_length);
- table->table_name_length= strlen(name);
- table->table_name= thd->strmake(name, table->table_name_length);
- table->alias= thd->strdup(name);
+ if (!thd->make_lex_string(&table->db, db->str, db->length) ||
+ !thd->make_lex_string(&table->table_name, name->str, name->length) ||
+ !thd->make_lex_string(&table->alias, name->str, name->length))
+ return NULL;
+
table->lock_type= locktype;
table->select_lex= lex->current_select;
table->cacheable_table= 1;
- table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name,
+ table->mdl_request.init(MDL_key::TABLE, table->db.str, table->table_name.str,
mdl_type, MDL_TRANSACTION);
lex->add_to_query_tables(table);
return table;
}
+
+
+Item *sp_head::adjust_assignment_source(THD *thd, Item *val, Item *val2)
+{
+ return val ? val : val2 ? val2 : new (thd->mem_root) Item_null(thd);
+}
+
+/**
+ Helper action for a SET statement.
+ Used to push a SP local variable into the assignment list.
+
+ @param var_type the SP local variable
+ @param val the value being assigned to the variable
+
+ @return TRUE if error, FALSE otherwise.
+*/
+
+bool
+sp_head::set_local_variable(THD *thd, sp_pcontext *spcont,
+ const Sp_rcontext_handler *rh,
+ sp_variable *spv, Item *val, LEX *lex,
+ bool responsible_to_free_lex)
+{
+ if (!(val= adjust_assignment_source(thd, val, spv->default_value)))
+ return true;
+
+ sp_instr_set *sp_set= new (thd->mem_root)
+ sp_instr_set(instructions(), spcont, rh,
+ spv->offset, val, lex,
+ responsible_to_free_lex);
+
+ return sp_set == NULL || add_instr(sp_set);
+}
+
+
+/**
+ Similar to set_local_variable(), but for ROW variable fields.
+*/
+
+bool
+sp_head::set_local_variable_row_field(THD *thd, sp_pcontext *spcont,
+ const Sp_rcontext_handler *rh,
+ sp_variable *spv, uint field_idx,
+ Item *val, LEX *lex)
+{
+ if (!(val= adjust_assignment_source(thd, val, NULL)))
+ return true;
+
+ sp_instr_set_row_field *sp_set= new (thd->mem_root)
+ sp_instr_set_row_field(instructions(),
+ spcont, rh,
+ spv->offset,
+ field_idx, val,
+ lex, true);
+ return sp_set == NULL || add_instr(sp_set);
+}
+
+
+bool
+sp_head::set_local_variable_row_field_by_name(THD *thd, sp_pcontext *spcont,
+ const Sp_rcontext_handler *rh,
+ sp_variable *spv,
+ const LEX_CSTRING *field_name,
+ Item *val, LEX *lex)
+{
+ if (!(val= adjust_assignment_source(thd, val, NULL)))
+ return true;
+
+ sp_instr_set_row_field_by_name *sp_set=
+ new (thd->mem_root) sp_instr_set_row_field_by_name(instructions(),
+ spcont, rh,
+ spv->offset,
+ *field_name,
+ val,
+ lex, true);
+ return sp_set == NULL || add_instr(sp_set);
+}
+
+
+bool sp_head::add_open_cursor(THD *thd, sp_pcontext *spcont, uint offset,
+ sp_pcontext *param_spcont,
+ List<sp_assignment_lex> *parameters)
+{
+ /*
+ The caller must make sure that the number of formal parameters matches
+ the number of actual parameters.
+ */
+ DBUG_ASSERT((param_spcont ? param_spcont->context_var_count() : 0) ==
+ (parameters ? parameters->elements : 0));
+
+ if (parameters &&
+ add_set_cursor_param_variables(thd, param_spcont, parameters))
+ return true;
+
+ sp_instr_copen *i= new (thd->mem_root)
+ sp_instr_copen(instructions(), spcont, offset);
+ return i == NULL || add_instr(i);
+}
+
+
+bool sp_head::add_for_loop_open_cursor(THD *thd, sp_pcontext *spcont,
+ sp_variable *index,
+ const sp_pcursor *pcursor, uint coffset,
+ sp_assignment_lex *param_lex,
+ Item_args *parameters)
+{
+ if (parameters &&
+ add_set_for_loop_cursor_param_variables(thd, pcursor->param_context(),
+ param_lex, parameters))
+ return true;
+
+ sp_instr *instr_copy_struct=
+ new (thd->mem_root) sp_instr_cursor_copy_struct(instructions(),
+ spcont, coffset,
+ pcursor->lex(),
+ index->offset);
+ if (instr_copy_struct == NULL || add_instr(instr_copy_struct))
+ return true;
+
+ sp_instr_copen *instr_copen=
+ new (thd->mem_root) sp_instr_copen(instructions(), spcont, coffset);
+ if (instr_copen == NULL || add_instr(instr_copen))
+ return true;
+
+ sp_instr_cfetch *instr_cfetch=
+ new (thd->mem_root) sp_instr_cfetch(instructions(),
+ spcont, coffset, false);
+ if (instr_cfetch == NULL || add_instr(instr_cfetch))
+ return true;
+ instr_cfetch->add_to_varlist(index);
+ return false;
+}
+
+
+bool
+sp_head::add_set_for_loop_cursor_param_variables(THD *thd,
+ sp_pcontext *param_spcont,
+ sp_assignment_lex *param_lex,
+ Item_args *parameters)
+{
+ DBUG_ASSERT(param_spcont->context_var_count() == parameters->argument_count());
+ for (uint idx= 0; idx < parameters->argument_count(); idx ++)
+ {
+ /*
+ param_lex is shared between multiple items (cursor parameters).
+ Only the last sp_instr_set is responsible for freeing param_lex.
+ See more comments in LEX::sp_for_loop_cursor_declarations in sql_lex.cc.
+ */
+ bool last= idx + 1 == parameters->argument_count();
+ sp_variable *spvar= param_spcont->get_context_variable(idx);
+ if (set_local_variable(thd, param_spcont,
+ &sp_rcontext_handler_local,
+ spvar, parameters->arguments()[idx],
+ param_lex, last))
+ return true;
+ }
+ return false;
+}
+
+
+bool sp_head::spvar_fill_row(THD *thd,
+ sp_variable *spvar,
+ Row_definition_list *defs)
+{
+ spvar->field_def.set_row_field_definitions(defs);
+ spvar->field_def.field_name= spvar->name;
+ if (fill_spvar_definition(thd, &spvar->field_def))
+ return true;
+ row_fill_field_definitions(thd, defs);
+ return false;
+}
+
+
+bool sp_head::spvar_fill_type_reference(THD *thd,
+ sp_variable *spvar,
+ const LEX_CSTRING &table,
+ const LEX_CSTRING &col)
+{
+ Qualified_column_ident *ref;
+ if (!(ref= new (thd->mem_root) Qualified_column_ident(&table, &col)))
+ return true;
+ fill_spvar_using_type_reference(spvar, ref);
+ return false;
+}
+
+
+bool sp_head::spvar_fill_type_reference(THD *thd,
+ sp_variable *spvar,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &table,
+ const LEX_CSTRING &col)
+{
+ Qualified_column_ident *ref;
+ if (!(ref= new (thd->mem_root) Qualified_column_ident(thd, &db, &table, &col)))
+ return true;
+ fill_spvar_using_type_reference(spvar, ref);
+ return false;
+}
+
+
+bool sp_head::spvar_fill_table_rowtype_reference(THD *thd,
+ sp_variable *spvar,
+ const LEX_CSTRING &table)
+{
+ Table_ident *ref;
+ if (!(ref= new (thd->mem_root) Table_ident(&table)))
+ return true;
+ fill_spvar_using_table_rowtype_reference(thd, spvar, ref);
+ return false;
+}
+
+
+bool sp_head::spvar_fill_table_rowtype_reference(THD *thd,
+ sp_variable *spvar,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &table)
+{
+ Table_ident *ref;
+ if (!(ref= new (thd->mem_root) Table_ident(thd, &db, &table, false)))
+ return true;
+ fill_spvar_using_table_rowtype_reference(thd, spvar, ref);
+ return false;
+}
+
+
+/*
+ In Oracle mode stored routines have an optional name
+ at the end of a declaration:
+ PROCEDURE p1 AS
+ BEGIN
+ NULL
+ END p1;
+ Check that the first p1 and the last p1 match.
+*/
+
+bool sp_head::check_package_routine_end_name(const LEX_CSTRING &end_name) const
+{
+ LEX_CSTRING non_qualified_name= m_name;
+ const char *errpos;
+ size_t ofs;
+ if (!end_name.length)
+ return false; // No end name
+ if (!(errpos= strrchr(m_name.str, '.')))
+ {
+ errpos= m_name.str;
+ goto err;
+ }
+ errpos++;
+ ofs= errpos - m_name.str;
+ non_qualified_name.str+= ofs;
+ non_qualified_name.length-= ofs;
+ if (Sp_handler::eq_routine_name(end_name, non_qualified_name))
+ return false;
+err:
+ my_error(ER_END_IDENTIFIER_DOES_NOT_MATCH, MYF(0), end_name.str, errpos);
+ return true;
+}
+
+
+ulong sp_head::sp_cache_version() const
+{
+ return m_parent ? m_parent->sp_cache_version() : m_sp_cache_version;
+}
diff --git a/sql/sp_head.h b/sql/sp_head.h
index 882ff32f5e6..493bb777bdf 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -27,7 +27,6 @@
are dependencies on include order for set_var.h and item.h. This
will be resolved later.
*/
-#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_class.h" // THD, set_var.h: THD
#include "set_var.h" // Item
#include "sp_pcontext.h" // sp_pcontext
@@ -40,13 +39,8 @@
@{
*/
-// Values for the type enum. This reflects the order of the enum declaration
-// in the CREATE TABLE command.
-//#define TYPE_ENUM_FUNCTION 1 #define TYPE_ENUM_PROCEDURE 2 #define
-//TYPE_ENUM_TRIGGER 3 #define TYPE_ENUM_PROXY 4
-
Item::Type
-sp_map_item_type(enum enum_field_types type);
+sp_map_item_type(const Type_handler *handler);
uint
sp_get_flags_for_command(LEX *lex);
@@ -107,43 +101,39 @@ protected:
/*************************************************************************/
-class sp_name : public Sql_alloc
+class sp_name : public Sql_alloc,
+ public Database_qualified_name
{
public:
-
- LEX_STRING m_db;
- LEX_STRING m_name;
- LEX_STRING m_qname;
bool m_explicit_name; /**< Prepend the db name? */
- sp_name(LEX_STRING db, LEX_STRING name, bool use_explicit_name)
- : m_db(db), m_name(name), m_explicit_name(use_explicit_name)
+ sp_name(const LEX_CSTRING *db, const LEX_CSTRING *name,
+ bool use_explicit_name)
+ : Database_qualified_name(db, name), m_explicit_name(use_explicit_name)
{
if (lower_case_table_names && m_db.str)
- m_db.length= my_casedn_str(files_charset_info, m_db.str);
- m_qname.str= 0;
- m_qname.length= 0;
+ m_db.length= my_casedn_str(files_charset_info, (char*) m_db.str);
}
- /** Create temporary sp_name object from MDL key. */
+ /** Create temporary sp_name object from MDL key. Store in qname_buff */
sp_name(const MDL_key *key, char *qname_buff);
- // Init. the qualified name from the db and name.
- void init_qname(THD *thd); // thd for memroot allocation
-
~sp_name()
{}
};
bool
-check_routine_name(LEX_STRING *ident);
+check_routine_name(const LEX_CSTRING *ident);
-class sp_head :private Query_arena, public Sql_alloc
+class sp_head :private Query_arena,
+ public Database_qualified_name,
+ public Sql_alloc
{
sp_head(const sp_head &); /**< Prevent use of these */
void operator=(sp_head &);
+protected:
MEM_ROOT main_mem_root;
public:
/** Possible values of m_flags */
@@ -172,44 +162,72 @@ public:
b) because in CONTAINS SQL case they don't provide enough
information anyway.
*/
- MODIFIES_DATA= 4096
+ MODIFIES_DATA= 4096,
+ /*
+ Marks routines that have column type references: DECLARE a t1.a%TYPE;
+ */
+ HAS_COLUMN_TYPE_REFS= 8192,
+ /* Set if has FETCH GROUP NEXT ROW instr. Used to ensure that only
+ functions with AGGREGATE keyword use the instr. */
+ HAS_AGGREGATE_INSTR= 16384
};
- stored_procedure_type m_type;
+ sp_package *m_parent;
+ const Sp_handler *m_handler;
uint m_flags; // Boolean attributes of a stored routine
Column_definition m_return_field_def; /**< This is used for FUNCTIONs only. */
const char *m_tmp_query; ///< Temporary pointer to sub query string
- st_sp_chistics *m_chistics;
+private:
+ /*
+ Private to guarantee that m_chistics.comment is properly set to:
+ - a string which is alloced on this->mem_root
+ - or (NULL,0)
+ set_chistics() makes sure this.
+ */
+ Sp_chistics m_chistics;
+public:
sql_mode_t m_sql_mode; ///< For SHOW CREATE and execution
- LEX_STRING m_qname; ///< db.name
- bool m_explicit_name; ///< Prepend the db name? */
- LEX_STRING m_db;
- LEX_STRING m_name;
- LEX_STRING m_params;
- LEX_STRING m_body;
- LEX_STRING m_body_utf8;
- LEX_STRING m_defstr;
- LEX_STRING m_definer_user;
- LEX_STRING m_definer_host;
-
+ bool m_explicit_name; /**< Prepend the db name? */
+ LEX_CSTRING m_qname; ///< db.name
+ LEX_CSTRING m_params;
+ LEX_CSTRING m_body;
+ LEX_CSTRING m_body_utf8;
+ LEX_CSTRING m_defstr;
+ AUTHID m_definer;
+
+ const st_sp_chistics &chistics() const { return m_chistics; }
+ const LEX_CSTRING &comment() const { return m_chistics.comment; }
+ void set_suid(enum_sp_suid_behaviour suid) { m_chistics.suid= suid; }
+ enum_sp_suid_behaviour suid() const { return m_chistics.suid; }
+ bool detistic() const { return m_chistics.detistic; }
+ enum_sp_data_access daccess() const { return m_chistics.daccess; }
+ enum_sp_aggregate_type agg_type() const { return m_chistics.agg_type; }
/**
Is this routine being executed?
*/
- bool is_invoked() const { return m_flags & IS_INVOKED; }
+ virtual bool is_invoked() const { return m_flags & IS_INVOKED; }
/**
Get the value of the SP cache version, as remembered
when the routine was inserted into the cache.
*/
- ulong sp_cache_version() const { return m_sp_cache_version; }
+ ulong sp_cache_version() const;
/** Set the value of the SP cache version. */
- void set_sp_cache_version(ulong version_arg)
+ void set_sp_cache_version(ulong version_arg) const
{
m_sp_cache_version= version_arg;
}
+
+ sp_rcontext *rcontext_create(THD *thd, Field *retval, List<Item> *args);
+ sp_rcontext *rcontext_create(THD *thd, Field *retval,
+ Item **args, uint arg_count);
+ sp_rcontext *rcontext_create(THD *thd, Field *retval,
+ Row_definition_list *list,
+ bool switch_security_ctx);
+ bool eq_routine_spec(const sp_head *) const;
private:
/**
Version of the stored routine cache at the moment when the
@@ -221,7 +239,7 @@ private:
is obsolete and should not be used --
sp_cache_flush_obsolete() will purge it.
*/
- ulong m_sp_cache_version;
+ mutable ulong m_sp_cache_version;
Stored_program_creation_ctx *m_creation_ctx;
/**
Boolean combination of (1<<flag), where flag is a member of
@@ -298,16 +316,13 @@ public:
being opened is probably enough).
*/
SQL_I_List<Item_trigger_field> m_trg_table_fields;
-private:
- // users must use sp= sp_head::create()
- sp_head(MEM_ROOT *mem_root_arg);
- // users must use sp_head::destroy(sp)
+protected:
+ sp_head(MEM_ROOT *mem_root, sp_package *parent, const Sp_handler *handler);
virtual ~sp_head();
-
public:
- static sp_head* create();
static void destroy(sp_head *sp);
+ static sp_head *create(sp_package *parent, const Sp_handler *handler);
/// Initialize after we have reset mem_root
void
@@ -315,7 +330,7 @@ public:
/** Copy sp name from parser. */
void
- init_sp_name(THD *thd, sp_name *spname);
+ init_sp_name(const sp_name *spname);
/** Set the body-definition start position. */
void
@@ -328,25 +343,192 @@ public:
bool
execute_trigger(THD *thd,
- const LEX_STRING *db_name,
- const LEX_STRING *table_name,
+ const LEX_CSTRING *db_name,
+ const LEX_CSTRING *table_name,
GRANT_INFO *grant_info);
bool
- execute_function(THD *thd, Item **args, uint argcount, Field *return_fld);
+ execute_function(THD *thd, Item **args, uint argcount, Field *return_fld,
+ sp_rcontext **nctx, Query_arena *call_arena);
bool
execute_procedure(THD *thd, List<Item> *args);
static void
- show_create_routine_get_fields(THD *thd, int type, List<Item> *fields);
+ show_create_routine_get_fields(THD *thd, const Sp_handler *sph,
+ List<Item> *fields);
bool
- show_create_routine(THD *thd, int type);
+ show_create_routine(THD *thd, const Sp_handler *sph);
+
+ MEM_ROOT *get_main_mem_root() { return &main_mem_root; }
int
add_instr(sp_instr *instr);
+ bool
+ add_instr_jump(THD *thd, sp_pcontext *spcont);
+
+ bool
+ add_instr_jump(THD *thd, sp_pcontext *spcont, uint dest);
+
+ bool
+ add_instr_jump_forward_with_backpatch(THD *thd, sp_pcontext *spcont,
+ sp_label *lab);
+ bool
+ add_instr_jump_forward_with_backpatch(THD *thd, sp_pcontext *spcont)
+ {
+ return add_instr_jump_forward_with_backpatch(thd, spcont,
+ spcont->last_label());
+ }
+
+ bool
+ add_instr_freturn(THD *thd, sp_pcontext *spcont, Item *item, LEX *lex);
+
+ bool
+ add_instr_preturn(THD *thd, sp_pcontext *spcont);
+
+ Item *adjust_assignment_source(THD *thd, Item *val, Item *val2);
+ /**
+ @param thd - the current thd
+ @param spcont - the current parse context
+ @param spv - the SP variable
+ @param val - the value to be assigned to the variable
+ @param lex - the LEX that was used to create "val"
+ @param responsible_to_free_lex - if the generated sp_instr_set should
+ free "lex".
+ @retval true - on error
+ @retval false - on success
+ */
+ bool set_local_variable(THD *thd, sp_pcontext *spcont,
+ const Sp_rcontext_handler *rh,
+ sp_variable *spv, Item *val, LEX *lex,
+ bool responsible_to_free_lex);
+ bool set_local_variable_row_field(THD *thd, sp_pcontext *spcont,
+ const Sp_rcontext_handler *rh,
+ sp_variable *spv, uint field_idx,
+ Item *val, LEX *lex);
+ bool set_local_variable_row_field_by_name(THD *thd, sp_pcontext *spcont,
+ const Sp_rcontext_handler *rh,
+ sp_variable *spv,
+ const LEX_CSTRING *field_name,
+ Item *val, LEX *lex);
+ bool check_package_routine_end_name(const LEX_CSTRING &end_name) const;
+private:
+ /**
+ Generate a code to set a single cursor parameter variable.
+ @param thd - current thd, for mem_root allocations.
+ @param param_spcont - the context of the parameter block
+ @param idx - the index of the parameter
+ @param prm - the actual parameter (contains information about
+ the assignment source expression Item,
+ its free list, and its LEX)
+ */
+ bool add_set_cursor_param_variable(THD *thd,
+ sp_pcontext *param_spcont, uint idx,
+ sp_assignment_lex *prm)
+ {
+ DBUG_ASSERT(idx < param_spcont->context_var_count());
+ sp_variable *spvar= param_spcont->get_context_variable(idx);
+ /*
+ add_instr() gets free_list from m_thd->free_list.
+ Initialize it before the set_local_variable() call.
+ */
+ DBUG_ASSERT(m_thd->free_list == NULL);
+ m_thd->free_list= prm->get_free_list();
+ if (set_local_variable(thd, param_spcont,
+ &sp_rcontext_handler_local,
+ spvar, prm->get_item(), prm, true))
+ return true;
+ /*
+ Safety:
+ The item and its free_list are now fully owned by the sp_instr_set
+ instance, created by set_local_variable(). The sp_instr_set instance
+ is now responsible for freeing the item and the free_list.
+ Reset the "item" and the "free_list" members of "prm",
+ to avoid double pointers to the same objects from "prm" and
+ from the sp_instr_set instance.
+ */
+ prm->set_item_and_free_list(NULL, NULL);
+ return false;
+ }
+
+ /**
+ Generate a code to set all cursor parameter variables.
+ This method is called only when parameters exists,
+ and the number of formal parameters matches the number of actual
+ parameters. See also comments to add_open_cursor().
+ */
+ bool add_set_cursor_param_variables(THD *thd, sp_pcontext *param_spcont,
+ List<sp_assignment_lex> *parameters)
+ {
+ DBUG_ASSERT(param_spcont->context_var_count() == parameters->elements);
+ sp_assignment_lex *prm;
+ List_iterator<sp_assignment_lex> li(*parameters);
+ for (uint idx= 0; (prm= li++); idx++)
+ {
+ if (add_set_cursor_param_variable(thd, param_spcont, idx, prm))
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ Generate a code to set all cursor parameter variables for a FOR LOOP, e.g.:
+ FOR index IN cursor(1,2,3)
+ @param
+ */
+ bool add_set_for_loop_cursor_param_variables(THD *thd,
+ sp_pcontext *param_spcont,
+ sp_assignment_lex *param_lex,
+ Item_args *parameters);
+
+public:
+ /**
+ Generate a code for an "OPEN cursor" statement.
+ @param thd - current thd, for mem_root allocations
+ @param spcont - the context of the cursor
+ @param offset - the offset of the cursor
+ @param param_spcont - the context of the cursor parameter block
+ @param parameters - the list of the OPEN actual parameters
+
+ The caller must make sure that the number of local variables
+ in "param_spcont" (formal parameters) matches the number of list elements
+ in "parameters" (actual parameters).
+ NULL in either of them means 0 parameters.
+ */
+ bool add_open_cursor(THD *thd, sp_pcontext *spcont,
+ uint offset,
+ sp_pcontext *param_spcont,
+ List<sp_assignment_lex> *parameters);
+
+ /**
+ Generate an initiation code for a CURSOR FOR LOOP, e.g.:
+ FOR index IN cursor -- cursor without parameters
+ FOR index IN cursor(1,2,3) -- cursor with parameters
+
+ The code generated by this method does the following during SP run-time:
+ - Sets all cursor parameter vartiables from "parameters"
+ - Initializes the index ROW-type variable from the cursor
+ (the structure is copied from the cursor to the index variable)
+ - The cursor gets opened
+ - The first records is fetched from the cursor to the variable "index".
+
+ @param thd - the current thread (for mem_root and error reporting)
+ @param spcont - the current parse context
+ @param index - the loop "index" ROW-type variable
+ @param pcursor - the cursor
+ @param coffset - the cursor offset
+ @param param_lex - the LEX that owns Items in "parameters"
+ @param parameters - the cursor parameters Item array
+ @retval true - on error (EOM)
+ @retval false - on success
+ */
+ bool add_for_loop_open_cursor(THD *thd, sp_pcontext *spcont,
+ sp_variable *index,
+ const sp_pcursor *pcursor, uint coffset,
+ sp_assignment_lex *param_lex,
+ Item_args *parameters);
/**
Returns true if any substatement in the routine directly
(not through another routine) modifies data/changes table.
@@ -368,6 +550,8 @@ public:
return i;
}
+ bool replace_instr_to_nop(THD *thd, uint ip);
+
/*
Resets lex in 'thd' and keeps a copy of the old one.
@@ -376,6 +560,23 @@ public:
bool
reset_lex(THD *thd);
+ bool
+ reset_lex(THD *thd, sp_lex_local *sublex);
+
+ /**
+ Merge two LEX instances.
+ @param oldlex - the upper level LEX we're going to restore to.
+ @param sublex - the local lex that have just parsed some substatement.
+ @returns - false on success, true on error (e.g. failed to
+ merge the routine list or the table list).
+ This method is shared by:
+ - restore_lex(), when the old LEX is popped by sp_head::m_lex.pop()
+ - THD::restore_from_local_lex_to_old_lex(), when the old LEX
+ is stored in the caller's local variable.
+ */
+ bool
+ merge_lex(THD *thd, LEX *oldlex, LEX *sublex);
+
/**
Restores lex in 'thd' from our copy, but keeps some status from the
one in 'thd', like ptr, tables, fields, etc.
@@ -383,16 +584,40 @@ public:
@todo Conflicting comment in sp_head.cc
*/
bool
- restore_lex(THD *thd);
+ restore_lex(THD *thd)
+ {
+ DBUG_ENTER("sp_head::restore_lex");
+ LEX *oldlex= (LEX *) m_lex.pop();
+ if (!oldlex)
+ DBUG_RETURN(false); // Nothing to restore
+ LEX *sublex= thd->lex;
+ if (thd->restore_from_local_lex_to_old_lex(oldlex))// This restores thd->lex
+ DBUG_RETURN(true);
+ if (!sublex->sp_lex_in_use)
+ {
+ sublex->sphead= NULL;
+ lex_end(sublex);
+ delete sublex;
+ }
+ DBUG_RETURN(false);
+ }
/// Put the instruction on the backpatch list, associated with the label.
int
push_backpatch(THD *thd, sp_instr *, sp_label *);
+ int
+ push_backpatch_goto(THD *thd, sp_pcontext *ctx, sp_label *lab);
/// Update all instruction with this label in the backpatch list to
/// the current position.
void
backpatch(sp_label *);
+ void
+ backpatch_goto(THD *thd, sp_label *, sp_label *);
+
+ /// Check for unresolved goto label
+ bool
+ check_unresolved_goto();
/// Start a new cont. backpatch level. If 'i' is NULL, the level is just incr.
int
@@ -406,26 +631,123 @@ public:
void
do_cont_backpatch();
- char *name(uint *lenp = 0) const
- {
- if (lenp)
- *lenp= (uint) m_name.length;
- return m_name.str;
- }
+ /// Add cpush instructions for all cursors declared in the current frame
+ bool sp_add_instr_cpush_for_cursors(THD *thd, sp_pcontext *pcontext);
+
+ const LEX_CSTRING *name() const
+ { return &m_name; }
char *create_string(THD *thd, ulong *lenp);
- Field *create_result_field(uint field_max_length, const char *field_name,
- TABLE *table);
+ Field *create_result_field(uint field_max_length, const LEX_CSTRING *field_name,
+ TABLE *table) const;
+
+
+ /**
+ Check and prepare an instance of Column_definition for field creation
+ (fill all necessary attributes), for variables, parameters and
+ function return values.
+
+ @param[in] thd Thread handle
+ @param[in] lex Yacc parsing context
+ @param[out] field_def An instance of create_field to be filled
+
+ @retval false on success
+ @retval true on error
+ */
+ bool fill_field_definition(THD *thd, Column_definition *field_def)
+ {
+ const Type_handler *h= field_def->type_handler();
+ return h->Column_definition_fix_attributes(field_def) ||
+ field_def->sp_prepare_create_field(thd, mem_root);
+ }
+ bool row_fill_field_definitions(THD *thd, Row_definition_list *row)
+ {
+ /*
+ Prepare all row fields. This will (among other things)
+ - convert VARCHAR lengths from character length to octet length
+ - calculate interval lengths for SET and ENUM
+ */
+ List_iterator<Spvar_definition> it(*row);
+ for (Spvar_definition *def= it++; def; def= it++)
+ {
+ if (fill_spvar_definition(thd, def))
+ return true;
+ }
+ return false;
+ }
+ /**
+ Check and prepare a Column_definition for a variable or a parameter.
+ */
+ bool fill_spvar_definition(THD *thd, Column_definition *def)
+ {
+ if (fill_field_definition(thd, def))
+ return true;
+ def->pack_flag|= FIELDFLAG_MAYBE_NULL;
+ return false;
+ }
+ bool fill_spvar_definition(THD *thd, Column_definition *def,
+ LEX_CSTRING *name)
+ {
+ def->field_name= *name;
+ return fill_spvar_definition(thd, def);
+ }
- bool fill_field_definition(THD *thd, LEX *lex,
- Column_definition *field_def);
+private:
+ /**
+ Set a column type reference for a parameter definition
+ */
+ void fill_spvar_using_type_reference(sp_variable *spvar,
+ Qualified_column_ident *ref)
+ {
+ spvar->field_def.set_column_type_ref(ref);
+ spvar->field_def.field_name= spvar->name;
+ m_flags|= sp_head::HAS_COLUMN_TYPE_REFS;
+ }
+ void fill_spvar_using_table_rowtype_reference(THD *thd,
+ sp_variable *spvar,
+ Table_ident *ref)
+ {
+ spvar->field_def.set_table_rowtype_ref(ref);
+ spvar->field_def.field_name= spvar->name;
+ fill_spvar_definition(thd, &spvar->field_def);
+ m_flags|= sp_head::HAS_COLUMN_TYPE_REFS;
+ }
+
+public:
+ bool spvar_fill_row(THD *thd, sp_variable *spvar, Row_definition_list *def);
+ bool spvar_fill_type_reference(THD *thd, sp_variable *spvar,
+ const LEX_CSTRING &table,
+ const LEX_CSTRING &column);
+ bool spvar_fill_type_reference(THD *thd, sp_variable *spvar,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &table,
+ const LEX_CSTRING &column);
+ bool spvar_fill_table_rowtype_reference(THD *thd, sp_variable *spvar,
+ const LEX_CSTRING &table);
+ bool spvar_fill_table_rowtype_reference(THD *thd, sp_variable *spvar,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &table);
+
+ void set_chistics(const st_sp_chistics &chistics);
+ inline void set_chistics_agg_type(enum enum_sp_aggregate_type type)
+ {
+ m_chistics.agg_type= type;
+ }
void set_info(longlong created, longlong modified,
- st_sp_chistics *chistics, sql_mode_t sql_mode);
+ const st_sp_chistics &chistics, sql_mode_t sql_mode);
- void set_definer(const char *definer, uint definerlen);
- void set_definer(const LEX_STRING *user_name, const LEX_STRING *host_name);
+ void set_definer(const char *definer, size_t definerlen)
+ {
+ AUTHID tmp;
+ tmp.parse(definer, definerlen);
+ m_definer.copy(mem_root, &tmp.user, &tmp.host);
+ }
+ void set_definer(const LEX_CSTRING *user_name, const LEX_CSTRING *host_name)
+ {
+ m_definer.copy(mem_root, user_name, host_name);
+ }
void reset_thd_mem_root(THD *thd);
@@ -445,8 +767,6 @@ public:
*/
void add_mark_lead(uint ip, List<sp_instr> *leads);
- void recursion_level_error(THD *thd);
-
inline sp_instr *
get_instr(uint i)
{
@@ -510,15 +830,27 @@ public:
*/
DBUG_PRINT("info", ("lex->get_stmt_unsafe_flags(): 0x%x",
prelocking_ctx->get_stmt_unsafe_flags()));
- DBUG_PRINT("info", ("sp_head(0x%p=%s)->unsafe_flags: 0x%x",
- this, name(), unsafe_flags));
+ DBUG_PRINT("info", ("sp_head(%p=%s)->unsafe_flags: 0x%x",
+ this, name()->str, unsafe_flags));
prelocking_ctx->set_stmt_unsafe_flags(unsafe_flags);
DBUG_VOID_RETURN;
}
sp_pcontext *get_parse_context() { return m_pcont; }
-private:
+ /*
+ Check EXECUTE access:
+ - in case of a standalone rotuine, for the routine itself
+ - in case of a package routine, for the owner package body
+ */
+ bool check_execute_access(THD *thd) const;
+
+ virtual sp_package *get_package()
+ {
+ return NULL;
+ }
+
+protected:
MEM_ROOT *m_thd_root; ///< Temp. store for thd's mem_root
THD *m_thd; ///< Set if we have reset mem_root
@@ -526,12 +858,17 @@ private:
sp_pcontext *m_pcont; ///< Parse context
List<LEX> m_lex; ///< Temp. store for the other lex
DYNAMIC_ARRAY m_instr; ///< The "instructions"
+
+ enum backpatch_instr_type { GOTO, CPOP, HPOP };
typedef struct
{
sp_label *lab;
sp_instr *instr;
+ backpatch_instr_type instr_type;
} bp_t;
List<bp_t> m_backpatch; ///< Instructions needing backpatching
+ List<bp_t> m_backpatch_goto; // Instructions needing backpatching (for goto)
+
/**
We need a special list for backpatching of instructions with a continue
destination (in the case of a continue handler catching an error in
@@ -569,9 +906,143 @@ private:
by routine.
*/
bool merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check);
+
+ /// Put the instruction on the a backpatch list, associated with the label.
+ int
+ push_backpatch(THD *thd, sp_instr *, sp_label *, List<bp_t> *list,
+ backpatch_instr_type itype);
+
}; // class sp_head : public Sql_alloc
+class sp_package: public sp_head
+{
+ bool validate_public_routines(THD *thd, sp_package *spec);
+ bool validate_private_routines(THD *thd);
+public:
+ class LexList: public List<LEX>
+ {
+ public:
+ LexList() { elements= 0; }
+ // Find a package routine by a non qualified name
+ LEX *find(const LEX_CSTRING &name, stored_procedure_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);
+ // Check if a routine with the given qualified name already exists
+ bool check_dup_qualified(const LEX_CSTRING &name, const Sp_handler *sph)
+ {
+ if (!find_qualified(name, sph->type()))
+ return false;
+ my_error(ER_SP_ALREADY_EXISTS, MYF(0), sph->type_str(), name.str);
+ return true;
+ }
+ bool check_dup_qualified(const sp_head *sp)
+ {
+ return check_dup_qualified(sp->m_name, sp->m_handler);
+ }
+ void cleanup();
+ };
+ /*
+ The LEX for a new package subroutine is initially assigned to
+ m_current_routine. After scanning parameters, return type and chistics,
+ the parser detects if we have a declaration or a definition, e.g.:
+ PROCEDURE p1(a INT);
+ vs
+ PROCEDURE p1(a INT) AS BEGIN NULL; END;
+ (i.e. either semicolon or the "AS" keyword)
+ m_current_routine is then added either to m_routine_implementations,
+ or m_routine_declarations, and then m_current_routine is set to NULL.
+ */
+ LEX *m_current_routine;
+ LexList m_routine_implementations;
+ LexList m_routine_declarations;
+
+ LEX *m_top_level_lex;
+ sp_rcontext *m_rcontext;
+ uint m_invoked_subroutine_count;
+ bool m_is_instantiated;
+ bool m_is_cloning_routine;
+
+private:
+ sp_package(MEM_ROOT *mem_root,
+ LEX *top_level_lex,
+ const sp_name *name,
+ const Sp_handler *sph);
+ ~sp_package();
+public:
+ static sp_package *create(LEX *top_level_lex, const sp_name *name,
+ const Sp_handler *sph);
+
+ bool add_routine_declaration(LEX *lex)
+ {
+ return m_routine_declarations.check_dup_qualified(lex->sphead) ||
+ m_routine_declarations.push_back(lex, &main_mem_root);
+ }
+ bool add_routine_implementation(LEX *lex)
+ {
+ return m_routine_implementations.check_dup_qualified(lex->sphead) ||
+ m_routine_implementations.push_back(lex, &main_mem_root);
+ }
+ sp_package *get_package() { return this; }
+ bool is_invoked() const
+ {
+ /*
+ Cannot flush a package out of the SP cache when:
+ - its initialization block is running
+ - one of its subroutine is running
+ */
+ return sp_head::is_invoked() || m_invoked_subroutine_count > 0;
+ }
+ sp_variable *find_package_variable(const LEX_CSTRING *name) const
+ {
+ /*
+ sp_head::m_pcont is a special level for routine parameters.
+ Variables declared inside CREATE PACKAGE BODY reside in m_children.at(0).
+ */
+ sp_pcontext *ctx= m_pcont->child_context(0);
+ return ctx ? ctx->find_variable(name, true) : NULL;
+ }
+ bool validate_after_parser(THD *thd);
+ bool instantiate_if_needed(THD *thd);
+};
+
+
+class sp_lex_cursor: public sp_lex_local, public Query_arena
+{
+public:
+ sp_lex_cursor(THD *thd, const LEX *oldlex, MEM_ROOT *mem_root_arg)
+ :sp_lex_local(thd, oldlex),
+ Query_arena(mem_root_arg, STMT_INITIALIZED_FOR_SP)
+ { }
+ sp_lex_cursor(THD *thd, const LEX *oldlex)
+ :sp_lex_local(thd, oldlex),
+ Query_arena(thd->lex->sphead->get_main_mem_root(), STMT_INITIALIZED_FOR_SP)
+ { }
+ ~sp_lex_cursor() { free_items(); }
+ void cleanup_stmt() { }
+ Query_arena *query_arena() { return this; }
+ bool validate()
+ {
+ DBUG_ASSERT(sql_command == SQLCOM_SELECT);
+ if (result)
+ {
+ my_error(ER_SP_BAD_CURSOR_SELECT, MYF(0));
+ return true;
+ }
+ return false;
+ }
+ bool stmt_finalize(THD *thd)
+ {
+ if (validate())
+ return true;
+ sp_lex_in_use= true;
+ free_list= thd->free_list;
+ thd->free_list= NULL;
+ return false;
+ }
+};
+
+
//
// "Instructions"...
//
@@ -586,6 +1057,7 @@ public:
uint marked;
uint m_ip; ///< My index
sp_pcontext *m_ctx; ///< My parse context
+ uint m_lineno;
/// Should give each a name or type code for debugging purposes?
sp_instr(uint ip, sp_pcontext *ctx)
@@ -729,6 +1201,9 @@ public:
int reset_lex_and_exec_core(THD *thd, uint *nextp, bool open_tables,
sp_instr* instr);
+ int cursor_reset_lex_and_exec_core(THD *thd, uint *nextp, bool open_tables,
+ sp_instr *instr);
+
inline uint sql_command() const
{
return (uint)m_lex->sql_command;
@@ -738,6 +1213,7 @@ public:
{
m_lex->safe_to_cache_query= 0;
}
+
private:
LEX *m_lex;
@@ -811,9 +1287,11 @@ class sp_instr_set : public sp_instr
public:
sp_instr_set(uint ip, sp_pcontext *ctx,
- uint offset, Item *val, enum enum_field_types type_arg,
+ const Sp_rcontext_handler *rh,
+ uint offset, Item *val,
LEX *lex, bool lex_resp)
- : sp_instr(ip, ctx), m_offset(offset), m_value(val), m_type(type_arg),
+ : sp_instr(ip, ctx),
+ m_rcontext_handler(rh), m_offset(offset), m_value(val),
m_lex_keeper(lex, lex_resp)
{}
@@ -826,16 +1304,89 @@ public:
virtual void print(String *str);
-private:
-
+protected:
+ sp_rcontext *get_rcontext(THD *thd) const;
+ const Sp_rcontext_handler *m_rcontext_handler;
uint m_offset; ///< Frame offset
Item *m_value;
- enum enum_field_types m_type; ///< The declared type
sp_lex_keeper m_lex_keeper;
-
}; // class sp_instr_set : public sp_instr
+/*
+ This class handles assignments of a ROW fields:
+ DECLARE rec ROW (a INT,b INT);
+ SET rec.a= 10;
+*/
+class sp_instr_set_row_field : public sp_instr_set
+{
+ sp_instr_set_row_field(const sp_instr_set_row_field &); // Prevent use of this
+ void operator=(sp_instr_set_row_field &);
+ uint m_field_offset;
+
+public:
+
+ sp_instr_set_row_field(uint ip, sp_pcontext *ctx,
+ const Sp_rcontext_handler *rh,
+ uint offset, uint field_offset,
+ Item *val,
+ LEX *lex, bool lex_resp)
+ : sp_instr_set(ip, ctx, rh, offset, val, lex, lex_resp),
+ m_field_offset(field_offset)
+ {}
+
+ virtual ~sp_instr_set_row_field()
+ {}
+
+ virtual int exec_core(THD *thd, uint *nextp);
+
+ virtual void print(String *str);
+}; // class sp_instr_set_field : public sp_instr_set
+
+
+/**
+ This class handles assignment instructions like this:
+ DECLARE
+ CURSOR cur IS SELECT * FROM t1;
+ rec cur%ROWTYPE;
+ BEGIN
+ rec.column1:= 10; -- This instruction
+ END;
+
+ The idea is that during sp_rcontext::create() we do not know the extact
+ structure of "rec". It gets resolved at run time, during the corresponding
+ sp_instr_cursor_copy_struct::exec_core().
+
+ So sp_instr_set_row_field_by_name searches for ROW fields by name,
+ while sp_instr_set_row_field (see above) searches for ROW fields by index.
+*/
+class sp_instr_set_row_field_by_name : public sp_instr_set
+{
+ // Prevent use of this
+ sp_instr_set_row_field_by_name(const sp_instr_set_row_field &);
+ void operator=(sp_instr_set_row_field_by_name &);
+ const LEX_CSTRING m_field_name;
+
+public:
+
+ sp_instr_set_row_field_by_name(uint ip, sp_pcontext *ctx,
+ const Sp_rcontext_handler *rh,
+ uint offset, const LEX_CSTRING &field_name,
+ Item *val,
+ LEX *lex, bool lex_resp)
+ : sp_instr_set(ip, ctx, rh, offset, val, lex, lex_resp),
+ m_field_name(field_name)
+ {}
+
+ virtual ~sp_instr_set_row_field_by_name()
+ {}
+
+ virtual int exec_core(THD *thd, uint *nextp);
+
+ virtual void print(String *str);
+}; // class sp_instr_set_field_by_name : public sp_instr_set
+
+
/**
Set NEW/OLD row field value instruction. Used in triggers.
*/
@@ -1007,6 +1558,41 @@ private:
}; // class sp_instr_jump_if_not : public sp_instr_jump
+class sp_instr_preturn : public sp_instr
+{
+ sp_instr_preturn(const sp_instr_preturn &); /**< Prevent use of these */
+ void operator=(sp_instr_preturn &);
+
+public:
+
+ sp_instr_preturn(uint ip, sp_pcontext *ctx)
+ : sp_instr(ip, ctx)
+ {}
+
+ virtual ~sp_instr_preturn()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp)
+ {
+ DBUG_ENTER("sp_instr_preturn::execute");
+ *nextp= UINT_MAX;
+ DBUG_RETURN(0);
+ }
+
+ virtual void print(String *str)
+ {
+ str->append(STRING_WITH_LEN("preturn"));
+ }
+
+ virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads)
+ {
+ marked= 1;
+ return UINT_MAX;
+ }
+
+}; // class sp_instr_preturn : public sp_instr
+
+
class sp_instr_freturn : public sp_instr
{
sp_instr_freturn(const sp_instr_freturn &); /**< Prevent use of these */
@@ -1015,8 +1601,8 @@ class sp_instr_freturn : public sp_instr
public:
sp_instr_freturn(uint ip, sp_pcontext *ctx,
- Item *val, enum enum_field_types type_arg, LEX *lex)
- : sp_instr(ip, ctx), m_value(val), m_type(type_arg),
+ Item *val, const Type_handler *handler, LEX *lex)
+ : sp_instr(ip, ctx), m_value(val), m_type_handler(handler),
m_lex_keeper(lex, TRUE)
{}
@@ -1038,7 +1624,7 @@ public:
protected:
Item *m_value;
- enum enum_field_types m_type;
+ const Type_handler *m_type_handler;
sp_lex_keeper m_lex_keeper;
}; // class sp_instr_freturn : public sp_instr
@@ -1096,8 +1682,6 @@ public:
{ return m_handler; }
private:
-
-private:
/// Handler.
sp_handler *m_handler;
@@ -1125,6 +1709,11 @@ public:
virtual ~sp_instr_hpop()
{}
+ void update_count(uint count)
+ {
+ m_count= count;
+ }
+
virtual int execute(THD *thd, uint *nextp);
virtual void print(String *str);
@@ -1171,7 +1760,8 @@ private:
/** This is DECLARE CURSOR */
-class sp_instr_cpush : public sp_instr
+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 &);
@@ -1217,6 +1807,11 @@ public:
virtual ~sp_instr_cpop()
{}
+ void update_count(uint count)
+ {
+ m_count= count;
+ }
+
virtual int execute(THD *thd, uint *nextp);
virtual void print(String *str);
@@ -1255,6 +1850,33 @@ private:
}; // class sp_instr_copen : public sp_instr_stmt
+/**
+ Initialize the structure of a cursor%ROWTYPE variable
+ from the LEX containing the cursor SELECT statement.
+*/
+class sp_instr_cursor_copy_struct: public sp_instr
+{
+ /**< Prevent use of these */
+ sp_instr_cursor_copy_struct(const sp_instr_cursor_copy_struct &);
+ void operator=(sp_instr_cursor_copy_struct &);
+ sp_lex_keeper m_lex_keeper;
+ uint m_cursor;
+ uint m_var;
+public:
+ sp_instr_cursor_copy_struct(uint ip, sp_pcontext *ctx, uint coffs,
+ sp_lex_cursor *lex, uint voffs)
+ : sp_instr(ip, ctx), m_lex_keeper(lex, FALSE),
+ m_cursor(coffs),
+ m_var(voffs)
+ {}
+ virtual ~sp_instr_cursor_copy_struct()
+ {}
+ virtual int execute(THD *thd, uint *nextp);
+ virtual int exec_core(THD *thd, uint *nextp);
+ virtual void print(String *str);
+};
+
+
class sp_instr_cclose : public sp_instr
{
sp_instr_cclose(const sp_instr_cclose &); /**< Prevent use of these */
@@ -1287,8 +1909,8 @@ class sp_instr_cfetch : public sp_instr
public:
- sp_instr_cfetch(uint ip, sp_pcontext *ctx, uint c)
- : sp_instr(ip, ctx), m_cursor(c)
+ sp_instr_cfetch(uint ip, sp_pcontext *ctx, uint c, bool error_on_no_data)
+ : sp_instr(ip, ctx), m_cursor(c), m_error_on_no_data(error_on_no_data)
{
m_varlist.empty();
}
@@ -1309,9 +1931,36 @@ private:
uint m_cursor;
List<sp_variable> m_varlist;
+ bool m_error_on_no_data;
}; // class sp_instr_cfetch : public sp_instr
+/*
+This class is created for the special fetch instruction
+FETCH GROUP NEXT ROW, used in the user-defined aggregate
+functions
+*/
+
+class sp_instr_agg_cfetch : public sp_instr
+{
+ sp_instr_agg_cfetch(const sp_instr_cfetch &); /**< Prevent use of these */
+ void operator=(sp_instr_cfetch &);
+
+public:
+
+ sp_instr_agg_cfetch(uint ip, sp_pcontext *ctx)
+ : sp_instr(ip, ctx){}
+
+ virtual ~sp_instr_agg_cfetch()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+ virtual void print(String *str);
+}; // class sp_instr_agg_cfetch : public sp_instr
+
+
+
class sp_instr_error : public sp_instr
{
@@ -1391,22 +2040,15 @@ void
sp_restore_security_context(THD *thd, Security_context *backup);
bool
-set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc,
- Security_context **save_ctx);
+set_routine_security_ctx(THD *thd, sp_head *sp, Security_context **save_ctx);
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
TABLE_LIST *
sp_add_to_query_tables(THD *thd, LEX *lex,
- const char *db, const char *name,
+ const LEX_CSTRING *db, const LEX_CSTRING *name,
thr_lock_type locktype,
enum_mdl_type mdl_type);
-Item *
-sp_prepare_func_item(THD* thd, Item **it_addr);
-
-bool
-sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr);
-
/**
@} (end of group Stored_Routines)
*/
diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc
index a6669795de3..433efda479b 100644
--- a/sql/sp_pcontext.cc
+++ b/sql/sp_pcontext.cc
@@ -13,7 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#ifdef USE_PRAGMA_IMPLEMENTATION
@@ -27,19 +27,50 @@ bool sp_condition_value::equals(const sp_condition_value *cv) const
{
DBUG_ASSERT(cv);
+ /*
+ The following test disallows duplicate handlers,
+ including user defined exceptions with the same WHEN clause:
+ DECLARE
+ a EXCEPTION;
+ b EXCEPTION;
+ BEGIN
+ RAUSE a;
+ EXCEPTION
+ WHEN a THEN RETURN 'a0';
+ WHEN a THEN RETURN 'a1';
+ END
+ */
if (this == cv)
return true;
- if (type != cv->type)
+ /*
+ The test below considers two conditions of the same type as equal
+ (except for the user defined exceptions) to avoid declaring duplicate
+ handlers.
+
+ All user defined conditions have type==SQLSTATE
+ with the same SQL state and error code.
+ It's OK to have multiple user defined conditions:
+ DECLARE
+ a EXCEPTION;
+ b EXCEPTION;
+ BEGIN
+ RAISE a;
+ EXCEPTION
+ WHEN a THEN RETURN 'a';
+ WHEN b THEN RETURN 'b';
+ END;
+ */
+ if (type != cv->type || m_is_user_defined || cv->m_is_user_defined)
return false;
switch (type)
{
case sp_condition_value::ERROR_CODE:
- return (mysqlerr == cv->mysqlerr);
+ return (get_sql_errno() == cv->get_sql_errno());
case sp_condition_value::SQLSTATE:
- return (strcmp(sql_state, cv->sql_state) == 0);
+ return Sql_state::eq(cv);
default:
return true;
@@ -56,6 +87,7 @@ void sp_pcontext::init(uint var_offset,
m_num_case_exprs= num_case_expressions;
m_labels.empty();
+ m_goto_labels.empty();
}
@@ -98,6 +130,12 @@ sp_pcontext *sp_pcontext::push_context(THD *thd, sp_pcontext::enum_scope scope)
}
+bool cmp_labels(sp_label *a, sp_label *b)
+{
+ return (lex_string_cmp(system_charset_info, &a->name, &b->name) == 0 &&
+ a->type == b->type);
+}
+
sp_pcontext *sp_pcontext::pop_context()
{
m_parent->m_max_var_index+= m_max_var_index;
@@ -109,6 +147,18 @@ sp_pcontext *sp_pcontext::pop_context()
if (m_num_case_exprs > m_parent->m_num_case_exprs)
m_parent->m_num_case_exprs= m_num_case_exprs;
+ /*
+ ** Push unresolved goto label to parent context
+ */
+ sp_label *label;
+ List_iterator_fast<sp_label> li(m_goto_labels);
+ while ((label= li++))
+ {
+ if (label->ip == 0)
+ {
+ m_parent->m_goto_labels.add_unique(label, &cmp_labels);
+ }
+ }
return m_parent;
}
@@ -121,12 +171,12 @@ uint sp_pcontext::diff_handlers(const sp_pcontext *ctx, bool exclusive) const
while (pctx && pctx != ctx)
{
- n+= pctx->m_handlers.elements();
+ n+= (uint)pctx->m_handlers.elements();
last_ctx= pctx;
pctx= pctx->parent_context();
}
if (pctx)
- return (exclusive && last_ctx ? n - last_ctx->m_handlers.elements() : n);
+ return (exclusive && last_ctx ? n -(uint) last_ctx->m_handlers.elements() : n);
return 0; // Didn't find ctx
}
@@ -139,27 +189,27 @@ uint sp_pcontext::diff_cursors(const sp_pcontext *ctx, bool exclusive) const
while (pctx && pctx != ctx)
{
- n+= pctx->m_cursors.elements();
+ n+= (uint)pctx->m_cursors.elements();
last_ctx= pctx;
pctx= pctx->parent_context();
}
if (pctx)
- return (exclusive && last_ctx ? n - last_ctx->m_cursors.elements() : n);
+ return (exclusive && last_ctx ? (uint)(n - last_ctx->m_cursors.elements()) : n);
return 0; // Didn't find ctx
}
-sp_variable *sp_pcontext::find_variable(LEX_STRING name,
+sp_variable *sp_pcontext::find_variable(const LEX_CSTRING *name,
bool current_scope_only) const
{
- uint i= m_vars.elements() - m_pboundary;
+ size_t i= m_vars.elements() - m_pboundary;
while (i--)
{
sp_variable *p= m_vars.at(i);
if (my_strnncoll(system_charset_info,
- (const uchar *)name.str, name.length,
+ (const uchar *)name->str, name->length,
(const uchar *)p->name.str, p->name.length) == 0)
{
return p;
@@ -172,10 +222,46 @@ sp_variable *sp_pcontext::find_variable(LEX_STRING name,
}
+/*
+ Find a variable by its run-time offset.
+ If the variable with a desired run-time offset is not found in this
+ context frame, it's recursively searched on parent context frames.
+
+ Note, context frames can have holes:
+ CREATE PROCEDURE p1() AS
+ x0 INT:=100;
+ CURSOR cur(p0 INT, p1 INT) IS SELECT p0, p1;
+ x1 INT:=101;
+ BEGIN
+ ...
+ END;
+ The variables (x0 and x1) and the cursor parameters (p0 and p1)
+ reside in separate parse context frames.
+
+ The variables reside on the top level parse context frame:
+ - x0 has frame offset 0 and run-time offset 0
+ - x1 has frame offset 1 and run-time offset 3
+
+ The cursor parameters reside on the second level parse context frame:
+ - p0 has frame offset 0 and run-time offset 1
+ - p1 has frame offset 1 and run-time offset 2
+
+ Run-time offsets on a frame can have holes, but offsets monotonocally grow,
+ so run-time offsets of all variables are not greater than the run-time offset
+ of the very last variable in this frame.
+*/
sp_variable *sp_pcontext::find_variable(uint offset) const
{
- if (m_var_offset <= offset && offset < m_var_offset + m_vars.elements())
- return m_vars.at(offset - m_var_offset); // This frame
+ if (m_var_offset <= offset &&
+ m_vars.elements() &&
+ offset <= get_last_context_variable()->offset)
+ {
+ for (uint i= 0; i < m_vars.elements(); i++)
+ {
+ if (m_vars.at(i)->offset == offset)
+ return m_vars.at(i); // This frame
+ }
+ }
return m_parent ?
m_parent->find_variable(offset) : // Some previous frame
@@ -183,10 +269,10 @@ sp_variable *sp_pcontext::find_variable(uint offset) const
}
-sp_variable *sp_pcontext::add_variable(THD *thd, LEX_STRING name)
+sp_variable *sp_pcontext::add_variable(THD *thd, const LEX_CSTRING *name)
{
sp_variable *p=
- new (thd->mem_root) sp_variable(name, current_var_count());
+ new (thd->mem_root) sp_variable(name, m_var_offset + m_max_var_index);
if (!p)
return NULL;
@@ -196,29 +282,66 @@ sp_variable *sp_pcontext::add_variable(THD *thd, LEX_STRING name)
return m_vars.append(p) ? NULL : p;
}
-
-sp_label *sp_pcontext::push_label(THD *thd, LEX_STRING name, uint ip)
+sp_label *sp_pcontext::push_label(THD *thd, const LEX_CSTRING *name, uint ip,
+ sp_label::enum_type type,
+ List<sp_label> *list)
{
sp_label *label=
- new (thd->mem_root) sp_label(name, ip, sp_label::IMPLICIT, this);
+ new (thd->mem_root) sp_label(name, ip, type, this);
if (!label)
return NULL;
- m_labels.push_front(label, thd->mem_root);
+ list->push_front(label, thd->mem_root);
return label;
}
+sp_label *sp_pcontext::find_goto_label(const LEX_CSTRING *name, bool recusive)
+{
+ List_iterator_fast<sp_label> li(m_goto_labels);
+ sp_label *lab;
+
+ while ((lab= li++))
+ {
+ if (lex_string_cmp(system_charset_info, name, &lab->name) == 0)
+ return lab;
+ }
+
+ if (!recusive)
+ return NULL;
+
+ /*
+ Note about exception handlers.
+ See SQL:2003 SQL/PSM (ISO/IEC 9075-4:2003),
+ section 13.1 <compound statement>,
+ syntax rule 4.
+ In short, a DECLARE HANDLER block can not refer
+ to labels from the parent context, as they are out of scope.
+ */
+ if (m_scope == HANDLER_SCOPE && m_parent)
+ {
+ if (m_parent->m_parent)
+ {
+ // Skip the parent context
+ return m_parent->m_parent->find_goto_label(name);
+ }
+ }
+
+ return m_parent && (m_scope == REGULAR_SCOPE) ?
+ m_parent->find_goto_label(name) :
+ NULL;
+}
+
-sp_label *sp_pcontext::find_label(LEX_STRING name)
+sp_label *sp_pcontext::find_label(const LEX_CSTRING *name)
{
List_iterator_fast<sp_label> li(m_labels);
sp_label *lab;
while ((lab= li++))
{
- if (my_strcasecmp(system_charset_info, name.str, lab->name.str) == 0)
+ if (lex_string_cmp(system_charset_info, name, &lab->name) == 0)
return lab;
}
@@ -236,8 +359,25 @@ sp_label *sp_pcontext::find_label(LEX_STRING name)
}
+sp_label *sp_pcontext::find_label_current_loop_start()
+{
+ List_iterator_fast<sp_label> li(m_labels);
+ sp_label *lab;
+
+ while ((lab= li++))
+ {
+ if (lab->type == sp_label::ITERATION)
+ return lab;
+ }
+ // See a comment in sp_pcontext::find_label()
+ return (m_parent && (m_scope == REGULAR_SCOPE)) ?
+ m_parent->find_label_current_loop_start() :
+ NULL;
+}
+
+
bool sp_pcontext::add_condition(THD *thd,
- LEX_STRING name,
+ const LEX_CSTRING *name,
sp_condition_value *value)
{
sp_condition *p= new (thd->mem_root) sp_condition(name, value);
@@ -249,18 +389,16 @@ bool sp_pcontext::add_condition(THD *thd,
}
-sp_condition_value *sp_pcontext::find_condition(LEX_STRING name,
+sp_condition_value *sp_pcontext::find_condition(const LEX_CSTRING *name,
bool current_scope_only) const
{
- uint i= m_conditions.elements();
+ size_t i= m_conditions.elements();
while (i--)
{
sp_condition *p= m_conditions.at(i);
- if (my_strnncoll(system_charset_info,
- (const uchar *) name.str, name.length,
- (const uchar *) p->name.str, p->name.length) == 0)
+ if (p->eq_name(name))
{
return p->value;
}
@@ -271,6 +409,53 @@ sp_condition_value *sp_pcontext::find_condition(LEX_STRING name,
NULL;
}
+sp_condition_value *
+sp_pcontext::find_declared_or_predefined_condition(THD *thd,
+ const LEX_CSTRING *name)
+ const
+{
+ sp_condition_value *p= find_condition(name, false);
+ if (p)
+ return p;
+ if (thd->variables.sql_mode & MODE_ORACLE)
+ return find_predefined_condition(name);
+ return NULL;
+}
+
+
+static sp_condition_value
+ // Warnings
+ cond_no_data_found(ER_SP_FETCH_NO_DATA, "01000"),
+ // Errors
+ cond_invalid_cursor(ER_SP_CURSOR_NOT_OPEN, "24000"),
+ cond_dup_val_on_index(ER_DUP_ENTRY, "23000"),
+ cond_dup_val_on_index2(ER_DUP_ENTRY_WITH_KEY_NAME, "23000"),
+ cond_too_many_rows(ER_TOO_MANY_ROWS, "42000");
+
+
+static sp_condition sp_predefined_conditions[]=
+{
+ // Warnings
+ sp_condition(STRING_WITH_LEN("NO_DATA_FOUND"), &cond_no_data_found),
+ // Errors
+ sp_condition(STRING_WITH_LEN("INVALID_CURSOR"), &cond_invalid_cursor),
+ sp_condition(STRING_WITH_LEN("DUP_VAL_ON_INDEX"), &cond_dup_val_on_index),
+ sp_condition(STRING_WITH_LEN("DUP_VAL_ON_INDEX"), &cond_dup_val_on_index2),
+ sp_condition(STRING_WITH_LEN("TOO_MANY_ROWS"), &cond_too_many_rows)
+};
+
+
+sp_condition_value *
+sp_pcontext::find_predefined_condition(const LEX_CSTRING *name) const
+{
+ for (uint i= 0; i < array_elements(sp_predefined_conditions) ; i++)
+ {
+ if (sp_predefined_conditions[i].eq_name(name))
+ return sp_predefined_conditions[i].value;
+ }
+ return NULL;
+}
+
sp_handler *sp_pcontext::add_handler(THD *thd,
sp_handler::enum_type type)
@@ -305,10 +490,57 @@ bool sp_pcontext::check_duplicate_handler(
}
+bool sp_condition_value::matches(const Sql_condition_identity &value,
+ const sp_condition_value *found_cv) const
+{
+ bool user_value_matched= !value.get_user_condition_value() ||
+ this == value.get_user_condition_value();
+
+ switch (type)
+ {
+ case sp_condition_value::ERROR_CODE:
+ return user_value_matched &&
+ value.get_sql_errno() == get_sql_errno() &&
+ (!found_cv || found_cv->type > sp_condition_value::ERROR_CODE);
+
+ case sp_condition_value::SQLSTATE:
+ return user_value_matched &&
+ Sql_state::eq(&value) &&
+ (!found_cv || found_cv->type > sp_condition_value::SQLSTATE);
+
+ case sp_condition_value::WARNING:
+ return user_value_matched &&
+ (value.Sql_state::is_warning() ||
+ value.get_level() == Sql_condition::WARN_LEVEL_WARN) &&
+ !found_cv;
+
+ case sp_condition_value::NOT_FOUND:
+ return user_value_matched &&
+ value.Sql_state::is_not_found() &&
+ !found_cv;
+
+ case sp_condition_value::EXCEPTION:
+ /*
+ In sql_mode=ORACLE this construct should catch both errors and warnings:
+ EXCEPTION
+ WHEN OTHERS THEN ...;
+ E.g. NO_DATA_FOUND is more like a warning than an error,
+ and it should be caught.
+
+ We don't check user_value_matched here.
+ "WHEN OTHERS" catches all user defined exception.
+ */
+ return (((current_thd->variables.sql_mode & MODE_ORACLE) ||
+ (value.Sql_state::is_exception() &&
+ value.get_level() == Sql_condition::WARN_LEVEL_ERROR)) &&
+ !found_cv);
+ }
+ return false;
+}
+
+
sp_handler*
-sp_pcontext::find_handler(const char *sql_state,
- uint sql_errno,
- Sql_condition::enum_warning_level level) const
+sp_pcontext::find_handler(const Sql_condition_identity &value) const
{
sp_handler *found_handler= NULL;
sp_condition_value *found_cv= NULL;
@@ -322,53 +554,10 @@ sp_pcontext::find_handler(const char *sql_state,
while ((cv= li++))
{
- switch (cv->type)
+ if (cv->matches(value, found_cv))
{
- case sp_condition_value::ERROR_CODE:
- if (sql_errno == cv->mysqlerr &&
- (!found_cv ||
- found_cv->type > sp_condition_value::ERROR_CODE))
- {
- found_cv= cv;
- found_handler= h;
- }
- break;
-
- case sp_condition_value::SQLSTATE:
- if (strcmp(sql_state, cv->sql_state) == 0 &&
- (!found_cv ||
- found_cv->type > sp_condition_value::SQLSTATE))
- {
- found_cv= cv;
- found_handler= h;
- }
- break;
-
- case sp_condition_value::WARNING:
- if ((is_sqlstate_warning(sql_state) ||
- level == Sql_condition::WARN_LEVEL_WARN) && !found_cv)
- {
- found_cv= cv;
- found_handler= h;
- }
- break;
-
- case sp_condition_value::NOT_FOUND:
- if (is_sqlstate_not_found(sql_state) && !found_cv)
- {
- found_cv= cv;
- found_handler= h;
- }
- break;
-
- case sp_condition_value::EXCEPTION:
- if (is_sqlstate_exception(sql_state) &&
- level == Sql_condition::WARN_LEVEL_ERROR && !found_cv)
- {
- found_cv= cv;
- found_handler= h;
- }
- break;
+ found_cv= cv;
+ found_handler= h;
}
}
}
@@ -411,64 +600,98 @@ sp_pcontext::find_handler(const char *sql_state,
if (!p || !p->m_parent)
return NULL;
- return p->m_parent->find_handler(sql_state, sql_errno, level);
+ return p->m_parent->find_handler(value);
}
-bool sp_pcontext::add_cursor(LEX_STRING name)
+bool sp_pcontext::add_cursor(const LEX_CSTRING *name, sp_pcontext *param_ctx,
+ sp_lex_cursor *lex)
{
if (m_cursors.elements() == m_max_cursor_index)
++m_max_cursor_index;
- return m_cursors.append(name);
+ return m_cursors.append(sp_pcursor(name, param_ctx, lex));
}
-bool sp_pcontext::find_cursor(LEX_STRING name,
- uint *poff,
- bool current_scope_only) const
+const sp_pcursor *sp_pcontext::find_cursor(const LEX_CSTRING *name,
+ uint *poff,
+ bool current_scope_only) const
{
- uint i= m_cursors.elements();
+ uint i= (uint)m_cursors.elements();
while (i--)
{
- LEX_STRING n= m_cursors.at(i);
+ LEX_CSTRING n= m_cursors.at(i);
if (my_strnncoll(system_charset_info,
- (const uchar *) name.str, name.length,
+ (const uchar *) name->str, name->length,
(const uchar *) n.str, n.length) == 0)
{
*poff= m_cursor_offset + i;
- return true;
+ return &m_cursors.at(i);
}
}
return (!current_scope_only && m_parent) ?
m_parent->find_cursor(name, poff, false) :
- false;
+ NULL;
}
void sp_pcontext::retrieve_field_definitions(
- List<Column_definition> *field_def_lst) const
+ List<Spvar_definition> *field_def_lst) const
{
/* Put local/context fields in the result list. */
+ size_t next_child= 0;
for (size_t i= 0; i < m_vars.elements(); ++i)
{
sp_variable *var_def= m_vars.at(i);
+ /*
+ The context can have holes in run-time offsets,
+ the missing offsets reside on the children contexts in such cases.
+ Example:
+ CREATE PROCEDURE p1() AS
+ x0 INT:=100; -- context 0, position 0, run-time 0
+ CURSOR cur(
+ p0 INT, -- context 1, position 0, run-time 1
+ p1 INT -- context 1, position 1, run-time 2
+ ) IS SELECT p0, p1;
+ x1 INT:=101; -- context 0, position 1, run-time 3
+ BEGIN
+ ...
+ END;
+ See more comments in sp_pcontext::find_variable().
+ We must retrieve the definitions in the order of their run-time offsets.
+ Check that there are children that should go before the current variable.
+ */
+ for ( ; next_child < m_children.elements(); next_child++)
+ {
+ sp_pcontext *child= m_children.at(next_child);
+ if (!child->context_var_count() ||
+ child->get_context_variable(0)->offset > var_def->offset)
+ break;
+ /*
+ All variables on the embedded context (that fills holes of the parent)
+ should have the run-time offset strictly less than var_def.
+ */
+ DBUG_ASSERT(child->get_context_variable(0)->offset < var_def->offset);
+ DBUG_ASSERT(child->get_last_context_variable()->offset < var_def->offset);
+ child->retrieve_field_definitions(field_def_lst);
+ }
field_def_lst->push_back(&var_def->field_def);
}
- /* Put the fields of the enclosed contexts in the result list. */
+ /* Put the fields of the remaining enclosed contexts in the result list. */
- for (size_t i= 0; i < m_children.elements(); ++i)
+ for (size_t i= next_child; i < m_children.elements(); ++i)
m_children.at(i)->retrieve_field_definitions(field_def_lst);
}
-const LEX_STRING *sp_pcontext::find_cursor(uint offset) const
+const sp_pcursor *sp_pcontext::find_cursor(uint offset) const
{
if (m_cursor_offset <= offset &&
offset < m_cursor_offset + m_cursors.elements())
@@ -480,3 +703,35 @@ const LEX_STRING *sp_pcontext::find_cursor(uint offset) const
m_parent->find_cursor(offset) : // Some previous frame
NULL; // Index out of bounds
}
+
+
+bool sp_pcursor::check_param_count_with_error(uint param_count) const
+{
+ if (param_count != (m_param_context ?
+ m_param_context->context_var_count() : 0))
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_CURSOR, MYF(0), LEX_CSTRING::str);
+ return true;
+ }
+ return false;
+}
+
+
+const Spvar_definition *
+sp_variable::find_row_field(const LEX_CSTRING *var_name,
+ const LEX_CSTRING *field_name,
+ uint *row_field_offset)
+{
+ if (!field_def.is_row())
+ {
+ my_printf_error(ER_UNKNOWN_ERROR,
+ "'%s' is not a row variable", MYF(0), var_name->str);
+ return NULL;
+ }
+ const Spvar_definition *def;
+ if ((def= field_def.find_row_field_by_name(field_name, row_field_offset)))
+ return def;
+ my_error(ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD, MYF(0),
+ var_name->str, field_name->str);
+ return NULL;
+}
diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h
index 76ddf730ace..4d36ca8b479 100644
--- a/sql/sp_pcontext.h
+++ b/sql/sp_pcontext.h
@@ -41,7 +41,7 @@ public:
};
/// Name of the SP-variable.
- LEX_STRING name;
+ LEX_CSTRING name;
/// Mode of the SP-variable.
enum_mode mode;
@@ -57,18 +57,33 @@ public:
Item *default_value;
/// Full type information (field meta-data) of the SP-variable.
- Column_definition field_def;
+ Spvar_definition field_def;
/// Field-type of the SP-variable.
- enum_field_types sql_type() const { return field_def.sql_type; }
+ const Type_handler *type_handler() const { return field_def.type_handler(); }
+
public:
- sp_variable(LEX_STRING _name, uint _offset)
+ sp_variable(const LEX_CSTRING *name_arg, uint offset_arg)
:Sql_alloc(),
- name(_name),
+ name(*name_arg),
mode(MODE_IN),
- offset(_offset),
+ offset(offset_arg),
default_value(NULL)
{ }
+ /*
+ Find a ROW field by its qualified name.
+ @param var_name - the name of the variable
+ @param field_name - the name of the variable field
+ @param[OUT] row_field_offset - the index of the field
+
+ @retval NULL if the variable with the given name was not found,
+ or it is not a row variable, or it does not have a field
+ with the given name, or a non-null pointer otherwise.
+ row_field_offset[0] is set only when the method returns !NULL.
+ */
+ const Spvar_definition *find_row_field(const LEX_CSTRING *var_name,
+ const LEX_CSTRING *field_name,
+ uint *row_field_offset);
};
///////////////////////////////////////////////////////////////////////////
@@ -80,6 +95,7 @@ public:
/// IF/WHILE/REPEAT/LOOP, when such statement is rewritten into a
/// combination of low-level jump/jump_if instructions and labels.
+
class sp_label : public Sql_alloc
{
public:
@@ -92,11 +108,14 @@ public:
BEGIN,
/// Label at iteration control
- ITERATION
+ ITERATION,
+
+ /// Label for jump
+ GOTO
};
/// Name of the label.
- LEX_STRING name;
+ LEX_CSTRING name;
/// Instruction pointer of the label.
uint ip;
@@ -108,15 +127,17 @@ public:
class sp_pcontext *ctx;
public:
- sp_label(LEX_STRING _name, uint _ip, enum_type _type, sp_pcontext *_ctx)
+ sp_label(const LEX_CSTRING *_name,
+ uint _ip, enum_type _type, sp_pcontext *_ctx)
:Sql_alloc(),
- name(_name),
+ name(*_name),
ip(_ip),
type(_type),
ctx(_ctx)
{ }
};
+
///////////////////////////////////////////////////////////////////////////
/// This class represents condition-value term in DECLARE CONDITION or
@@ -126,8 +147,9 @@ public:
/// In some sense, this class is a union -- a set of filled attributes
/// depends on the sp_condition_value::type value.
-class sp_condition_value : public Sql_alloc
+class sp_condition_value : public Sql_alloc, public Sql_state_errno
{
+ bool m_is_user_defined;
public:
enum enum_type
{
@@ -141,29 +163,31 @@ public:
/// Type of the condition value.
enum_type type;
- /// SQLSTATE of the condition value.
- char sql_state[SQLSTATE_LENGTH+1];
-
- /// MySQL error code of the condition value.
- uint mysqlerr;
-
public:
sp_condition_value(uint _mysqlerr)
:Sql_alloc(),
- type(ERROR_CODE),
- mysqlerr(_mysqlerr)
+ Sql_state_errno(_mysqlerr),
+ m_is_user_defined(false),
+ type(ERROR_CODE)
{ }
- sp_condition_value(const char *_sql_state)
+ sp_condition_value(uint _mysqlerr, const char *_sql_state)
:Sql_alloc(),
+ Sql_state_errno(_mysqlerr, _sql_state),
+ m_is_user_defined(false),
+ type(ERROR_CODE)
+ { }
+
+ sp_condition_value(const char *_sql_state, bool is_user_defined= false)
+ :Sql_alloc(),
+ Sql_state_errno(0, _sql_state),
+ m_is_user_defined(is_user_defined),
type(SQLSTATE)
- {
- memcpy(sql_state, _sql_state, SQLSTATE_LENGTH);
- sql_state[SQLSTATE_LENGTH]= 0;
- }
+ { }
sp_condition_value(enum_type _type)
:Sql_alloc(),
+ m_is_user_defined(false),
type(_type)
{
DBUG_ASSERT(type != ERROR_CODE && type != SQLSTATE);
@@ -175,8 +199,39 @@ public:
///
/// @return true if the instances are equal, false otherwise.
bool equals(const sp_condition_value *cv) const;
+
+
+ /**
+ Checks if this condition is OK for search.
+ See also sp_context::find_handler().
+
+ @param identity - The condition identity
+ @param found_cv - A previously found matching condition or NULL.
+ @return true - If the current value matches identity and
+ makes a stronger match than the previously
+ found condition found_cv.
+ @return false - If the current value does not match identity,
+ of the current value makes a weaker match than found_cv.
+ */
+ bool matches(const Sql_condition_identity &identity,
+ const sp_condition_value *found_cv) const;
+
+ Sql_user_condition_identity get_user_condition_identity() const
+ {
+ return Sql_user_condition_identity(m_is_user_defined ? this : NULL);
+ }
+};
+
+
+class sp_condition_value_user_defined: public sp_condition_value
+{
+public:
+ sp_condition_value_user_defined()
+ :sp_condition_value("45000", true)
+ { }
};
+
///////////////////////////////////////////////////////////////////////////
/// This class represents 'DECLARE CONDITION' statement.
@@ -186,19 +241,66 @@ class sp_condition : public Sql_alloc
{
public:
/// Name of the condition.
- LEX_STRING name;
+ LEX_CSTRING name;
/// Value of the condition.
sp_condition_value *value;
public:
- sp_condition(LEX_STRING _name, sp_condition_value *_value)
+ sp_condition(const LEX_CSTRING *name_arg, sp_condition_value *value_arg)
:Sql_alloc(),
- name(_name),
- value(_value)
+ name(*name_arg),
+ value(value_arg)
{ }
+ sp_condition(const char *name_arg, size_t name_length_arg,
+ sp_condition_value *value_arg)
+ :value(value_arg)
+ {
+ name.str= name_arg;
+ name.length= name_length_arg;
+ }
+ 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;
+ }
};
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+ class sp_pcursor.
+ Stores information about a cursor:
+ - Cursor's name in LEX_STRING.
+ - Cursor's formal parameter descriptions.
+
+ Formal parameter descriptions reside in a separate context block,
+ pointed by the "m_param_context" member.
+
+ m_param_context can be NULL. This means a cursor with no parameters.
+ Otherwise, the number of variables in m_param_context means
+ the number of cursor's formal parameters.
+
+ Note, m_param_context can be not NULL, but have no variables.
+ This is also means a cursor with no parameters (similar to NULL).
+*/
+class sp_pcursor: public LEX_CSTRING
+{
+ class sp_pcontext *m_param_context; // Formal parameters
+ class sp_lex_cursor *m_lex; // The cursor statement LEX
+public:
+ sp_pcursor(const LEX_CSTRING *name, class sp_pcontext *param_ctx,
+ class sp_lex_cursor *lex)
+ :LEX_CSTRING(*name), m_param_context(param_ctx), m_lex(lex)
+ { }
+ class sp_pcontext *param_context() const { return m_param_context; }
+ class sp_lex_cursor *lex() const { return m_lex; }
+ bool check_param_count_with_error(uint param_count) const;
+};
+
+
///////////////////////////////////////////////////////////////////////////
/// This class represents 'DECLARE HANDLER' statement.
@@ -267,6 +369,12 @@ public:
HANDLER_SCOPE
};
+ class Lex_for_loop: public Lex_for_loop_st
+ {
+ public:
+ Lex_for_loop() { init(); }
+ };
+
public:
sp_pcontext();
~sp_pcontext();
@@ -286,6 +394,9 @@ public:
sp_pcontext *parent_context() const
{ return m_parent; }
+ sp_pcontext *child_context(uint i) const
+ { return i < m_children.elements() ? m_children.at(i) : NULL; }
+
/// Calculate and return the number of handlers to pop between the given
/// context and this one.
///
@@ -329,9 +440,22 @@ public:
uint context_var_count() const
{ return (uint)m_vars.elements(); }
- /// @return map index in this parsing context to runtime offset.
- uint var_context2runtime(uint i) const
- { return m_var_offset + i; }
+ /// return the i-th variable on the current context
+ sp_variable *get_context_variable(uint i) const
+ {
+ DBUG_ASSERT(i < m_vars.elements());
+ return m_vars.at(i);
+ }
+
+ /*
+ Return the i-th last context variable.
+ If i is 0, then return the very last variable in m_vars.
+ */
+ sp_variable *get_last_context_variable(uint i= 0) const
+ {
+ DBUG_ASSERT(i < m_vars.elements());
+ return m_vars.at(m_vars.elements() - i - 1);
+ }
/// Add SP-variable to the parsing context.
///
@@ -339,13 +463,13 @@ public:
/// @param name Name of the SP-variable.
///
/// @return instance of newly added SP-variable.
- sp_variable *add_variable(THD *thd, LEX_STRING name);
+ sp_variable *add_variable(THD *thd, const LEX_CSTRING *name);
/// Retrieve full type information about SP-variables in this parsing
/// context and its children.
///
/// @param field_def_lst[out] Container to store type information.
- void retrieve_field_definitions(List<Column_definition> *field_def_lst) const;
+ void retrieve_field_definitions(List<Spvar_definition> *field_def_lst) const;
/// Find SP-variable by name.
///
@@ -358,7 +482,7 @@ public:
/// @param current_scope_only A flag if we search only in current scope.
///
/// @return instance of found SP-variable, or NULL if not found.
- sp_variable *find_variable(LEX_STRING name, bool current_scope_only) const;
+ sp_variable *find_variable(const LEX_CSTRING *name, bool current_scope_only) const;
/// Find SP-variable by the offset in the root parsing context.
///
@@ -401,9 +525,31 @@ public:
// Labels.
/////////////////////////////////////////////////////////////////////////
- sp_label *push_label(THD *thd, LEX_STRING name, uint ip);
+ sp_label *push_label(THD *thd, const LEX_CSTRING *name, uint ip,
+ sp_label::enum_type type, List<sp_label> * list);
- sp_label *find_label(LEX_STRING name);
+ sp_label *push_label(THD *thd, const LEX_CSTRING *name, uint ip,
+ sp_label::enum_type type)
+ { return push_label(thd, name, ip, type, &m_labels); }
+
+ sp_label *push_goto_label(THD *thd, const LEX_CSTRING *name, uint ip,
+ sp_label::enum_type type)
+ { return push_label(thd, name, ip, type, &m_goto_labels); }
+
+ sp_label *push_label(THD *thd, const LEX_CSTRING *name, uint ip)
+ { return push_label(thd, name, ip, sp_label::IMPLICIT); }
+
+ sp_label *push_goto_label(THD *thd, const LEX_CSTRING *name, uint ip)
+ { return push_goto_label(thd, name, ip, sp_label::GOTO); }
+
+ sp_label *find_label(const LEX_CSTRING *name);
+
+ sp_label *find_goto_label(const LEX_CSTRING *name, bool recusive);
+
+ sp_label *find_goto_label(const LEX_CSTRING *name)
+ { return find_goto_label(name, true); }
+
+ sp_label *find_label_current_loop_start();
sp_label *last_label()
{
@@ -415,19 +561,50 @@ public:
return label;
}
+ sp_label *last_goto_label()
+ {
+ return m_goto_labels.head();
+ }
+
sp_label *pop_label()
{ return m_labels.pop(); }
+ bool block_label_declare(LEX_CSTRING *label)
+ {
+ sp_label *lab= find_label(label);
+ if (lab)
+ {
+ my_error(ER_SP_LABEL_REDEFINE, MYF(0), label->str);
+ return true;
+ }
+ return false;
+ }
+
/////////////////////////////////////////////////////////////////////////
// Conditions.
/////////////////////////////////////////////////////////////////////////
- bool add_condition(THD *thd, LEX_STRING name, sp_condition_value *value);
+ bool add_condition(THD *thd, const LEX_CSTRING *name,
+ sp_condition_value *value);
/// See comment for find_variable() above.
- sp_condition_value *find_condition(LEX_STRING name,
+ sp_condition_value *find_condition(const LEX_CSTRING *name,
bool current_scope_only) const;
+ sp_condition_value *
+ find_declared_or_predefined_condition(THD *thd, const LEX_CSTRING *name) const;
+
+ bool declare_condition(THD *thd, const LEX_CSTRING *name,
+ sp_condition_value *val)
+ {
+ if (find_condition(name, true))
+ {
+ my_error(ER_SP_DUP_COND, MYF(0), name->str);
+ return true;
+ }
+ return add_condition(thd, name, val);
+ }
+
/////////////////////////////////////////////////////////////////////////
// Handlers.
/////////////////////////////////////////////////////////////////////////
@@ -454,26 +631,46 @@ public:
/// Find an SQL handler for the given SQL condition according to the
/// SQL-handler resolution rules. This function is used at runtime.
///
- /// @param sql_state The SQL condition state
- /// @param sql_errno The error code
+ /// @param value The error code and the SQL state
/// @param level The SQL condition level
///
/// @return a pointer to the found SQL-handler or NULL.
- sp_handler *find_handler(const char *sql_state,
- uint sql_errno,
- Sql_condition::enum_warning_level level) const;
+ sp_handler *find_handler(const Sql_condition_identity &identity) const;
/////////////////////////////////////////////////////////////////////////
// Cursors.
/////////////////////////////////////////////////////////////////////////
- bool add_cursor(LEX_STRING name);
+ bool add_cursor(const LEX_CSTRING *name, sp_pcontext *param_ctx,
+ class sp_lex_cursor *lex);
/// See comment for find_variable() above.
- bool find_cursor(LEX_STRING name, uint *poff, bool current_scope_only) const;
+ const sp_pcursor *find_cursor(const LEX_CSTRING *name,
+ uint *poff, bool current_scope_only) const;
+
+ const sp_pcursor *find_cursor_with_error(const LEX_CSTRING *name,
+ uint *poff,
+ bool current_scope_only) const
+ {
+ const sp_pcursor *pcursor= find_cursor(name, poff, current_scope_only);
+ if (!pcursor)
+ {
+ my_error(ER_SP_CURSOR_MISMATCH, MYF(0), name->str);
+ return NULL;
+ }
+ return pcursor;
+ }
+ /// Find cursor by offset (for SHOW {PROCEDURE|FUNCTION} CODE only).
+ const sp_pcursor *find_cursor(uint offset) const;
- /// Find cursor by offset (for debugging only).
- const LEX_STRING *find_cursor(uint offset) const;
+ const sp_pcursor *get_cursor_by_local_frame_offset(uint offset) const
+ { return &m_cursors.at(offset); }
+
+ uint cursor_offset() const
+ { return m_cursor_offset; }
+
+ uint frame_cursor_count() const
+ { return (uint)m_cursors.elements(); }
uint max_cursor_index() const
{ return m_max_cursor_index + (uint)m_cursors.elements(); }
@@ -481,6 +678,15 @@ public:
uint current_cursor_count() const
{ return m_cursor_offset + (uint)m_cursors.elements(); }
+ void set_for_loop(const Lex_for_loop_st &for_loop)
+ {
+ m_for_loop.init(for_loop);
+ }
+ const Lex_for_loop_st &for_loop()
+ {
+ return m_for_loop;
+ }
+
private:
/// Constructor for a tree node.
/// @param prev the parent parsing context
@@ -493,6 +699,8 @@ private:
sp_pcontext(const sp_pcontext &);
void operator=(sp_pcontext &);
+ sp_condition_value *find_predefined_condition(const LEX_CSTRING *name) const;
+
private:
/// m_max_var_index -- number of variables (including all types of arguments)
/// in this context including all children contexts.
@@ -538,19 +746,41 @@ private:
Dynamic_array<sp_condition *> m_conditions;
/// Stack of cursors.
- Dynamic_array<LEX_STRING> m_cursors;
+ Dynamic_array<sp_pcursor> m_cursors;
/// Stack of SQL-handlers.
Dynamic_array<sp_handler *> m_handlers;
- /// List of labels.
+ /*
+ In the below example the label <<lab>> has two meanings:
+ - GOTO lab : must go before the beginning of the loop
+ - CONTINUE lab : must go to the beginning of the loop
+ We solve this by storing block labels and goto labels into separate lists.
+
+ BEGIN
+ <<lab>>
+ FOR i IN a..10 LOOP
+ ...
+ GOTO lab;
+ ...
+ CONTINUE lab;
+ ...
+ END LOOP;
+ END;
+ */
+ /// List of block labels
List<sp_label> m_labels;
+ /// List of goto labels
+ List<sp_label> m_goto_labels;
/// Children contexts, used for destruction.
Dynamic_array<sp_pcontext *> m_children;
/// Scope of this parsing context.
enum_scope m_scope;
+
+ /// FOR LOOP characteristics
+ Lex_for_loop m_for_loop;
}; // class sp_pcontext : public Sql_alloc
diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc
index 80a1ffe82c2..61d7c708983 100644
--- a/sql/sp_rcontext.cc
+++ b/sql/sp_rcontext.cc
@@ -13,7 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#ifdef USE_PRAGMA_IMPLEMENTATION
@@ -26,16 +26,49 @@
#include "sp_rcontext.h"
#include "sp_pcontext.h"
#include "sql_select.h" // create_virtual_tmp_table
+#include "sql_base.h" // open_tables_only_view_structure
+#include "sql_acl.h" // SELECT_ACL
+#include "sql_parse.h" // check_table_access
+
+
+Sp_rcontext_handler_local sp_rcontext_handler_local;
+Sp_rcontext_handler_package_body sp_rcontext_handler_package_body;
+
+sp_rcontext *Sp_rcontext_handler_local::get_rcontext(sp_rcontext *ctx) const
+{
+ return ctx;
+}
+
+sp_rcontext *Sp_rcontext_handler_package_body::get_rcontext(sp_rcontext *ctx) const
+{
+ return ctx->m_sp->m_parent->m_rcontext;
+}
+
+const LEX_CSTRING *Sp_rcontext_handler_local::get_name_prefix() const
+{
+ return &empty_clex_str;
+}
+
+const LEX_CSTRING *Sp_rcontext_handler_package_body::get_name_prefix() const
+{
+ static const LEX_CSTRING sp_package_body_variable_prefix_clex_str=
+ {STRING_WITH_LEN("PACKAGE_BODY.")};
+ return &sp_package_body_variable_prefix_clex_str;
+}
+
///////////////////////////////////////////////////////////////////////////
// sp_rcontext implementation.
///////////////////////////////////////////////////////////////////////////
-sp_rcontext::sp_rcontext(const sp_pcontext *root_parsing_ctx,
+sp_rcontext::sp_rcontext(const sp_head *owner,
+ const sp_pcontext *root_parsing_ctx,
Field *return_value_fld,
bool in_sub_stmt)
:end_partial_result_set(false),
+ pause_state(false), quit_func(false), instr_ptr(0),
+ m_sp(owner),
m_root_parsing_ctx(root_parsing_ctx),
m_var_table(NULL),
m_return_value_fld(return_value_fld),
@@ -48,9 +81,7 @@ sp_rcontext::sp_rcontext(const sp_pcontext *root_parsing_ctx,
sp_rcontext::~sp_rcontext()
{
- if (m_var_table)
- free_blobs(m_var_table);
-
+ delete m_var_table;
// Leave m_handlers, m_handler_call_stack, m_var_items, m_cstack
// and m_case_expr_holders untouched.
// They are allocated in mem roots and will be freed accordingly.
@@ -58,14 +89,16 @@ sp_rcontext::~sp_rcontext()
sp_rcontext *sp_rcontext::create(THD *thd,
+ const sp_head *owner,
const sp_pcontext *root_parsing_ctx,
- Field *return_value_fld)
+ Field *return_value_fld,
+ Row_definition_list &field_def_lst)
{
SELECT_LEX *save_current_select;
- sp_rcontext *ctx= new (thd->mem_root) sp_rcontext(root_parsing_ctx,
+ sp_rcontext *ctx= new (thd->mem_root) sp_rcontext(owner,
+ root_parsing_ctx,
return_value_fld,
thd->in_sub_stmt);
-
if (!ctx)
return NULL;
@@ -74,8 +107,8 @@ sp_rcontext *sp_rcontext::create(THD *thd,
thd->lex->current_select= 0;
if (ctx->alloc_arrays(thd) ||
- ctx->init_var_table(thd) ||
- ctx->init_var_items(thd))
+ ctx->init_var_table(thd, field_def_lst) ||
+ ctx->init_var_items(thd, field_def_lst))
{
delete ctx;
ctx= 0;
@@ -86,6 +119,39 @@ sp_rcontext *sp_rcontext::create(THD *thd,
}
+bool Row_definition_list::
+ adjust_formal_params_to_actual_params(THD *thd, List<Item> *args)
+{
+ List_iterator<Spvar_definition> it(*this);
+ List_iterator<Item> it_args(*args);
+ DBUG_ASSERT(elements >= args->elements );
+ Spvar_definition *def;
+ Item *arg;
+ while ((def= it++) && (arg= it_args++))
+ {
+ if (def->type_handler()->adjust_spparam_type(def, arg))
+ return true;
+ }
+ return false;
+}
+
+
+bool Row_definition_list::
+ adjust_formal_params_to_actual_params(THD *thd,
+ Item **args, uint arg_count)
+{
+ List_iterator<Spvar_definition> it(*this);
+ DBUG_ASSERT(elements >= arg_count );
+ Spvar_definition *def;
+ for (uint i= 0; (def= it++) && (i < arg_count) ; i++)
+ {
+ if (def->type_handler()->adjust_spparam_type(def, args[i]))
+ return true;
+ }
+ return false;
+}
+
+
bool sp_rcontext::alloc_arrays(THD *thd)
{
{
@@ -108,45 +174,243 @@ bool sp_rcontext::alloc_arrays(THD *thd)
}
-bool sp_rcontext::init_var_table(THD *thd)
+bool sp_rcontext::init_var_table(THD *thd,
+ List<Spvar_definition> &field_def_lst)
{
- List<Column_definition> field_def_lst;
-
if (!m_root_parsing_ctx->max_var_index())
return false;
- m_root_parsing_ctx->retrieve_field_definitions(&field_def_lst);
-
DBUG_ASSERT(field_def_lst.elements == m_root_parsing_ctx->max_var_index());
if (!(m_var_table= create_virtual_tmp_table(thd, field_def_lst)))
return true;
- m_var_table->copy_blobs= true;
- m_var_table->alias.set("", 0, m_var_table->alias.charset());
+ return false;
+}
+
+/**
+ Check if we have access to use a column as a %TYPE reference.
+ @return false - OK
+ @return true - access denied
+*/
+static inline bool
+check_column_grant_for_type_ref(THD *thd, TABLE_LIST *table_list,
+ const char *str, size_t length,
+ Field *fld)
+{
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ table_list->table->grant.want_privilege= SELECT_ACL;
+ return check_column_grant_in_table_ref(thd, table_list, str, length, fld);
+#else
return false;
+#endif
}
-bool sp_rcontext::init_var_items(THD *thd)
+/**
+ This method implementation is very close to fill_schema_table_by_open().
+*/
+bool Qualified_column_ident::resolve_type_ref(THD *thd, Column_definition *def)
+{
+ Open_tables_backup open_tables_state_backup;
+ thd->reset_n_backup_open_tables_state(&open_tables_state_backup);
+
+ TABLE_LIST *table_list;
+ Field *src;
+ LEX *save_lex= thd->lex;
+ bool rc= true;
+
+ sp_lex_local lex(thd, thd->lex);
+ thd->lex= &lex;
+
+ lex.context_analysis_only= CONTEXT_ANALYSIS_ONLY_VIEW;
+ // Make %TYPE variables see temporary tables that shadow permanent tables
+ thd->temporary_tables= open_tables_state_backup.temporary_tables;
+
+ if ((table_list= lex.select_lex.add_table_to_list(thd, this, NULL, 0,
+ TL_READ_NO_INSERT,
+ MDL_SHARED_READ)) &&
+ !check_table_access(thd, SELECT_ACL, table_list, TRUE, UINT_MAX, FALSE) &&
+ !open_tables_only_view_structure(thd, table_list,
+ thd->mdl_context.has_locks()))
+ {
+ if (likely((src= lex.query_tables->table->find_field_by_name(&m_column))))
+ {
+ if (!(rc= check_column_grant_for_type_ref(thd, table_list,
+ m_column.str,
+ m_column.length, src)))
+ {
+ *def= Column_definition(thd, src, NULL/*No defaults,no constraints*/);
+ def->flags&= (uint) ~NOT_NULL_FLAG;
+ rc= def->sp_prepare_create_field(thd, thd->mem_root);
+ }
+ }
+ else
+ my_error(ER_BAD_FIELD_ERROR, MYF(0), m_column.str, table.str);
+ }
+
+ lex.unit.cleanup();
+ thd->temporary_tables= NULL; // Avoid closing temporary tables
+ close_thread_tables(thd);
+ thd->lex= save_lex;
+ thd->restore_backup_open_tables_state(&open_tables_state_backup);
+ return rc;
+}
+
+
+/**
+ This method resolves the structure of a variable declared as:
+ rec t1%ROWTYPE;
+ It opens the table "t1" and copies its structure to %ROWTYPE variable.
+*/
+bool Table_ident::resolve_table_rowtype_ref(THD *thd,
+ Row_definition_list &defs)
+{
+ Open_tables_backup open_tables_state_backup;
+ thd->reset_n_backup_open_tables_state(&open_tables_state_backup);
+
+ TABLE_LIST *table_list;
+ LEX *save_lex= thd->lex;
+ bool rc= true;
+
+ /*
+ Create a temporary LEX on stack and switch to it.
+ In case of VIEW, open_tables_only_view_structure() will open more
+ tables/views recursively. We want to avoid them to stick to the current LEX.
+ */
+ sp_lex_local lex(thd, thd->lex);
+ thd->lex= &lex;
+
+ lex.context_analysis_only= CONTEXT_ANALYSIS_ONLY_VIEW;
+ // Make %ROWTYPE variables see temporary tables that shadow permanent tables
+ thd->temporary_tables= open_tables_state_backup.temporary_tables;
+
+ if ((table_list= lex.select_lex.add_table_to_list(thd, this, NULL, 0,
+ TL_READ_NO_INSERT,
+ MDL_SHARED_READ)) &&
+ !check_table_access(thd, SELECT_ACL, table_list, TRUE, UINT_MAX, FALSE) &&
+ !open_tables_only_view_structure(thd, table_list,
+ thd->mdl_context.has_locks()))
+ {
+ for (Field **src= lex.query_tables->table->field; *src; src++)
+ {
+ /*
+ Make field names on the THD memory root,
+ as the table will be closed and freed soon,
+ in the end of this method.
+ */
+ LEX_CSTRING tmp= src[0]->field_name;
+ Spvar_definition *def;
+ if ((rc= check_column_grant_for_type_ref(thd, table_list,
+ tmp.str, tmp.length,src[0])) ||
+ (rc= !(src[0]->field_name.str= thd->strmake(tmp.str, tmp.length))) ||
+ (rc= !(def= new (thd->mem_root) Spvar_definition(thd, *src))))
+ break;
+ src[0]->field_name.str= tmp.str; // Restore field name, just in case.
+ def->flags&= (uint) ~NOT_NULL_FLAG;
+ if ((rc= def->sp_prepare_create_field(thd, thd->mem_root)))
+ break;
+ defs.push_back(def, thd->mem_root);
+ }
+ }
+
+ lex.unit.cleanup();
+ thd->temporary_tables= NULL; // Avoid closing temporary tables
+ close_thread_tables(thd);
+ thd->lex= save_lex;
+ thd->restore_backup_open_tables_state(&open_tables_state_backup);
+ return rc;
+}
+
+
+bool Row_definition_list::resolve_type_refs(THD *thd)
+{
+ List_iterator<Spvar_definition> it(*this);
+ Spvar_definition *def;
+ while ((def= it++))
+ {
+ if (def->is_column_type_ref() &&
+ def->column_type_ref()->resolve_type_ref(thd, def))
+ return true;
+ }
+ return false;
+};
+
+
+bool sp_rcontext::init_var_items(THD *thd,
+ List<Spvar_definition> &field_def_lst)
{
uint num_vars= m_root_parsing_ctx->max_var_index();
m_var_items.reset(
- static_cast<Item **> (
+ static_cast<Item_field **> (
thd->alloc(num_vars * sizeof (Item *))),
num_vars);
if (!m_var_items.array())
return true;
- for (uint idx = 0; idx < num_vars; ++idx)
+ DBUG_ASSERT(field_def_lst.elements == num_vars);
+ List_iterator<Spvar_definition> it(field_def_lst);
+ Spvar_definition *def= it++;
+
+ for (uint idx= 0; idx < num_vars; ++idx, def= it++)
{
- if (!(m_var_items[idx]= new (thd->mem_root) Item_field(thd, m_var_table->field[idx])))
- return true;
+ Field *field= m_var_table->field[idx];
+ if (def->is_table_rowtype_ref())
+ {
+ Row_definition_list defs;
+ Item_field_row *item= new (thd->mem_root) Item_field_row(thd, field);
+ if (!(m_var_items[idx]= item) ||
+ def->table_rowtype_ref()->resolve_table_rowtype_ref(thd, defs) ||
+ item->row_create_items(thd, &defs))
+ return true;
+ }
+ else if (def->is_cursor_rowtype_ref())
+ {
+ Row_definition_list defs;
+ Item_field_row *item= new (thd->mem_root) Item_field_row(thd, field);
+ if (!(m_var_items[idx]= item))
+ return true;
+ }
+ else if (def->is_row())
+ {
+ Item_field_row *item= new (thd->mem_root) Item_field_row(thd, field);
+ if (!(m_var_items[idx]= item) ||
+ item->row_create_items(thd, def->row_field_definitions()))
+ return true;
+ }
+ else
+ {
+ if (!(m_var_items[idx]= new (thd->mem_root) Item_field(thd, field)))
+ return true;
+ }
}
+ return false;
+}
+
+bool Item_field_row::row_create_items(THD *thd, List<Spvar_definition> *list)
+{
+ DBUG_ASSERT(list);
+ DBUG_ASSERT(field);
+ Virtual_tmp_table **ptable= field->virtual_tmp_table_addr();
+ DBUG_ASSERT(ptable);
+ if (!(ptable[0]= create_virtual_tmp_table(thd, *list)))
+ return true;
+
+ if (alloc_arguments(thd, list->elements))
+ return true;
+
+ List_iterator<Spvar_definition> it(*list);
+ Spvar_definition *def;
+ for (arg_count= 0; (def= it++); arg_count++)
+ {
+ if (!(args[arg_count]= new (thd->mem_root)
+ Item_field(thd, ptable[0]->field[arg_count])))
+ return true;
+ }
return false;
}
@@ -157,49 +421,36 @@ bool sp_rcontext::set_return_value(THD *thd, Item **return_value_item)
m_return_value_set = true;
- return sp_eval_expr(thd, m_return_value_fld, return_value_item);
+ return thd->sp_eval_expr(m_return_value_fld, return_value_item);
}
-bool sp_rcontext::push_cursor(THD *thd, sp_lex_keeper *lex_keeper,
- sp_instr_cpush *i)
+void sp_rcontext::push_cursor(sp_cursor *c)
{
- /*
- We should create cursors in the callers arena, as
- it could be (and usually is) used in several instructions.
- */
- sp_cursor *c= new (callers_arena->mem_root) sp_cursor(thd, lex_keeper, i);
+ m_cstack[m_ccount++]= c;
+}
- if (c == NULL)
- return true;
- m_cstack[m_ccount++]= c;
- return false;
+void sp_rcontext::pop_cursor(THD *thd)
+{
+ DBUG_ASSERT(m_ccount > 0);
+ if (m_cstack[m_ccount - 1]->is_open())
+ m_cstack[m_ccount - 1]->close(thd);
+ m_ccount--;
}
-void sp_rcontext::pop_cursors(uint count)
+void sp_rcontext::pop_cursors(THD *thd, size_t count)
{
DBUG_ASSERT(m_ccount >= count);
-
while (count--)
- delete m_cstack[--m_ccount];
+ pop_cursor(thd);
}
-bool sp_rcontext::push_handler(sp_handler *handler, uint first_ip)
+bool sp_rcontext::push_handler(sp_instr_hpush_jump *entry)
{
- /*
- We should create handler entries in the callers arena, as
- they could be (and usually are) used in several instructions.
- */
- sp_handler_entry *he=
- new (callers_arena->mem_root) sp_handler_entry(handler, first_ip);
-
- if (he == NULL)
- return true;
-
- return m_handlers.append(he);
+ return m_handlers.append(entry);
}
@@ -224,19 +475,17 @@ bool sp_rcontext::handle_sql_condition(THD *thd,
handlers from this context are applicable: try to locate one
in the outer scope.
*/
- if (thd->is_fatal_sub_stmt_error && m_in_sub_stmt)
+ if (unlikely(thd->is_fatal_sub_stmt_error) && m_in_sub_stmt)
DBUG_RETURN(false);
Diagnostics_area *da= thd->get_stmt_da();
const sp_handler *found_handler= NULL;
const Sql_condition *found_condition= NULL;
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
{
found_handler=
- cur_spi->m_ctx->find_handler(da->get_sqlstate(),
- da->sql_errno(),
- Sql_condition::WARN_LEVEL_ERROR);
+ cur_spi->m_ctx->find_handler(da->get_error_condition_identity());
if (found_handler)
found_condition= da->get_error_condition();
@@ -250,12 +499,10 @@ bool sp_rcontext::handle_sql_condition(THD *thd,
*/
if (!found_condition)
{
- Sql_condition *condition=
- new (callers_arena->mem_root) Sql_condition(callers_arena->mem_root);
- condition->set(da->sql_errno(), da->get_sqlstate(),
- Sql_condition::WARN_LEVEL_ERROR,
- da->message());
- found_condition= condition;
+ found_condition=
+ new (callers_arena->mem_root) Sql_condition(callers_arena->mem_root,
+ da->get_error_condition_identity(),
+ da->message());
}
}
else if (da->current_statement_warn_count())
@@ -272,10 +519,7 @@ bool sp_rcontext::handle_sql_condition(THD *thd,
if (c->get_level() == Sql_condition::WARN_LEVEL_WARN ||
c->get_level() == Sql_condition::WARN_LEVEL_NOTE)
{
- const sp_handler *handler=
- cur_spi->m_ctx->find_handler(c->get_sqlstate(),
- c->get_sql_errno(),
- c->get_level());
+ const sp_handler *handler= cur_spi->m_ctx->find_handler(*c);
if (handler)
{
found_handler= handler;
@@ -294,12 +538,12 @@ bool sp_rcontext::handle_sql_condition(THD *thd,
DBUG_ASSERT(found_condition);
- sp_handler_entry *handler_entry= NULL;
+ sp_instr_hpush_jump *handler_entry= NULL;
for (size_t i= 0; i < m_handlers.elements(); ++i)
{
- sp_handler_entry *h= m_handlers.at(i);
+ sp_instr_hpush_jump *h= m_handlers.at(i);
- if (h->handler == found_handler)
+ if (h->get_handler() == found_handler)
{
handler_entry= h;
break;
@@ -328,7 +572,7 @@ bool sp_rcontext::handle_sql_condition(THD *thd,
// Mark active conditions so that they can be deleted when the handler exits.
da->mark_sql_conditions_for_removal();
- uint continue_ip= handler_entry->handler->type == sp_handler::CONTINUE ?
+ uint continue_ip= handler_entry->get_handler()->type == sp_handler::CONTINUE ?
cur_spi->get_cont_dest() : 0;
/* End aborted result set. */
@@ -347,7 +591,7 @@ bool sp_rcontext::handle_sql_condition(THD *thd,
new (callers_arena->mem_root) Handler_call_frame(cond_info, continue_ip);
m_handler_call_stack.append(frame);
- *ip= handler_entry->first_ip;
+ *ip= handler_entry->m_ip + 1;
DBUG_RETURN(true);
}
@@ -372,15 +616,66 @@ uint sp_rcontext::exit_handler(Diagnostics_area *da)
}
-int sp_rcontext::set_variable(THD *thd, Field *field, Item **value)
+int sp_rcontext::set_variable(THD *thd, uint idx, Item **value)
{
- if (!value)
- {
- field->set_null();
- return 0;
- }
+ DBUG_ENTER("sp_rcontext::set_variable");
+ DBUG_ASSERT(value);
+ DBUG_RETURN(thd->sp_eval_expr(m_var_table->field[idx], value));
+}
+
+
+int sp_rcontext::set_variable_row_field(THD *thd, uint var_idx, uint field_idx,
+ Item **value)
+{
+ DBUG_ENTER("sp_rcontext::set_variable_row_field");
+ DBUG_ASSERT(value);
+ Virtual_tmp_table *vtable= virtual_tmp_table_for_row(var_idx);
+ DBUG_RETURN(thd->sp_eval_expr(vtable->field[field_idx], value));
+}
+
+
+int sp_rcontext::set_variable_row_field_by_name(THD *thd, uint var_idx,
+ const LEX_CSTRING &field_name,
+ Item **value)
+{
+ DBUG_ENTER("sp_rcontext::set_variable_row_field_by_name");
+ uint field_idx;
+ if (find_row_field_by_name_or_error(&field_idx, var_idx, field_name))
+ DBUG_RETURN(1);
+ DBUG_RETURN(set_variable_row_field(thd, var_idx, field_idx, value));
+}
+
- return sp_eval_expr(thd, field, value);
+int sp_rcontext::set_variable_row(THD *thd, uint var_idx, List<Item> &items)
+{
+ DBUG_ENTER("sp_rcontext::set_variable_row");
+ DBUG_ASSERT(get_variable(var_idx)->cols() == items.elements);
+ Virtual_tmp_table *vtable= virtual_tmp_table_for_row(var_idx);
+ Sp_eval_expr_state state(thd);
+ DBUG_RETURN(vtable->sp_set_all_fields_from_item_list(thd, items));
+}
+
+
+Virtual_tmp_table *sp_rcontext::virtual_tmp_table_for_row(uint var_idx)
+{
+ DBUG_ASSERT(get_variable(var_idx)->type() == Item::FIELD_ITEM);
+ DBUG_ASSERT(get_variable(var_idx)->cmp_type() == ROW_RESULT);
+ Field *field= m_var_table->field[var_idx];
+ Virtual_tmp_table **ptable= field->virtual_tmp_table_addr();
+ DBUG_ASSERT(ptable);
+ DBUG_ASSERT(ptable[0]);
+ return ptable[0];
+}
+
+
+bool sp_rcontext::find_row_field_by_name_or_error(uint *field_idx,
+ uint var_idx,
+ const LEX_CSTRING &field_name)
+{
+ Virtual_tmp_table *vtable= virtual_tmp_table_for_row(var_idx);
+ Field *row= m_var_table->field[var_idx];
+ return vtable->sp_find_field_by_name_or_error(field_idx,
+ row->field_name, field_name);
}
@@ -392,7 +687,7 @@ Item_cache *sp_rcontext::create_case_expr_holder(THD *thd,
thd->set_n_backup_active_arena(thd->spcont->callers_arena, &current_arena);
- holder= Item_cache::get_cache(thd, item);
+ holder= item->get_cache(thd);
thd->restore_active_arena(thd->spcont->callers_arena, &current_arena);
@@ -403,7 +698,7 @@ Item_cache *sp_rcontext::create_case_expr_holder(THD *thd,
bool sp_rcontext::set_case_expr(THD *thd, int case_expr_id,
Item **case_expr_item_ptr)
{
- Item *case_expr_item= sp_prepare_func_item(thd, case_expr_item_ptr);
+ Item *case_expr_item= thd->sp_prepare_func_item(case_expr_item_ptr);
if (!case_expr_item)
return true;
@@ -426,20 +721,6 @@ bool sp_rcontext::set_case_expr(THD *thd, int case_expr_id,
///////////////////////////////////////////////////////////////////////////
-sp_cursor::sp_cursor(THD *thd_arg, sp_lex_keeper *lex_keeper, sp_instr_cpush *i):
- result(thd_arg),
- m_lex_keeper(lex_keeper),
- server_side_cursor(NULL),
- m_i(i)
-{
- /*
- currsor can't be stored in QC, so we should prevent opening QC for
- try to write results which are absent.
- */
- lex_keeper->disable_query_cache();
-}
-
-
/*
Open an SP cursor
@@ -475,6 +756,7 @@ int sp_cursor::close(THD *thd)
MYF(0));
return -1;
}
+ sp_cursor_statistics::reset();
destroy();
return 0;
}
@@ -487,7 +769,7 @@ void sp_cursor::destroy()
}
-int sp_cursor::fetch(THD *thd, List<sp_variable> *vars)
+int sp_cursor::fetch(THD *thd, List<sp_variable> *vars, bool error_on_no_data)
{
if (! server_side_cursor)
{
@@ -495,13 +777,17 @@ int sp_cursor::fetch(THD *thd, List<sp_variable> *vars)
MYF(0));
return -1;
}
- if (vars->elements != result.get_field_count())
+ if (vars->elements != result.get_field_count() &&
+ (vars->elements != 1 ||
+ result.get_field_count() !=
+ thd->spcont->get_variable(vars->head()->offset)->cols()))
{
my_message(ER_SP_WRONG_NO_OF_FETCH_ARGS,
ER_THD(thd, ER_SP_WRONG_NO_OF_FETCH_ARGS), MYF(0));
return -1;
}
+ m_fetch_count++;
DBUG_EXECUTE_IF("bug23032_emit_warning",
push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
ER_UNKNOWN_ERROR,
@@ -525,14 +811,24 @@ int sp_cursor::fetch(THD *thd, List<sp_variable> *vars)
*/
if (! server_side_cursor->is_open())
{
+ m_found= false;
+ if (!error_on_no_data)
+ return 0;
my_message(ER_SP_FETCH_NO_DATA, ER_THD(thd, ER_SP_FETCH_NO_DATA), MYF(0));
return -1;
}
+ m_found= true;
+ m_row_count++;
return 0;
}
+bool sp_cursor::export_structure(THD *thd, Row_definition_list *list)
+{
+ return server_side_cursor->export_structure(thd, list);
+}
+
///////////////////////////////////////////////////////////////////////////
// sp_cursor::Select_fetch_into_spvars implementation.
///////////////////////////////////////////////////////////////////////////
@@ -550,15 +846,16 @@ int sp_cursor::Select_fetch_into_spvars::prepare(List<Item> &fields,
}
-int sp_cursor::Select_fetch_into_spvars::send_data(List<Item> &items)
+bool sp_cursor::Select_fetch_into_spvars::
+ send_data_to_variable_list(List<sp_variable> &vars, List<Item> &items)
{
- List_iterator_fast<sp_variable> spvar_iter(*spvar_list);
+ List_iterator_fast<sp_variable> spvar_iter(vars);
List_iterator_fast<Item> item_iter(items);
sp_variable *spvar;
Item *item;
/* Must be ensured by the caller */
- DBUG_ASSERT(spvar_list->elements == items.elements);
+ DBUG_ASSERT(vars.elements == items.elements);
/*
Assign the row fetched from a server side cursor to stored
@@ -571,3 +868,25 @@ int sp_cursor::Select_fetch_into_spvars::send_data(List<Item> &items)
}
return false;
}
+
+
+int sp_cursor::Select_fetch_into_spvars::send_data(List<Item> &items)
+{
+ Item *item;
+ /*
+ If we have only one variable in spvar_list, and this is a ROW variable,
+ and the number of fields in the ROW variable matches the number of
+ fields in the query result, we fetch to this ROW variable.
+
+ If there is one variable, and it is a ROW variable, but its number
+ of fields does not match the number of fields in the query result,
+ we go through send_data_to_variable_list(). It will report an error
+ on attempt to assign a scalar value to a ROW variable.
+ */
+ return spvar_list->elements == 1 &&
+ (item= thd->spcont->get_variable(spvar_list->head()->offset)) &&
+ item->type_handler() == &type_handler_row &&
+ item->cols() == items.elements ?
+ thd->spcont->set_variable_row(thd, spvar_list->head()->offset, items) :
+ send_data_to_variable_list(*spvar_list, items);
+}
diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h
index a0a009adb8f..0e0e8921f86 100644
--- a/sql/sp_rcontext.h
+++ b/sql/sp_rcontext.h
@@ -31,9 +31,11 @@
class sp_cursor;
class sp_lex_keeper;
class sp_instr_cpush;
+class sp_instr_hpush_jump;
class Query_arena;
class sp_head;
class Item_cache;
+class Virtual_tmp_table;
/*
@@ -69,13 +71,16 @@ public:
///
/// @return valid sp_rcontext object or NULL in case of OOM-error.
static sp_rcontext *create(THD *thd,
+ const sp_head *owner,
const sp_pcontext *root_parsing_ctx,
- Field *return_value_fld);
+ Field *return_value_fld,
+ Row_definition_list &defs);
~sp_rcontext();
private:
- sp_rcontext(const sp_pcontext *root_parsing_ctx,
+ sp_rcontext(const sp_head *owner,
+ const sp_pcontext *root_parsing_ctx,
Field *return_value_fld,
bool in_sub_stmt);
@@ -83,27 +88,6 @@ private:
sp_rcontext(const sp_rcontext &);
void operator=(sp_rcontext &);
-private:
- /// This is an auxillary class to store entering instruction pointer for an
- /// SQL-handler.
- class sp_handler_entry : public Sql_alloc
- {
- public:
- /// Handler definition (from parsing context).
- const sp_handler *handler;
-
- /// Instruction pointer to the first instruction.
- uint first_ip;
-
- /// The constructor.
- ///
- /// @param _handler sp_handler object.
- /// @param _first_ip first instruction pointer.
- sp_handler_entry(const sp_handler *_handler, uint _first_ip)
- :handler(_handler), first_ip(_first_ip)
- { }
- };
-
public:
/// This class stores basic information about SQL-condition, such as:
/// - SQL error code;
@@ -120,18 +104,10 @@ public:
/// standard SQL-condition processing (Diagnostics_area should contain an
/// object for active SQL-condition, not just information stored in DA's
/// fields).
- class Sql_condition_info : public Sql_alloc
+ class Sql_condition_info : public Sql_alloc,
+ public Sql_condition_identity
{
public:
- /// SQL error code.
- uint sql_errno;
-
- /// Error level.
- Sql_condition::enum_warning_level level;
-
- /// SQLSTATE.
- char sql_state[SQLSTATE_LENGTH + 1];
-
/// Text message.
char *message;
@@ -141,12 +117,8 @@ public:
/// @param arena Query arena for SP
Sql_condition_info(const Sql_condition *_sql_condition,
Query_arena *arena)
- :sql_errno(_sql_condition->get_sql_errno()),
- level(_sql_condition->get_level())
+ :Sql_condition_identity(*_sql_condition)
{
- memcpy(sql_state, _sql_condition->get_sqlstate(), SQLSTATE_LENGTH);
- sql_state[SQLSTATE_LENGTH]= '\0';
-
message= strdup_root(arena->mem_root, _sql_condition->get_message_text());
}
};
@@ -186,25 +158,53 @@ public:
/// (if one is found). Otherwise the client will hang due to a violation
/// of the client/server protocol.
bool end_partial_result_set;
+ bool pause_state;
+ bool quit_func;
+ uint instr_ptr;
-#ifndef DBUG_OFF
/// The stored program for which this runtime context is created. Used for
- /// checking if correct runtime context is used for variable handling.
- sp_head *sp;
-#endif
+ /// checking if correct runtime context is used for variable handling,
+ /// and to access the package run-time context.
+ /// Also used by slow log.
+ const sp_head *m_sp;
/////////////////////////////////////////////////////////////////////////
// SP-variables.
/////////////////////////////////////////////////////////////////////////
- int set_variable(THD *thd, uint var_idx, Item **value)
- { return set_variable(thd, m_var_table->field[var_idx], value); }
+ uint argument_count() const
+ {
+ return m_root_parsing_ctx->context_var_count();
+ }
- Item *get_item(uint var_idx) const
+ int set_variable(THD *thd, uint var_idx, Item **value);
+ int set_variable_row_field(THD *thd, uint var_idx, uint field_idx,
+ Item **value);
+ int set_variable_row_field_by_name(THD *thd, uint var_idx,
+ const LEX_CSTRING &field_name,
+ Item **value);
+ int set_variable_row(THD *thd, uint var_idx, List<Item> &items);
+
+ int set_parameter(THD *thd, uint var_idx, Item **value)
+ {
+ DBUG_ASSERT(var_idx < argument_count());
+ return set_variable(thd, var_idx, value);
+ }
+
+ Item_field *get_variable(uint var_idx) const
{ return m_var_items[var_idx]; }
- Item **get_item_addr(uint var_idx) const
- { return m_var_items.array() + var_idx; }
+ Item **get_variable_addr(uint var_idx) const
+ { return ((Item **) m_var_items.array()) + var_idx; }
+
+ Item_field *get_parameter(uint var_idx) const
+ {
+ DBUG_ASSERT(var_idx < argument_count());
+ return get_variable(var_idx);
+ }
+
+ bool find_row_field_by_name_or_error(uint *field_idx, uint var_idx,
+ const LEX_CSTRING &field_name);
bool set_return_value(THD *thd, Item **return_value_item);
@@ -215,18 +215,16 @@ public:
// SQL-handlers.
/////////////////////////////////////////////////////////////////////////
- /// Create a new sp_handler_entry instance and push it to the handler call
- /// stack.
+ /// Push an sp_instr_hpush_jump instance to the handler call stack.
///
- /// @param handler SQL-handler object.
- /// @param first_ip First instruction pointer of the handler.
+ /// @param entry The condition handler entry
///
/// @return error flag.
/// @retval false on success.
/// @retval true on error.
- bool push_handler(sp_handler *handler, uint first_ip);
+ bool push_handler(sp_instr_hpush_jump *entry);
- /// Pop and delete given number of sp_handler_entry instances from the handler
+ /// Pop and delete given number of instances from the handler
/// call stack.
///
/// @param count Number of handler entries to pop & delete.
@@ -273,23 +271,20 @@ public:
// Cursors.
/////////////////////////////////////////////////////////////////////////
- /// Create a new sp_cursor instance and push it to the cursor stack.
+ /// Push a cursor to the cursor stack.
///
- /// @param lex_keeper SP-instruction execution helper.
- /// @param i Cursor-push instruction.
+ /// @param cursor The cursor
///
- /// @return error flag.
- /// @retval false on success.
- /// @retval true on error.
- bool push_cursor(THD *thd, sp_lex_keeper *lex_keeper, sp_instr_cpush *i);
+ void push_cursor(sp_cursor *cur);
+ void pop_cursor(THD *thd);
/// Pop and delete given number of sp_cursor instance from the cursor stack.
///
/// @param count Number of cursors to pop & delete.
- void pop_cursors(uint count);
+ void pop_cursors(THD *thd, size_t count);
- void pop_all_cursors()
- { pop_cursors(m_ccount); }
+ void pop_all_cursors(THD *thd)
+ { pop_cursors(thd, m_ccount); }
sp_cursor *get_cursor(uint i) const
{ return m_cstack[i]; }
@@ -346,7 +341,7 @@ private:
/// @return error flag.
/// @retval false on success.
/// @retval true on error.
- bool init_var_table(THD *thd);
+ bool init_var_table(THD *thd, List<Spvar_definition> &defs);
/// Create and initialize an Item-adapter (Item_field) for each SP-var field.
///
@@ -355,7 +350,7 @@ private:
/// @return error flag.
/// @retval false on success.
/// @retval true on error.
- bool init_var_items(THD *thd);
+ bool init_var_items(THD *thd, List<Spvar_definition> &defs);
/// Create an instance of appropriate Item_cache class depending on the
/// specified type in the callers arena.
@@ -369,18 +364,18 @@ private:
/// @return Pointer to valid object on success, or NULL in case of error.
Item_cache *create_case_expr_holder(THD *thd, const Item *item) const;
- int set_variable(THD *thd, Field *field, Item **value);
+ Virtual_tmp_table *virtual_tmp_table_for_row(uint idx);
private:
/// Top-level (root) parsing context for this runtime context.
const sp_pcontext *m_root_parsing_ctx;
/// Virtual table for storing SP-variables.
- TABLE *m_var_table;
+ Virtual_tmp_table *m_var_table;
/// Collection of Item_field proxies, each of them points to the
/// corresponding field in m_var_table.
- Bounds_checked_array<Item *> m_var_items;
+ Bounds_checked_array<Item_field *> m_var_items;
/// This is a pointer to a field, which should contain return value for
/// stored functions (only). For stored procedures, this pointer is NULL.
@@ -394,7 +389,7 @@ private:
bool m_in_sub_stmt;
/// Stack of visible handlers.
- Dynamic_array<sp_handler_entry *> m_handlers;
+ Dynamic_array<sp_instr_hpush_jump *> m_handlers;
/// Stack of caught SQL conditions.
Dynamic_array<Handler_call_frame *> m_handler_call_stack;
@@ -409,61 +404,4 @@ private:
Bounds_checked_array<Item_cache *> m_case_expr_holders;
}; // class sp_rcontext : public Sql_alloc
-///////////////////////////////////////////////////////////////////////////
-// sp_cursor declaration.
-///////////////////////////////////////////////////////////////////////////
-
-class Server_side_cursor;
-typedef class st_select_lex_unit SELECT_LEX_UNIT;
-
-/* A mediator between stored procedures and server side cursors */
-
-class sp_cursor : public Sql_alloc
-{
-private:
- /// An interceptor of cursor result set used to implement
- /// FETCH <cname> INTO <varlist>.
- class Select_fetch_into_spvars: public select_result_interceptor
- {
- List<sp_variable> *spvar_list;
- uint field_count;
- public:
- Select_fetch_into_spvars(THD *thd_arg): select_result_interceptor(thd_arg) {}
- uint get_field_count() { return field_count; }
- void set_spvar_list(List<sp_variable> *vars) { spvar_list= vars; }
-
- virtual bool send_eof() { return FALSE; }
- virtual int send_data(List<Item> &items);
- virtual int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
-};
-
-public:
- sp_cursor(THD *thd_arg, sp_lex_keeper *lex_keeper, sp_instr_cpush *i);
-
- virtual ~sp_cursor()
- { destroy(); }
-
- sp_lex_keeper *get_lex_keeper() { return m_lex_keeper; }
-
- int open(THD *thd);
-
- int close(THD *thd);
-
- my_bool is_open()
- { return MY_TEST(server_side_cursor); }
-
- int fetch(THD *, List<sp_variable> *vars);
-
- sp_instr_cpush *get_instr()
- { return m_i; }
-
-private:
- Select_fetch_into_spvars result;
- sp_lex_keeper *m_lex_keeper;
- Server_side_cursor *server_side_cursor;
- sp_instr_cpush *m_i; // My push instruction
- void destroy();
-
-}; // class sp_cursor : public Sql_alloc
-
#endif /* _SP_RCONTEXT_H_ */
diff --git a/sql/spatial.cc b/sql/spatial.cc
index 83905fc9f3d..2b36468e158 100644
--- a/sql/spatial.cc
+++ b/sql/spatial.cc
@@ -15,7 +15,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "spatial.h"
#include "gstream.h" // Gis_read_stream
@@ -234,7 +234,7 @@ static void get_point(double *x, double *y, const char *data)
/***************************** Geometry *******************************/
-Geometry::Class_info *Geometry::find_class(const char *name, uint32 len)
+Geometry::Class_info *Geometry::find_class(const char *name, size_t len)
{
for (Class_info **cur_rt= ci_collection;
cur_rt < ci_collection_end; cur_rt++)
@@ -498,7 +498,7 @@ Geometry *Geometry::create_from_json(Geometry_buffer *buffer,
key_buf[key_len++]= (uchar)je->s.c_next | 0x20; /* make it lowercase. */
}
- if (je->s.error)
+ if (unlikely(je->s.error))
goto err_return;
if (key_len == type_keyname_len &&
@@ -2048,6 +2048,7 @@ bool Gis_multi_point::init_from_json(json_engine_t *je, bool er_on_3D,
if (je->s.error)
return TRUE;
+
if (n_points == 0)
{
je->s.error= Geometry::GEOJ_EMPTY_COORDINATES;
@@ -2323,6 +2324,7 @@ bool Gis_multi_line_string::init_from_json(json_engine_t *je, bool er_on_3D,
n_line_strings++;
}
+
if (je->s.error)
return TRUE;
@@ -2721,8 +2723,10 @@ bool Gis_multi_polygon::init_from_json(json_engine_t *je, bool er_on_3D,
n_polygons++;
}
+
if (je->s.error)
return TRUE;
+
if (n_polygons == 0)
{
je->s.error= Geometry::GEOJ_EMPTY_COORDINATES;
diff --git a/sql/spatial.h b/sql/spatial.h
index e78da6e615d..55f450b1b1b 100644
--- a/sql/spatial.h
+++ b/sql/spatial.h
@@ -342,7 +342,7 @@ protected:
return ((type_id < wkb_point) || (type_id > wkb_last)) ?
NULL : ci_collection[type_id];
}
- static Class_info *find_class(const char *name, uint32 len);
+ static Class_info *find_class(const char *name, size_t len);
const char *append_points(String *txt, uint32 n_points,
const char *data, uint32 offset) const;
bool create_point(String *result, const char *data) const;
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 894988c4931..d8a81cea53c 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -25,7 +25,7 @@
in the relevant fields. Empty strings comes last.
*/
-#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
#include "sql_acl.h" // MYSQL_DB_FIELD_COUNT, ACL_ACCESS
#include "sql_base.h" // close_mysql_tables
@@ -59,147 +59,30 @@
bool mysql_user_table_is_in_short_password_format= false;
-static const
-TABLE_FIELD_TYPE mysql_db_table_fields[MYSQL_DB_FIELD_COUNT] = {
- {
- { C_STRING_WITH_LEN("Host") },
- { C_STRING_WITH_LEN("char(60)") },
- {NULL, 0}
- },
- {
- { C_STRING_WITH_LEN("Db") },
- { C_STRING_WITH_LEN("char(64)") },
- {NULL, 0}
- },
- {
- { C_STRING_WITH_LEN("User") },
- { C_STRING_WITH_LEN("char(") },
- {NULL, 0}
- },
- {
- { C_STRING_WITH_LEN("Select_priv") },
- { C_STRING_WITH_LEN("enum('N','Y')") },
- { C_STRING_WITH_LEN("utf8") }
- },
- {
- { C_STRING_WITH_LEN("Insert_priv") },
- { C_STRING_WITH_LEN("enum('N','Y')") },
- { C_STRING_WITH_LEN("utf8") }
- },
- {
- { C_STRING_WITH_LEN("Update_priv") },
- { C_STRING_WITH_LEN("enum('N','Y')") },
- { C_STRING_WITH_LEN("utf8") }
- },
- {
- { C_STRING_WITH_LEN("Delete_priv") },
- { C_STRING_WITH_LEN("enum('N','Y')") },
- { C_STRING_WITH_LEN("utf8") }
- },
- {
- { C_STRING_WITH_LEN("Create_priv") },
- { C_STRING_WITH_LEN("enum('N','Y')") },
- { C_STRING_WITH_LEN("utf8") }
- },
- {
- { C_STRING_WITH_LEN("Drop_priv") },
- { C_STRING_WITH_LEN("enum('N','Y')") },
- { C_STRING_WITH_LEN("utf8") }
- },
- {
- { C_STRING_WITH_LEN("Grant_priv") },
- { C_STRING_WITH_LEN("enum('N','Y')") },
- { C_STRING_WITH_LEN("utf8") }
- },
- {
- { C_STRING_WITH_LEN("References_priv") },
- { C_STRING_WITH_LEN("enum('N','Y')") },
- { C_STRING_WITH_LEN("utf8") }
- },
- {
- { C_STRING_WITH_LEN("Index_priv") },
- { C_STRING_WITH_LEN("enum('N','Y')") },
- { C_STRING_WITH_LEN("utf8") }
- },
- {
- { C_STRING_WITH_LEN("Alter_priv") },
- { C_STRING_WITH_LEN("enum('N','Y')") },
- { C_STRING_WITH_LEN("utf8") }
- },
- {
- { C_STRING_WITH_LEN("Create_tmp_table_priv") },
- { C_STRING_WITH_LEN("enum('N','Y')") },
- { C_STRING_WITH_LEN("utf8") }
- },
- {
- { C_STRING_WITH_LEN("Lock_tables_priv") },
- { C_STRING_WITH_LEN("enum('N','Y')") },
- { C_STRING_WITH_LEN("utf8") }
- },
- {
- { C_STRING_WITH_LEN("Create_view_priv") },
- { C_STRING_WITH_LEN("enum('N','Y')") },
- { C_STRING_WITH_LEN("utf8") }
- },
- {
- { C_STRING_WITH_LEN("Show_view_priv") },
- { C_STRING_WITH_LEN("enum('N','Y')") },
- { C_STRING_WITH_LEN("utf8") }
- },
- {
- { C_STRING_WITH_LEN("Create_routine_priv") },
- { C_STRING_WITH_LEN("enum('N','Y')") },
- { C_STRING_WITH_LEN("utf8") }
- },
- {
- { C_STRING_WITH_LEN("Alter_routine_priv") },
- { C_STRING_WITH_LEN("enum('N','Y')") },
- { C_STRING_WITH_LEN("utf8") }
- },
- {
- { C_STRING_WITH_LEN("Execute_priv") },
- { C_STRING_WITH_LEN("enum('N','Y')") },
- { C_STRING_WITH_LEN("utf8") }
- },
- {
- { C_STRING_WITH_LEN("Event_priv") },
- { C_STRING_WITH_LEN("enum('N','Y')") },
- { C_STRING_WITH_LEN("utf8") }
- },
- {
- { C_STRING_WITH_LEN("Trigger_priv") },
- { C_STRING_WITH_LEN("enum('N','Y')") },
- { C_STRING_WITH_LEN("utf8") }
- }
+static LEX_CSTRING native_password_plugin_name= {
+ STRING_WITH_LEN("mysql_native_password")
};
-const TABLE_FIELD_DEF
-mysql_db_table_def= {MYSQL_DB_FIELD_COUNT, mysql_db_table_fields, 0, (uint*) 0 };
-
-static LEX_STRING native_password_plugin_name= {
- C_STRING_WITH_LEN("mysql_native_password")
-};
-
-static LEX_STRING old_password_plugin_name= {
- C_STRING_WITH_LEN("mysql_old_password")
+static LEX_CSTRING old_password_plugin_name= {
+ STRING_WITH_LEN("mysql_old_password")
};
/// @todo make it configurable
-LEX_STRING *default_auth_plugin_name= &native_password_plugin_name;
+LEX_CSTRING *default_auth_plugin_name= &native_password_plugin_name;
/*
Wildcard host, matches any hostname
*/
-LEX_STRING host_not_specified= { C_STRING_WITH_LEN("%") };
+LEX_CSTRING host_not_specified= { STRING_WITH_LEN("%") };
/*
Constants, used in the SHOW GRANTS command.
Their actual string values are irrelevant, they're always compared
as pointers to these string constants.
*/
-LEX_STRING current_user= { C_STRING_WITH_LEN("*current_user") };
-LEX_STRING current_role= { C_STRING_WITH_LEN("*current_role") };
-LEX_STRING current_user_and_current_role= { C_STRING_WITH_LEN("*current_user_and_current_role") };
+LEX_CSTRING current_user= { STRING_WITH_LEN("*current_user") };
+LEX_CSTRING current_role= { STRING_WITH_LEN("*current_role") };
+LEX_CSTRING current_user_and_current_role= { STRING_WITH_LEN("*current_user_and_current_role") };
#ifndef NO_EMBEDDED_ACCESS_CHECKS
static plugin_ref old_password_plugin;
@@ -241,9 +124,9 @@ class ACL_USER_BASE :public ACL_ACCESS
public:
static void *operator new(size_t size, MEM_ROOT *mem_root)
{ return (void*) alloc_root(mem_root, size); }
-
+ static void operator delete(void *, MEM_ROOT *){}
uchar flags; // field used to store various state information
- LEX_STRING user;
+ LEX_CSTRING user;
/* list to hold references to granted roles (ACL_ROLE instances) */
DYNAMIC_ARRAY role_grants;
};
@@ -252,15 +135,15 @@ class ACL_USER :public ACL_USER_BASE
{
public:
acl_host_and_ip host;
- uint hostname_length;
+ size_t hostname_length;
USER_RESOURCES user_resource;
uint8 salt[SCRAMBLE_LENGTH + 1]; // scrambled password in binary form
uint8 salt_len; // 0 - no password, 4 - 3.20, 8 - 4.0, 20 - 4.1.1
enum SSL_type ssl_type;
const char *ssl_cipher, *x509_issuer, *x509_subject;
- LEX_STRING plugin;
- LEX_STRING auth_string;
- LEX_STRING default_rolename;
+ LEX_CSTRING plugin;
+ LEX_CSTRING auth_string;
+ LEX_CSTRING default_rolename;
ACL_USER *copy(MEM_ROOT *root)
{
@@ -337,7 +220,7 @@ class ACL_DB :public ACL_ACCESS
{
public:
acl_host_and_ip host;
- char *user,*db;
+ const char *user,*db;
ulong initial_access; /* access bits present in the table */
};
@@ -361,8 +244,8 @@ static bool show_database_privileges(THD *, const char *, const char *,
char *, size_t);
static bool show_table_and_column_privileges(THD *, const char *, const char *,
char *, size_t);
-static int show_routine_grants(THD *, const char *, const char *, HASH *,
- const char *, int, char *, int);
+static int show_routine_grants(THD *, const char *, const char *,
+ const Sp_handler *sph, char *, int);
class Grant_tables;
class User_table;
@@ -529,10 +412,10 @@ public:
}
static int store_pk(TABLE *table,
- const LEX_STRING *host,
- const LEX_STRING *user,
- const LEX_STRING *proxied_host,
- const LEX_STRING *proxied_user)
+ const LEX_CSTRING *host,
+ const LEX_CSTRING *user,
+ const LEX_CSTRING *proxied_host,
+ const LEX_CSTRING *proxied_user)
{
DBUG_ENTER("ACL_PROXY_USER::store_pk");
DBUG_PRINT("info", ("host=%s, user=%s, proxied_host=%s, proxied_user=%s",
@@ -559,10 +442,10 @@ public:
}
static int store_data_record(TABLE *table,
- const LEX_STRING *host,
- const LEX_STRING *user,
- const LEX_STRING *proxied_host,
- const LEX_STRING *proxied_user,
+ const LEX_CSTRING *host,
+ const LEX_CSTRING *user,
+ const LEX_CSTRING *proxied_host,
+ const LEX_CSTRING *proxied_user,
bool with_grant,
const char *grantor)
{
@@ -615,8 +498,8 @@ struct ROLE_GRANT_PAIR : public Sql_alloc
LEX_STRING hashkey;
bool with_admin;
- bool init(MEM_ROOT *mem, char *username, char *hostname, char *rolename,
- bool with_admin_option);
+ bool init(MEM_ROOT *mem, const char *username, const char *hostname,
+ const char *rolename, bool with_admin_option);
};
static uchar* acl_role_map_get_key(ROLE_GRANT_PAIR *entry, size_t *length,
@@ -626,8 +509,8 @@ static uchar* acl_role_map_get_key(ROLE_GRANT_PAIR *entry, size_t *length,
return (uchar*) entry->hashkey.str;
}
-bool ROLE_GRANT_PAIR::init(MEM_ROOT *mem, char *username,
- char *hostname, char *rolename,
+bool ROLE_GRANT_PAIR::init(MEM_ROOT *mem, const char *username,
+ const char *hostname, const char *rolename,
bool with_admin_option)
{
size_t uname_l = safe_strlen(username);
@@ -694,9 +577,9 @@ bool ROLE_GRANT_PAIR::init(MEM_ROOT *mem, char *username,
#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
#define NORMAL_HANDSHAKE_SIZE 6
-#define ROLE_ASSIGN_COLUMN_IDX 43
-#define DEFAULT_ROLE_COLUMN_IDX 44
-#define MAX_STATEMENT_TIME_COLUMN_IDX 45
+#define ROLE_ASSIGN_COLUMN_IDX 44
+#define DEFAULT_ROLE_COLUMN_IDX 45
+#define MAX_STATEMENT_TIME_COLUMN_IDX 46
/* various flags valid for ACL_USER */
#define IS_ROLE (1L << 0)
@@ -725,6 +608,7 @@ static MEM_ROOT acl_memroot, grant_memroot;
static bool initialized=0;
static bool allow_all_hosts=1;
static HASH acl_check_hosts, column_priv_hash, proc_priv_hash, func_priv_hash;
+static 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 */
@@ -737,10 +621,10 @@ static void rebuild_role_grants(void);
static ACL_USER *find_user_exact(const char *host, const char *user);
static ACL_USER *find_user_wild(const char *host, const char *user, const char *ip= 0);
static ACL_ROLE *find_acl_role(const char *user);
-static ROLE_GRANT_PAIR *find_role_grant_pair(const LEX_STRING *u, const LEX_STRING *h, const LEX_STRING *r);
+static ROLE_GRANT_PAIR *find_role_grant_pair(const LEX_CSTRING *u, const LEX_CSTRING *h, const LEX_CSTRING *r);
static ACL_USER_BASE *find_acl_user_base(const char *user, const char *host);
static bool update_user_table(THD *, const User_table &, const char *, const char *, const
- char *, uint);
+ char *, size_t new_password_len);
static bool acl_load(THD *thd, const Grant_tables& grant_tables);
static inline void get_grantor(THD *thd, char* grantor);
static bool add_role_user_mapping(const char *uname, const char *hname, const char *rname);
@@ -755,6 +639,31 @@ static int traverse_role_graph_down(ACL_USER_BASE *, void *,
int (*) (ACL_USER_BASE *, void *),
int (*) (ACL_USER_BASE *, ACL_ROLE *, void *));
+
+HASH *Sp_handler_procedure::get_priv_hash() const
+{
+ return &proc_priv_hash;
+}
+
+
+HASH *Sp_handler_function::get_priv_hash() const
+{
+ return &func_priv_hash;
+}
+
+
+HASH *Sp_handler_package_spec::get_priv_hash() const
+{
+ return &package_spec_priv_hash;
+}
+
+
+HASH *Sp_handler_package_body::get_priv_hash() const
+{
+ return &package_body_priv_hash;
+}
+
+
/*
Enumeration of ACL/GRANT tables in the mysql database
*/
@@ -842,7 +751,7 @@ class Grant_table_base
if (table_exists())
return 0;
- my_error(ER_NO_SUCH_TABLE, MYF(0), tl.db, tl.alias);
+ my_error(ER_NO_SUCH_TABLE, MYF(0), tl.db.str, tl.alias.str);
return 1;
}
@@ -996,6 +905,7 @@ class User_table: public Grant_table_base
return get_YN_as_bool(is_role());
}
+
ulong get_access() const
{
ulong access= Grant_table_base::get_access();
@@ -1044,6 +954,9 @@ class User_table: public Grant_table_base
if (num_fields() <= 38 && (access & SUPER_ACL))
access|= TRIGGER_ACL;
+ if (num_fields() <= 46 && (access & DELETE_ACL))
+ access|= DELETE_HISTORY_ACL;
+
return access & GLOBAL_ACLS;
}
@@ -1056,9 +969,7 @@ class User_table: public Grant_table_base
void init(enum thr_lock_type lock_type)
{
/* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
- tl.init_one_table(C_STRING_WITH_LEN("mysql"),
- C_STRING_WITH_LEN("user"),
- NULL, lock_type);
+ tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_USER_NAME, NULL, lock_type);
Grant_table_base::init(lock_type, false);
}
@@ -1102,9 +1013,7 @@ class Db_table: public Grant_table_base
void init(enum thr_lock_type lock_type)
{
/* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
- tl.init_one_table(C_STRING_WITH_LEN("mysql"),
- C_STRING_WITH_LEN("db"),
- NULL, lock_type);
+ tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_DB_NAME, NULL, lock_type);
Grant_table_base::init(lock_type, false);
}
};
@@ -1129,9 +1038,8 @@ class Tables_priv_table: public Grant_table_base
void init(enum thr_lock_type lock_type, Grant_table_base *next_table= NULL)
{
/* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
- tl.init_one_table(C_STRING_WITH_LEN("mysql"),
- C_STRING_WITH_LEN("tables_priv"),
- NULL, lock_type);
+ LEX_CSTRING MYSQL_TABLES_PRIV_NAME={STRING_WITH_LEN("tables_priv") };
+ tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_TABLES_PRIV_NAME, NULL, lock_type);
Grant_table_base::init(lock_type, false);
}
};
@@ -1155,9 +1063,8 @@ class Columns_priv_table: public Grant_table_base
void init(enum thr_lock_type lock_type)
{
/* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
- tl.init_one_table(C_STRING_WITH_LEN("mysql"),
- C_STRING_WITH_LEN("columns_priv"),
- NULL, lock_type);
+ LEX_CSTRING MYSQL_COLUMNS_PRIV_NAME={ STRING_WITH_LEN("columns_priv") };
+ tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_COLUMNS_PRIV_NAME, NULL, lock_type);
Grant_table_base::init(lock_type, false);
}
};
@@ -1176,9 +1083,8 @@ class Host_table: public Grant_table_base
void init(enum thr_lock_type lock_type)
{
/* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
- tl.init_one_table(C_STRING_WITH_LEN("mysql"),
- C_STRING_WITH_LEN("host"),
- NULL, lock_type);
+ LEX_CSTRING MYSQL_HOST_NAME={STRING_WITH_LEN("host") };
+ tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_HOST_NAME, NULL, lock_type);
Grant_table_base::init(lock_type, true);
}
};
@@ -1203,9 +1109,8 @@ class Procs_priv_table: public Grant_table_base
void init(enum thr_lock_type lock_type)
{
/* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
- tl.init_one_table(C_STRING_WITH_LEN("mysql"),
- C_STRING_WITH_LEN("procs_priv"),
- NULL, lock_type);
+ LEX_CSTRING MYSQL_PROCS_PRIV_NAME={STRING_WITH_LEN("procs_priv") };
+ tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PROCS_PRIV_NAME, NULL, lock_type);
Grant_table_base::init(lock_type, true);
}
};
@@ -1229,9 +1134,8 @@ class Proxies_priv_table: public Grant_table_base
void init(enum thr_lock_type lock_type)
{
/* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
- tl.init_one_table(C_STRING_WITH_LEN("mysql"),
- C_STRING_WITH_LEN("proxies_priv"),
- NULL, lock_type);
+ LEX_CSTRING MYSQL_PROXIES_PRIV_NAME={STRING_WITH_LEN("proxies_priv") };
+ tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PROXIES_PRIV_NAME, NULL, lock_type);
Grant_table_base::init(lock_type, true);
}
};
@@ -1252,9 +1156,8 @@ class Roles_mapping_table: public Grant_table_base
void init(enum thr_lock_type lock_type)
{
/* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
- tl.init_one_table(C_STRING_WITH_LEN("mysql"),
- C_STRING_WITH_LEN("roles_mapping"),
- NULL, lock_type);
+ LEX_CSTRING MYSQL_ROLES_MAPPING_NAME={STRING_WITH_LEN("roles_mapping") };
+ tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_ROLES_MAPPING_NAME, NULL, lock_type);
Grant_table_base::init(lock_type, true);
}
};
@@ -1476,6 +1379,8 @@ enum enum_acl_lists
COLUMN_PRIVILEGES_HASH,
PROC_PRIVILEGES_HASH,
FUNC_PRIVILEGES_HASH,
+ PACKAGE_SPEC_PRIVILEGES_HASH,
+ PACKAGE_BODY_PRIVILEGES_HASH,
PROXY_USERS_ACL,
ROLES_MAPPINGS_HASH
};
@@ -1537,7 +1442,7 @@ static bool has_validation_plugins()
MariaDB_PASSWORD_VALIDATION_PLUGIN, NULL);
}
-struct validation_data { LEX_STRING *user, *password; };
+struct validation_data { LEX_CSTRING *user, *password; };
static my_bool do_validate(THD *, plugin_ref plugin, void *arg)
{
@@ -1554,7 +1459,7 @@ static bool validate_password(LEX_USER *user, THD *thd)
{
struct validation_data data= { &user->user,
user->pwtext.str ? &user->pwtext :
- const_cast<LEX_STRING *>(&empty_lex_str) };
+ const_cast<LEX_CSTRING *>(&empty_clex_str) };
if (plugin_foreach(NULL, do_validate,
MariaDB_PASSWORD_VALIDATION_PLUGIN, &data))
{
@@ -1587,7 +1492,7 @@ static bool validate_password(LEX_USER *user, THD *thd)
*/
static void
-set_user_salt(ACL_USER *acl_user, const char *password, uint password_len)
+set_user_salt(ACL_USER *acl_user, const char *password, size_t password_len)
{
if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH)
{
@@ -1603,7 +1508,7 @@ set_user_salt(ACL_USER *acl_user, const char *password, uint password_len)
acl_user->salt_len= 0;
}
-static char *fix_plugin_ptr(char *name)
+static const char *fix_plugin_ptr(const char *name)
{
if (my_strcasecmp(system_charset_info, name,
native_password_plugin_name.str) == 0)
@@ -1631,12 +1536,10 @@ static char *fix_plugin_ptr(char *name)
*/
static bool fix_user_plugin_ptr(ACL_USER *user)
{
- if (my_strcasecmp(system_charset_info, user->plugin.str,
- native_password_plugin_name.str) == 0)
+ if (lex_string_eq(&user->plugin, &native_password_plugin_name))
user->plugin= native_password_plugin_name;
else
- if (my_strcasecmp(system_charset_info, user->plugin.str,
- old_password_plugin_name.str) == 0)
+ if (lex_string_eq(&user->plugin, &old_password_plugin_name))
user->plugin= old_password_plugin_name;
else
return true;
@@ -1675,12 +1578,10 @@ static bool fix_lex_user(THD *thd, LEX_USER *user)
DBUG_ASSERT(user->plugin.length || !user->auth.length);
DBUG_ASSERT(!(user->plugin.length && (user->pwtext.length || user->pwhash.length)));
- if (my_strcasecmp(system_charset_info, user->plugin.str,
- native_password_plugin_name.str) == 0)
+ if (lex_string_eq(&user->plugin, &native_password_plugin_name))
check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
else
- if (my_strcasecmp(system_charset_info, user->plugin.str,
- old_password_plugin_name.str) == 0)
+ if (lex_string_eq(&user->plugin, &old_password_plugin_name))
check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
else
if (user->plugin.length)
@@ -1694,8 +1595,8 @@ static bool fix_lex_user(THD *thd, LEX_USER *user)
if (user->plugin.length)
{
user->pwhash= user->auth;
- user->plugin= empty_lex_str;
- user->auth= empty_lex_str;
+ user->plugin= empty_clex_str;
+ user->auth= empty_clex_str;
}
if (user->pwhash.length && user->pwhash.length != check_length)
@@ -1813,7 +1714,7 @@ bool acl_init(bool dont_read_acl_tables)
Choose from either native or old password plugins when assigning a password
*/
-static bool set_user_plugin (ACL_USER *user, int password_len)
+static bool set_user_plugin (ACL_USER *user, size_t password_len)
{
switch (password_len)
{
@@ -1863,12 +1764,12 @@ 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_ALLOC_BLOCK_SIZE, 0, MYF(0));
+ init_sql_alloc(&acl_memroot, "ACL", 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, thd))
DBUG_RETURN(true);
- while (!(read_record_info.read_record(&read_record_info)))
+ while (!(read_record_info.read_record()))
{
ACL_HOST host;
update_hostname(&host.host, get_field(&acl_memroot, host_table.host()));
@@ -1978,7 +1879,7 @@ static bool acl_load(THD *thd, const Grant_tables& tables)
}
allow_all_hosts=0;
- while (!(read_record_info.read_record(&read_record_info)))
+ while (!(read_record_info.read_record()))
{
ACL_USER user;
bool is_role= FALSE;
@@ -2013,7 +1914,7 @@ static bool acl_load(THD *thd, const Grant_tables& tables)
char *password= const_cast<char*>("");
if (user_table.password())
password= get_field(&acl_memroot, user_table.password());
- uint password_len= safe_strlen(password);
+ size_t password_len= safe_strlen(password);
user.auth_string.str= safe_str(password);
user.auth_string.length= password_len;
set_user_salt(&user, password, password_len);
@@ -2022,7 +1923,7 @@ static bool acl_load(THD *thd, const Grant_tables& tables)
continue;
{
- user.access= user_table.get_access() & GLOBAL_ACLS;
+ user.access= user_table.get_access();
user.sort= get_sort(2, user.host.hostname, user.user.str);
user.hostname_length= safe_strlen(user.host.hostname);
user.user_resource.user_conn= 0;
@@ -2161,15 +2062,16 @@ static bool acl_load(THD *thd, const Grant_tables& tables)
const Db_table& db_table= tables.db_table();
if (db_table.init_read_record(&read_record_info, thd))
DBUG_RETURN(TRUE);
- while (!(read_record_info.read_record(&read_record_info)))
+ while (!(read_record_info.read_record()))
{
ACL_DB db;
+ char *db_name;
db.user=get_field(&acl_memroot, db_table.user());
const char *hostname= get_field(&acl_memroot, db_table.host());
if (!hostname && find_acl_role(db.user))
hostname= "";
update_hostname(&db.host, hostname);
- db.db=get_field(&acl_memroot, db_table.db());
+ db.db= db_name= get_field(&acl_memroot, db_table.db());
if (!db.db)
{
sql_print_warning("Found an entry in the 'db' table with empty database name; Skipped");
@@ -2197,8 +2099,8 @@ static bool acl_load(THD *thd, const Grant_tables& tables)
sql_print_warning(ER_THD(thd, ER_WRONG_DB_NAME), db.db);
continue;
}
- my_casedn_str(files_charset_info, db.db);
- if (strcmp(db.db, tmp_name) != 0)
+ my_casedn_str(files_charset_info, db_name);
+ if (strcmp(db_name, tmp_name) != 0)
{
sql_print_warning("'db' entry '%s %s@%s' had database in mixed "
"case that has been forced to lowercase because "
@@ -2226,7 +2128,7 @@ static bool acl_load(THD *thd, const Grant_tables& tables)
{
if (proxies_priv_table.init_read_record(&read_record_info, thd))
DBUG_RETURN(TRUE);
- while (!(read_record_info.read_record(&read_record_info)))
+ while (!(read_record_info.read_record()))
{
ACL_PROXY_USER proxy;
proxy.init(proxies_priv_table, &acl_memroot);
@@ -2254,8 +2156,8 @@ static bool acl_load(THD *thd, const Grant_tables& tables)
DBUG_RETURN(TRUE);
MEM_ROOT temp_root;
- init_alloc_root(&temp_root, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
- while (!(read_record_info.read_record(&read_record_info)))
+ init_alloc_root(&temp_root, "ACL_tmp", 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()));
char *username= safe_str(get_field(&temp_root, roles_mapping_table.user()));
@@ -2349,7 +2251,7 @@ bool acl_reload(THD *thd)
To avoid deadlocks we should obtain table locks before
obtaining acl_cache->lock mutex.
*/
- if ((result= tables.open_and_lock(thd)))
+ if (unlikely((result= tables.open_and_lock(thd))))
{
DBUG_ASSERT(result <= 0);
/*
@@ -2522,8 +2424,8 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b)
TRUE Error
*/
-bool acl_getroot(Security_context *sctx, char *user, char *host,
- char *ip, char *db)
+bool acl_getroot(Security_context *sctx, const char *user, const char *host,
+ const char *ip, const char *db)
{
int res= 1;
uint i;
@@ -2767,20 +2669,20 @@ end:
}
-int acl_check_setrole(THD *thd, char *rolename, ulonglong *access)
+int acl_check_setrole(THD *thd, const char *rolename, ulonglong *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, char *rolename, ulonglong access)
+int acl_setrole(THD *thd, const char *rolename, ulonglong access)
{
/* merge the privileges */
Security_context *sctx= thd->security_ctx;
sctx->master_access= static_cast<ulong>(access);
- if (thd->db)
- sctx->db_access= acl_get(sctx->host, sctx->ip, sctx->user, thd->db, FALSE);
+ if (thd->db.str)
+ sctx->db_access= acl_get(sctx->host, sctx->ip, sctx->user, thd->db.str, FALSE);
if (!strcasecmp(rolename, "NONE"))
{
@@ -2788,8 +2690,8 @@ int acl_setrole(THD *thd, char *rolename, ulonglong access)
}
else
{
- if (thd->db)
- sctx->db_access|= acl_get("", "", rolename, thd->db, FALSE);
+ if (thd->db.str)
+ sctx->db_access|= acl_get("", "", rolename, thd->db.str, FALSE);
/* mark the current role */
strmake_buf(thd->security_ctx->priv_role, rolename);
}
@@ -2813,15 +2715,15 @@ static void acl_update_role(const char *rolename, ulong privileges)
static void acl_update_user(const char *user, const char *host,
- const char *password, uint password_len,
+ const char *password, size_t password_len,
enum SSL_type ssl_type,
const char *ssl_cipher,
const char *x509_issuer,
const char *x509_subject,
USER_RESOURCES *mqh,
ulong privileges,
- const LEX_STRING *plugin,
- const LEX_STRING *auth)
+ const LEX_CSTRING *plugin,
+ const LEX_CSTRING *auth)
{
mysql_mutex_assert_owner(&acl_cache->lock);
@@ -2891,15 +2793,15 @@ static void acl_insert_role(const char *rolename, ulong privileges)
static void acl_insert_user(const char *user, const char *host,
- const char *password, uint password_len,
+ const char *password, size_t password_len,
enum SSL_type ssl_type,
const char *ssl_cipher,
const char *x509_issuer,
const char *x509_subject,
USER_RESOURCES *mqh,
ulong privileges,
- const LEX_STRING *plugin,
- const LEX_STRING *auth)
+ const LEX_CSTRING *plugin,
+ const LEX_CSTRING *auth)
{
ACL_USER acl_user;
@@ -3272,7 +3174,7 @@ static void remove_ptr_from_dynarray(DYNAMIC_ARRAY *array, void *ptr)
{
DBUG_ASSERT(!found);
delete_dynamic_element(array, i);
- IF_DBUG(found= true, break);
+ IF_DBUG_ASSERT(found= true, break);
}
}
DBUG_ASSERT(found);
@@ -3506,6 +3408,7 @@ bool change_password(THD *thd, LEX_USER *user)
acl_user->auth_string.str= strmake_root(&acl_memroot, user->pwhash.str, user->pwhash.length);
acl_user->auth_string.length= user->pwhash.length;
set_user_salt(acl_user, user->pwhash.str, user->pwhash.length);
+
set_user_plugin(acl_user, user->pwhash.length);
}
else
@@ -3530,7 +3433,7 @@ bool change_password(THD *thd, LEX_USER *user)
DBUG_ASSERT(query_length);
thd->clear_error();
result= thd->binlog_query(THD::STMT_QUERY_TYPE, buff, query_length,
- FALSE, FALSE, FALSE, 0);
+ FALSE, FALSE, FALSE, 0) > 0;
}
end:
close_mysql_tables(thd);
@@ -3668,7 +3571,8 @@ int acl_set_default_role(THD *thd, const char *host, const char *user,
user_table.default_role()->store(acl_user->default_rolename.str,
acl_user->default_rolename.length,
system_charset_info);
- if ((error=table->file->ha_update_row(table->record[1],table->record[0])) &&
+ if (unlikely(error= table->file->ha_update_row(table->record[1],
+ table->record[0])) &&
error != HA_ERR_RECORD_IS_THE_SAME)
{
mysql_mutex_unlock(&acl_cache->lock);
@@ -3684,7 +3588,7 @@ int acl_set_default_role(THD *thd, const char *host, const char *user,
DBUG_ASSERT(query_length);
thd->clear_error();
result= thd->binlog_query(THD::STMT_QUERY_TYPE, buff, query_length,
- FALSE, FALSE, FALSE, 0);
+ FALSE, FALSE, FALSE, 0) > 0;
}
end:
close_mysql_tables(thd);
@@ -3936,8 +3840,7 @@ bool hostname_requires_resolving(const char *hostname)
void set_authentication_plugin_from_password(const User_table& user_table,
- const char* password,
- uint password_length)
+ const char* password, size_t password_length)
{
if (password_length == SCRAMBLED_PASSWORD_CHAR_LENGTH ||
password_length == 0)
@@ -3972,7 +3875,7 @@ void set_authentication_plugin_from_password(const User_table& user_table,
static bool update_user_table(THD *thd, const User_table& user_table,
const char *host, const char *user,
- const char *new_password, uint new_password_len)
+ const char *new_password, size_t new_password_len)
{
char user_key[MAX_KEY_LENGTH];
int error;
@@ -4006,7 +3909,8 @@ static bool update_user_table(THD *thd, const User_table& user_table,
user_table.password()->store(new_password, new_password_len, system_charset_info);
- if ((error=table->file->ha_update_row(table->record[1],table->record[0])) &&
+ if (unlikely(error= table->file->ha_update_row(table->record[1],
+ table->record[0])) &&
error != HA_ERR_RECORD_IS_THE_SAME)
{
table->file->print_error(error,MYF(0)); /* purecov: deadcode */
@@ -4033,14 +3937,13 @@ static bool test_if_create_new_users(THD *thd)
{
TABLE_LIST tl;
ulong db_access;
- tl.init_one_table(C_STRING_WITH_LEN("mysql"),
- C_STRING_WITH_LEN("user"), "user", TL_WRITE);
+ tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_USER_NAME, NULL, TL_WRITE);
create_new_users= 1;
db_access=acl_get(sctx->host, sctx->ip,
- sctx->priv_user, tl.db, 0);
+ sctx->priv_user, tl.db.str, 0);
if (sctx->priv_role[0])
- db_access|= acl_get("", "", sctx->priv_role, tl.db, 0);
+ db_access|= acl_get("", "", sctx->priv_role, tl.db.str, 0);
if (!(db_access & INSERT_ACL))
{
if (check_grant(thd, INSERT_ACL, &tl, FALSE, UINT_MAX, TRUE))
@@ -4082,7 +3985,7 @@ static int replace_user_table(THD *thd, const User_table &user_table,
}
}
else
- combo.pwhash= empty_lex_str;
+ combo.pwhash= empty_clex_str;
/* if the user table is not up to date, we can't handle role updates */
if (!user_table.is_role() && handle_as_role)
@@ -4301,8 +4204,8 @@ static int replace_user_table(THD *thd, const User_table &user_table,
*/
if (cmp_record(table, record[1]))
{
- if ((error=
- table->file->ha_update_row(table->record[1],table->record[0])) &&
+ if (unlikely(error= table->file->ha_update_row(table->record[1],
+ table->record[0])) &&
error != HA_ERR_RECORD_IS_THE_SAME)
{ // This should never happen
table->file->print_error(error,MYF(0)); /* purecov: deadcode */
@@ -4313,8 +4216,9 @@ static int replace_user_table(THD *thd, const User_table &user_table,
error= 0;
}
}
- else if ((error=table->file->ha_write_row(table->record[0]))) // insert
- { // This should never happen
+ else if (unlikely(error=table->file->ha_write_row(table->record[0])))
+ {
+ // This should never happen
if (table->file->is_fatal_error(error, HA_CHECK_DUP))
{
table->file->print_error(error,MYF(0)); /* purecov: deadcode */
@@ -4325,7 +4229,7 @@ static int replace_user_table(THD *thd, const User_table &user_table,
error=0; // Privileges granted / revoked
end:
- if (!error)
+ if (likely(!error))
{
acl_cache->clear(1); // Clear privilege cache
if (old_row_exists)
@@ -4439,18 +4343,19 @@ static int replace_db_table(TABLE *table, const char *db,
/* update old existing row */
if (rights)
{
- if ((error= table->file->ha_update_row(table->record[1],
- table->record[0])) &&
+ if (unlikely((error= table->file->ha_update_row(table->record[1],
+ table->record[0]))) &&
error != HA_ERR_RECORD_IS_THE_SAME)
goto table_error; /* purecov: deadcode */
}
else /* must have been a revoke of all privileges */
{
- if ((error= table->file->ha_delete_row(table->record[1])))
+ if (unlikely((error= table->file->ha_delete_row(table->record[1]))))
goto table_error; /* purecov: deadcode */
}
}
- else if (rights && (error= table->file->ha_write_row(table->record[0])))
+ else if (rights &&
+ (unlikely(error= table->file->ha_write_row(table->record[0]))))
{
if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
goto table_error; /* purecov: deadcode */
@@ -4499,8 +4404,8 @@ abort:
@param revoke_grant true for REVOKE, false for GRANT
*/
static int
-replace_roles_mapping_table(TABLE *table, LEX_STRING *user, LEX_STRING *host,
- LEX_STRING *role, bool with_admin,
+replace_roles_mapping_table(TABLE *table, LEX_CSTRING *user, LEX_CSTRING *host,
+ LEX_CSTRING *role, bool with_admin,
ROLE_GRANT_PAIR *existing, bool revoke_grant)
{
DBUG_ENTER("replace_roles_mapping_table");
@@ -4527,7 +4432,7 @@ replace_roles_mapping_table(TABLE *table, LEX_STRING *user, LEX_STRING *host,
}
if (revoke_grant && !with_admin)
{
- if ((error= table->file->ha_delete_row(table->record[1])))
+ if (unlikely((error= table->file->ha_delete_row(table->record[1]))))
{
DBUG_PRINT("info", ("error deleting row '%s' '%s' '%s'",
host->str, user->str, role->str));
@@ -4538,7 +4443,8 @@ replace_roles_mapping_table(TABLE *table, LEX_STRING *user, LEX_STRING *host,
{
table->field[3]->store(!revoke_grant + 1);
- if ((error= table->file->ha_update_row(table->record[1], table->record[0])))
+ if (unlikely((error= table->file->ha_update_row(table->record[1],
+ table->record[0]))))
{
DBUG_PRINT("info", ("error updating row '%s' '%s' '%s'",
host->str, user->str, role->str));
@@ -4550,7 +4456,7 @@ replace_roles_mapping_table(TABLE *table, LEX_STRING *user, LEX_STRING *host,
table->field[3]->store(with_admin + 1);
- if ((error= table->file->ha_write_row(table->record[0])))
+ if (unlikely((error= table->file->ha_write_row(table->record[0]))))
{
DBUG_PRINT("info", ("error inserting row '%s' '%s' '%s'",
host->str, user->str, role->str));
@@ -4581,7 +4487,7 @@ table_error:
@param revoke_grant true for REVOKE, false for GRANT
*/
static int
-update_role_mapping(LEX_STRING *user, LEX_STRING *host, LEX_STRING *role,
+update_role_mapping(LEX_CSTRING *user, LEX_CSTRING *host, LEX_CSTRING *role,
bool with_admin, ROLE_GRANT_PAIR *existing, bool revoke_grant)
{
if (revoke_grant)
@@ -4681,7 +4587,7 @@ replace_proxies_priv_table(THD *thd, TABLE *table, const LEX_USER *user,
get_grantor(thd, grantor);
- if ((error= table->file->ha_index_init(0, 1)))
+ if (unlikely((error= table->file->ha_index_init(0, 1))))
{
table->file->print_error(error, MYF(0));
DBUG_PRINT("info", ("ha_index_init error"));
@@ -4718,18 +4624,18 @@ replace_proxies_priv_table(THD *thd, TABLE *table, const LEX_USER *user,
/* update old existing row */
if (!revoke_grant)
{
- if ((error= table->file->ha_update_row(table->record[1],
- table->record[0])) &&
+ if (unlikely(error= table->file->ha_update_row(table->record[1],
+ table->record[0])) &&
error != HA_ERR_RECORD_IS_THE_SAME)
goto table_error; /* purecov: inspected */
}
else
{
- if ((error= table->file->ha_delete_row(table->record[1])))
+ if (unlikely((error= table->file->ha_delete_row(table->record[1]))))
goto table_error; /* purecov: inspected */
}
}
- else if ((error= table->file->ha_write_row(table->record[0])))
+ else if (unlikely((error= table->file->ha_write_row(table->record[0]))))
{
DBUG_PRINT("info", ("error inserting the row"));
if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
@@ -5070,10 +4976,11 @@ static GRANT_NAME *name_hash_search(HASH *name_hash,
static GRANT_NAME *
routine_hash_search(const char *host, const char *ip, const char *db,
- const char *user, const char *tname, bool proc, bool exact)
+ const char *user, const char *tname, const Sp_handler *sph,
+ bool exact)
{
return (GRANT_TABLE*)
- name_hash_search(proc ? &proc_priv_hash : &func_priv_hash,
+ name_hash_search(sph->get_priv_hash(),
host, ip, db, user, tname, exact, TRUE);
}
@@ -5088,7 +4995,7 @@ table_hash_search(const char *host, const char *ip, const char *db,
static GRANT_COLUMN *
-column_hash_search(GRANT_TABLE *t, const char *cname, uint length)
+column_hash_search(GRANT_TABLE *t, const char *cname, size_t length)
{
if (!my_hash_inited(&t->hash_columns))
return (GRANT_COLUMN*) 0;
@@ -5131,7 +5038,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 (error)
+ if (unlikely(error))
{
table->file->print_error(error, MYF(0));
DBUG_RETURN(-1);
@@ -5191,7 +5098,7 @@ static int replace_column_table(GRANT_TABLE *g_t,
error=table->file->ha_update_row(table->record[1],table->record[0]);
else
error=table->file->ha_delete_row(table->record[1]);
- if (error && error != HA_ERR_RECORD_IS_THE_SAME)
+ if (unlikely(error) && error != HA_ERR_RECORD_IS_THE_SAME)
{
table->file->print_error(error,MYF(0)); /* purecov: inspected */
result= -1; /* purecov: inspected */
@@ -5207,7 +5114,7 @@ static int replace_column_table(GRANT_TABLE *g_t,
else // new grant
{
GRANT_COLUMN *grant_column;
- if ((error=table->file->ha_write_row(table->record[0])))
+ if (unlikely((error=table->file->ha_write_row(table->record[0]))))
{
table->file->print_error(error,MYF(0)); /* purecov: inspected */
result= -1; /* purecov: inspected */
@@ -5262,8 +5169,9 @@ static int replace_column_table(GRANT_TABLE *g_t,
if (privileges)
{
int tmp_error;
- if ((tmp_error=table->file->ha_update_row(table->record[1],
- table->record[0])) &&
+ if (unlikely(tmp_error=
+ table->file->ha_update_row(table->record[1],
+ table->record[0])) &&
tmp_error != HA_ERR_RECORD_IS_THE_SAME)
{ /* purecov: deadcode */
table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
@@ -5279,7 +5187,8 @@ static int replace_column_table(GRANT_TABLE *g_t,
else
{
int tmp_error;
- if ((tmp_error = table->file->ha_delete_row(table->record[1])))
+ if (unlikely((tmp_error=
+ table->file->ha_delete_row(table->record[1]))))
{ /* purecov: deadcode */
table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
result= -1; /* purecov: deadcode */
@@ -5405,18 +5314,18 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
{
if (store_table_rights || store_col_rights)
{
- if ((error=table->file->ha_update_row(table->record[1],
- table->record[0])) &&
+ if (unlikely(error=table->file->ha_update_row(table->record[1],
+ table->record[0])) &&
error != HA_ERR_RECORD_IS_THE_SAME)
goto table_error; /* purecov: deadcode */
}
- else if ((error = table->file->ha_delete_row(table->record[1])))
+ else if (unlikely((error = table->file->ha_delete_row(table->record[1]))))
goto table_error; /* purecov: deadcode */
}
else
{
error=table->file->ha_write_row(table->record[0]);
- if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
+ if (unlikely(table->file->is_fatal_error(error, HA_CHECK_DUP_KEY)))
goto table_error; /* purecov: deadcode */
}
@@ -5448,13 +5357,14 @@ table_error:
static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
TABLE *table, const LEX_USER &combo,
const char *db, const char *routine_name,
- bool is_proc, ulong rights, bool revoke_grant)
+ const Sp_handler *sph,
+ ulong rights, bool revoke_grant)
{
char grantor[USER_HOST_BUFF_SIZE];
int old_row_exists= 1;
int error=0;
ulong store_proc_rights;
- HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
+ HASH *hash= sph->get_priv_hash();
DBUG_ENTER("replace_routine_table");
if (revoke_grant && !grant_name->init_privs) // only inherited role privs
@@ -5478,9 +5388,7 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
table->field[2]->store(combo.user.str,combo.user.length, &my_charset_latin1);
table->field[3]->store(routine_name,(uint) strlen(routine_name),
&my_charset_latin1);
- table->field[4]->store((longlong)(is_proc ?
- TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION),
- TRUE);
+ table->field[4]->store((longlong) sph->type(), true);
store_record(table,record[1]); // store at pos 1
if (table->file->ha_index_read_idx_map(table->record[0], 0,
@@ -5532,18 +5440,18 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
{
if (store_proc_rights)
{
- if ((error=table->file->ha_update_row(table->record[1],
- table->record[0])) &&
- error != HA_ERR_RECORD_IS_THE_SAME)
+ if (unlikely(error=table->file->ha_update_row(table->record[1],
+ table->record[0])) &&
+ error != HA_ERR_RECORD_IS_THE_SAME)
goto table_error;
}
- else if ((error= table->file->ha_delete_row(table->record[1])))
+ else if (unlikely((error= table->file->ha_delete_row(table->record[1]))))
goto table_error;
}
else
{
error=table->file->ha_write_row(table->record[0]);
- if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
+ if (unlikely(table->file->is_fatal_error(error, HA_CHECK_DUP_KEY)))
goto table_error;
}
@@ -5596,10 +5504,34 @@ table_error:
******************************************************************/
struct PRIVS_TO_MERGE
{
- enum what { ALL, GLOBAL, DB, TABLE_COLUMN, PROC, FUNC } what;
+ enum what
+ {
+ ALL, GLOBAL, DB, TABLE_COLUMN, PROC, FUNC, PACKAGE_SPEC, PACKAGE_BODY
+ } what;
const char *db, *name;
};
+
+static enum PRIVS_TO_MERGE::what sp_privs_to_merge(stored_procedure_type type)
+{
+ switch (type) {
+ case TYPE_ENUM_FUNCTION:
+ return PRIVS_TO_MERGE::FUNC;
+ case TYPE_ENUM_PROCEDURE:
+ return PRIVS_TO_MERGE::PROC;
+ case TYPE_ENUM_PACKAGE:
+ return PRIVS_TO_MERGE::PACKAGE_SPEC;
+ case TYPE_ENUM_PACKAGE_BODY:
+ return PRIVS_TO_MERGE::PACKAGE_BODY;
+ case TYPE_ENUM_TRIGGER:
+ case TYPE_ENUM_PROXY:
+ break;
+ }
+ DBUG_ASSERT(0);
+ return PRIVS_TO_MERGE::PROC;
+}
+
+
static int init_role_for_merging(ACL_ROLE *role, void *context)
{
role->counter= 0;
@@ -5913,7 +5845,7 @@ static int db_name_sort(const int *db1, const int *db2)
/**
update ACL_DB for given database and a given role with merged privileges
- @param merged ACL_DB of the role in question (or NULL if it wasn't found)
+ @param merged ACL_DB of the role in question (or -1 if it wasn't found)
@param first first ACL_DB in an array for the database in question
@param access new privileges for the given role on the gived database
@param role the name of the given role
@@ -5923,7 +5855,8 @@ 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, char *role)
+static int update_role_db(int merged, int first, ulong access,
+ const char *role)
{
if (first < 0)
return 0;
@@ -6141,8 +6074,8 @@ static int update_role_columns(GRANT_TABLE *merged,
4 - GRANT_TABLE was deleted
*/
static int update_role_table_columns(GRANT_TABLE *merged,
- GRANT_TABLE **first, GRANT_TABLE **last,
- ulong privs, ulong cols, char *role)
+ GRANT_TABLE **first, GRANT_TABLE **last,
+ ulong privs, ulong cols, const char *role)
{
if (!first)
return 0;
@@ -6271,7 +6204,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, char *role, HASH *hash)
+ ulong privs, const char *role, HASH *hash)
{
if (!first)
return 0;
@@ -6408,7 +6341,14 @@ static int merge_role_privileges(ACL_ROLE *role __attribute__((unused)),
if (all || data->what == PRIVS_TO_MERGE::FUNC)
changed|= merge_role_routine_grant_privileges(grantee,
data->db, data->name, &role_hash, &func_priv_hash);
-
+ if (all || data->what == PRIVS_TO_MERGE::PACKAGE_SPEC)
+ changed|= merge_role_routine_grant_privileges(grantee,
+ data->db, data->name, &role_hash,
+ &package_spec_priv_hash);
+ if (all || data->what == PRIVS_TO_MERGE::PACKAGE_BODY)
+ changed|= merge_role_routine_grant_privileges(grantee,
+ data->db, data->name, &role_hash,
+ &package_body_priv_hash);
return !changed; // don't recurse into the subgraph if privs didn't change
}
@@ -6493,7 +6433,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
List_iterator <LEX_USER> str_list (user_list);
LEX_USER *Str, *tmp_Str;
bool create_new_users=0;
- char *db_name, *table_name;
+ const char *db_name, *table_name;
DBUG_ENTER("mysql_table_grant");
if (rights & ~TABLE_ACLS)
@@ -6523,13 +6463,13 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
column->column.ptr(), NULL, NULL,
NULL, TRUE, FALSE,
&unused_field_idx, FALSE, &dummy);
- if (f == (Field*)0)
+ if (unlikely(f == (Field*)0))
{
my_error(ER_BAD_FIELD_ERROR, MYF(0),
- column->column.c_ptr(), table_list->alias);
+ column->column.c_ptr(), table_list->alias.str);
DBUG_RETURN(TRUE);
}
- if (f == (Field *)-1)
+ if (unlikely(f == (Field *)-1))
DBUG_RETURN(TRUE);
column_priv|= column->rights;
}
@@ -6539,9 +6479,10 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
{
if (!(rights & CREATE_ACL))
{
- if (!ha_table_exists(thd, table_list->db, table_list->table_name, 0))
+ if (!ha_table_exists(thd, &table_list->db, &table_list->table_name))
{
- my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->alias);
+ my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db.str,
+ table_list->alias.str);
DBUG_RETURN(TRUE);
}
}
@@ -6552,7 +6493,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
table_list->grant.want_privilege);
my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
command, thd->security_ctx->priv_user,
- thd->security_ctx->host_or_ip, table_list->alias);
+ thd->security_ctx->host_or_ip, table_list->alias.str);
DBUG_RETURN(-1);
}
}
@@ -6611,7 +6552,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
0, revoke_grant, create_new_users,
MY_TEST(thd->variables.sql_mode &
MODE_NO_AUTO_CREATE_USER));
- if (error)
+ if (unlikely(error))
{
result= TRUE; // Remember error
continue; // Add next user
@@ -6628,7 +6569,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
if (revoke_grant)
{
my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
- Str->user.str, Str->host.str, table_list->table_name);
+ Str->user.str, Str->host.str, table_list->table_name.str);
result= TRUE;
continue;
}
@@ -6726,7 +6667,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
@param thd Thread handle
@param table_list List of routines to give grant
- @param is_proc Is this a list of procedures?
+ @param sph SP handler
@param user_list List of users to give grant
@param rights Table level grant
@param revoke_grant Is this is a REVOKE command?
@@ -6736,7 +6677,8 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
@retval TRUE An error occurred.
*/
-bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
+bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list,
+ const Sp_handler *sph,
List <LEX_USER> &user_list, ulong rights,
bool revoke_grant, bool write_to_binlog)
{
@@ -6744,7 +6686,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
LEX_USER *Str, *tmp_Str;
bool create_new_users= 0;
int result;
- char *db_name, *table_name;
+ const char *db_name, *table_name;
DBUG_ENTER("mysql_routine_grant");
if (rights & ~PROC_ACLS)
@@ -6757,7 +6699,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
if (!revoke_grant)
{
- if (sp_exist_routines(thd, table_list, is_proc))
+ if (sph->sp_exist_routines(thd, table_list))
DBUG_RETURN(TRUE);
}
@@ -6795,10 +6737,10 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
continue;
}
- db_name= table_list->db;
- table_name= table_list->table_name;
+ db_name= table_list->db.str;
+ table_name= table_list->table_name.str;
grant_name= routine_hash_search(Str->host.str, NullS, db_name,
- Str->user.str, table_name, is_proc, 1);
+ Str->user.str, table_name, sph, 1);
if (!grant_name || !grant_name->init_privs)
{
if (revoke_grant)
@@ -6812,8 +6754,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
Str->user.str, table_name,
rights, TRUE);
if (!grant_name ||
- my_hash_insert(is_proc ?
- &proc_priv_hash : &func_priv_hash,(uchar*) grant_name))
+ my_hash_insert(sph->get_priv_hash(), (uchar*) grant_name))
{
result= TRUE;
continue;
@@ -6824,7 +6765,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
instead of TABLE directly. */
if (tables.procs_priv_table().no_such_table() ||
replace_routine_table(thd, grant_name, tables.procs_priv_table().table(),
- *Str, db_name, table_name, is_proc, rights,
+ *Str, db_name, table_name, sph, rights,
revoke_grant) != 0)
{
result= TRUE;
@@ -6832,7 +6773,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
}
if (Str->is_role())
propagate_role_grants(find_acl_role(Str->user.str),
- is_proc ? PRIVS_TO_MERGE::PROC : PRIVS_TO_MERGE::FUNC,
+ sp_privs_to_merge(sph->type()),
db_name, table_name);
}
thd->mem_root= old_root;
@@ -6854,7 +6795,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
append a user or role name to a buffer that will be later used as an error message
*/
static void append_user(THD *thd, String *str,
- const LEX_STRING *u, const LEX_STRING *h)
+ const LEX_CSTRING *u, const LEX_CSTRING *h)
{
if (str->length())
str->append(',');
@@ -6897,11 +6838,11 @@ static int can_grant_role_callback(ACL_USER_BASE *grantee,
return 0; // keep searching
if (grantee->flags & IS_ROLE)
- pair= find_role_grant_pair(&grantee->user, &empty_lex_str, &role->user);
+ pair= find_role_grant_pair(&grantee->user, &empty_clex_str, &role->user);
else
{
ACL_USER *user= (ACL_USER *)grantee;
- LEX_STRING host= { user->host.hostname, user->hostname_length };
+ LEX_CSTRING host= { user->host.hostname, user->hostname_length };
pair= find_role_grant_pair(&user->user, &host, &role->user);
}
if (!pair->with_admin)
@@ -6946,9 +6887,9 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
bool create_new_user, no_auto_create_user;
String wrong_users;
LEX_USER *user, *granted_role;
- LEX_STRING rolename;
- LEX_STRING username;
- LEX_STRING hostname;
+ LEX_CSTRING rolename;
+ LEX_CSTRING username;
+ LEX_CSTRING hostname;
ACL_ROLE *role, *role_as_user;
List_iterator <LEX_USER> user_list(list);
@@ -7002,9 +6943,9 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
}
if (!(role_as_user= find_acl_role(thd->security_ctx->priv_role)))
{
- LEX_STRING ls= { thd->security_ctx->priv_role,
- strlen(thd->security_ctx->priv_role) };
- append_user(thd, &wrong_users, &ls, &empty_lex_str);
+ LEX_CSTRING ls= { thd->security_ctx->priv_role,
+ strlen(thd->security_ctx->priv_role) };
+ append_user(thd, &wrong_users, &ls, &empty_clex_str);
result= 1;
continue;
}
@@ -7012,13 +6953,13 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
/* can not grant current_role to current_role */
if (granted_role->user.str == current_role.str)
{
- append_user(thd, &wrong_users, &role_as_user->user, &empty_lex_str);
+ append_user(thd, &wrong_users, &role_as_user->user, &empty_clex_str);
result= 1;
continue;
}
username.str= thd->security_ctx->priv_role;
username.length= strlen(username.str);
- hostname= empty_lex_str;
+ hostname= empty_clex_str;
}
else if (user->user.str == current_user.str)
{
@@ -7034,12 +6975,12 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
hostname= user->host;
else
if ((role_as_user= find_acl_role(user->user.str)))
- hostname= empty_lex_str;
+ hostname= empty_clex_str;
else
{
if (is_invalid_role_name(username.str))
{
- append_user(thd, &wrong_users, &username, &empty_lex_str);
+ append_user(thd, &wrong_users, &username, &empty_clex_str);
result= 1;
continue;
}
@@ -7101,7 +7042,7 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
if (role_as_user &&
traverse_role_graph_down(role, 0, 0, 0) == ROLE_CYCLE_FOUND)
{
- append_user(thd, &wrong_users, &username, &empty_lex_str);
+ append_user(thd, &wrong_users, &username, &empty_clex_str);
result= 1;
undo_add_role_user_mapping(grantee, role);
continue;
@@ -7136,7 +7077,7 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
thd->lex->with_admin_option,
hash_entry, revoke))
{
- append_user(thd, &wrong_users, &username, &empty_lex_str);
+ append_user(thd, &wrong_users, &username, &empty_clex_str);
result= 1;
if (!revoke)
{
@@ -7294,6 +7235,8 @@ void grant_free(void)
my_hash_free(&column_priv_hash);
my_hash_free(&proc_priv_hash);
my_hash_free(&func_priv_hash);
+ my_hash_free(&package_spec_priv_hash);
+ my_hash_free(&package_body_priv_hash);
free_root(&grant_memroot,MYF(0));
DBUG_VOID_RETURN;
}
@@ -7360,7 +7303,11 @@ static bool grant_load(THD *thd,
0,0,0, (my_hash_get_key) get_grant_table, 0,0);
(void) my_hash_init(&func_priv_hash, &my_charset_utf8_bin,
0,0,0, (my_hash_get_key) get_grant_table, 0,0);
- init_sql_alloc(&grant_memroot, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
+ (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));
t_table= tables_priv.table();
c_table= columns_priv.table();
@@ -7442,16 +7389,9 @@ static bool grant_load(THD *thd,
continue;
}
}
- if (procs_priv.routine_type()->val_int() == TYPE_ENUM_PROCEDURE)
- {
- hash= &proc_priv_hash;
- }
- else
- if (procs_priv.routine_type()->val_int() == TYPE_ENUM_FUNCTION)
- {
- hash= &func_priv_hash;
- }
- else
+ stored_procedure_type type= (stored_procedure_type)procs_priv.routine_type()->val_int();
+ const Sp_handler *sph= Sp_handler::handler(type);
+ if (!sph || !(hash= sph->get_priv_hash()))
{
sql_print_warning("'procs_priv' entry '%s' "
"ignored, bad routine type",
@@ -7516,6 +7456,7 @@ static my_bool propagate_role_grants_action(void *role_ptr,
bool grant_reload(THD *thd)
{
HASH old_column_priv_hash, old_proc_priv_hash, old_func_priv_hash;
+ HASH old_package_spec_priv_hash, old_package_body_priv_hash;
MEM_ROOT old_mem;
int result;
DBUG_ENTER("grant_reload");
@@ -7535,6 +7476,8 @@ bool grant_reload(THD *thd)
old_column_priv_hash= column_priv_hash;
old_proc_priv_hash= proc_priv_hash;
old_func_priv_hash= func_priv_hash;
+ old_package_spec_priv_hash= package_spec_priv_hash;
+ old_package_body_priv_hash= package_body_priv_hash;
/*
Create a new memory pool but save the current memory pool to make an undo
@@ -7552,6 +7495,8 @@ bool grant_reload(THD *thd)
column_priv_hash= old_column_priv_hash; /* purecov: deadcode */
proc_priv_hash= old_proc_priv_hash;
func_priv_hash= old_func_priv_hash;
+ package_spec_priv_hash= old_package_spec_priv_hash;
+ package_body_priv_hash= old_package_body_priv_hash;
grant_memroot= old_mem; /* purecov: deadcode */
}
else
@@ -7559,6 +7504,8 @@ bool grant_reload(THD *thd)
my_hash_free(&old_column_priv_hash);
my_hash_free(&old_proc_priv_hash);
my_hash_free(&old_func_priv_hash);
+ my_hash_free(&old_package_spec_priv_hash);
+ my_hash_free(&old_package_body_priv_hash);
free_root(&old_mem,MYF(0));
}
@@ -7624,7 +7571,7 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
Security_context *sctx= thd->security_ctx;
uint i;
- ulong orig_want_access= want_access;
+ ulong original_want_access= want_access;
bool locked= 0;
GRANT_TABLE *grant_table;
GRANT_TABLE *grant_table_role= NULL;
@@ -7658,8 +7605,25 @@ 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;
+
+ /*
+ If sequence is used as part of NEXT VALUE, PREVIOUS VALUE or SELECT,
+ we need to modify the requested access rights depending on how the
+ sequence is used.
+ */
+ if (t_ref->sequence &&
+ !(want_access & ~(SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL)))
+ {
+ /*
+ We want to have either SELECT or INSERT rights to sequences depending
+ on how they are accessed
+ */
+ orig_want_access= ((t_ref->lock_type == TL_WRITE_ALLOW_WRITE) ?
+ INSERT_ACL : SELECT_ACL);
+ }
- if (tl->with || !tl->db ||
+ if (tl->with || !tl->db.str ||
(tl->select_lex &&
(tl->with= tl->select_lex->find_table_def_in_with_clauses(tl))))
continue;
@@ -7835,7 +7799,7 @@ static void check_grant_column_int(GRANT_TABLE *grant_table, const char *name,
bool check_grant_column(THD *thd, GRANT_INFO *grant,
const char *db_name, const char *table_name,
- const char *name, uint length, Security_context *sctx)
+ const char *name, size_t length, Security_context *sctx)
{
ulong want_access= grant->want_privilege & ~grant->privilege;
DBUG_ENTER("check_grant_column");
@@ -7861,8 +7825,10 @@ bool check_grant_column(THD *thd, GRANT_INFO *grant,
grant->version= grant_version; /* purecov: inspected */
}
- check_grant_column_int(grant->grant_table_user, name, length, &want_access);
- check_grant_column_int(grant->grant_table_role, name, length, &want_access);
+ check_grant_column_int(grant->grant_table_user, name, (uint)length,
+ &want_access);
+ check_grant_column_int(grant->grant_table_role, name, (uint)length,
+ &want_access);
mysql_rwlock_unlock(&LOCK_grant);
if (!want_access)
@@ -7886,6 +7852,8 @@ bool check_grant_column(THD *thd, GRANT_INFO *grant,
table_ref table reference where to check the field
name name of field to check
length length of name
+ fld use fld object to check invisibility when it is
+ not 0, not_found_field, view_ref_found
DESCRIPTION
Check the access rights to a column depending on the type of table
@@ -7900,13 +7868,17 @@ bool check_grant_column(THD *thd, GRANT_INFO *grant,
*/
bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
- const char *name, uint length)
+ const char *name, size_t length,
+ Field *fld)
{
GRANT_INFO *grant;
const char *db_name;
const char *table_name;
Security_context *sctx= table_ref->security_ctx ?
table_ref->security_ctx : thd->security_ctx;
+ if (fld && fld != not_found_field && fld != view_ref_found
+ && fld->invisible >= INVISIBLE_SYSTEM)
+ return false;
if (table_ref->view || table_ref->field_translation)
{
@@ -7982,7 +7954,10 @@ bool check_grant_all_columns(THD *thd, ulong want_access_arg,
for (; !fields->end_of_fields(); fields->next())
{
- const char *field_name= fields->name();
+ if (fields->field() &&
+ fields->field()->invisible >= INVISIBLE_SYSTEM)
+ continue;
+ LEX_CSTRING *field_name= fields->name();
if (table_name != fields->get_table_name())
{
@@ -8019,16 +7994,15 @@ bool check_grant_all_columns(THD *thd, ulong want_access_arg,
if (grant_table)
{
GRANT_COLUMN *grant_column=
- column_hash_search(grant_table, field_name,
- (uint) strlen(field_name));
+ column_hash_search(grant_table, field_name->str, field_name->length);
if (grant_column)
have_access= grant_column->rights;
}
if (grant_table_role)
{
GRANT_COLUMN *grant_column=
- column_hash_search(grant_table_role, field_name,
- (uint) strlen(field_name));
+ column_hash_search(grant_table_role, field_name->str,
+ field_name->length);
if (grant_column)
have_access|= grant_column->rights;
}
@@ -8060,7 +8034,7 @@ err:
command,
sctx->priv_user,
sctx->host_or_ip,
- fields->name(),
+ fields->name()->str,
table_name);
return 1;
}
@@ -8156,7 +8130,9 @@ bool check_grant_db(THD *thd, const char *db)
if (error)
error= check_grant_db_routine(thd, db, &proc_priv_hash) &&
- check_grant_db_routine(thd, db, &func_priv_hash);
+ check_grant_db_routine(thd, db, &func_priv_hash) &&
+ check_grant_db_routine(thd, db, &package_spec_priv_hash) &&
+ check_grant_db_routine(thd, db, &package_body_priv_hash);
mysql_rwlock_unlock(&LOCK_grant);
@@ -8172,7 +8148,7 @@ bool check_grant_db(THD *thd, const char *db)
thd Thread handler
want_access Bits of privileges user needs to have
procs List of routines to check. The user should have 'want_access'
- is_proc True if the list is all procedures, else functions
+ sph SP handler
no_errors If 0 then we write an error. The error is sent directly to
the client
@@ -8182,7 +8158,8 @@ bool check_grant_db(THD *thd, const char *db)
****************************************************************************/
bool check_grant_routine(THD *thd, ulong want_access,
- TABLE_LIST *procs, bool is_proc, bool no_errors)
+ TABLE_LIST *procs, const Sp_handler *sph,
+ bool no_errors)
{
TABLE_LIST *table;
Security_context *sctx= thd->security_ctx;
@@ -8199,13 +8176,13 @@ bool check_grant_routine(THD *thd, ulong want_access,
for (table= procs; table; table= table->next_global)
{
GRANT_NAME *grant_proc;
- if ((grant_proc= routine_hash_search(host, sctx->ip, table->db, user,
- table->table_name, is_proc, 0)))
+ if ((grant_proc= routine_hash_search(host, sctx->ip, table->db.str, user,
+ table->table_name.str, sph, 0)))
table->grant.privilege|= grant_proc->privs;
if (role[0]) /* current role set check */
{
- if ((grant_proc= routine_hash_search("", NULL, table->db, role,
- table->table_name, is_proc, 0)))
+ if ((grant_proc= routine_hash_search("", NULL, table->db.str, role,
+ table->table_name.str, sph, 0)))
table->grant.privilege|= grant_proc->privs;
}
@@ -8224,7 +8201,7 @@ err:
char buff[1024];
const char *command="";
if (table)
- strxmov(buff, table->db, ".", table->table_name, NullS);
+ strxmov(buff, table->db.str, ".", table->table_name.str, NullS);
if (want_access & EXECUTE_ACL)
command= "execute";
else if (want_access & ALTER_PROC_ACL)
@@ -8254,7 +8231,7 @@ err:
*/
bool check_routine_level_acl(THD *thd, const char *db, const char *name,
- bool is_proc)
+ const Sp_handler *sph)
{
bool no_routine_acl= 1;
GRANT_NAME *grant_proc;
@@ -8263,7 +8240,7 @@ bool check_routine_level_acl(THD *thd, const char *db, const char *name,
if ((grant_proc= routine_hash_search(sctx->priv_host,
sctx->ip, db,
sctx->priv_user,
- name, is_proc, 0)))
+ name, sph, 0)))
no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS);
if (no_routine_acl && sctx->priv_role[0]) /* current set role check */
@@ -8271,7 +8248,7 @@ bool check_routine_level_acl(THD *thd, const char *db, const char *name,
if ((grant_proc= routine_hash_search("",
NULL, db,
sctx->priv_role,
- name, is_proc, 0)))
+ name, sph, 0)))
no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS);
}
mysql_rwlock_unlock(&LOCK_grant);
@@ -8287,7 +8264,7 @@ ulong get_table_grant(THD *thd, TABLE_LIST *table)
{
ulong privilege;
Security_context *sctx= thd->security_ctx;
- const char *db = table->db ? table->db : thd->db;
+ const char *db = table->db.str ? table->db.str : thd->db.str;
GRANT_TABLE *grant_table;
GRANT_TABLE *grant_table_role= NULL;
@@ -8297,10 +8274,10 @@ ulong get_table_grant(THD *thd, TABLE_LIST *table)
grant_table_role= NULL;
#else
grant_table= table_hash_search(sctx->host, sctx->ip, db, sctx->priv_user,
- table->table_name, 0);
+ table->table_name.str, 0);
if (sctx->priv_role[0])
grant_table_role= table_hash_search("", "", db, sctx->priv_role,
- table->table_name, 0);
+ table->table_name.str, 0);
#endif
table->grant.grant_table_user= grant_table; // Remember for column test
table->grant.grant_table_role= grant_table_role;
@@ -8422,13 +8399,12 @@ static void add_user_option(String *grant, double value, const char *name)
}
}
-static void add_user_parameters(String *result, ACL_USER* acl_user,
+static void add_user_parameters(THD *thd, String *result, ACL_USER* acl_user,
bool with_grant)
{
- result->append(STRING_WITH_LEN("@'"));
- result->append(acl_user->host.hostname, acl_user->hostname_length,
- system_charset_info);
- result->append('\'');
+ result->append('@');
+ append_identifier(thd, result, acl_user->host.hostname,
+ acl_user->hostname_length);
if (acl_user->plugin.str == native_password_plugin_name.str ||
acl_user->plugin.str == old_password_plugin_name.str)
@@ -8437,18 +8413,18 @@ static void add_user_parameters(String *result, ACL_USER* acl_user,
{
DBUG_ASSERT(acl_user->salt_len);
result->append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '"));
- result->append(acl_user->auth_string.str, acl_user->auth_string.length);
+ result->append(&acl_user->auth_string);
result->append('\'');
}
}
else
{
result->append(STRING_WITH_LEN(" IDENTIFIED VIA "));
- result->append(acl_user->plugin.str, acl_user->plugin.length);
+ result->append(&acl_user->plugin);
if (acl_user->auth_string.length)
{
result->append(STRING_WITH_LEN(" USING '"));
- result->append(acl_user->auth_string.str, acl_user->auth_string.length);
+ result->append(&acl_user->auth_string);
result->append('\'');
}
}
@@ -8517,13 +8493,14 @@ static const char *command_array[]=
"ALTER", "SHOW DATABASES", "SUPER", "CREATE TEMPORARY TABLES",
"LOCK TABLES", "EXECUTE", "REPLICATION SLAVE", "REPLICATION CLIENT",
"CREATE VIEW", "SHOW VIEW", "CREATE ROUTINE", "ALTER ROUTINE",
- "CREATE USER", "EVENT", "TRIGGER", "CREATE TABLESPACE"
+ "CREATE USER", "EVENT", "TRIGGER", "CREATE TABLESPACE",
+ "DELETE HISTORY"
};
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, 13, 11, 5, 7, 17, 14,
};
@@ -8543,12 +8520,20 @@ static bool print_grants_for_role(THD *thd, ACL_ROLE * role)
if (show_table_and_column_privileges(thd, role->user.str, "", buff, sizeof(buff)))
return TRUE;
- if (show_routine_grants(thd, role->user.str, "", &proc_priv_hash,
- STRING_WITH_LEN("PROCEDURE"), buff, sizeof(buff)))
+ if (show_routine_grants(thd, role->user.str, "", &sp_handler_procedure,
+ buff, sizeof(buff)))
+ return TRUE;
+
+ if (show_routine_grants(thd, role->user.str, "", &sp_handler_function,
+ buff, sizeof(buff)))
+ return TRUE;
+
+ if (show_routine_grants(thd, role->user.str, "", &sp_handler_package_spec,
+ buff, sizeof(buff)))
return TRUE;
- if (show_routine_grants(thd, role->user.str, "", &func_priv_hash,
- STRING_WITH_LEN("FUNCTION"), buff, sizeof(buff)))
+ if (show_routine_grants(thd, role->user.str, "", &sp_handler_package_body,
+ buff, sizeof(buff)))
return TRUE;
return FALSE;
@@ -8563,18 +8548,21 @@ bool mysql_show_create_user(THD *thd, LEX_USER *lex_user)
Protocol *protocol= thd->protocol;
bool error= false;
ACL_USER *acl_user;
+ uint head_length;
DBUG_ENTER("mysql_show_create_user");
if (get_show_user(thd, lex_user, &username, &hostname, NULL))
DBUG_RETURN(TRUE);
List<Item> field_list;
- strxmov(buff, "CREATE USER for ", username, "@", hostname, NullS);
+ head_length= (uint) (strxmov(buff, "CREATE USER for ", username, "@",
+ hostname, NullS) - buff);
Item_string *field = new (thd->mem_root) Item_string_ascii(thd, "", 0);
if (!field)
DBUG_RETURN(true); // Error given my my_alloc()
- field->name= buff;
+ field->name.str= buff;
+ field->name.length= head_length;
field->max_length= sizeof(buff);
field_list.push_back(field, thd->mem_root);
if (protocol->send_result_set_metadata(&field_list,
@@ -8597,11 +8585,9 @@ bool mysql_show_create_user(THD *thd, LEX_USER *lex_user)
goto end;
}
- result.append("CREATE USER '");
- result.append(username);
- result.append('\'');
-
- add_user_parameters(&result, acl_user, false);
+ result.append("CREATE USER ");
+ append_identifier(thd, &result, username, strlen(username));
+ add_user_parameters(thd, &result, acl_user, false);
protocol->prepare_for_resend();
protocol->store(result.ptr(), result.length(), result.charset());
@@ -8629,10 +8615,12 @@ static int show_grants_callback(ACL_USER_BASE *role, void *data)
}
void mysql_show_grants_get_fields(THD *thd, List<Item> *fields,
- const char *name)
+ const char *name, size_t length)
{
Item_string *field=new (thd->mem_root) Item_string_ascii(thd, "", 0);
- field->name= (char *) name;
+ /* Set name explicit to avoid character set conversions */
+ field->name.str= name;
+ field->name.length= length;
field->max_length=1024;
fields->push_back(field, thd->mem_root);
}
@@ -8703,7 +8691,7 @@ bool mysql_show_grants(THD *thd, LEX_USER *lex_user)
ACL_ROLE *acl_role= NULL;
char buff[1024];
Protocol *protocol= thd->protocol;
- const char *username= NULL, *hostname= NULL, *rolename= NULL;
+ const char *username= NULL, *hostname= NULL, *rolename= NULL, *end;
DBUG_ENTER("mysql_show_grants");
if (!initialized)
@@ -8718,12 +8706,12 @@ bool mysql_show_grants(THD *thd, LEX_USER *lex_user)
DBUG_ASSERT(rolename || username);
List<Item> field_list;
- if (!username)
- strxmov(buff,"Grants for ",rolename, NullS);
+ if (username)
+ end= strxmov(buff,"Grants for ",username,"@",hostname, NullS);
else
- strxmov(buff,"Grants for ",username,"@",hostname, NullS);
+ end= strxmov(buff,"Grants for ",rolename, NullS);
- mysql_show_grants_get_fields(thd, &field_list, buff);
+ mysql_show_grants_get_fields(thd, &field_list, buff, (uint) (end-buff));
if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
@@ -8761,12 +8749,20 @@ bool mysql_show_grants(THD *thd, LEX_USER *lex_user)
if (show_table_and_column_privileges(thd, username, hostname, buff, sizeof(buff)))
goto end;
- if (show_routine_grants(thd, username, hostname, &proc_priv_hash,
- STRING_WITH_LEN("PROCEDURE"), buff, sizeof(buff)))
+ if (show_routine_grants(thd, username, hostname, &sp_handler_procedure,
+ buff, sizeof(buff)))
goto end;
- if (show_routine_grants(thd, username, hostname, &func_priv_hash,
- STRING_WITH_LEN("FUNCTION"), buff, sizeof(buff)))
+ if (show_routine_grants(thd, username, hostname, &sp_handler_function,
+ buff, sizeof(buff)))
+ goto end;
+
+ if (show_routine_grants(thd, username, hostname, &sp_handler_package_spec,
+ buff, sizeof(buff)))
+ goto end;
+
+ if (show_routine_grants(thd, username, hostname, &sp_handler_package_body,
+ buff, sizeof(buff)))
goto end;
if (show_proxy_grants(thd, username, hostname, buff, sizeof(buff)))
@@ -8804,9 +8800,9 @@ end:
DBUG_RETURN(error);
}
-static ROLE_GRANT_PAIR *find_role_grant_pair(const LEX_STRING *u,
- const LEX_STRING *h,
- const LEX_STRING *r)
+static ROLE_GRANT_PAIR *find_role_grant_pair(const LEX_CSTRING *u,
+ const LEX_CSTRING *h,
+ const LEX_CSTRING *r)
{
char buf[1024];
String pair_key(buf, sizeof(buf), &my_charset_bin);
@@ -8827,7 +8823,7 @@ static bool show_role_grants(THD *thd, const char *username,
{
uint counter;
Protocol *protocol= thd->protocol;
- LEX_STRING host= {const_cast<char*>(hostname), strlen(hostname)};
+ LEX_CSTRING host= {const_cast<char*>(hostname), strlen(hostname)};
String grant(buff,sizeof(buff),system_charset_info);
for (counter= 0; counter < acl_entry->role_grants.elements; counter++)
@@ -8836,17 +8832,14 @@ static bool show_role_grants(THD *thd, const char *username,
grant.append(STRING_WITH_LEN("GRANT "));
ACL_ROLE *acl_role= *(dynamic_element(&acl_entry->role_grants, counter,
ACL_ROLE**));
- grant.append(acl_role->user.str, acl_role->user.length,
- system_charset_info);
- grant.append(STRING_WITH_LEN(" TO '"));
- grant.append(acl_entry->user.str, acl_entry->user.length,
- system_charset_info);
+ append_identifier(thd, &grant, acl_role->user.str, acl_role->user.length);
+ grant.append(STRING_WITH_LEN(" TO "));
+ append_identifier(thd, &grant, acl_entry->user.str, acl_entry->user.length);
if (!(acl_entry->flags & IS_ROLE))
{
- grant.append(STRING_WITH_LEN("'@'"));
- grant.append(&host);
+ grant.append('@');
+ append_identifier(thd, &grant, host.str, host.length);
}
- grant.append('\'');
ROLE_GRANT_PAIR *pair=
find_role_grant_pair(&acl_entry->user, &host, &acl_role->user);
@@ -8900,13 +8893,12 @@ static bool show_global_privileges(THD *thd, ACL_USER_BASE *acl_entry,
}
}
}
- global.append (STRING_WITH_LEN(" ON *.* TO '"));
- global.append(acl_entry->user.str, acl_entry->user.length,
- system_charset_info);
- global.append('\'');
+ global.append (STRING_WITH_LEN(" ON *.* TO "));
+ append_identifier(thd, &global, acl_entry->user.str, acl_entry->user.length);
if (!handle_as_role)
- add_user_parameters(&global, (ACL_USER *)acl_entry, (want_access & GRANT_ACL));
+ add_user_parameters(thd, &global, (ACL_USER *)acl_entry,
+ (want_access & GRANT_ACL));
protocol->prepare_for_resend();
protocol->store(global.ptr(),global.length(),global.charset());
@@ -8917,6 +8909,21 @@ static bool show_global_privileges(THD *thd, ACL_USER_BASE *acl_entry,
}
+
+static void add_to_user(THD *thd, String *result, const char *user,
+ bool is_user, const char *host)
+{
+ result->append(STRING_WITH_LEN(" TO "));
+ append_identifier(thd, result, user, strlen(user));
+ if (is_user)
+ {
+ result->append('@');
+ // host and lex_user->host are equal except for case
+ append_identifier(thd, result, host, strlen(host));
+ }
+}
+
+
static bool show_database_privileges(THD *thd, const char *username,
const char *hostname,
char *buff, size_t buffsize)
@@ -8977,16 +8984,8 @@ static bool show_database_privileges(THD *thd, const char *username,
}
db.append (STRING_WITH_LEN(" ON "));
append_identifier(thd, &db, acl_db->db, strlen(acl_db->db));
- db.append (STRING_WITH_LEN(".* TO '"));
- db.append(username, strlen(username),
- system_charset_info);
- if (*hostname)
- {
- db.append (STRING_WITH_LEN("'@'"));
- // host and lex_user->host are equal except for case
- db.append(host, strlen(host), system_charset_info);
- }
- db.append ('\'');
+ db.append (STRING_WITH_LEN(".*"));
+ add_to_user(thd, &db, username, (*hostname), host);
if (want_access & GRANT_ACL)
db.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
protocol->prepare_for_resend();
@@ -9117,16 +9116,7 @@ static bool show_table_and_column_privileges(THD *thd, const char *username,
global.append('.');
append_identifier(thd, &global, grant_table->tname,
strlen(grant_table->tname));
- global.append(STRING_WITH_LEN(" TO '"));
- global.append(username, strlen(username),
- system_charset_info);
- if (*hostname)
- {
- global.append(STRING_WITH_LEN("'@'"));
- // host and lex_user->host are equal except for case
- global.append(host, strlen(host), system_charset_info);
- }
- global.append('\'');
+ add_to_user(thd, &global, username, (*hostname), host);
if (table_access & GRANT_ACL)
global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
protocol->prepare_for_resend();
@@ -9144,12 +9134,13 @@ static bool show_table_and_column_privileges(THD *thd, const char *username,
static int show_routine_grants(THD* thd,
const char *username, const char *hostname,
- HASH *hash, const char *type, int typelen,
+ const Sp_handler *sph,
char *buff, int buffsize)
{
uint counter, index;
int error= 0;
Protocol *protocol= thd->protocol;
+ HASH *hash= sph->get_priv_hash();
/* Add routine access */
for (index=0 ; index < hash->records ; index++)
{
@@ -9203,23 +9194,15 @@ static int show_routine_grants(THD* thd,
}
}
global.append(STRING_WITH_LEN(" ON "));
- global.append(type,typelen);
+ LEX_CSTRING tmp= sph->type_lex_cstring();
+ global.append(&tmp);
global.append(' ');
append_identifier(thd, &global, grant_proc->db,
strlen(grant_proc->db));
global.append('.');
append_identifier(thd, &global, grant_proc->tname,
strlen(grant_proc->tname));
- global.append(STRING_WITH_LEN(" TO '"));
- global.append(username, strlen(username),
- system_charset_info);
- if (*hostname)
- {
- global.append(STRING_WITH_LEN("'@'"));
- // host and lex_user->host are equal except for case
- global.append(host, strlen(host), system_charset_info);
- }
- global.append('\'');
+ add_to_user(thd, &global, username, (*hostname), host);
if (proc_access & GRANT_ACL)
global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
protocol->prepare_for_resend();
@@ -9314,8 +9297,8 @@ static int modify_grant_table(TABLE *table, Field *host_field,
system_charset_info);
user_field->store(user_to->user.str, user_to->user.length,
system_charset_info);
- if ((error= table->file->ha_update_row(table->record[1],
- table->record[0])) &&
+ if (unlikely(error= table->file->ha_update_row(table->record[1],
+ table->record[0])) &&
error != HA_ERR_RECORD_IS_THE_SAME)
table->file->print_error(error, MYF(0));
else
@@ -9324,7 +9307,7 @@ static int modify_grant_table(TABLE *table, Field *host_field,
else
{
/* delete */
- if ((error=table->file->ha_delete_row(table->record[0])))
+ if (unlikely((error=table->file->ha_delete_row(table->record[0]))))
table->file->print_error(error, MYF(0));
}
@@ -9356,11 +9339,9 @@ static int handle_roles_mappings_table(TABLE *table, bool drop,
DBUG_PRINT("info", ("Rewriting entry in roles_mapping table: %s@%s",
user_from->user.str, user_from->host.str));
table->use_all_columns();
- if ((error= table->file->ha_rnd_init(1)))
- {
- table->file->print_error(error, MYF(0));
+
+ if (unlikely(table->file->ha_rnd_init_with_error(1)))
result= -1;
- }
else
{
while((error= table->file->ha_rnd_next(table->record[0])) !=
@@ -9391,7 +9372,7 @@ static int handle_roles_mappings_table(TABLE *table, bool drop,
if (drop) /* drop if requested */
{
- if ((error= table->file->ha_delete_row(table->record[0])))
+ if (unlikely((error= table->file->ha_delete_row(table->record[0]))))
table->file->print_error(error, MYF(0));
}
else if (user_to)
@@ -9399,8 +9380,8 @@ static int handle_roles_mappings_table(TABLE *table, bool drop,
store_record(table, record[1]);
role_field->store(user_to->user.str, user_to->user.length,
system_charset_info);
- if ((error= table->file->ha_update_row(table->record[1],
- table->record[0])) &&
+ if (unlikely(error= table->file->ha_update_row(table->record[1],
+ table->record[0])) &&
error != HA_ERR_RECORD_IS_THE_SAME)
table->file->print_error(error, MYF(0));
}
@@ -9491,13 +9472,14 @@ static int handle_grant_table(THD *thd, const Grant_table_base& grant_table,
error= table->file->ha_index_read_idx_map(table->record[0], 0,
user_key, (key_part_map)3,
HA_READ_KEY_EXACT);
- if (!error && !*host_str)
- { // verify that we got a role or a user, as needed
+ if (!unlikely(error) && !*host_str)
+ {
+ // verify that we got a role or a user, as needed
if (static_cast<const User_table&>(grant_table).check_is_role() !=
user_from->is_role())
error= HA_ERR_KEY_NOT_FOUND;
}
- if (error)
+ if (unlikely(error))
{
if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
{
@@ -9521,11 +9503,8 @@ static int handle_grant_table(THD *thd, const Grant_table_base& grant_table,
And their host- and user fields are not consecutive.
Thus, we need to do a table scan to find all matching records.
*/
- if ((error= table->file->ha_rnd_init(1)))
- {
- table->file->print_error(error, MYF(0));
+ if (unlikely(table->file->ha_rnd_init_with_error(1)))
result= -1;
- }
else
{
#ifdef EXTRA_DEBUG
@@ -9635,7 +9614,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
DBUG_RETURN(1);
/* this calls for a role update */
- char *old_key= acl_role->user.str;
+ const char *old_key= acl_role->user.str;
size_t old_key_length= acl_role->user.length;
if (drop)
{
@@ -9675,7 +9654,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
elements= acl_users.elements;
break;
case DB_ACL:
- elements= acl_dbs.elements();
+ elements= int(acl_dbs.elements());
break;
case COLUMN_PRIVILEGES_HASH:
grant_name_hash= &column_priv_hash;
@@ -9689,6 +9668,14 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
grant_name_hash= &func_priv_hash;
elements= grant_name_hash->records;
break;
+ case PACKAGE_SPEC_PRIVILEGES_HASH:
+ grant_name_hash= &package_spec_priv_hash;
+ elements= grant_name_hash->records;
+ break;
+ case PACKAGE_BODY_PRIVILEGES_HASH:
+ grant_name_hash= &package_body_priv_hash;
+ elements= grant_name_hash->records;
+ break;
case PROXY_USERS_ACL:
elements= acl_proxy_users.elements;
break;
@@ -9727,6 +9714,8 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
case COLUMN_PRIVILEGES_HASH:
case PROC_PRIVILEGES_HASH:
case FUNC_PRIVILEGES_HASH:
+ case PACKAGE_SPEC_PRIVILEGES_HASH:
+ case PACKAGE_BODY_PRIVILEGES_HASH:
grant_name= (GRANT_NAME*) my_hash_element(grant_name_hash, idx);
user= grant_name->user;
host= grant_name->host.hostname;
@@ -9809,6 +9798,8 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
case COLUMN_PRIVILEGES_HASH:
case PROC_PRIVILEGES_HASH:
case FUNC_PRIVILEGES_HASH:
+ case PACKAGE_SPEC_PRIVILEGES_HASH:
+ case PACKAGE_BODY_PRIVILEGES_HASH:
my_hash_delete(grant_name_hash, (uchar*) grant_name);
/*
In our HASH implementation on deletion one elements
@@ -9854,6 +9845,8 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
case COLUMN_PRIVILEGES_HASH:
case PROC_PRIVILEGES_HASH:
case FUNC_PRIVILEGES_HASH:
+ case PACKAGE_SPEC_PRIVILEGES_HASH:
+ case PACKAGE_BODY_PRIVILEGES_HASH:
{
/*
Save old hash key and its length to be able to properly update
@@ -10036,6 +10029,26 @@ static int handle_grant_data(THD *thd, Grant_tables& tables, bool drop,
if (search_only)
goto end;
}
+ /* Handle package spec array. */
+ if ((handle_grant_struct(PACKAGE_SPEC_PRIVILEGES_HASH,
+ drop, user_from, user_to) || found)
+ && ! result)
+ {
+ result= 1; /* At least one record/element found. */
+ /* If search is requested, we do not need to search further. */
+ if (search_only)
+ goto end;
+ }
+ /* Handle package body array. */
+ if ((handle_grant_struct(PACKAGE_BODY_PRIVILEGES_HASH,
+ drop, user_from, user_to) || found)
+ && ! result)
+ {
+ result= 1; /* At least one record/element found. */
+ /* If search is requested, we do not need to search further. */
+ if (search_only)
+ goto end;
+ }
}
/* Handle tables table. */
@@ -10595,6 +10608,45 @@ int mysql_alter_user(THD* thd, List<LEX_USER> &users_list)
DBUG_RETURN(result);
}
+
+static bool
+mysql_revoke_sp_privs(THD *thd,
+ Grant_tables *tables,
+ const Sp_handler *sph,
+ const LEX_USER *lex_user)
+{
+ bool rc= false;
+ uint counter, revoked;
+ do {
+ HASH *hash= sph->get_priv_hash();
+ for (counter= 0, revoked= 0 ; counter < hash->records ; )
+ {
+ const char *user,*host;
+ GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
+ user= safe_str(grant_proc->user);
+ host= safe_str(grant_proc->host.hostname);
+
+ if (!strcmp(lex_user->user.str, user) &&
+ !strcmp(lex_user->host.str, host))
+ {
+ 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)
+ {
+ revoked= 1;
+ continue;
+ }
+ rc= true; // Something went wrong
+ }
+ counter++;
+ }
+ } while (revoked);
+ return rc;
+}
+
+
/*
Revoke all privileges from a list of users.
@@ -10611,7 +10663,7 @@ int mysql_alter_user(THD* thd, List<LEX_USER> &users_list)
bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
{
- uint counter, revoked, is_proc;
+ uint counter, revoked;
int result;
ACL_DB *acl_db;
DBUG_ENTER("mysql_revoke_all");
@@ -10740,32 +10792,11 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
} while (revoked);
/* Remove procedure access */
- for (is_proc=0; is_proc<2; is_proc++) do {
- HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
- for (counter= 0, revoked= 0 ; counter < hash->records ; )
- {
- const char *user,*host;
- GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
- user= safe_str(grant_proc->user);
- host= safe_str(grant_proc->host.hostname);
-
- if (!strcmp(lex_user->user.str,user) &&
- !strcmp(lex_user->host.str, host))
- {
- if (replace_routine_table(thd, grant_proc,
- tables.procs_priv_table().table(),
- *lex_user,
- grant_proc->db, grant_proc->tname,
- is_proc, ~(ulong)0, 1) == 0)
- {
- revoked= 1;
- continue;
- }
- result= -1; // Something went wrong
- }
- counter++;
- }
- } while (revoked);
+ if (mysql_revoke_sp_privs(thd, &tables, &sp_handler_function, lex_user) ||
+ mysql_revoke_sp_privs(thd, &tables, &sp_handler_procedure, lex_user) ||
+ mysql_revoke_sp_privs(thd, &tables, &sp_handler_package_spec, lex_user) ||
+ mysql_revoke_sp_privs(thd, &tables, &sp_handler_package_body, lex_user))
+ result= -1;
ACL_USER_BASE *user_or_role;
/* remove role grants */
@@ -10917,11 +10948,11 @@ Silence_routine_definer_errors::handle_condition(
*/
bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
- bool is_proc)
+ const Sp_handler *sph)
{
uint counter, revoked;
int result;
- HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
+ HASH *hash= sph->get_priv_hash();
Silence_routine_definer_errors error_handler;
DBUG_ENTER("sp_revoke_privileges");
@@ -10957,7 +10988,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,
- is_proc, ~(ulong)0, 1) == 0)
+ sph, ~(ulong)0, 1) == 0)
{
revoked= 1;
continue;
@@ -10982,7 +11013,7 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
@param thd The current thread.
@param sp_db
@param sp_name
- @param is_proc
+ @param sph
@return
@retval FALSE Success
@@ -10990,7 +11021,7 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
*/
bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
- bool is_proc)
+ const Sp_handler *sph)
{
Security_context *sctx= thd->security_ctx;
LEX_USER *combo;
@@ -11001,7 +11032,7 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
Dummy_error_handler error_handler;
DBUG_ENTER("sp_grant_privileges");
- if (!(combo=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
+ if (!(combo=(LEX_USER*) thd->alloc(sizeof(LEX_USER))))
DBUG_RETURN(TRUE);
combo->user.str= (char *) sctx->priv_user;
@@ -11020,8 +11051,10 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
bzero((char*)tables, sizeof(TABLE_LIST));
user_list.empty();
- tables->db= (char*)sp_db;
- tables->table_name= tables->alias= (char*)sp_name;
+ tables->db.str= sp_db;
+ tables->db.length= sp_db ? strlen(sp_db) : 0;
+ tables->table_name.str= tables->alias.str= sp_name;
+ tables->table_name.length= tables->alias.length= sp_name ? strlen(sp_name) : 0;
thd->make_lex_string(&combo->user, combo->user.str, strlen(combo->user.str));
thd->make_lex_string(&combo->host, combo->host.str, strlen(combo->host.str));
@@ -11046,7 +11079,7 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
as all errors will be handled later.
*/
thd->push_internal_handler(&error_handler);
- result= mysql_routine_grant(thd, tables, is_proc, user_list,
+ result= mysql_routine_grant(thd, tables, sph, user_list,
DEFAULT_CREATE_PROC_ACLS, FALSE, FALSE);
thd->pop_internal_handler();
DBUG_RETURN(result);
@@ -11208,8 +11241,8 @@ static int enabled_roles_insert(ACL_USER_BASE *role, void *context_data)
struct APPLICABLE_ROLES_DATA
{
TABLE *table;
- const LEX_STRING host;
- const LEX_STRING user_and_host;
+ const LEX_CSTRING host;
+ const LEX_CSTRING user_and_host;
ACL_USER *user;
};
@@ -11220,9 +11253,9 @@ applicable_roles_insert(ACL_USER_BASE *grantee, ACL_ROLE *role, void *ptr)
CHARSET_INFO *cs= system_charset_info;
TABLE *table= data->table;
bool is_role= grantee != data->user;
- const LEX_STRING *user_and_host= is_role ? &grantee->user
+ const LEX_CSTRING *user_and_host= is_role ? &grantee->user
: &data->user_and_host;
- const LEX_STRING *host= is_role ? &empty_lex_str : &data->host;
+ const LEX_CSTRING *host= is_role ? &empty_clex_str : &data->host;
restore_record(table, s->default_values);
table->field[0]->store(user_and_host->str, user_and_host->length, cs);
@@ -11241,7 +11274,7 @@ applicable_roles_insert(ACL_USER_BASE *grantee, ACL_ROLE *role, void *ptr)
if (!is_role)
{
if (data->user->default_rolename.length &&
- !strcmp(data->user->default_rolename.str, role->user.str))
+ lex_string_eq(&data->user->default_rolename, &role->user))
table->field[3]->store(STRING_WITH_LEN("YES"), cs);
else
table->field[3]->store(STRING_WITH_LEN("NO"), cs);
@@ -11292,7 +11325,7 @@ static int show_database_grants(THD *thd, SHOW_VAR *var, char *buff,
{
var->type= SHOW_UINT;
var->value= buff;
- *(uint *)buff= acl_dbs.elements();
+ *(uint *)buff= uint(acl_dbs.elements());
return 0;
}
@@ -11309,6 +11342,8 @@ SHOW_VAR acl_statistics[] = {
{"database_grants", (char*)show_database_grants, SHOW_SIMPLE_FUNC},
{"function_grants", (char*)&func_priv_hash.records, SHOW_ULONG},
{"procedure_grants", (char*)&proc_priv_hash.records, SHOW_ULONG},
+ {"package_spec_grants", (char*)&package_spec_priv_hash.records, SHOW_ULONG},
+ {"package_body_grants", (char*)&package_body_priv_hash.records, SHOW_ULONG},
{"proxy_users", (char*)&acl_proxy_users.elements, SHOW_UINT},
{"role_grants", (char*)&acl_roles_mappings.records, SHOW_ULONG},
{"roles", (char*)&acl_roles.records, SHOW_ULONG},
@@ -11844,7 +11879,7 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
/* global privileges */
grant->privilege= sctx->master_access;
- if (!thd->db || strcmp(db, thd->db))
+ if (!thd->db.str || strcmp(db, thd->db.str))
{
/* db privileges */
grant->privilege|= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, 0);
@@ -11892,7 +11927,7 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
****************************************************************************/
bool check_routine_level_acl(THD *thd, const char *db, const char *name,
- bool is_proc)
+ const Sp_handler *sph)
{
return FALSE;
}
@@ -11940,7 +11975,7 @@ LEX_USER *get_current_user(THD *thd, LEX_USER *user, bool lock)
if (lock)
mysql_mutex_lock(&acl_cache->lock);
if (find_acl_role(dup->user.str))
- dup->host= empty_lex_str;
+ dup->host= empty_clex_str;
else
dup->host= host_not_specified;
if (lock)
@@ -11955,7 +11990,7 @@ LEX_USER *get_current_user(THD *thd, LEX_USER *user, bool lock)
struct ACL_internal_schema_registry_entry
{
- const LEX_STRING *m_name;
+ const LEX_CSTRING *m_name;
const ACL_internal_schema_access *m_access;
};
@@ -11976,7 +12011,7 @@ static uint m_registry_array_size= 0;
@param access the schema ACL specific rules
*/
void ACL_internal_schema_registry::register_schema
- (const LEX_STRING *name, const ACL_internal_schema_access *access)
+ (const LEX_CSTRING *name, const ACL_internal_schema_access *access)
{
DBUG_ASSERT(m_registry_array_size < array_elements(registry_array));
@@ -12083,10 +12118,11 @@ struct MPVIO_EXT :public MYSQL_PLUGIN_VIO
MYSQL_SERVER_AUTH_INFO auth_info;
ACL_USER *acl_user; ///< a copy, independent from acl_users array
plugin_ref plugin; ///< what plugin we're under
- LEX_STRING db; ///< db name from the handshake packet
+ LEX_CSTRING db; ///< db name from the handshake packet
/** when restarting a plugin this caches the last client reply */
struct {
- char *plugin, *pkt; ///< pointers into NET::buff
+ const char *plugin;
+ char *pkt; ///< pointer into NET::buff
uint pkt_len;
} cached_client_reply;
/** this caches the first plugin packet for restart request on the client */
@@ -12441,7 +12477,7 @@ static bool find_mpvio_user(MPVIO_EXT *mpvio)
}
mpvio->auth_info.user_name= sctx->user;
- mpvio->auth_info.user_name_length= strlen(sctx->user);
+ mpvio->auth_info.user_name_length= (uint)strlen(sctx->user);
mpvio->auth_info.auth_string= mpvio->acl_user->auth_string.str;
mpvio->auth_info.auth_string_length= (unsigned long) mpvio->acl_user->auth_string.length;
strmake_buf(mpvio->auth_info.authenticated_as, safe_str(mpvio->acl_user->user.str));
@@ -12479,12 +12515,10 @@ read_client_connect_attrs(char **ptr, char *end, CHARSET_INFO *from_cs)
if (length > 65535)
return true;
-#ifdef HAVE_PSI_THREAD_INTERFACE
- if (PSI_THREAD_CALL(set_thread_connect_attrs)(*ptr, (size_t)length, from_cs) &&
+ if (PSI_CALL_set_thread_connect_attrs(*ptr, (uint)length, from_cs) &&
current_thd->variables.log_warnings)
sql_print_warning("Connection attributes of length %llu were truncated",
length);
-#endif
return false;
}
@@ -12526,7 +12560,7 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
*passwd > 127 and become 2**32-127+ after casting to uint.
*/
uint passwd_len= (thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
- (uchar) (*passwd++) : strlen(passwd));
+ (uchar) (*passwd++) : (uint)strlen(passwd));
db+= passwd_len + 1;
/*
@@ -12540,7 +12574,7 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
DBUG_RETURN (1);
}
- uint db_len= strlen(db);
+ size_t db_len= strlen(db);
char *next_field= db + db_len + 1;
@@ -12574,7 +12608,7 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
connection is closed. We don't want to accidentally free a wrong
pointer if connect failed.
*/
- thd->reset_db(NULL, 0);
+ thd->reset_db(&null_clex_str);
if (!initialized)
{
@@ -12588,7 +12622,7 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
if (find_mpvio_user(mpvio))
DBUG_RETURN(1);
- char *client_plugin;
+ const char *client_plugin;
if (thd->client_capabilities & CLIENT_PLUGIN_AUTH)
{
if (next_field >= end)
@@ -12699,7 +12733,7 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
DBUG_PRINT("info", ("Reading user information over SSL layer"));
pkt_len= my_net_read(net);
- if (pkt_len == packet_error || pkt_len < NORMAL_HANDSHAKE_SIZE)
+ if (unlikely(pkt_len == packet_error || pkt_len < NORMAL_HANDSHAKE_SIZE))
{
DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
pkt_len));
@@ -12740,7 +12774,7 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
char *user= end;
char *passwd= strend(user)+1;
- uint user_len= (uint)(passwd - user - 1), db_len;
+ size_t user_len= (size_t)(passwd - user - 1), db_len;
char *db= passwd;
char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8
uint dummy_errors;
@@ -12782,11 +12816,15 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
db_len= safe_strlen(db);
char *next_field;
- char *client_plugin= next_field= passwd + passwd_len + (db ? db_len + 1 : 0);
+ const char *client_plugin= next_field= passwd + passwd_len + (db ? db_len + 1 : 0);
- /* Since 4.1 all database names are stored in utf8 */
- if (thd->copy_with_error(system_charset_info, &mpvio->db,
- thd->charset(), db, db_len))
+ /*
+ Since 4.1 all database names are stored in utf8
+ The cast is ok as copy_with_error will create a new area for db
+ */
+ if (unlikely(thd->copy_with_error(system_charset_info,
+ (LEX_STRING*) &mpvio->db,
+ thd->charset(), db, db_len)))
return packet_error;
user_len= copy_and_convert(user_buff, sizeof(user_buff) - 1,
@@ -12812,7 +12850,7 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
Security_context *sctx= thd->security_ctx;
- my_free(sctx->user);
+ my_free((char*) sctx->user);
if (!(sctx->user= my_strndup(user, user_len, MYF(MY_WME))))
return packet_error; /* The error is set by my_strdup(). */
@@ -12822,7 +12860,7 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
connection is closed. We don't want to accidentally free a wrong
pointer if connect failed.
*/
- thd->reset_db(NULL, 0);
+ thd->reset_db(&null_clex_str);
if (!initialized)
{
@@ -12876,11 +12914,10 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
restarted and a server auth plugin will read the data that the client
has just send. Cache them to return in the next server_mpvio_read_packet().
*/
- if (my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str,
- plugin_name(mpvio->plugin)->str) != 0)
+ if (!lex_string_eq(&mpvio->acl_user->plugin, plugin_name(mpvio->plugin)))
{
mpvio->cached_client_reply.pkt= passwd;
- mpvio->cached_client_reply.pkt_len= passwd_len;
+ mpvio->cached_client_reply.pkt_len= (uint)passwd_len;
mpvio->cached_client_reply.plugin= client_plugin;
mpvio->status= MPVIO_EXT::RESTART;
return packet_error;
@@ -12909,7 +12946,7 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
}
*buff= (uchar*) passwd;
- return passwd_len;
+ return (ulong)passwd_len;
#else
return 0;
#endif
@@ -13024,7 +13061,7 @@ static int server_mpvio_read_packet(MYSQL_PLUGIN_VIO *param, uchar **buf)
else
pkt_len= my_net_read(&mpvio->auth_info.thd->net);
- if (pkt_len == packet_error)
+ if (unlikely(pkt_len == packet_error))
goto err;
mpvio->packets_read++;
@@ -13036,7 +13073,7 @@ static int server_mpvio_read_packet(MYSQL_PLUGIN_VIO *param, uchar **buf)
if (mpvio->packets_read == 1)
{
pkt_len= parse_client_handshake_packet(mpvio, buf, pkt_len);
- if (pkt_len == packet_error)
+ if (unlikely(pkt_len == packet_error))
goto err;
}
else
@@ -13173,7 +13210,7 @@ static bool acl_check_ssl(THD *thd, const ACL_USER *acl_user)
}
-static int do_auth_once(THD *thd, const LEX_STRING *auth_plugin_name,
+static int do_auth_once(THD *thd, const LEX_CSTRING *auth_plugin_name,
MPVIO_EXT *mpvio)
{
int res= CR_OK, old_status= MPVIO_EXT::FAILURE;
@@ -13255,7 +13292,7 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len)
{
int res= CR_OK;
MPVIO_EXT mpvio;
- const LEX_STRING *auth_plugin_name= default_auth_plugin_name;
+ const LEX_CSTRING *auth_plugin_name= default_auth_plugin_name;
enum enum_server_command command= com_change_user_pkt_len ? COM_CHANGE_USER
: COM_CONNECT;
DBUG_ENTER("acl_authenticate");
@@ -13307,8 +13344,7 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len)
{
DBUG_ASSERT(mpvio.acl_user);
DBUG_ASSERT(command == COM_CHANGE_USER ||
- my_strcasecmp(system_charset_info, auth_plugin_name->str,
- mpvio.acl_user->plugin.str));
+ !lex_string_eq(auth_plugin_name, &mpvio.acl_user->plugin));
auth_plugin_name= &mpvio.acl_user->plugin;
res= do_auth_once(thd, auth_plugin_name, &mpvio);
}
@@ -13571,11 +13607,9 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len)
else
my_ok(thd);
-#ifdef HAVE_PSI_THREAD_INTERFACE
- PSI_THREAD_CALL(set_thread_user_host)
- (thd->main_security_ctx.user, strlen(thd->main_security_ctx.user),
- thd->main_security_ctx.host_or_ip, strlen(thd->main_security_ctx.host_or_ip));
-#endif
+ 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));
/* Ready to handle queries */
DBUG_RETURN(0);
@@ -13705,7 +13739,7 @@ static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio,
We need to figure out the correct scramble length here.
*/
if (pkt_len == SCRAMBLE_LENGTH_323 + 1)
- pkt_len= strnlen((char*)pkt, pkt_len);
+ pkt_len= (int)strnlen((char*)pkt, pkt_len);
if (pkt_len == 0) /* no password */
return info->auth_string[0] ? CR_AUTH_USER_CREDENTIALS : CR_OK;
diff --git a/sql/sql_acl.h b/sql/sql_acl.h
index e9063673b8e..dc8a085c96c 100644
--- a/sql/sql_acl.h
+++ b/sql/sql_acl.h
@@ -2,7 +2,7 @@
#define SQL_ACL_INCLUDED
/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
- 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
@@ -17,7 +17,6 @@
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" /* NO_EMBEDDED_ACCESS_CHECKS */
#include "violite.h" /* SSL_type */
#include "sql_class.h" /* LEX_COLUMN */
@@ -50,6 +49,7 @@
#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[]
@@ -63,12 +63,13 @@
(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)
+ 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)
+ SHOW_VIEW_ACL | TRIGGER_ACL | DELETE_HISTORY_ACL)
#define COL_ACLS \
(SELECT_ACL | INSERT_ACL | UPDATE_ACL | REFERENCES_ACL)
@@ -86,7 +87,7 @@
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)
+ CREATE_TABLESPACE_ACL | DELETE_HISTORY_ACL)
#define DEFAULT_CREATE_PROC_ACLS \
(ALTER_PROC_ACL | EXECUTE_ACL)
@@ -118,31 +119,37 @@
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) << 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_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) << 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_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) | \
@@ -176,16 +183,17 @@ enum mysql_db_table_field
MYSQL_DB_FIELD_EXECUTE_PRIV,
MYSQL_DB_FIELD_EVENT_PRIV,
MYSQL_DB_FIELD_TRIGGER_PRIV,
+ MYSQL_DB_FIELD_DELETE_VERSIONING_ROWS_PRIV,
MYSQL_DB_FIELD_COUNT
};
extern const TABLE_FIELD_DEF mysql_db_table_def;
extern bool mysql_user_table_is_in_short_password_format;
-extern LEX_STRING host_not_specified;
-extern LEX_STRING current_user;
-extern LEX_STRING current_role;
-extern LEX_STRING current_user_and_current_role;
+extern LEX_CSTRING host_not_specified;
+extern LEX_CSTRING current_user;
+extern LEX_CSTRING current_role;
+extern LEX_CSTRING current_user_and_current_role;
static inline int access_denied_error_code(int passwd_used)
@@ -198,7 +206,6 @@ static inline int access_denied_error_code(int passwd_used)
#endif
}
-
/* prototypes */
bool hostname_requires_resolving(const char *hostname);
@@ -208,8 +215,8 @@ void acl_free(bool end=0);
ulong acl_get(const char *host, const char *ip,
const char *user, const char *db, my_bool db_is_pattern);
bool acl_authenticate(THD *thd, uint com_change_user_pkt_len);
-bool acl_getroot(Security_context *sctx, char *user, char *host,
- char *ip, char *db);
+bool acl_getroot(Security_context *sctx, const char *user, const char *host,
+ const char *ip, const char *db);
bool acl_check_host(const char *host, const char *ip);
bool check_change_password(THD *thd, LEX_USER *user);
bool change_password(THD *thd, LEX_USER *user);
@@ -220,7 +227,7 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &user_list,
int mysql_table_grant(THD *thd, TABLE_LIST *table, List <LEX_USER> &user_list,
List <LEX_COLUMN> &column_list, ulong rights,
bool revoke);
-bool mysql_routine_grant(THD *thd, TABLE_LIST *table, bool is_proc,
+bool mysql_routine_grant(THD *thd, TABLE_LIST *table, const Sp_handler *sph,
List <LEX_USER> &user_list, ulong rights,
bool revoke, bool write_to_binlog);
bool grant_init();
@@ -230,13 +237,14 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
bool any_combination_will_do, uint number, bool no_errors);
bool check_grant_column (THD *thd, GRANT_INFO *grant,
const char *db_name, const char *table_name,
- const char *name, uint length, Security_context *sctx);
+ const char *name, size_t length, Security_context *sctx);
bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
- const char *name, uint length);
+ const char *name, size_t length, Field *fld);
bool check_grant_all_columns(THD *thd, ulong want_access,
Field_iterator_table_ref *fields);
bool check_grant_routine(THD *thd, ulong want_access,
- TABLE_LIST *procs, bool is_proc, bool no_error);
+ TABLE_LIST *procs, 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,
@@ -249,7 +257,7 @@ ulong get_column_grant(THD *thd, GRANT_INFO *grant,
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,
- const char *name);
+ const char *name, size_t length);
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);
@@ -264,11 +272,11 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list);
void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
const char *db, const char *table);
bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
- bool is_proc);
+ const Sp_handler *sph);
bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
- bool is_proc);
+ const Sp_handler *sph);
bool check_routine_level_acl(THD *thd, const char *db, const char *name,
- bool is_proc);
+ const Sp_handler *sph);
bool is_acl_user(const char *host, const char *user);
int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond);
int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond);
@@ -391,7 +399,7 @@ public:
class ACL_internal_schema_registry
{
public:
- static void register_schema(const LEX_STRING *name,
+ static void register_schema(const LEX_CSTRING *name,
const ACL_internal_schema_access *access);
static const ACL_internal_schema_access *lookup(const char *name);
};
@@ -407,9 +415,10 @@ get_cached_table_access(GRANT_INTERNAL_INFO *grant_internal_info,
bool acl_check_proxy_grant_access (THD *thd, const char *host, const char *user,
bool with_grant);
-int acl_setrole(THD *thd, char *rolename, ulonglong access);
-int acl_check_setrole(THD *thd, char *rolename, ulonglong *access);
-int acl_check_set_default_role(THD *thd, const char *host, const char *user, const char *role);
+int acl_setrole(THD *thd, const char *rolename, ulonglong access);
+int acl_check_setrole(THD *thd, const char *rolename, ulonglong *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,
const char *rolename);
diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc
index af4b9127f8f..89ef6d7793f 100644
--- a/sql/sql_admin.cc
+++ b/sql/sql_admin.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2010, 2015, Oracle and/or its affiliates.
- Copyright (c) 2011, 2018, MariaDB
+ Copyright (c) 2011, 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
@@ -14,7 +14,8 @@
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_class.h" // THD and my_global.h
+#include "mariadb.h"
+#include "sql_class.h" // THD
#include "keycaches.h" // get_key_cache
#include "sql_base.h" // Open_table_context
#include "lock.h" // MYSQL_OPEN_*
@@ -76,7 +77,7 @@ static int send_check_errmsg(THD *thd, TABLE_LIST* table,
{
Protocol *protocol= thd->protocol;
protocol->prepare_for_resend();
- protocol->store(table->alias, system_charset_info);
+ protocol->store(table->alias.str, table->alias.length, system_charset_info);
protocol->store((char*) operator_name, system_charset_info);
protocol->store(STRING_WITH_LEN("error"), system_charset_info);
protocol->store(errmsg, system_charset_info);
@@ -122,7 +123,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
*/
table_list->mdl_request.init(MDL_key::TABLE,
- table_list->db, table_list->table_name,
+ table_list->db.str, table_list->table_name.str,
MDL_EXCLUSIVE, MDL_TRANSACTION);
if (lock_table_names(thd, table_list, table_list->next_global,
@@ -134,7 +135,8 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
if (share == NULL)
DBUG_RETURN(0); // Can't open frm file
- if (open_table_from_share(thd, share, "", 0, 0, 0, &tmp_table, FALSE))
+ if (open_table_from_share(thd, share, &empty_clex_str, 0, 0, 0,
+ &tmp_table, FALSE))
{
tdc_release_share(share);
DBUG_RETURN(0); // Out of memory
@@ -173,7 +175,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
/*
Check if this is a table type that stores index and data separately,
like ISAM or MyISAM. We assume fixed order of engine file name
- extentions array. First element of engine file name extentions array
+ extensions array. First element of engine file name extensions array
is meta/index file extention. Second element - data file extention.
*/
ext= table->file->bas_ext();
@@ -188,7 +190,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
if (!mysql_file_stat(key_file_misc, from, &stat_info, MYF(0)))
goto end; // Can't use USE_FRM flag
- my_snprintf(tmp, sizeof(tmp), "%s-%lx_%lx",
+ my_snprintf(tmp, sizeof(tmp), "%s-%lx_%llx",
from, current_pid, thd->thread_id);
if (table_list->table)
@@ -217,7 +219,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
"Failed renaming data file");
goto end;
}
- if (dd_recreate_table(thd, table_list->db, table_list->table_name))
+ if (dd_recreate_table(thd, table_list->db.str, table_list->table_name.str))
{
error= send_check_errmsg(thd, table_list, "repair",
"Failed generating table from .frm file");
@@ -265,7 +267,7 @@ end:
tdc_release_share(table->s);
}
/* In case of a temporary table there will be no metadata lock. */
- if (error && has_mdl_lock)
+ if (unlikely(error) && has_mdl_lock)
thd->mdl_context.release_transactional_locks();
DBUG_RETURN(error);
@@ -337,19 +339,20 @@ static bool open_only_one_table(THD* thd, TABLE_LIST* table,
to differentiate from ALTER TABLE...CHECK PARTITION on which view is not
allowed.
*/
- if (lex->alter_info.flags & Alter_info::ALTER_ADMIN_PARTITION ||
+ if (lex->alter_info.partition_flags & ALTER_PARTITION_ADMIN ||
!is_view_operator_func)
{
- table->required_type=FRMTYPE_TABLE;
- DBUG_ASSERT(!lex->only_view);
+ table->required_type= TABLE_TYPE_NORMAL;
+ DBUG_ASSERT(lex->table_type != TABLE_TYPE_VIEW);
}
- else if (lex->only_view)
+ else if (lex->table_type == TABLE_TYPE_VIEW)
{
- table->required_type= FRMTYPE_VIEW;
+ table->required_type= lex->table_type;
}
- else if (!lex->only_view && lex->sql_command == SQLCOM_REPAIR)
+ else if ((lex->table_type != TABLE_TYPE_VIEW) &&
+ lex->sql_command == SQLCOM_REPAIR)
{
- table->required_type= FRMTYPE_TABLE;
+ table->required_type= TABLE_TYPE_NORMAL;
}
if (lex->sql_command == SQLCOM_CHECK ||
@@ -391,6 +394,7 @@ static bool open_only_one_table(THD* thd, TABLE_LIST* table,
open_error= (thd->open_temporary_tables(table) ||
open_and_lock_tables(thd, table, TRUE, 0));
}
+
#ifndef DBUG_OFF
dbug_err:
#endif
@@ -445,7 +449,6 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
int compl_result_code;
bool need_repair_or_alter= 0;
wait_for_commit* suspended_wfc;
-
DBUG_ENTER("mysql_admin_table");
DBUG_PRINT("enter", ("extra_open_options: %u", extra_open_options));
@@ -488,14 +491,14 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
for (table= tables; table; table= table->next_local)
{
char table_name[SAFE_NAME_LEN*2+2];
- char *db= table->db;
+ const char *db= table->db.str;
bool fatal_error=0;
bool open_error;
bool collect_eis= FALSE;
bool open_for_modify= org_open_for_modify;
- DBUG_PRINT("admin", ("table: '%s'.'%s'", table->db, table->table_name));
- strxmov(table_name, db, ".", table->table_name, NullS);
+ DBUG_PRINT("admin", ("table: '%s'.'%s'", db, table->table_name.str));
+ strxmov(table_name, db, ".", table->table_name.str, NullS);
thd->open_options|= extra_open_options;
table->lock_type= lock_type;
/*
@@ -521,7 +524,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
If open_and_lock_tables() failed, close_thread_tables() will close
the table and table->table can therefore be invalid.
*/
- if (open_error)
+ if (unlikely(open_error))
table->table= NULL;
/*
@@ -529,7 +532,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
so any errors opening the table are logical errors.
In these cases it does not make sense to try to repair.
*/
- if (open_error && thd->locked_tables_mode)
+ if (unlikely(open_error) && thd->locked_tables_mode)
{
result_code= HA_ADMIN_FAILED;
goto send_result;
@@ -544,7 +547,7 @@ 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, table->table_name,
+ table->mdl_request.init(MDL_key::TABLE, table->db.str, table->table_name.str,
MDL_SHARED_NO_READ_WRITE, MDL_TRANSACTION);
}
@@ -558,7 +561,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
*/
Alter_info *alter_info= &lex->alter_info;
- if (alter_info->flags & Alter_info::ALTER_ADMIN_PARTITION)
+ if (alter_info->partition_flags & ALTER_PARTITION_ADMIN)
{
if (!table->table->part_info)
{
@@ -767,7 +770,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
}
/*
- The check for Alter_info::ALTER_ADMIN_PARTITION implements this logic:
+ The check for ALTER_PARTITION_ADMIN implements this logic:
do not collect EITS STATS for this syntax:
ALTER TABLE ... ANALYZE PARTITION p
EITS statistics is global (not per-partition). Collecting global stats
@@ -776,7 +779,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
*/
collect_eis=
(table->table->s->table_category == TABLE_CATEGORY_USER &&
- !(lex->alter_info.flags & Alter_info::ALTER_ADMIN_PARTITION) &&
+ !(lex->alter_info.flags & ALTER_PARTITION_ADMIN) &&
(get_use_stat_tables_mode(thd) > NEVER ||
lex->with_persistent_for_clause));
}
@@ -796,7 +799,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
Here we close and reopen table in read mode because operation of
collecting statistics is long and it will be better do not block
the table completely.
- InnoDB/XtraDB will allow read/write and MyISAM read/insert.
+ InnoDB will allow read/write and MyISAM read/insert.
*/
trans_commit_stmt(thd);
trans_commit(thd);
@@ -804,7 +807,7 @@ 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, table->table_name,
+ table->mdl_request.init(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);
@@ -814,7 +817,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
repair_table_use_frm, FALSE);
thd->open_options&= ~extra_open_options;
- if (!open_error)
+ if (unlikely(!open_error))
{
TABLE *tab= table->table;
Field **field_ptr= tab->field;
@@ -831,7 +834,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_NO_EIS_FOR_FIELD,
ER_THD(thd, ER_NO_EIS_FOR_FIELD),
- (*field_ptr)->field_name);
+ (*field_ptr)->field_name.str);
}
}
else
@@ -1009,7 +1012,7 @@ send_result_message:
Alter_info *alter_info= &lex->alter_info;
protocol->store(STRING_WITH_LEN("note"), system_charset_info);
- if (alter_info->flags & Alter_info::ALTER_ADMIN_PARTITION)
+ if (alter_info->partition_flags & ALTER_PARTITION_ADMIN)
{
protocol->store(STRING_WITH_LEN(
"Table does not support optimize on partitions. All partitions "
@@ -1048,15 +1051,15 @@ send_result_message:
if (!thd->open_temporary_tables(table) &&
(table->table= open_ltable(thd, table, lock_type, 0)))
{
- uint save_flags;
+ ulonglong save_flags;
/* Store the original value of alter_info->flags */
save_flags= alter_info->flags;
/*
- Reset the ALTER_ADMIN_PARTITION bit in alter_info->flags
+ Reset the ALTER_PARTITION_ADMIN bit in alter_info->flags
to force analyze on all partitions.
*/
- alter_info->flags &= ~(Alter_info::ALTER_ADMIN_PARTITION);
+ alter_info->partition_flags &= ~(ALTER_PARTITION_ADMIN);
result_code= table->table->file->ha_analyze(thd, check_opt);
if (result_code == HA_ADMIN_ALREADY_DONE)
result_code= HA_ADMIN_OK;
@@ -1125,11 +1128,11 @@ send_result_message:
if (what_to_upgrade)
length= my_snprintf(buf, sizeof(buf),
ER_THD(thd, ER_TABLE_NEEDS_UPGRADE),
- what_to_upgrade, table->table_name);
+ what_to_upgrade, table->table_name.str);
else
length= my_snprintf(buf, sizeof(buf),
ER_THD(thd, ER_TABLE_NEEDS_REBUILD),
- table->table_name);
+ table->table_name.str);
protocol->store(buf, length, system_charset_info);
fatal_error=1;
break;
@@ -1161,7 +1164,7 @@ send_result_message:
else if (open_for_modify || fatal_error)
{
tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED,
- table->db, table->table_name, FALSE);
+ table->db.str, table->table_name.str, FALSE);
/*
May be something modified. Consequently, we have to
invalidate the query cache.
@@ -1246,7 +1249,7 @@ err:
*/
bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* tables,
- LEX_STRING *key_cache_name)
+ const LEX_CSTRING *key_cache_name)
{
HA_CHECK_OPT check_opt;
KEY_CACHE *key_cache;
@@ -1317,7 +1320,7 @@ bool Sql_cmd_analyze_table::execute(THD *thd)
"analyze", lock_type, 1, 0, 0, 0,
&handler::ha_analyze, 0);
/* ! we write after unlocking the table */
- if (!res && !m_lex->no_write_to_binlog)
+ if (!res && !m_lex->no_write_to_binlog && (!opt_readonly || thd->slave_thread))
{
/*
Presumably, ANALYZE and binlog writing doesn't require synchronization
@@ -1344,6 +1347,7 @@ bool Sql_cmd_check_table::execute(THD *thd)
if (check_table_access(thd, SELECT_ACL, first_table,
TRUE, UINT_MAX, FALSE))
goto error; /* purecov: inspected */
+
res= mysql_admin_table(thd, first_table, &m_lex->check_opt, "check",
lock_type, 0, 0, HA_OPEN_FOR_REPAIR, 0,
&handler::ha_check, &view_check);
@@ -1367,13 +1371,14 @@ bool Sql_cmd_optimize_table::execute(THD *thd)
FALSE, UINT_MAX, FALSE))
goto error; /* purecov: inspected */
WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table);
+
res= (specialflag & SPECIAL_NO_NEW_FUNC) ?
mysql_recreate_table(thd, first_table, true) :
mysql_admin_table(thd, first_table, &m_lex->check_opt,
"optimize", TL_WRITE, 1, 0, 0, 0,
&handler::ha_optimize, 0);
/* ! we write after unlocking the table */
- if (!res && !m_lex->no_write_to_binlog)
+ if (!res && !m_lex->no_write_to_binlog && (!opt_readonly || thd->slave_thread))
{
/*
Presumably, OPTIMIZE and binlog writing doesn't require synchronization
@@ -1407,7 +1412,7 @@ bool Sql_cmd_repair_table::execute(THD *thd)
&handler::ha_repair, &view_repair);
/* ! we write after unlocking the table */
- if (!res && !m_lex->no_write_to_binlog)
+ if (!res && !m_lex->no_write_to_binlog && (!opt_readonly || thd->slave_thread))
{
/*
Presumably, REPAIR and binlog writing doesn't require synchronization
diff --git a/sql/sql_admin.h b/sql/sql_admin.h
index e764ebb1472..d31726d32a4 100644
--- a/sql/sql_admin.h
+++ b/sql/sql_admin.h
@@ -20,7 +20,7 @@
#define SQL_ADMIN_MSG_TEXT_SIZE (128 * 1024)
bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* table_list,
- LEX_STRING *key_cache_name);
+ const LEX_CSTRING *key_cache_name);
bool mysql_preload_keys(THD* thd, TABLE_LIST* table_list);
int reassign_keycache_tables(THD* thd, KEY_CACHE *src_cache,
KEY_CACHE *dst_cache);
diff --git a/sql/sql_alloc.h b/sql/sql_alloc.h
new file mode 100644
index 00000000000..153b0401e29
--- /dev/null
+++ b/sql/sql_alloc.h
@@ -0,0 +1,53 @@
+#ifndef SQL_ALLOC_INCLUDED
+#define SQL_ALLOC_INCLUDED
+/* Copyright (c) 2000, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2017, 2018, 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-1301 USA */
+
+#include <my_sys.h> /* alloc_root, MEM_ROOT, TRASH */
+
+THD *thd_get_current_thd();
+
+/* mysql standard class memory allocator */
+
+class Sql_alloc
+{
+public:
+ static void *operator new(size_t size) throw ()
+ {
+ return thd_alloc(thd_get_current_thd(), size);
+ }
+ static void *operator new[](size_t size) throw ()
+ {
+ return thd_alloc(thd_get_current_thd(), size);
+ }
+ static void *operator new[](size_t size, MEM_ROOT *mem_root) throw ()
+ { return alloc_root(mem_root, size); }
+ static void *operator new(size_t size, MEM_ROOT *mem_root) throw()
+ { return alloc_root(mem_root, size); }
+ static void operator delete(void *ptr, size_t size) { TRASH_FREE(ptr, size); }
+ static void operator delete(void *, MEM_ROOT *){}
+ static void operator delete[](void *, MEM_ROOT *)
+ { /* never called */ }
+ static void operator delete[](void *ptr, size_t size) { TRASH_FREE(ptr, size); }
+#ifdef HAVE_valgrind
+ bool dummy_for_valgrind;
+ inline Sql_alloc() :dummy_for_valgrind(0) {}
+#else
+ inline Sql_alloc() {}
+#endif
+ inline ~Sql_alloc() {}
+};
+#endif /* SQL_ALLOC_INCLUDED */
diff --git a/sql/sql_alter.cc b/sql/sql_alter.cc
index 8716dde5727..a68dcb31a4c 100644
--- a/sql/sql_alter.cc
+++ b/sql/sql_alter.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
- Copyright (c) 2016, MariaDB Corporation
+ Copyright (c) 2016, 2018, 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
@@ -14,6 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
+#include "mariadb.h"
#include "sql_parse.h" // check_access
#include "sql_table.h" // mysql_alter_table,
// mysql_exchange_partition
@@ -26,7 +27,7 @@ Alter_info::Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root)
key_list(rhs.key_list, mem_root),
create_list(rhs.create_list, mem_root),
check_constraint_list(rhs.check_constraint_list, mem_root),
- flags(rhs.flags),
+ flags(rhs.flags), partition_flags(rhs.partition_flags),
keys_onoff(rhs.keys_onoff),
partition_names(rhs.partition_names, mem_root),
num_parts(rhs.num_parts),
@@ -50,61 +51,233 @@ Alter_info::Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root)
}
-bool Alter_info::set_requested_algorithm(const LEX_STRING *str)
+bool Alter_info::set_requested_algorithm(const LEX_CSTRING *str)
{
// To avoid adding new keywords to the grammar, we match strings here.
- if (!my_strcasecmp(system_charset_info, str->str, "INPLACE"))
+ if (lex_string_eq(str, STRING_WITH_LEN("INPLACE")))
requested_algorithm= ALTER_TABLE_ALGORITHM_INPLACE;
- else if (!my_strcasecmp(system_charset_info, str->str, "COPY"))
+ else if (lex_string_eq(str, STRING_WITH_LEN("COPY")))
requested_algorithm= ALTER_TABLE_ALGORITHM_COPY;
- else if (!my_strcasecmp(system_charset_info, str->str, "DEFAULT"))
+ else if (lex_string_eq(str, STRING_WITH_LEN("DEFAULT")))
requested_algorithm= ALTER_TABLE_ALGORITHM_DEFAULT;
+ else if (lex_string_eq(str, STRING_WITH_LEN("NOCOPY")))
+ requested_algorithm= ALTER_TABLE_ALGORITHM_NOCOPY;
+ else if (lex_string_eq(str, STRING_WITH_LEN("INSTANT")))
+ requested_algorithm= ALTER_TABLE_ALGORITHM_INSTANT;
else
return true;
return false;
}
+void Alter_info::set_requested_algorithm(enum_alter_table_algorithm algo_val)
+{
+ requested_algorithm= algo_val;
+}
-bool Alter_info::set_requested_lock(const LEX_STRING *str)
+bool Alter_info::set_requested_lock(const LEX_CSTRING *str)
{
// To avoid adding new keywords to the grammar, we match strings here.
- if (!my_strcasecmp(system_charset_info, str->str, "NONE"))
+ if (lex_string_eq(str, STRING_WITH_LEN("NONE")))
requested_lock= ALTER_TABLE_LOCK_NONE;
- else if (!my_strcasecmp(system_charset_info, str->str, "SHARED"))
+ else if (lex_string_eq(str, STRING_WITH_LEN("SHARED")))
requested_lock= ALTER_TABLE_LOCK_SHARED;
- else if (!my_strcasecmp(system_charset_info, str->str, "EXCLUSIVE"))
+ else if (lex_string_eq(str, STRING_WITH_LEN("EXCLUSIVE")))
requested_lock= ALTER_TABLE_LOCK_EXCLUSIVE;
- else if (!my_strcasecmp(system_charset_info, str->str, "DEFAULT"))
+ else if (lex_string_eq(str, STRING_WITH_LEN("DEFAULT")))
requested_lock= ALTER_TABLE_LOCK_DEFAULT;
else
return true;
return false;
}
+const char* Alter_info::algorithm_clause(THD *thd) const
+{
+ switch (algorithm(thd)) {
+ case ALTER_TABLE_ALGORITHM_INPLACE:
+ return "ALGORITHM=INPLACE";
+ case ALTER_TABLE_ALGORITHM_COPY:
+ return "ALGORITHM=COPY";
+ case ALTER_TABLE_ALGORITHM_NONE:
+ DBUG_ASSERT(0);
+ /* Fall through */
+ case ALTER_TABLE_ALGORITHM_DEFAULT:
+ return "ALGORITHM=DEFAULT";
+ case ALTER_TABLE_ALGORITHM_NOCOPY:
+ return "ALGORITHM=NOCOPY";
+ case ALTER_TABLE_ALGORITHM_INSTANT:
+ return "ALGORITHM=INSTANT";
+ }
+
+ return NULL; /* purecov: begin deadcode */
+}
+
+const char* Alter_info::lock() const
+{
+ switch (requested_lock) {
+ case ALTER_TABLE_LOCK_SHARED:
+ return "LOCK=SHARED";
+ case ALTER_TABLE_LOCK_NONE:
+ return "LOCK=NONE";
+ case ALTER_TABLE_LOCK_DEFAULT:
+ return "LOCK=DEFAULT";
+ case ALTER_TABLE_LOCK_EXCLUSIVE:
+ return "LOCK=EXCLUSIVE";
+ }
+ return NULL; /* purecov: begin deadcode */
+}
+
+
+bool Alter_info::supports_algorithm(THD *thd, enum_alter_inplace_result result,
+ const Alter_inplace_info *ha_alter_info)
+{
+ switch (result) {
+ case HA_ALTER_INPLACE_EXCLUSIVE_LOCK:
+ case HA_ALTER_INPLACE_SHARED_LOCK:
+ case HA_ALTER_INPLACE_NO_LOCK:
+ case HA_ALTER_INPLACE_INSTANT:
+ return false;
+ case HA_ALTER_INPLACE_COPY_NO_LOCK:
+ case HA_ALTER_INPLACE_COPY_LOCK:
+ if (algorithm(thd) >= Alter_info::ALTER_TABLE_ALGORITHM_NOCOPY)
+ {
+ ha_alter_info->report_unsupported_error(algorithm_clause(thd),
+ "ALGORITHM=INPLACE");
+ return true;
+ }
+ return false;
+ case HA_ALTER_INPLACE_NOCOPY_NO_LOCK:
+ case HA_ALTER_INPLACE_NOCOPY_LOCK:
+ if (algorithm(thd) == Alter_info::ALTER_TABLE_ALGORITHM_INSTANT)
+ {
+ ha_alter_info->report_unsupported_error("ALGORITHM=INSTANT",
+ "ALGORITHM=NOCOPY");
+ return true;
+ }
+ return false;
+ case HA_ALTER_INPLACE_NOT_SUPPORTED:
+ if (algorithm(thd) >= Alter_info::ALTER_TABLE_ALGORITHM_INPLACE)
+ {
+ ha_alter_info->report_unsupported_error(algorithm_clause(thd),
+ "ALGORITHM=COPY");
+ return true;
+ }
+ return false;
+ case HA_ALTER_ERROR:
+ return true;
+ }
+ /* purecov: begin deadcode */
+ DBUG_ASSERT(0);
+ return false;
+}
+
+
+bool Alter_info::supports_lock(THD *thd, enum_alter_inplace_result result,
+ const Alter_inplace_info *ha_alter_info)
+{
+ switch (result) {
+ case HA_ALTER_INPLACE_EXCLUSIVE_LOCK:
+ // If SHARED lock and no particular algorithm was requested, use COPY.
+ if (requested_lock == Alter_info::ALTER_TABLE_LOCK_SHARED &&
+ algorithm(thd) == Alter_info::ALTER_TABLE_ALGORITHM_DEFAULT &&
+ thd->variables.alter_algorithm ==
+ Alter_info::ALTER_TABLE_ALGORITHM_DEFAULT)
+ return false;
+
+ if (requested_lock == Alter_info::ALTER_TABLE_LOCK_SHARED ||
+ requested_lock == Alter_info::ALTER_TABLE_LOCK_NONE)
+ {
+ ha_alter_info->report_unsupported_error(lock(), "LOCK=EXCLUSIVE");
+ return true;
+ }
+ return false;
+ case HA_ALTER_INPLACE_NO_LOCK:
+ case HA_ALTER_INPLACE_INSTANT:
+ case HA_ALTER_INPLACE_COPY_NO_LOCK:
+ case HA_ALTER_INPLACE_NOCOPY_NO_LOCK:
+ return false;
+ case HA_ALTER_INPLACE_COPY_LOCK:
+ case HA_ALTER_INPLACE_NOCOPY_LOCK:
+ case HA_ALTER_INPLACE_NOT_SUPPORTED:
+ case HA_ALTER_INPLACE_SHARED_LOCK:
+ if (requested_lock == Alter_info::ALTER_TABLE_LOCK_NONE)
+ {
+ ha_alter_info->report_unsupported_error("LOCK=NONE", "LOCK=SHARED");
+ return true;
+ }
+ return false;
+ case HA_ALTER_ERROR:
+ return true;
+ }
+ /* purecov: begin deadcode */
+ DBUG_ASSERT(0);
+ return false;
+}
+
+bool Alter_info::vers_prohibited(THD *thd) const
+{
+ if (thd->slave_thread ||
+ thd->variables.vers_alter_history != VERS_ALTER_HISTORY_ERROR)
+ {
+ return false;
+ }
+ if (flags & (
+ ALTER_PARSER_ADD_COLUMN |
+ ALTER_PARSER_DROP_COLUMN |
+ ALTER_CHANGE_COLUMN |
+ ALTER_COLUMN_ORDER))
+ {
+ return true;
+ }
+ if (flags & ALTER_ADD_INDEX)
+ {
+ List_iterator_fast<Key> key_it(const_cast<List<Key> &>(key_list));
+ Key *key;
+ while ((key= key_it++))
+ {
+ if (key->type == Key::PRIMARY || key->type == Key::UNIQUE)
+ return true;
+ }
+ }
+ return false;
+}
+
+Alter_info::enum_alter_table_algorithm
+Alter_info::algorithm(const THD *thd) const
+{
+ if (requested_algorithm == ALTER_TABLE_ALGORITHM_NONE)
+ return (Alter_info::enum_alter_table_algorithm) thd->variables.alter_algorithm;
+ return requested_algorithm;
+}
+
Alter_table_ctx::Alter_table_ctx()
: datetime_field(NULL), error_if_not_empty(false),
tables_opened(0),
- db(NULL), table_name(NULL), alias(NULL),
- new_db(NULL), new_name(NULL), new_alias(NULL),
+ 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),
fk_error_if_delete_row(false), fk_error_id(NULL),
fk_error_table(NULL)
-#ifndef DBUG_OFF
+#ifdef DBUG_ASSERT_EXISTS
, tmp_table(false)
#endif
{
}
+/*
+ TODO: new_name_arg changes if lower case table names.
+ Should be copied or converted before call
+*/
Alter_table_ctx::Alter_table_ctx(THD *thd, TABLE_LIST *table_list,
uint tables_opened_arg,
- char *new_db_arg, char *new_name_arg)
+ const LEX_CSTRING *new_db_arg,
+ const LEX_CSTRING *new_name_arg)
: datetime_field(NULL), error_if_not_empty(false),
tables_opened(tables_opened_arg),
- new_db(new_db_arg), new_name(new_name_arg),
+ new_db(*new_db_arg), new_name(*new_name_arg),
fk_error_if_delete_row(false), fk_error_id(NULL),
fk_error_table(NULL)
-#ifndef DBUG_OFF
+#ifdef DBUG_ASSERT_EXISTS
, tmp_table(false)
#endif
{
@@ -117,28 +290,31 @@ Alter_table_ctx::Alter_table_ctx(THD *thd, TABLE_LIST *table_list,
table_name= table_list->table_name;
alias= (lower_case_table_names == 2) ? table_list->alias : table_name;
- if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db))
+ if (!new_db.str || !my_strcasecmp(table_alias_charset, new_db.str, db.str))
new_db= db;
- if (new_name)
+ if (new_name.str)
{
- DBUG_PRINT("info", ("new_db.new_name: '%s'.'%s'", new_db, new_name));
+ DBUG_PRINT("info", ("new_db.new_name: '%s'.'%s'", new_db.str, new_name.str));
- if (lower_case_table_names == 1) // Convert new_name/new_alias to lower case
+ if (lower_case_table_names == 1) // Convert new_name/new_alias to lower
{
- my_casedn_str(files_charset_info, new_name);
+ new_name.length= my_casedn_str(files_charset_info, (char*) new_name.str);
new_alias= new_name;
}
else if (lower_case_table_names == 2) // Convert new_name to lower case
{
- strmov(new_alias= new_alias_buff, new_name);
- my_casedn_str(files_charset_info, new_name);
+ new_alias.str= new_alias_buff;
+ new_alias.length= new_name.length;
+ strmov(new_alias_buff, new_name.str);
+ new_name.length= my_casedn_str(files_charset_info, (char*) new_name.str);
+
}
else
new_alias= new_name; // LCTN=0 => case sensitive + case preserving
if (!is_database_changed() &&
- !my_strcasecmp(table_alias_charset, new_name, table_name))
+ !my_strcasecmp(table_alias_charset, new_name.str, table_name.str))
{
/*
Source and destination table names are equal:
@@ -154,22 +330,23 @@ Alter_table_ctx::Alter_table_ctx(THD *thd, TABLE_LIST *table_list,
new_name= table_name;
}
- my_snprintf(tmp_name, sizeof(tmp_name), "%s-%lx_%lx", tmp_file_prefix,
- current_pid, thd->thread_id);
+ tmp_name.str= tmp_name_buff;
+ tmp_name.length= my_snprintf(tmp_name_buff, sizeof(tmp_name_buff), "%s-%lx_%llx",
+ tmp_file_prefix, current_pid, thd->thread_id);
/* Safety fix for InnoDB */
if (lower_case_table_names)
- my_casedn_str(files_charset_info, tmp_name);
+ tmp_name.length= my_casedn_str(files_charset_info, tmp_name_buff);
if (table_list->table->s->tmp_table == NO_TMP_TABLE)
{
- build_table_filename(path, sizeof(path) - 1, db, table_name, "", 0);
+ build_table_filename(path, sizeof(path) - 1, db.str, table_name.str, "", 0);
- build_table_filename(new_path, sizeof(new_path) - 1, new_db, new_name, "", 0);
+ build_table_filename(new_path, sizeof(new_path) - 1, new_db.str, new_name.str, "", 0);
build_table_filename(new_filename, sizeof(new_filename) - 1,
- new_db, new_name, reg_ext, 0);
+ new_db.str, new_name.str, reg_ext, 0);
- build_table_filename(tmp_path, sizeof(tmp_path) - 1, new_db, tmp_name, "",
+ build_table_filename(tmp_path, sizeof(tmp_path) - 1, new_db.str, tmp_name.str, "",
FN_IS_TMP);
}
else
@@ -180,7 +357,7 @@ Alter_table_ctx::Alter_table_ctx(THD *thd, TABLE_LIST *table_list,
this case. This fact is enforced with assert.
*/
build_tmptable_filename(thd, tmp_path, sizeof(tmp_path));
-#ifndef DBUG_OFF
+#ifdef DBUG_ASSERT_EXISTS
tmp_table= true;
#endif
}
@@ -221,25 +398,28 @@ bool Sql_cmd_alter_table::execute(THD *thd)
DBUG_ENTER("Sql_cmd_alter_table::execute");
- if (thd->is_fatal_error) /* out of memory creating a copy of alter_info */
+ if (unlikely(thd->is_fatal_error))
+ {
+ /* out of memory creating a copy of alter_info */
DBUG_RETURN(TRUE);
+ }
/*
We also require DROP priv for ALTER TABLE ... DROP PARTITION, as well
as for RENAME TO, as being done by SQLCOM_RENAME_TABLE
*/
- if (alter_info.flags & (Alter_info::ALTER_DROP_PARTITION |
- Alter_info::ALTER_RENAME))
+ if ((alter_info.partition_flags & ALTER_PARTITION_DROP) ||
+ (alter_info.flags & ALTER_RENAME))
priv_needed|= DROP_ACL;
/* Must be set in the parser */
- DBUG_ASSERT(select_lex->db);
- DBUG_ASSERT(!(alter_info.flags & Alter_info::ALTER_EXCHANGE_PARTITION));
- DBUG_ASSERT(!(alter_info.flags & Alter_info::ALTER_ADMIN_PARTITION));
- if (check_access(thd, priv_needed, first_table->db,
+ DBUG_ASSERT(select_lex->db.str);
+ DBUG_ASSERT(!(alter_info.partition_flags & ALTER_PARTITION_EXCHANGE));
+ DBUG_ASSERT(!(alter_info.partition_flags & ALTER_PARTITION_ADMIN));
+ if (check_access(thd, priv_needed, first_table->db.str,
&first_table->grant.privilege,
&first_table->grant.m_internal,
0, 0) ||
- check_access(thd, INSERT_ACL | CREATE_ACL, select_lex->db,
+ check_access(thd, INSERT_ACL | CREATE_ACL, select_lex->db.str,
&priv,
NULL, /* Don't use first_tab->grant with sel_lex->db */
0, 0))
@@ -295,9 +475,7 @@ bool Sql_cmd_alter_table::execute(THD *thd)
{
// Rename of table
TABLE_LIST tmp_table;
- tmp_table.init_one_table(select_lex->db, strlen(select_lex->db),
- lex->name.str, lex->name.length,
- lex->name.str, TL_IGNORE);
+ tmp_table.init_one_table(&select_lex->db, &lex->name, 0, TL_IGNORE);
tmp_table.grant.privilege= priv;
if (check_grant(thd, INSERT_ACL | CREATE_ACL, &tmp_table, FALSE,
UINT_MAX, FALSE))
@@ -315,22 +493,23 @@ bool Sql_cmd_alter_table::execute(THD *thd)
"INDEX DIRECTORY");
create_info.data_file_name= create_info.index_file_name= NULL;
-#ifdef WITH_WSREP
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ thd->work_part_info= 0;
+#endif
+
if (WSREP(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 : NULL),
- ((lex->name.str) ? lex->name.str : NULL),
- first_table,
- &alter_info);
+ WSREP_TO_ISOLATION_BEGIN_ALTER((lex->name.str ? select_lex->db.str : NULL),
+ (lex->name.str ? lex->name.str : NULL),
+ first_table, &alter_info);
thd->variables.auto_increment_offset = 1;
thd->variables.auto_increment_increment = 1;
}
-#endif /* WITH_WSREP */
- result= mysql_alter_table(thd, select_lex->db, lex->name.str,
+ result= mysql_alter_table(thd, &select_lex->db, &lex->name,
&create_info,
first_table,
&alter_info,
@@ -352,7 +531,7 @@ bool Sql_cmd_discard_import_tablespace::execute(THD *thd)
/* first table of first SELECT_LEX */
TABLE_LIST *table_list= (TABLE_LIST*) select_lex->table_list.first;
- if (check_access(thd, ALTER_ACL, table_list->db,
+ if (check_access(thd, ALTER_ACL, table_list->db.str,
&table_list->grant.privilege,
&table_list->grant.m_internal,
0, 0))
diff --git a/sql/sql_alter.h b/sql/sql_alter.h
index c6558c3bc8c..53d0c8438f8 100644
--- a/sql/sql_alter.h
+++ b/sql/sql_alter.h
@@ -1,5 +1,5 @@
/* Copyright (c) 2010, 2014, Oracle and/or its affiliates.
- Copyright (c) 2013, 2014, Monty Program Ab.
+ Copyright (c) 2013, 2018, 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
@@ -29,118 +29,38 @@ class Key;
class Alter_info
{
public:
- /*
- These flags are set by the parser and describes the type of
- operation(s) specified by the ALTER TABLE statement.
-
- They do *not* describe the type operation(s) to be executed
- by the storage engine. For example, we don't yet know the
- type of index to be added/dropped.
- */
-
- // Set for ADD [COLUMN]
- static const uint ALTER_ADD_COLUMN = 1L << 0;
-
- // Set for DROP [COLUMN]
- static const uint ALTER_DROP_COLUMN = 1L << 1;
-
- // Set for CHANGE [COLUMN] | MODIFY [CHANGE]
- // Set by mysql_recreate_table()
- static const uint ALTER_CHANGE_COLUMN = 1L << 2;
-
- // Set for ADD INDEX | ADD KEY | ADD PRIMARY KEY | ADD UNIQUE KEY |
- // ADD UNIQUE INDEX | ALTER ADD [COLUMN]
- static const uint ALTER_ADD_INDEX = 1L << 3;
-
- // Set for DROP PRIMARY KEY | DROP FOREIGN KEY | DROP KEY | DROP INDEX
- static const uint ALTER_DROP_INDEX = 1L << 4;
-
- // Set for RENAME [TO]
- static const uint ALTER_RENAME = 1L << 5;
-
- // Set for ORDER BY
- static const uint ALTER_ORDER = 1L << 6;
-
- // Set for table_options
- static const uint ALTER_OPTIONS = 1L << 7;
-
- // Set for ALTER [COLUMN] ... SET DEFAULT ... | DROP DEFAULT
- static const uint ALTER_CHANGE_COLUMN_DEFAULT = 1L << 8;
-
- // Set for DISABLE KEYS | ENABLE KEYS
- static const uint ALTER_KEYS_ONOFF = 1L << 9;
-
- // Set for FORCE
- // Set for ENGINE(same engine)
- // Set by mysql_recreate_table()
- static const uint ALTER_RECREATE = 1L << 10;
-
- // Set for ADD PARTITION
- static const uint ALTER_ADD_PARTITION = 1L << 11;
-
- // Set for DROP PARTITION
- static const uint ALTER_DROP_PARTITION = 1L << 12;
-
- // Set for COALESCE PARTITION
- static const uint ALTER_COALESCE_PARTITION = 1L << 13;
-
- // Set for REORGANIZE PARTITION ... INTO
- static const uint ALTER_REORGANIZE_PARTITION = 1L << 14;
-
- // Set for partition_options
- static const uint ALTER_PARTITION = 1L << 15;
-
- // Set for LOAD INDEX INTO CACHE ... PARTITION
- // Set for CACHE INDEX ... PARTITION
- static const uint ALTER_ADMIN_PARTITION = 1L << 16;
-
- // Set for REORGANIZE PARTITION
- static const uint ALTER_TABLE_REORG = 1L << 17;
-
- // Set for REBUILD PARTITION
- static const uint ALTER_REBUILD_PARTITION = 1L << 18;
-
- // Set for partitioning operations specifying ALL keyword
- static const uint ALTER_ALL_PARTITION = 1L << 19;
-
- // Set for REMOVE PARTITIONING
- static const uint ALTER_REMOVE_PARTITIONING = 1L << 20;
-
- // Set for ADD FOREIGN KEY
- static const uint ADD_FOREIGN_KEY = 1L << 21;
-
- // Set for DROP FOREIGN KEY
- static const uint DROP_FOREIGN_KEY = 1L << 22;
-
- // Set for EXCHANGE PARITION
- static const uint ALTER_EXCHANGE_PARTITION = 1L << 23;
-
- // Set by Sql_cmd_alter_table_truncate_partition::execute()
- static const uint ALTER_TRUNCATE_PARTITION = 1L << 24;
-
- // Set for ADD [COLUMN] FIRST | AFTER
- static const uint ALTER_COLUMN_ORDER = 1L << 25;
-
- static const uint ALTER_ADD_CHECK_CONSTRAINT = 1L << 27;
- static const uint ALTER_DROP_CHECK_CONSTRAINT = 1L << 28;
- static const uint ALTER_RENAME_COLUMN = 1L << 29;
enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE };
+ bool vers_prohibited(THD *thd) const;
+
/**
The different values of the ALGORITHM clause.
Describes which algorithm to use when altering the table.
*/
enum enum_alter_table_algorithm
{
- // In-place if supported, copy otherwise.
+/*
+ Use thd->variables.alter_algorithm for alter method. If this is also
+ default then use the fastest possible ALTER TABLE method
+ (INSTANT, NOCOPY, INPLACE, COPY)
+*/
ALTER_TABLE_ALGORITHM_DEFAULT,
+ // Copy if supported, error otherwise.
+ ALTER_TABLE_ALGORITHM_COPY,
+
// In-place if supported, error otherwise.
ALTER_TABLE_ALGORITHM_INPLACE,
- // Copy if supported, error otherwise.
- ALTER_TABLE_ALGORITHM_COPY
+ // No Copy will refuse any operation which does rebuild.
+ ALTER_TABLE_ALGORITHM_NOCOPY,
+
+ // Instant should allow any operation that changes metadata only.
+ ALTER_TABLE_ALGORITHM_INSTANT,
+
+ // When there is no specification of algorithm during alter table.
+ ALTER_TABLE_ALGORITHM_NONE
};
@@ -153,7 +73,7 @@ public:
// Maximum supported level of concurency for the given operation.
ALTER_TABLE_LOCK_DEFAULT,
- // Allow concurrent reads & writes. If not supported, give erorr.
+ // Allow concurrent reads & writes. If not supported, give error.
ALTER_TABLE_LOCK_NONE,
// Allow concurrent reads only. If not supported, give error.
@@ -173,27 +93,34 @@ public:
// List of columns, used by both CREATE and ALTER TABLE.
List<Create_field> create_list;
- static const uint CHECK_CONSTRAINT_IF_NOT_EXISTS= 1;
+ enum flags_bits
+ {
+ CHECK_CONSTRAINT_IF_NOT_EXISTS= 1
+ };
List<Virtual_column_info> check_constraint_list;
// Type of ALTER TABLE operation.
- uint flags;
+ alter_table_operations flags;
+ ulong partition_flags;
// Enable or disable keys.
enum_enable_or_disable keys_onoff;
// List of partitions.
- List<char> partition_names;
+ List<const char> partition_names;
// Number of partitions.
uint num_parts;
+private:
// Type of ALTER TABLE algorithm.
enum_alter_table_algorithm requested_algorithm;
+
+public:
// Type of ALTER TABLE lock.
enum_alter_table_lock requested_lock;
Alter_info() :
- flags(0),
+ flags(0), partition_flags(0),
keys_onoff(LEAVE_AS_IS),
num_parts(0),
- requested_algorithm(ALTER_TABLE_ALGORITHM_DEFAULT),
+ requested_algorithm(ALTER_TABLE_ALGORITHM_NONE),
requested_lock(ALTER_TABLE_LOCK_DEFAULT)
{}
@@ -205,10 +132,11 @@ public:
create_list.empty();
check_constraint_list.empty();
flags= 0;
+ partition_flags= 0;
keys_onoff= LEAVE_AS_IS;
num_parts= 0;
partition_names.empty();
- requested_algorithm= ALTER_TABLE_ALGORITHM_DEFAULT;
+ requested_algorithm= ALTER_TABLE_ALGORITHM_NONE;
requested_lock= ALTER_TABLE_LOCK_DEFAULT;
}
@@ -240,7 +168,7 @@ public:
@retval false Supported value found, state updated
@retval true Not supported value, no changes made
*/
- bool set_requested_algorithm(const LEX_STRING *str);
+ bool set_requested_algorithm(const LEX_CSTRING *str);
/**
@@ -253,7 +181,58 @@ public:
@retval true Not supported value, no changes made
*/
- bool set_requested_lock(const LEX_STRING *str);
+ bool set_requested_lock(const LEX_CSTRING *str);
+
+ /**
+ Set the requested algorithm to the given algorithm value
+ @param algo_value algorithm to be set
+ */
+ void set_requested_algorithm(enum_alter_table_algorithm algo_value);
+
+ /**
+ Returns the algorithm value in the format "algorithm=value"
+ */
+ const char* algorithm_clause(THD *thd) const;
+
+ /**
+ Returns the lock value in the format "lock=value"
+ */
+ const char* lock() const;
+
+ /**
+ Check whether the given result can be supported
+ with the specified user alter algorithm.
+
+ @param thd Thread handle
+ @param result Operation supported for inplace alter
+ @param ha_alter_info Structure describing changes to be done
+ by ALTER TABLE and holding data during
+ in-place alter
+ @retval false Supported operation
+ @retval true Not supported value
+ */
+ bool supports_algorithm(THD *thd, enum_alter_inplace_result result,
+ const Alter_inplace_info *ha_alter_info);
+
+ /**
+ Check whether the given result can be supported
+ with the specified user lock type.
+
+ @param result Operation supported for inplace alter
+ @param ha_alter_info Structure describing changes to be done
+ by ALTER TABLE and holding data during
+ in-place alter
+ @retval false Supported lock type
+ @retval true Not supported value
+ */
+ bool supports_lock(THD *thd, enum_alter_inplace_result result,
+ const Alter_inplace_info *ha_alter_info);
+
+ /**
+ Return user requested algorithm. If user does not specify
+ algorithm then return alter_algorithm variable value.
+ */
+ enum_alter_table_algorithm algorithm(const THD *thd) const;
private:
Alter_info &operator=(const Alter_info &rhs); // not implemented
@@ -268,19 +247,19 @@ public:
Alter_table_ctx();
Alter_table_ctx(THD *thd, TABLE_LIST *table_list, uint tables_opened_arg,
- char *new_db_arg, char *new_name_arg);
+ const LEX_CSTRING *new_db_arg, const LEX_CSTRING *new_name_arg);
/**
@return true if the table is moved to another database, false otherwise.
*/
bool is_database_changed() const
- { return (new_db != db); };
+ { return (new_db.str != db.str); };
/**
@return true if the table is renamed, false otherwise.
*/
bool is_table_renamed() const
- { return (is_database_changed() || new_name != table_name); };
+ { return (is_database_changed() || new_name.str != table_name.str); };
/**
@return filename (including .frm) for the new table.
@@ -330,13 +309,14 @@ public:
Create_field *datetime_field;
bool error_if_not_empty;
uint tables_opened;
- char *db;
- char *table_name;
- char *alias;
- char *new_db;
- char *new_name;
- char *new_alias;
- char tmp_name[80];
+ LEX_CSTRING db;
+ LEX_CSTRING table_name;
+ LEX_CSTRING alias;
+ LEX_CSTRING new_db;
+ LEX_CSTRING new_name;
+ LEX_CSTRING new_alias;
+ LEX_CSTRING tmp_name;
+ char tmp_buff[80];
/**
Indicates that if a row is deleted during copying of data from old version
of table to the new version ER_FK_CANNOT_DELETE_PARENT error should be
@@ -350,12 +330,13 @@ public:
private:
char new_filename[FN_REFLEN + 1];
- char new_alias_buff[FN_REFLEN + 1];
+ char new_alias_buff[NAME_LEN + 1];
+ char tmp_name_buff[NAME_LEN + 1];
char path[FN_REFLEN + 1];
char new_path[FN_REFLEN + 1];
char tmp_path[FN_REFLEN + 1];
-#ifndef DBUG_OFF
+#ifdef DBUG_ASSERT_EXISTS
/** Indicates that we are altering temporary table. Used only in asserts. */
bool tmp_table;
#endif
@@ -412,6 +393,31 @@ public:
/**
+ Sql_cmd_alter_sequence represents the ALTER SEQUENCE statement.
+*/
+class Sql_cmd_alter_sequence : public Sql_cmd,
+ public DDL_options
+{
+public:
+ /**
+ Constructor, used to represent a ALTER TABLE statement.
+ */
+ Sql_cmd_alter_sequence(const DDL_options &options)
+ :DDL_options(options)
+ {}
+
+ ~Sql_cmd_alter_sequence()
+ {}
+
+ enum_sql_command sql_command_code() const
+ {
+ return SQLCOM_ALTER_SEQUENCE;
+ }
+ bool execute(THD *thd);
+};
+
+
+/**
Sql_cmd_alter_table_tablespace represents ALTER TABLE
IMPORT/DISCARD TABLESPACE statements.
*/
diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc
index a00b4448d32..65049348869 100644
--- a/sql/sql_analyse.cc
+++ b/sql/sql_analyse.cc
@@ -29,7 +29,7 @@
#define MYSQL_LEX 1
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "procedure.h"
#include "sql_analyse.h"
@@ -72,7 +72,7 @@ Procedure *
proc_analyse_init(THD *thd, ORDER *param, select_result *result,
List<Item> &field_list)
{
- char *proc_name = (*param->item)->name;
+ const char *proc_name = (*param->item)->name.str;
analyse *pc = new analyse(result);
field_info **f_info;
DBUG_ENTER("proc_analyse_init");
@@ -88,7 +88,7 @@ proc_analyse_init(THD *thd, ORDER *param, select_result *result,
else if (param->next)
{
// first parameter
- if (!(*param->item)->fixed && (*param->item)->fix_fields(thd, param->item))
+ if ((*param->item)->fix_fields_if_needed(thd, param->item))
{
DBUG_PRINT("info", ("fix_fields() for the first parameter failed"));
goto err;
@@ -107,7 +107,7 @@ proc_analyse_init(THD *thd, ORDER *param, select_result *result,
goto err;
}
// second parameter
- if (!(*param->item)->fixed && (*param->item)->fix_fields(thd, param->item))
+ if ((*param->item)->fix_fields_if_needed(thd, param->item))
{
DBUG_PRINT("info", ("fix_fields() for the second parameter failed"));
goto err;
@@ -298,9 +298,10 @@ bool get_ev_num_info(EV_NUM_INFO *ev_info, NUM_INFO *info, const char *num)
} // get_ev_num_info
-void free_string(void* str, TREE_FREE, void*)
+int free_string(void* str, TREE_FREE, void*)
{
((String*)str)->free();
+ return 0;
}
@@ -374,7 +375,7 @@ void field_str::add()
if (!tree_insert(&tree, (void*) &s, 0, tree.custom_arg))
{
room_in_tree = 0; // Remove tree, out of RAM ?
- delete_tree(&tree);
+ delete_tree(&tree, 0);
}
else
{
@@ -382,7 +383,7 @@ void field_str::add()
if ((treemem += length) > pc->max_treemem)
{
room_in_tree = 0; // Remove tree, too big tree
- delete_tree(&tree);
+ delete_tree(&tree, 0);
}
}
}
@@ -441,7 +442,7 @@ void field_real::add()
if (!(element = tree_insert(&tree, (void*) &num, 0, tree.custom_arg)))
{
room_in_tree = 0; // Remove tree, out of RAM ?
- delete_tree(&tree);
+ delete_tree(&tree, 0);
}
/*
if element->count == 1, this element can be found only once from tree
@@ -450,7 +451,7 @@ void field_real::add()
else if (element->count == 1 && (tree_elements++) >= pc->max_tree_elements)
{
room_in_tree = 0; // Remove tree, too many elements
- delete_tree(&tree);
+ delete_tree(&tree, 0);
}
}
@@ -507,7 +508,7 @@ void field_decimal::add()
if (!(element = tree_insert(&tree, (void*)buf, 0, tree.custom_arg)))
{
room_in_tree = 0; // Remove tree, out of RAM ?
- delete_tree(&tree);
+ delete_tree(&tree, 0);
}
/*
if element->count == 1, this element can be found only once from tree
@@ -516,7 +517,7 @@ void field_decimal::add()
else if (element->count == 1 && (tree_elements++) >= pc->max_tree_elements)
{
room_in_tree = 0; // Remove tree, too many elements
- delete_tree(&tree);
+ delete_tree(&tree, 0);
}
}
@@ -574,7 +575,7 @@ void field_longlong::add()
if (!(element = tree_insert(&tree, (void*) &num, 0, tree.custom_arg)))
{
room_in_tree = 0; // Remove tree, out of RAM ?
- delete_tree(&tree);
+ delete_tree(&tree, 0);
}
/*
if element->count == 1, this element can be found only once from tree
@@ -583,7 +584,7 @@ void field_longlong::add()
else if (element->count == 1 && (tree_elements++) >= pc->max_tree_elements)
{
room_in_tree = 0; // Remove tree, too many elements
- delete_tree(&tree);
+ delete_tree(&tree, 0);
}
}
@@ -630,7 +631,7 @@ void field_ulonglong::add()
if (!(element = tree_insert(&tree, (void*) &num, 0, tree.custom_arg)))
{
room_in_tree = 0; // Remove tree, out of RAM ?
- delete_tree(&tree);
+ delete_tree(&tree, 0);
}
/*
if element->count == 1, this element can be found only once from tree
@@ -639,7 +640,7 @@ void field_ulonglong::add()
else if (element->count == 1 && (tree_elements++) >= pc->max_tree_elements)
{
room_in_tree = 0; // Remove tree, too many elements
- delete_tree(&tree);
+ delete_tree(&tree, 0);
}
}
@@ -1241,4 +1242,3 @@ uint check_ulonglong(const char *str, uint length)
while (*cmp && *cmp++ == *str++) ;
return ((uchar) str[-1] <= (uchar) cmp[-1]) ? smaller : bigger;
} /* check_ulonlong */
-
diff --git a/sql/sql_analyse.h b/sql/sql_analyse.h
index 440e49d509c..9cdb93f4d6f 100644
--- a/sql/sql_analyse.h
+++ b/sql/sql_analyse.h
@@ -68,7 +68,7 @@ int compare_ulonglong2(void* cmp_arg __attribute__((unused)),
int compare_decimal2(int* len, const char *s, const char *t);
Procedure *proc_analyse_init(THD *thd, ORDER *param, select_result *result,
List<Item> &field_list);
-void free_string(void* str, TREE_FREE, void*);
+int free_string(void* str, TREE_FREE, void*);
class analyse;
class field_info :public Sql_alloc
@@ -86,7 +86,7 @@ public:
nulls(0), min_length(0), max_length(0), room_in_tree(1),
found(0),item(a), pc(b) {};
- virtual ~field_info() { delete_tree(&tree); }
+ virtual ~field_info() { delete_tree(&tree, 0); }
virtual void add() = 0;
virtual void get_opt_type(String*, ha_rows) = 0;
virtual String *get_min_arg(String *) = 0;
diff --git a/sql/sql_analyze_stmt.cc b/sql/sql_analyze_stmt.cc
index 6c146c1ec04..f1c6e2c73ea 100644
--- a/sql/sql_analyze_stmt.cc
+++ b/sql/sql_analyze_stmt.cc
@@ -18,7 +18,7 @@
#pragma implementation // gcc: Class implementation
#endif
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "sql_select.h"
#include "my_json_writer.h"
diff --git a/sql/sql_array.h b/sql/sql_array.h
index ed8686a2d0b..56dba784d7d 100644
--- a/sql/sql_array.h
+++ b/sql/sql_array.h
@@ -190,9 +190,10 @@ public:
return *((Elem*)pop_dynamic(&array));
}
- void del(uint idx)
+ void del(size_t idx)
{
- delete_dynamic_element(&array, idx);
+ DBUG_ASSERT(idx <= array.max_element);
+ delete_dynamic_element(&array, (uint)idx);
}
size_t elements() const
@@ -203,7 +204,7 @@ public:
void elements(size_t num_elements)
{
DBUG_ASSERT(num_elements <= array.max_element);
- array.elements= num_elements;
+ array.elements= (uint)num_elements;
}
void clear()
@@ -224,12 +225,12 @@ public:
bool resize(size_t new_size, Elem default_val)
{
size_t old_size= elements();
- if (allocate_dynamic(&array, new_size))
+ if (unlikely(allocate_dynamic(&array, (uint)new_size)))
return true;
if (new_size > old_size)
{
- set_dynamic(&array, (uchar*)&default_val, new_size - 1);
+ set_dynamic(&array, (uchar*)&default_val, (uint)(new_size - 1));
/*for (size_t i= old_size; i != new_size; i++)
{
at(i)= default_val;
@@ -262,4 +263,6 @@ public:
}
};
+typedef Bounds_checked_array<Item*> Ref_ptr_array;
+
#endif /* SQL_ARRAY_INCLUDED */
diff --git a/sql/sql_audit.cc b/sql/sql_audit.cc
index aed0457e1ff..ed175ae4865 100644
--- a/sql/sql_audit.cc
+++ b/sql/sql_audit.cc
@@ -13,8 +13,9 @@
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>
+#include "mariadb.h"
#include "sql_priv.h"
+#include "mysqld.h"
#include "sql_audit.h"
extern int initialize_audit_plugin(st_plugin_int *plugin);
diff --git a/sql/sql_audit.h b/sql/sql_audit.h
index 03f2f3aca33..97317203e34 100644
--- a/sql/sql_audit.h
+++ b/sql/sql_audit.h
@@ -18,8 +18,6 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
-
#include <mysql/plugin_audit.h>
#include "sql_class.h"
@@ -124,15 +122,13 @@ void mysql_audit_general_log(THD *thd, time_t time,
event.general_thread_id= (unsigned long)thd->thread_id;
event.general_charset= thd->variables.character_set_client;
event.database= thd->db;
- event.database_length= (unsigned int)thd->db_length;
event.query_id= thd->query_id;
}
else
{
event.general_thread_id= 0;
event.general_charset= global_system_variables.character_set_client;
- event.database= "";
- event.database_length= 0;
+ event.database= null_clex_str;
event.query_id= 0;
}
@@ -156,6 +152,7 @@ static inline
void mysql_audit_general(THD *thd, uint event_subtype,
int error_code, const char *msg)
{
+ DBUG_ENTER("mysql_audit_general");
if (mysql_audit_general_enabled())
{
char user_buff[MAX_USER_HOST_SIZE];
@@ -177,7 +174,6 @@ void mysql_audit_general(THD *thd, uint event_subtype,
event.general_charset= thd->query_string.charset();
event.general_rows= thd->get_stmt_da()->current_row_for_warning();
event.database= thd->db;
- event.database_length= (uint)thd->db_length;
event.query_id= thd->query_id;
}
else
@@ -189,13 +185,13 @@ void mysql_audit_general(THD *thd, uint event_subtype,
event.general_query_length= 0;
event.general_charset= &my_charset_bin;
event.general_rows= 0;
- event.database= "";
- event.database_length= 0;
+ event.database= null_clex_str;
event.query_id= 0;
}
mysql_audit_notify(thd, MYSQL_AUDIT_GENERAL_CLASS, &event);
}
+ DBUG_VOID_RETURN;
}
static inline
@@ -223,7 +219,6 @@ void mysql_audit_notify_connection_connect(THD *thd)
event.ip= sctx->ip;
event.ip_length= safe_strlen_uint(sctx->ip);
event.database= thd->db;
- event.database_length= safe_strlen_uint(thd->db);
mysql_audit_notify(thd, MYSQL_AUDIT_CONNECTION_CLASS, &event);
}
@@ -253,7 +248,6 @@ void mysql_audit_notify_connection_disconnect(THD *thd, int errcode)
event.ip= sctx->ip;
event.ip_length= safe_strlen_uint(sctx->ip) ;
event.database= thd->db;
- event.database_length= safe_strlen_uint(thd->db);
mysql_audit_notify(thd, MYSQL_AUDIT_CONNECTION_CLASS, &event);
}
@@ -284,7 +278,6 @@ void mysql_audit_notify_connection_change_user(THD *thd)
event.ip= sctx->ip;
event.ip_length= safe_strlen_uint(sctx->ip);
event.database= thd->db;
- event.database_length= safe_strlen_uint(thd->db);
mysql_audit_notify(thd, MYSQL_AUDIT_CONNECTION_CLASS, &event);
}
@@ -310,14 +303,10 @@ void mysql_audit_external_lock_ex(THD *thd, my_thread_id thread_id,
event.proxy_user= sctx->proxy_user;
event.host= host;
event.ip= ip;
- event.database= share->db.str;
- event.database_length= (unsigned int)share->db.length;
- event.table= share->table_name.str;
- event.table_length= (unsigned int)share->table_name.length;
- event.new_database= 0;
- event.new_database_length= 0;
- event.new_table= 0;
- event.new_table_length= 0;
+ event.database= share->db;
+ event.table= share->table_name;
+ event.new_database= null_clex_str;
+ event.new_table= null_clex_str;
event.query_id= query_id;
mysql_audit_notify(thd, MYSQL_AUDIT_TABLE_CLASS, &event);
@@ -352,15 +341,11 @@ void mysql_audit_create_table(TABLE *table)
event.proxy_user= sctx->proxy_user;
event.host= sctx->host;
event.ip= sctx->ip;
- event.database= share->db.str;
- event.database_length= (unsigned int)share->db.length;
- event.table= share->table_name.str;
- event.table_length= (unsigned int)share->table_name.length;
- event.new_database= 0;
- event.new_database_length= 0;
- event.new_table= 0;
- event.new_table_length= 0;
- event.query_id= thd->query_id;
+ event.database= share->db;
+ event.table= share->table_name;
+ event.new_database= null_clex_str;
+ event.new_table= null_clex_str;
+ event.query_id= thd->query_id;
mysql_audit_notify(thd, MYSQL_AUDIT_TABLE_CLASS, &event);
}
@@ -384,23 +369,20 @@ void mysql_audit_drop_table(THD *thd, TABLE_LIST *table)
event.proxy_user= sctx->proxy_user;
event.host= sctx->host;
event.ip= sctx->ip;
- event.database= table->db;
- event.database_length= (unsigned int)table->db_length;
- event.table= table->table_name;
- event.table_length= (unsigned int)table->table_name_length;
- event.new_database= 0;
- event.new_database_length= 0;
- event.new_table= 0;
- event.new_table_length= 0;
- event.query_id= thd->query_id;
+ event.database= table->db;
+ event.table= table->table_name;
+ event.new_database= null_clex_str;
+ event.new_table= null_clex_str;
+ event.query_id= thd->query_id;
mysql_audit_notify(thd, MYSQL_AUDIT_TABLE_CLASS, &event);
}
}
static inline
-void mysql_audit_rename_table(THD *thd, const char *old_db, const char *old_tb,
- const char *new_db, const char *new_tb)
+void mysql_audit_rename_table(THD *thd, const LEX_CSTRING *old_db,
+ const LEX_CSTRING *old_tb,
+ const LEX_CSTRING *new_db, const LEX_CSTRING *new_tb)
{
if (mysql_audit_table_enabled())
{
@@ -417,14 +399,10 @@ void mysql_audit_rename_table(THD *thd, const char *old_db, const char *old_tb,
event.proxy_user= sctx->proxy_user;
event.host= sctx->host;
event.ip= sctx->ip;
- event.database= old_db;
- event.database_length= strlen_uint(old_db);
- event.table= old_tb;
- event.table_length= strlen_uint(old_tb);
- event.new_database= new_db;
- event.new_database_length= strlen_uint(new_db);
- event.new_table= new_tb;
- event.new_table_length= strlen_uint(new_tb);
+ event.database= *old_db;
+ event.table= *old_tb;
+ event.new_database= *new_db;
+ event.new_table= *new_tb;
event.query_id= thd->query_id;
mysql_audit_notify(thd, MYSQL_AUDIT_TABLE_CLASS, &event);
@@ -450,13 +428,9 @@ void mysql_audit_alter_table(THD *thd, TABLE_LIST *table)
event.host= sctx->host;
event.ip= sctx->ip;
event.database= table->db;
- event.database_length= (unsigned int)table->db_length;
event.table= table->table_name;
- event.table_length= (unsigned int)table->table_name_length;
- event.new_database= 0;
- event.new_database_length= 0;
- event.new_table= 0;
- event.new_table_length= 0;
+ event.new_database= null_clex_str;
+ event.new_table= null_clex_str;
event.query_id= thd->query_id;
mysql_audit_notify(thd, MYSQL_AUDIT_TABLE_CLASS, &event);
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index cc77b58cb3e..73ed7a855a2 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -17,7 +17,7 @@
/* Basic functions needed by many modules */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_base.h" // setup_table_map
#include "sql_priv.h"
#include "unireg.h"
@@ -199,8 +199,8 @@ uint get_table_def_key(const TABLE_LIST *table_list, const char **key)
from key used by MDL subsystem.
*/
DBUG_ASSERT(!strcmp(table_list->get_db_name(),
- table_list->mdl_request.key.db_name()) &&
- !strcmp(table_list->get_table_name(),
+ table_list->mdl_request.key.db_name()));
+ DBUG_ASSERT(!strcmp(table_list->get_table_name(),
table_list->mdl_request.key.name()));
*key= (const char*)table_list->mdl_request.key.ptr() + 1;
@@ -244,8 +244,9 @@ struct list_open_tables_arg
static my_bool list_open_tables_callback(TDC_element *element,
list_open_tables_arg *arg)
{
- char *db= (char*) element->m_key;
- char *table_name= (char*) element->m_key + strlen((char*) element->m_key) + 1;
+ const char *db= (char*) element->m_key;
+ size_t db_length= strlen(db);
+ const char *table_name= db + db_length + 1;
if (arg->db && my_strcasecmp(system_charset_info, arg->db, db))
return FALSE;
@@ -253,8 +254,10 @@ static my_bool list_open_tables_callback(TDC_element *element,
return FALSE;
/* Check if user has SELECT privilege for any column in the table */
- arg->table_list.db= db;
- arg->table_list.table_name= table_name;
+ arg->table_list.db.str= db;
+ 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;
if (check_table_access(arg->thd, SELECT_ACL, &arg->table_list, TRUE, 1, TRUE))
@@ -381,8 +384,8 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables,
for (TABLE_LIST *table= tables; table; table= table->next_local)
{
/* tdc_remove_table() also sets TABLE_SHARE::version to 0. */
- found|= tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED, table->db,
- table->table_name, TRUE);
+ found|= tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED, table->db.str,
+ table->table_name.str, TRUE);
}
if (!found)
wait_for_refresh=0; // Nothing to wait for
@@ -413,8 +416,8 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables,
{
int err;
/* A check that the table was locked for write is done by the caller. */
- TABLE *table= find_table_for_mdl_upgrade(thd, table_list->db,
- table_list->table_name, &err);
+ TABLE *table= find_table_for_mdl_upgrade(thd, table_list->db.str,
+ table_list->table_name.str, &err);
/* May return NULL if this table has already been closed via an alias. */
if (! table)
@@ -464,7 +467,7 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables,
{
if (thd->killed)
break;
- if (tdc_wait_for_old_version(thd, table->db, table->table_name, timeout,
+ if (tdc_wait_for_old_version(thd, table->db.str, table->table_name.str, timeout,
MDL_wait_for_subgraph::DEADLOCK_WEIGHT_DDL,
refresh_version))
{
@@ -504,7 +507,7 @@ err_with_reopen:
struct close_cached_connection_tables_arg
{
THD *thd;
- LEX_STRING *connection;
+ LEX_CSTRING *connection;
TABLE_LIST *tables;
};
@@ -532,9 +535,9 @@ static my_bool close_cached_connection_tables_callback(
/* close_cached_tables() only uses these elements */
if (!(tmp= (TABLE_LIST*) alloc_root(arg->thd->mem_root, sizeof(TABLE_LIST))) ||
- !(tmp->db= strdup_root(arg->thd->mem_root, element->share->db.str)) ||
- !(tmp->table_name= strdup_root(arg->thd->mem_root,
- element->share->table_name.str)))
+ !(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;
@@ -549,7 +552,7 @@ end:
}
-bool close_cached_connection_tables(THD *thd, LEX_STRING *connection)
+bool close_cached_connection_tables(THD *thd, LEX_CSTRING *connection)
{
close_cached_connection_tables_arg argument;
DBUG_ENTER("close_cached_connections");
@@ -651,7 +654,7 @@ close_all_tables_for_name(THD *thd, TABLE_SHARE *share,
DBUG_ASSERT(!share->tmp_table);
char key[MAX_DBKEY_LENGTH];
- uint key_length= share->table_cache_key.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;
@@ -889,6 +892,12 @@ void close_thread_table(THD *thd, TABLE **table_ptr)
table->file->update_global_index_stats();
}
+ /*
+ This look is needed to allow THD::notify_shared_lock() to
+ traverse the thd->open_tables list without having to worry that
+ some of the tables are removed from under it
+ */
+
mysql_mutex_lock(&thd->LOCK_thd_data);
*table_ptr=table->next;
mysql_mutex_unlock(&thd->LOCK_thd_data);
@@ -924,8 +933,7 @@ void close_thread_table(THD *thd, TABLE **table_ptr)
table_name Table name
NOTES:
- This is called by find_table_in_local_list() and
- find_table_in_global_list().
+ This is called by find_table_in_global_list().
RETURN VALUES
NULL Table not found
@@ -934,13 +942,13 @@ void close_thread_table(THD *thd, TABLE **table_ptr)
TABLE_LIST *find_table_in_list(TABLE_LIST *table,
TABLE_LIST *TABLE_LIST::*link,
- const char *db_name,
- const char *table_name)
+ const LEX_CSTRING *db_name,
+ const LEX_CSTRING *table_name)
{
for (; table; table= table->*link )
{
- if (strcmp(table->db, db_name) == 0 &&
- strcmp(table->table_name, table_name) == 0)
+ if (cmp(&table->db, db_name) == 0 &&
+ cmp(&table->table_name, table_name) == 0)
break;
}
return table;
@@ -986,9 +994,9 @@ TABLE_LIST* find_dup_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list,
uint check_flag)
{
TABLE_LIST *res= 0;
- const char *d_name, *t_name, *t_alias;
+ LEX_CSTRING *d_name, *t_name, *t_alias;
DBUG_ENTER("find_dup_table");
- DBUG_PRINT("enter", ("table alias: %s", table->alias));
+ DBUG_PRINT("enter", ("table alias: %s", table->alias.str));
/*
If this function called for query which update table (INSERT/UPDATE/...)
@@ -1012,12 +1020,12 @@ TABLE_LIST* find_dup_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list,
*/
DBUG_ASSERT(table);
}
- d_name= table->db;
- t_name= table->table_name;
- t_alias= table->alias;
+ d_name= &table->db;
+ t_name= &table->table_name;
+ t_alias= &table->alias;
retry:
- DBUG_PRINT("info", ("real table: %s.%s", d_name, t_name));
+ DBUG_PRINT("info", ("real table: %s.%s", d_name->str, t_name->str));
for (TABLE_LIST *tl= table_list; tl ; tl= tl->next_global, res= 0)
{
if (tl->select_lex && tl->select_lex->master_unit() &&
@@ -1053,7 +1061,7 @@ retry:
/* Skip if table alias does not match. */
if (check_flag & CHECK_DUP_ALLOW_DIFFERENT_ALIAS)
{
- if (my_strcasecmp(table_alias_charset, t_alias, res->alias))
+ if (my_strcasecmp(table_alias_charset, t_alias->str, res->alias.str))
continue;
}
@@ -1123,14 +1131,32 @@ unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list,
table= table->find_table_for_update();
- if (table->table && table->table->file->ht->db_type == DB_TYPE_MRG_MYISAM)
+ if (table->table &&
+ table->table->file->ha_table_flags() & HA_CAN_MULTISTEP_MERGE)
{
TABLE_LIST *child;
dup= NULL;
/* Check duplicates of all merge children. */
- for (child= table->next_global; child && child->parent_l == table;
+ for (child= table->next_global; child;
child= child->next_global)
{
+ if (child->table &&
+ child->table->file->ha_table_flags() & HA_CAN_MULTISTEP_MERGE)
+ continue;
+
+ /*
+ Ensure that the child has one parent that is the table that is
+ updated.
+ */
+ TABLE_LIST *tmp_parent= child;
+ while ((tmp_parent= tmp_parent->parent_l))
+ {
+ if (tmp_parent == table)
+ break;
+ }
+ if (!tmp_parent)
+ break;
+
if ((dup= find_dup_table(thd, child, child->next_global, check_flag)))
break;
}
@@ -1139,6 +1165,8 @@ unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list,
dup= find_dup_table(thd, table, table_list, check_flag);
return dup;
}
+
+
/*
Issue correct error message in case we found 2 duplicate tables which
prevent some update operation
@@ -1163,10 +1191,10 @@ void update_non_unique_table_error(TABLE_LIST *update,
update->view == duplicate->view ||
update->view_name.length != duplicate->view_name.length ||
update->view_db.length != duplicate->view_db.length ||
- my_strcasecmp(table_alias_charset,
- update->view_name.str, duplicate->view_name.str) != 0 ||
- my_strcasecmp(table_alias_charset,
- update->view_db.str, duplicate->view_db.str) != 0)
+ lex_string_cmp(table_alias_charset,
+ &update->view_name, &duplicate->view_name) != 0 ||
+ lex_string_cmp(table_alias_charset,
+ &update->view_db, &duplicate->view_db) != 0)
{
/*
it is not the same view repeated (but it can be parts of the same copy
@@ -1178,21 +1206,21 @@ void update_non_unique_table_error(TABLE_LIST *update,
if (update->view == duplicate->view)
my_error(!strncmp(operation, "INSERT", 6) ?
ER_NON_INSERTABLE_TABLE : ER_NON_UPDATABLE_TABLE, MYF(0),
- update->alias, operation);
+ update->alias.str, operation);
else
my_error(ER_VIEW_PREVENT_UPDATE, MYF(0),
- (duplicate->view ? duplicate->alias : update->alias),
- operation, update->alias);
+ (duplicate->view ? duplicate->alias.str : update->alias.str),
+ operation, update->alias.str);
return;
}
if (duplicate->view)
{
- my_error(ER_VIEW_PREVENT_UPDATE, MYF(0), duplicate->alias, operation,
- update->alias);
+ my_error(ER_VIEW_PREVENT_UPDATE, MYF(0), duplicate->alias.str, operation,
+ update->alias.str);
return;
}
}
- my_error(ER_UPDATE_TABLE_USED, MYF(0), update->alias, operation);
+ my_error(ER_UPDATE_TABLE_USED, MYF(0), update->alias.str, operation);
}
@@ -1257,8 +1285,8 @@ bool wait_while_table_is_used(THD *thd, TABLE *table,
prelocked mode, e.g. if we do CREATE TABLE .. SELECT f1();
*/
-void drop_open_table(THD *thd, TABLE *table, const char *db_name,
- const char *table_name)
+void drop_open_table(THD *thd, TABLE *table, const LEX_CSTRING *db_name,
+ const LEX_CSTRING *table_name)
{
DBUG_ENTER("drop_open_table");
if (table->s->tmp_table)
@@ -1271,7 +1299,7 @@ void drop_open_table(THD *thd, TABLE *table, const char *db_name,
table->file->extra(HA_EXTRA_PREPARE_FOR_DROP);
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, table_name,
+ 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);
@@ -1469,6 +1497,16 @@ open_table_get_mdl_lock(THD *thd, Open_table_context *ot_ctx,
return FALSE;
}
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+/* Set all [named] partitions as used. */
+static int set_partitions_as_used(TABLE_LIST *tl, TABLE *t)
+{
+ if (t->part_info)
+ return t->file->change_partitions_to_open(tl->partition_names);
+ return 0;
+}
+#endif
+
/**
Check if the given table is actually a VIEW that was LOCK-ed
@@ -1492,13 +1530,12 @@ bool is_locked_view(THD *thd, TABLE_LIST *t)
TABLES breaks metadata locking protocol (potentially can lead
to deadlocks) it should be disallowed.
*/
- if (thd->mdl_context.is_lock_owner(MDL_key::TABLE,
- t->db, t->table_name,
- MDL_SHARED))
+ if (thd->mdl_context.is_lock_owner(MDL_key::TABLE, t->db.str,
+ t->table_name.str, MDL_SHARED))
{
char path[FN_REFLEN + 1];
build_table_filename(path, sizeof(path) - 1,
- t->db, t->table_name, reg_ext, 0);
+ t->db.str, t->table_name.str, reg_ext, 0);
/*
Note that we can't be 100% sure that it is a view since it's
possible that we either simply have not found unused TABLE
@@ -1568,11 +1605,14 @@ bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx)
TABLE *table;
const char *key;
uint key_length;
- char *alias= table_list->alias;
+ const char *alias= table_list->alias.str;
uint flags= ot_ctx->get_flags();
MDL_ticket *mdl_ticket;
TABLE_SHARE *share;
uint gts_flags;
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ int part_names_error=0;
+#endif
DBUG_ENTER("open_table");
/*
@@ -1607,7 +1647,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx)
DBUG_RETURN(true);
}
- if (!table_list->db)
+ if (!table_list->db.str)
{
my_error(ER_NO_DB_ERROR, MYF(0));
DBUG_RETURN(true);
@@ -1677,6 +1717,9 @@ bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx)
table->query_id= thd->query_id;
table->init(thd, table_list);
DBUG_PRINT("info",("Using locked table"));
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ part_names_error= set_partitions_as_used(table_list, table);
+#endif
goto reset;
}
@@ -1691,7 +1734,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx)
locked tables list was created.
*/
if (thd->locked_tables_mode == LTM_PRELOCKED)
- my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->alias);
+ my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db.str, table_list->alias.str);
else
my_error(ER_TABLE_NOT_LOCKED, MYF(0), alias);
DBUG_RETURN(TRUE);
@@ -1777,7 +1820,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx)
if (table_list->open_strategy == TABLE_LIST::OPEN_IF_EXISTS)
{
- if (!ha_table_exists(thd, table_list->db, table_list->table_name))
+ if (!ha_table_exists(thd, &table_list->db, &table_list->table_name))
DBUG_RETURN(FALSE);
}
else if (table_list->open_strategy == TABLE_LIST::OPEN_STUB)
@@ -1796,7 +1839,7 @@ retry_share:
share= tdc_acquire_share(thd, table_list, gts_flags, &table);
- if (!share)
+ if (unlikely(!share))
{
/*
Hide "Table doesn't exist" errors if the table belongs to a view.
@@ -1838,7 +1881,12 @@ retry_share:
my_error(ER_WRONG_MRG_TABLE, MYF(0));
goto err_lock;
}
-
+ if (table_list->sequence)
+ {
+ my_error(ER_NOT_SEQUENCE, MYF(0), table_list->db.str,
+ table_list->alias.str);
+ goto err_lock;
+ }
/*
This table is a view. Validate its metadata version: in particular,
that it was a view when the statement was prepared.
@@ -1883,8 +1931,8 @@ retry_share:
bool wait_result;
thd->push_internal_handler(&mdl_deadlock_handler);
- wait_result= tdc_wait_for_old_version(thd, table_list->db,
- table_list->table_name,
+ wait_result= tdc_wait_for_old_version(thd, table_list->db.str,
+ table_list->table_name.str,
ot_ctx->get_timeout(),
mdl_ticket->get_deadlock_weight());
thd->pop_internal_handler();
@@ -1917,6 +1965,9 @@ retry_share:
{
DBUG_ASSERT(table->file != NULL);
MYSQL_REBIND_TABLE(table->file);
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ part_names_error= set_partitions_as_used(table_list, table);
+#endif
}
else
{
@@ -1926,12 +1977,13 @@ retry_share:
if (!(table=(TABLE*) my_malloc(sizeof(*table),MYF(MY_WME))))
goto err_lock;
- error= open_table_from_share(thd, share, alias,
+ error= open_table_from_share(thd, share, &table_list->alias,
HA_OPEN_KEYFILE | HA_TRY_READ_ONLY,
EXTRA_RECORD,
- thd->open_options, table, FALSE);
+ thd->open_options, table, FALSE,
+ IF_PARTITIONING(table_list->partition_names,0));
- if (error)
+ if (unlikely(error))
{
my_free(table);
@@ -1977,11 +2029,14 @@ retry_share:
table_list->table= table;
#ifdef WITH_PARTITION_STORAGE_ENGINE
- if (table->part_info)
+ if (unlikely(table->part_info))
{
- /* Set all [named] partitions as used. */
- if (table->part_info->set_partition_bitmaps(table_list))
+ /* Partitions specified were incorrect.*/
+ if (part_names_error)
+ {
+ table->file->print_error(part_names_error, MYF(0));
DBUG_RETURN(true);
+ }
}
else if (table_list->partition_names)
{
@@ -1990,6 +2045,11 @@ retry_share:
DBUG_RETURN(true);
}
#endif
+ if (table_list->sequence && table->s->table_type != TABLE_TYPE_SEQUENCE)
+ {
+ my_error(ER_NOT_SEQUENCE, MYF(0), table_list->db.str, table_list->alias.str);
+ DBUG_RETURN(true);
+ }
DBUG_RETURN(FALSE);
@@ -2054,7 +2114,7 @@ TABLE *find_table_for_mdl_upgrade(THD *thd, const char *db,
TABLE *tab= find_locked_table(thd->open_tables, db, table_name);
int error;
- if (!tab)
+ if (unlikely(!tab))
{
error= ER_TABLE_NOT_LOCKED;
goto err_exit;
@@ -2066,8 +2126,8 @@ TABLE *find_table_for_mdl_upgrade(THD *thd, const char *db,
cases don't take a global IX lock in order to be compatible with
global read lock.
*/
- if (!thd->mdl_context.is_lock_owner(MDL_key::GLOBAL, "", "",
- MDL_INTENTION_EXCLUSIVE))
+ if (unlikely(!thd->mdl_context.is_lock_owner(MDL_key::GLOBAL, "", "",
+ MDL_INTENTION_EXCLUSIVE)))
{
error= ER_TABLE_NOT_LOCKED_FOR_WRITE;
goto err_exit;
@@ -2124,28 +2184,30 @@ Locked_tables_list::init_locked_tables(THD *thd)
table= table->next, m_locked_tables_count++)
{
TABLE_LIST *src_table_list= table->pos_in_table_list;
- char *db, *table_name, *alias;
- size_t db_len= table->s->db.length;
- size_t table_name_len= table->s->table_name.length;
- size_t alias_len= table->alias.length();
+ LEX_CSTRING db, table_name, alias;
+
+ db.length= table->s->db.length;
+ table_name.length= table->s->table_name.length;
+ alias.length= table->alias.length();
TABLE_LIST *dst_table_list;
if (! multi_alloc_root(&m_locked_tables_root,
&dst_table_list, sizeof(*dst_table_list),
- &db, db_len + 1,
- &table_name, table_name_len + 1,
- &alias, alias_len + 1,
+ &db.str, (size_t) db.length + 1,
+ &table_name.str, (size_t) table_name.length + 1,
+ &alias.str, (size_t) alias.length + 1,
NullS))
{
reset();
return TRUE;
}
- memcpy(db, table->s->db.str, db_len + 1);
- memcpy(table_name, table->s->table_name.str, table_name_len + 1);
- strmake(alias, table->alias.ptr(), alias_len);
- dst_table_list->init_one_table(db, db_len, table_name, table_name_len,
- alias, table->reginfo.lock_type);
+ memcpy((char*) db.str, table->s->db.str, db.length + 1);
+ memcpy((char*) table_name.str, table->s->table_name.str,
+ table_name.length + 1);
+ memcpy((char*) alias.str, table->alias.c_ptr(), alias.length + 1);
+ dst_table_list->init_one_table(&db, &table_name,
+ &alias, table->reginfo.lock_type);
dst_table_list->table= table;
dst_table_list->mdl_request.ticket= src_table_list->mdl_request.ticket;
@@ -2372,7 +2434,7 @@ unlink_all_closed_tables(THD *thd, MYSQL_LOCK *lock, size_t reopen_count)
DBUG_ASSERT(thd->open_tables == m_reopen_array[reopen_count]->table);
thd->open_tables->pos_in_locked_tables->table= NULL;
- thd->open_tables->pos_in_locked_tables= 0;
+ thd->open_tables->pos_in_locked_tables= NULL;
close_thread_table(thd, &thd->open_tables);
}
@@ -2441,7 +2503,7 @@ bool
Locked_tables_list::reopen_tables(THD *thd, bool need_reopen)
{
Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN);
- size_t reopen_count= 0;
+ uint reopen_count= 0;
MYSQL_LOCK *lock;
MYSQL_LOCK *merged_lock;
DBUG_ENTER("Locked_tables_list::reopen_tables");
@@ -2514,7 +2576,7 @@ Locked_tables_list::reopen_tables(THD *thd, bool need_reopen)
break something else.
*/
lock= mysql_lock_tables(thd, tables, reopen_count,
- MYSQL_OPEN_REOPEN);
+ MYSQL_OPEN_REOPEN | MYSQL_LOCK_USE_MALLOC);
thd->in_lock_tables= 0;
if (lock == NULL || (merged_lock=
mysql_lock_merge(thd->lock, lock)) == NULL)
@@ -2545,7 +2607,7 @@ bool Locked_tables_list::restore_lock(THD *thd, TABLE_LIST *dst_table_list,
{
MYSQL_LOCK *merged_lock;
DBUG_ENTER("restore_lock");
- DBUG_ASSERT(!strcmp(dst_table_list->table_name, table->s->table_name.str));
+ DBUG_ASSERT(!strcmp(dst_table_list->table_name.str, table->s->table_name.str));
/* Ensure we have the memory to add the table back */
if (!(merged_lock= mysql_lock_merge(thd->lock, lock)))
@@ -2774,8 +2836,8 @@ ret:
static bool open_table_entry_fini(THD *thd, TABLE_SHARE *share, TABLE *entry)
{
- if (Table_triggers_list::check_n_load(thd, share->db.str,
- share->table_name.str, entry, 0))
+ if (Table_triggers_list::check_n_load(thd, &share->db,
+ &share->table_name, entry, 0))
return TRUE;
/*
@@ -2792,10 +2854,9 @@ static bool open_table_entry_fini(THD *thd, TABLE_SHARE *share, TABLE *entry)
query.length(0);
query.append("DELETE FROM ");
- append_identifier(thd, &query, share->db.str, share->db.length);
+ append_identifier(thd, &query, &share->db);
query.append(".");
- append_identifier(thd, &query, share->table_name.str,
- share->table_name.length);
+ append_identifier(thd, &query, &share->table_name);
/*
we bypass thd->binlog_query() here,
@@ -2831,7 +2892,7 @@ static bool auto_repair_table(THD *thd, TABLE_LIST *table_list)
DBUG_ASSERT(! share->is_view);
- if (open_table_from_share(thd, share, table_list->alias,
+ if (open_table_from_share(thd, share, &table_list->alias,
HA_OPEN_KEYFILE | HA_TRY_READ_ONLY,
EXTRA_RECORD,
ha_open_options | HA_OPEN_FOR_REPAIR,
@@ -2856,7 +2917,7 @@ static bool auto_repair_table(THD *thd, TABLE_LIST *table_list)
tdc_release_share(share);
/* Remove the repaired share from the table cache. */
tdc_remove_table(thd, TDC_RT_REMOVE_ALL,
- table_list->db, table_list->table_name,
+ table_list->db.str, table_list->table_name.str,
FALSE);
end_free:
my_free(entry);
@@ -2959,10 +3020,7 @@ request_backoff_action(enum_open_table_action action_arg,
m_failed_table= (TABLE_LIST*) m_thd->alloc(sizeof(TABLE_LIST));
if (m_failed_table == NULL)
return TRUE;
- m_failed_table->init_one_table(table->db, table->db_length,
- table->table_name,
- table->table_name_length,
- table->alias, TL_WRITE);
+ m_failed_table->init_one_table(&table->db, &table->table_name, &table->alias, TL_WRITE);
m_failed_table->open_strategy= table->open_strategy;
m_failed_table->mdl_request.set_type(MDL_EXCLUSIVE);
}
@@ -3031,8 +3089,8 @@ Open_table_context::recover_from_failed_open()
get_timeout(), 0)))
break;
- tdc_remove_table(m_thd, TDC_RT_REMOVE_ALL, m_failed_table->db,
- m_failed_table->table_name, FALSE);
+ 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
@@ -3067,8 +3125,8 @@ Open_table_context::recover_from_failed_open()
get_timeout(), 0)))
break;
- tdc_remove_table(m_thd, TDC_RT_REMOVE_ALL, m_failed_table->db,
- m_failed_table->table_name, FALSE);
+ tdc_remove_table(m_thd, TDC_RT_REMOVE_ALL, m_failed_table->db.str,
+ m_failed_table->table_name.str, FALSE);
result= auto_repair_table(m_thd, m_failed_table);
/*
@@ -3167,6 +3225,46 @@ thr_lock_type read_lock_type_for_table(THD *thd,
/*
+ Extend the prelocking set with tables and routines used by a routine.
+
+ @param[in] thd Thread context.
+ @param[in] rt Element of prelocking set to be processed.
+ @param[in] ot_ctx Context of open_table used to recover from
+ locking failures.
+ @retval false Success.
+ @retval true Failure (Conflicting metadata lock, OOM, other errors).
+*/
+static bool
+sp_acquire_mdl(THD *thd, Sroutine_hash_entry *rt, Open_table_context *ot_ctx)
+{
+ DBUG_ENTER("sp_acquire_mdl");
+ /*
+ Since we acquire only shared lock on routines we don't
+ need to care about global intention exclusive locks.
+ */
+ DBUG_ASSERT(rt->mdl_request.type == MDL_SHARED);
+
+ /*
+ Waiting for a conflicting metadata lock to go away may
+ lead to a deadlock, detected by MDL subsystem.
+ If possible, we try to resolve such deadlocks by releasing all
+ metadata locks and restarting the pre-locking process.
+ To prevent the error from polluting the diagnostics area
+ in case of successful resolution, install a special error
+ handler for ER_LOCK_DEADLOCK error.
+ */
+ MDL_deadlock_handler mdl_deadlock_handler(ot_ctx);
+
+ thd->push_internal_handler(&mdl_deadlock_handler);
+ bool result= thd->mdl_context.acquire_lock(&rt->mdl_request,
+ ot_ctx->get_timeout());
+ thd->pop_internal_handler();
+
+ DBUG_RETURN(result);
+}
+
+
+/*
Handle element of prelocking set other than table. E.g. cache routine
and, if prelocking strategy prescribes so, extend the prelocking set
with tables and routines used by it.
@@ -3206,6 +3304,17 @@ open_and_process_routine(THD *thd, Query_tables_list *prelocking_ctx,
switch (mdl_type)
{
+ case MDL_key::PACKAGE_BODY:
+ DBUG_ASSERT(rt != (Sroutine_hash_entry*)prelocking_ctx->sroutines_list.first);
+ /*
+ No need to cache the package body itself.
+ It gets cached during open_and_process_routine()
+ for the first used package routine. See the package related code
+ in the "case" below.
+ */
+ if (sp_acquire_mdl(thd, rt, ot_ctx))
+ DBUG_RETURN(TRUE);
+ break;
case MDL_key::FUNCTION:
case MDL_key::PROCEDURE:
{
@@ -3221,34 +3330,14 @@ open_and_process_routine(THD *thd, Query_tables_list *prelocking_ctx,
mdl_type != MDL_key::PROCEDURE)
{
/*
- Since we acquire only shared lock on routines we don't
- need to care about global intention exclusive locks.
- */
- DBUG_ASSERT(rt->mdl_request.type == MDL_SHARED);
-
- /*
- Waiting for a conflicting metadata lock to go away may
- lead to a deadlock, detected by MDL subsystem.
- If possible, we try to resolve such deadlocks by releasing all
- metadata locks and restarting the pre-locking process.
- To prevent the error from polluting the diagnostics area
- in case of successful resolution, install a special error
- handler for ER_LOCK_DEADLOCK error.
+ TODO: If this is a package routine, we should not put MDL
+ TODO: on the routine itself. We should put only the package MDL.
*/
- MDL_deadlock_handler mdl_deadlock_handler(ot_ctx);
-
- thd->push_internal_handler(&mdl_deadlock_handler);
- bool result= thd->mdl_context.acquire_lock(&rt->mdl_request,
- ot_ctx->get_timeout());
- thd->pop_internal_handler();
-
- if (result)
+ if (sp_acquire_mdl(thd, rt, ot_ctx))
DBUG_RETURN(TRUE);
- DEBUG_SYNC(thd, "after_shared_lock_pname");
-
/* Ensures the routine is up-to-date and cached, if exists. */
- if (sp_cache_routine(thd, rt, has_prelocking_list, &sp))
+ if (rt->sp_cache_routine(thd, has_prelocking_list, &sp))
DBUG_RETURN(TRUE);
/* Remember the version of the routine in the parse tree. */
@@ -3261,8 +3350,24 @@ open_and_process_routine(THD *thd, Query_tables_list *prelocking_ctx,
*routine_modifies_data= sp->modifies_data();
if (!has_prelocking_list)
+ {
prelocking_strategy->handle_routine(thd, prelocking_ctx, rt, sp,
need_prelocking);
+ if (sp->m_parent)
+ {
+ /*
+ If it's a package routine, we need also to handle the
+ package body, as its initialization section can use
+ some tables and routine calls.
+ TODO: Only package public routines actually need this.
+ TODO: Skip package body handling for private routines.
+ */
+ *routine_modifies_data|= sp->m_parent->modifies_data();
+ prelocking_strategy->handle_routine(thd, prelocking_ctx, rt,
+ sp->m_parent,
+ need_prelocking);
+ }
+ }
}
}
else
@@ -3273,7 +3378,7 @@ open_and_process_routine(THD *thd, Query_tables_list *prelocking_ctx,
Validating routine version is unnecessary, since CALL
does not affect the prepared statement prelocked list.
*/
- if (sp_cache_routine(thd, rt, FALSE, &sp))
+ if (rt->sp_cache_routine(thd, false, &sp))
DBUG_RETURN(TRUE);
}
}
@@ -3331,10 +3436,12 @@ bool extend_table_list(THD *thd, TABLE_LIST *tables,
{
bool error= false;
LEX *lex= thd->lex;
+ bool maybe_need_prelocking=
+ (tables->updating && tables->lock_type >= TL_WRITE_ALLOW_WRITE)
+ || thd->lex->default_used;
if (thd->locked_tables_mode <= LTM_LOCK_TABLES &&
- ! has_prelocking_list && tables->updating &&
- tables->lock_type >= TL_WRITE_ALLOW_WRITE)
+ ! has_prelocking_list && maybe_need_prelocking)
{
bool need_prelocking= FALSE;
TABLE_LIST **save_query_tables_last= lex->query_tables_last;
@@ -3408,10 +3515,8 @@ open_and_process_table(THD *thd, TABLE_LIST *tables, uint *counter, uint flags,
obtain proper metadata locks and do other necessary steps like
stored routine processing.
*/
- tables->db= tables->view_db.str;
- tables->db_length= tables->view_db.length;
- tables->table_name= tables->view_name.str;
- tables->table_name_length= tables->view_name.length;
+ tables->db= tables->view_db;
+ tables->table_name= tables->view_name;
}
else if (tables->select_lex)
{
@@ -3443,8 +3548,7 @@ open_and_process_table(THD *thd, TABLE_LIST *tables, uint *counter, uint flags,
}
}
- if (!tables->derived &&
- is_infoschema_db(tables->db, tables->db_length))
+ if (!tables->derived && is_infoschema_db(&tables->db))
{
/*
Check whether the information schema contains a table
@@ -3461,7 +3565,7 @@ open_and_process_table(THD *thd, TABLE_LIST *tables, uint *counter, uint flags,
lex->sql_command == SQLCOM_SHOW_KEYS)))
{
my_error(ER_UNKNOWN_TABLE, MYF(0),
- tables->table_name, INFORMATION_SCHEMA_NAME.str);
+ tables->table_name.str, INFORMATION_SCHEMA_NAME.str);
DBUG_RETURN(1);
}
}
@@ -3500,7 +3604,7 @@ open_and_process_table(THD *thd, TABLE_LIST *tables, uint *counter, uint flags,
goto end;
}
DBUG_PRINT("tcache", ("opening table: '%s'.'%s' item: %p",
- tables->db, tables->table_name, tables)); //psergey: invalid read of size 1 here
+ tables->db.str, tables->table_name.str, tables));
(*counter)++;
/*
@@ -3511,9 +3615,14 @@ open_and_process_table(THD *thd, TABLE_LIST *tables, uint *counter, uint flags,
/*
If this TABLE_LIST object has an associated open TABLE object
(TABLE_LIST::table is not NULL), that TABLE object must be a pre-opened
- temporary table.
+ temporary table or SEQUENCE (see sequence_insert()).
*/
- DBUG_ASSERT(is_temporary_table(tables));
+ DBUG_ASSERT(is_temporary_table(tables) || tables->table->s->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);
+ }
}
else if (tables->open_type == OT_TEMPORARY_ONLY)
{
@@ -3609,12 +3718,12 @@ open_and_process_table(THD *thd, TABLE_LIST *tables, uint *counter, uint flags,
error= open_table(thd, tables, ot_ctx);
}
- if (error)
+ if (unlikely(error))
{
if (! ot_ctx->can_recover_from_failed_open() && safe_to_ignore_table)
{
DBUG_PRINT("info", ("open_table: ignoring table '%s'.'%s'",
- tables->db, tables->alias));
+ tables->db.str, tables->alias.str));
error= FALSE;
}
goto end;
@@ -3661,7 +3770,7 @@ open_and_process_table(THD *thd, TABLE_LIST *tables, uint *counter, uint flags,
goto end;
error= extend_table_list(thd, tables, prelocking_strategy, has_prelocking_list);
- if (error)
+ if (unlikely(error))
goto end;
/* Copy grant information from TABLE_LIST instance to TABLE one. */
@@ -3670,7 +3779,7 @@ open_and_process_table(THD *thd, TABLE_LIST *tables, uint *counter, uint flags,
/* Check and update metadata version of a base table. */
error= check_and_update_table_version(thd, tables, tables->table->s);
- if (error)
+ if (unlikely(error))
goto end;
/*
After opening a MERGE table add the children to the query list of
@@ -3704,7 +3813,7 @@ process_view_routines:
if (need_prelocking && ! lex->requires_prelocking())
lex->mark_as_requiring_prelocking(save_query_tables_last);
- if (error)
+ if (unlikely(error))
goto end;
}
@@ -3782,7 +3891,7 @@ 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, "",
+ schema_request->init(MDL_key::SCHEMA, table->db.str, "",
MDL_INTENTION_EXCLUSIVE,
MDL_TRANSACTION);
mdl_requests.push_front(schema_request);
@@ -3795,8 +3904,9 @@ lock_table_names(THD *thd, const DDL_options_st &options,
DBUG_RETURN(FALSE);
/* Check if CREATE TABLE without REPLACE was used */
- create_table= thd->lex->sql_command == SQLCOM_CREATE_TABLE &&
- !options.or_replace();
+ create_table= ((thd->lex->sql_command == SQLCOM_CREATE_TABLE ||
+ thd->lex->sql_command == SQLCOM_CREATE_SEQUENCE) &&
+ !options.or_replace());
if (!(flags & MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK))
{
@@ -3837,17 +3947,17 @@ lock_table_names(THD *thd, const DDL_options_st &options,
We come here in the case of lock timeout when executing CREATE TABLE.
Verify that table does exist (it usually does, as we got a lock conflict)
*/
- if (ha_table_exists(thd, tables_start->db, tables_start->table_name))
+ if (ha_table_exists(thd, &tables_start->db, &tables_start->table_name))
{
if (options.if_not_exists())
{
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_TABLE_EXISTS_ERROR,
ER_THD(thd, ER_TABLE_EXISTS_ERROR),
- tables_start->table_name);
+ tables_start->table_name.str);
}
else
- my_error(ER_TABLE_EXISTS_ERROR, MYF(0), tables_start->table_name);
+ my_error(ER_TABLE_EXISTS_ERROR, MYF(0), tables_start->table_name.str);
DBUG_RETURN(TRUE);
}
/*
@@ -3921,7 +4031,8 @@ open_tables_check_upgradable_mdl(THD *thd, TABLE_LIST *tables_start,
Note that find_table_for_mdl_upgrade() will report an error if
no suitable ticket is found.
*/
- if (!find_table_for_mdl_upgrade(thd, table->db, table->table_name, NULL))
+ if (!find_table_for_mdl_upgrade(thd, table->db.str, table->table_name.str,
+ NULL))
return TRUE;
}
@@ -4077,7 +4188,7 @@ restart:
prelocking_strategy, has_prelocking_list,
&ot_ctx);
- if (error)
+ if (unlikely(error))
{
if (ot_ctx.can_recover_from_failed_open())
{
@@ -4159,7 +4270,7 @@ restart:
if (need_prelocking && ! *start)
*start= thd->lex->query_tables;
- if (error)
+ if (unlikely(error))
{
if (ot_ctx.can_recover_from_failed_open())
{
@@ -4208,7 +4319,7 @@ restart:
continue;
/* Schema tables may not have a TABLE object here. */
- if (tbl->file->ht->db_type == DB_TYPE_MRG_MYISAM)
+ if (tbl->file->ha_table_flags() & HA_CAN_MULTISTEP_MERGE)
{
/* MERGE tables need to access parent and child TABLE_LISTs. */
DBUG_ASSERT(tbl->pos_in_table_list == tables);
@@ -4239,7 +4350,7 @@ restart:
(*start)->table &&
(*start)->table->file->ht == myisam_hton &&
wsrep_thd_exec_mode(thd) == LOCAL_STATE &&
- !is_stat_table((*start)->db, (*start)->alias) &&
+ !is_stat_table(&(*start)->db, &(*start)->alias) &&
thd->get_command() != COM_STMT_PREPARE &&
((thd->lex->sql_command == SQLCOM_INSERT ||
thd->lex->sql_command == SQLCOM_INSERT_SELECT ||
@@ -4258,7 +4369,7 @@ WSREP_ERROR_LABEL:
THD_STAGE_INFO(thd, stage_after_opening_tables);
thd_proc_info(thd, 0);
- if (error && *table_to_open)
+ if (unlikely(error) && *table_to_open)
{
(*table_to_open)->table= NULL;
}
@@ -4321,22 +4432,72 @@ handle_routine(THD *thd, Query_tables_list *prelocking_ctx,
@note this can be changed to use a hash, instead of scanning the linked
list, if the performance of this function will ever become an issue
*/
-
-bool table_already_fk_prelocked(TABLE_LIST *tl, LEX_STRING *db,
- LEX_STRING *table, thr_lock_type lock_type)
+bool table_already_fk_prelocked(TABLE_LIST *tl, LEX_CSTRING *db,
+ LEX_CSTRING *table, thr_lock_type lock_type)
{
for (; tl; tl= tl->next_global )
{
if (tl->lock_type >= lock_type &&
- tl->prelocking_placeholder == TABLE_LIST::FK &&
- strcmp(tl->db, db->str) == 0 &&
- strcmp(tl->table_name, table->str) == 0)
+ tl->prelocking_placeholder == TABLE_LIST::PRELOCK_FK &&
+ strcmp(tl->db.str, db->str) == 0 &&
+ strcmp(tl->table_name.str, table->str) == 0)
return true;
}
return false;
}
+static bool internal_table_exists(TABLE_LIST *global_list,
+ const char *table_name)
+{
+ do
+ {
+ if (global_list->table_name.str == table_name)
+ return 1;
+ } while ((global_list= global_list->next_global));
+ return 0;
+}
+
+
+static bool
+add_internal_tables(THD *thd, Query_tables_list *prelocking_ctx,
+ TABLE_LIST *tables)
+{
+ TABLE_LIST *global_table_list= prelocking_ctx->query_tables;
+ DBUG_ENTER("add_internal_tables");
+
+ do
+ {
+ DBUG_PRINT("info", ("table name: %s", tables->table_name.str));
+ /*
+ Skip table if already in the list. Can happen with prepared statements
+ */
+ if (tables->next_local &&
+ internal_table_exists(global_table_list, tables->table_name.str))
+ continue;
+
+ TABLE_LIST *tl= (TABLE_LIST *) thd->alloc(sizeof(TABLE_LIST));
+ if (!tl)
+ DBUG_RETURN(TRUE);
+ tl->init_one_table_for_prelocking(&tables->db,
+ &tables->table_name,
+ NULL, tables->lock_type,
+ TABLE_LIST::PRELOCK_NONE,
+ 0, 0,
+ &prelocking_ctx->query_tables_last,
+ tables->for_insert_data);
+ /*
+ Store link to the new table_list that will be used by open so that
+ Item_func_nextval() can find it
+ */
+ tables->next_local= tl;
+ DBUG_PRINT("info", ("table name: %s added", tables->table_name.str));
+ } while ((tables= tables->next_global));
+ DBUG_RETURN(FALSE);
+}
+
+
+
/**
Defines how prelocking algorithm for DML statements should handle table list
elements:
@@ -4363,21 +4524,24 @@ bool DML_prelocking_strategy::
handle_table(THD *thd, Query_tables_list *prelocking_ctx,
TABLE_LIST *table_list, bool *need_prelocking)
{
+ DBUG_ENTER("handle_table");
+ TABLE *table= table_list->table;
/* We rely on a caller to check that table is going to be changed. */
- DBUG_ASSERT(table_list->lock_type >= TL_WRITE_ALLOW_WRITE);
+ DBUG_ASSERT(table_list->lock_type >= TL_WRITE_ALLOW_WRITE ||
+ thd->lex->default_used);
if (table_list->trg_event_map)
{
- if (table_list->table->triggers)
+ if (table->triggers)
{
*need_prelocking= TRUE;
- if (table_list->table->triggers->
+ if (table->triggers->
add_tables_and_routines_for_triggers(thd, prelocking_ctx, table_list))
return TRUE;
}
- if (table_list->table->file->referenced_by_foreign_key())
+ if (table->file->referenced_by_foreign_key())
{
List <FOREIGN_KEY_INFO> fk_list;
List_iterator<FOREIGN_KEY_INFO> fk_list_it(fk_list);
@@ -4386,12 +4550,12 @@ handle_table(THD *thd, Query_tables_list *prelocking_ctx,
arena= thd->activate_stmt_arena_if_needed(&backup);
- table_list->table->file->get_parent_foreign_key_list(thd, &fk_list);
- if (thd->is_error())
+ table->file->get_parent_foreign_key_list(thd, &fk_list);
+ if (unlikely(thd->is_error()))
{
if (arena)
thd->restore_active_arena(arena, &backup);
- return TRUE;
+ DBUG_RETURN(TRUE);
}
*need_prelocking= TRUE;
@@ -4399,12 +4563,11 @@ handle_table(THD *thd, Query_tables_list *prelocking_ctx,
while ((fk= fk_list_it++))
{
// FK_OPTION_RESTRICT and FK_OPTION_NO_ACTION only need read access
- static bool can_write[]= { true, false, true, true, false, true };
uint8 op= table_list->trg_event_map;
thr_lock_type lock_type;
- if ((op & (1 << TRG_EVENT_DELETE) && can_write[fk->delete_method])
- || (op & (1 << TRG_EVENT_UPDATE) && can_write[fk->update_method]))
+ if ((op & (1 << TRG_EVENT_DELETE) && fk_modifies_child(fk->delete_method))
+ || (op & (1 << TRG_EVENT_UPDATE) && fk_modifies_child(fk->update_method)))
lock_type= TL_WRITE_ALLOW_WRITE;
else
lock_type= TL_READ;
@@ -4415,17 +4578,94 @@ handle_table(THD *thd, Query_tables_list *prelocking_ctx,
continue;
TABLE_LIST *tl= (TABLE_LIST *) thd->alloc(sizeof(TABLE_LIST));
- tl->init_one_table_for_prelocking(fk->foreign_db->str, fk->foreign_db->length,
- fk->foreign_table->str, fk->foreign_table->length,
- NULL, lock_type, false, table_list->belong_to_view,
- op, &prelocking_ctx->query_tables_last);
+ tl->init_one_table_for_prelocking(fk->foreign_db,
+ fk->foreign_table,
+ NULL, lock_type,
+ TABLE_LIST::PRELOCK_FK,
+ table_list->belong_to_view, op,
+ &prelocking_ctx->query_tables_last,
+ table_list->for_insert_data);
}
if (arena)
thd->restore_active_arena(arena, &backup);
}
}
- return FALSE;
+ /* Open any tables used by DEFAULT (like sequence tables) */
+ DBUG_PRINT("info", ("table: %p name: %s db: %s flags: %u",
+ table_list, table_list->table_name.str,
+ table_list->db.str, table_list->for_insert_data));
+ if (table->internal_tables &&
+ (table_list->for_insert_data ||
+ thd->lex->default_used))
+ {
+ Query_arena *arena, backup;
+ bool error;
+ arena= thd->activate_stmt_arena_if_needed(&backup);
+ error= add_internal_tables(thd, prelocking_ctx,
+ table->internal_tables);
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+ if (unlikely(error))
+ {
+ *need_prelocking= TRUE;
+ DBUG_RETURN(TRUE);
+ }
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ Open all tables used by DEFAULT functions.
+
+ This is different from normal open_and_lock_tables() as we may
+ already have other tables opened and locked and we have to merge the
+ new table with the old ones.
+*/
+
+bool open_and_lock_internal_tables(TABLE *table, bool lock_table)
+{
+ THD *thd= table->in_use;
+ TABLE_LIST *tl;
+ MYSQL_LOCK *save_lock,*new_lock;
+ DBUG_ENTER("open_and_lock_internal_tables");
+
+ /* remove pointer to old select_lex which is already destroyed */
+ for (tl= table->internal_tables ; tl ; tl= tl->next_global)
+ tl->select_lex= 0;
+
+ uint counter;
+ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
+ TABLE_LIST *tmp= table->internal_tables;
+ DML_prelocking_strategy prelocking_strategy;
+
+ if (open_tables(thd, thd->lex->create_info, &tmp, &counter, 0,
+ &prelocking_strategy))
+ goto err;
+
+ if (lock_table)
+ {
+ save_lock= thd->lock;
+ thd->lock= 0;
+ if (lock_tables(thd, table->internal_tables, counter,
+ MYSQL_LOCK_USE_MALLOC))
+ goto err;
+
+ if (!(new_lock= mysql_lock_merge(save_lock, thd->lock)))
+ {
+ thd->lock= save_lock;
+ mysql_unlock_tables(thd, save_lock, 1);
+ /* We don't have to close tables as caller will do that */
+ goto err;
+ }
+ thd->lock= new_lock;
+ }
+ DBUG_RETURN(0);
+
+err:
+ thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
+ DBUG_RETURN(1);
}
@@ -4589,7 +4829,7 @@ static bool check_lock_and_start_stmt(THD *thd,
Prelocking placeholder is not set for TABLE_LIST that
are directly used by TOP level statement.
*/
- DBUG_ASSERT(table_list->prelocking_placeholder == false);
+ DBUG_ASSERT(table_list->prelocking_placeholder == TABLE_LIST::PRELOCK_NONE);
/*
TL_WRITE_DEFAULT and TL_READ_DEFAULT are supposed to be parser only
@@ -4602,7 +4842,6 @@ static bool check_lock_and_start_stmt(THD *thd,
Last argument routine_modifies_data for read_lock_type_for_table()
is ignored, as prelocking placeholder will never be set here.
*/
- DBUG_ASSERT(table_list->prelocking_placeholder == false);
if (table_list->lock_type == TL_WRITE_DEFAULT)
lock_type= thd->update_lock_default;
else if (table_list->lock_type == TL_READ_DEFAULT)
@@ -4610,14 +4849,14 @@ static bool check_lock_and_start_stmt(THD *thd,
else
lock_type= table_list->lock_type;
- if ((int) lock_type > (int) TL_WRITE_ALLOW_WRITE &&
- (int) table_list->table->reginfo.lock_type <= (int) TL_WRITE_ALLOW_WRITE)
+ if ((int) lock_type >= (int) TL_WRITE_ALLOW_WRITE &&
+ (int) table_list->table->reginfo.lock_type < (int) TL_WRITE_ALLOW_WRITE)
{
my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0),
table_list->table->alias.c_ptr());
DBUG_RETURN(1);
}
- if ((error= table_list->table->file->start_stmt(thd, lock_type)))
+ if (unlikely((error= table_list->table->file->start_stmt(thd, lock_type))))
{
table_list->table->file->print_error(error, MYF(0));
DBUG_RETURN(1);
@@ -4682,7 +4921,7 @@ TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l,
/* Set requested lock type. */
table_l->lock_type= lock_type;
/* Allow to open real tables only. */
- table_l->required_type= FRMTYPE_TABLE;
+ table_l->required_type= TABLE_TYPE_NORMAL;
/* Open the table. */
if (open_and_lock_tables(thd, table_l, FALSE, flags,
@@ -4738,7 +4977,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type,
THD_STAGE_INFO(thd, stage_opening_tables);
thd->current_tablenr= 0;
/* open_ltable can be used only for BASIC TABLEs */
- table_list->required_type= FRMTYPE_TABLE;
+ table_list->required_type= TABLE_TYPE_NORMAL;
/* This function can't properly handle requests for such metadata locks. */
DBUG_ASSERT(table_list->mdl_request.type < MDL_SHARED_UPGRADABLE);
@@ -4757,7 +4996,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type,
break;
}
- if (!error)
+ if (likely(!error))
{
/*
We can't have a view or some special "open_strategy" in this function
@@ -4765,7 +5004,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type,
*/
DBUG_ASSERT(table_list->table);
table= table_list->table;
- if (table->file->ht->db_type == DB_TYPE_MRG_MYISAM)
+ if (table->file->ha_table_flags() & HA_CAN_MULTISTEP_MERGE)
{
/* A MERGE table must not come here. */
/* purecov: begin tested */
@@ -4927,6 +5166,51 @@ end:
}
+/**
+ Open a table to read its structure, e.g. for:
+ - SHOW FIELDS
+ - delayed SP variable data type definition: DECLARE a t1.a%TYPE
+
+ The flag MYSQL_OPEN_GET_NEW_TABLE is passed to make %TYPE work
+ in stored functions, as during a stored function call
+ (e.g. in a SELECT query) the tables referenced in %TYPE can already be locked,
+ and attempt to open it again would return an error in open_table().
+
+ The flag MYSQL_OPEN_GET_NEW_TABLE is not really needed for
+ SHOW FIELDS or for a "CALL sp()" statement, but it's not harmful,
+ so let's pass it unconditionally.
+*/
+
+bool open_tables_only_view_structure(THD *thd, TABLE_LIST *table_list,
+ bool can_deadlock)
+{
+ DBUG_ENTER("open_tables_only_view_structure");
+ /*
+ Let us set fake sql_command so views won't try to merge
+ themselves into main statement. If we don't do this,
+ SELECT * from information_schema.xxxx will cause problems.
+ SQLCOM_SHOW_FIELDS is used because it satisfies
+ 'LEX::only_view_structure()'.
+ */
+ enum_sql_command save_sql_command= thd->lex->sql_command;
+ thd->lex->sql_command= SQLCOM_SHOW_FIELDS;
+ bool rc= (thd->open_temporary_tables(table_list) ||
+ open_normal_and_derived_tables(thd, table_list,
+ (MYSQL_OPEN_IGNORE_FLUSH |
+ MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL |
+ MYSQL_OPEN_GET_NEW_TABLE |
+ (can_deadlock ?
+ MYSQL_OPEN_FAIL_ON_MDL_CONFLICT : 0)),
+ DT_INIT | DT_PREPARE | DT_CREATE));
+ /*
+ Restore old value of sql_command back as it is being looked at in
+ process_table() function.
+ */
+ thd->lex->sql_command= save_sql_command;
+ DBUG_RETURN(rc);
+}
+
+
/*
Mark all real tables in the list as free for reuse.
@@ -5282,7 +5566,7 @@ Field *view_ref_found= (Field*) 0x2;
static void update_field_dependencies(THD *thd, Field *field, TABLE *table)
{
DBUG_ENTER("update_field_dependencies");
- if (thd->mark_used_columns != MARK_COLUMNS_NONE)
+ if (should_mark_column(thd->column_usage))
{
MY_BITMAP *bitmap;
@@ -5296,7 +5580,7 @@ static void update_field_dependencies(THD *thd, Field *field, TABLE *table)
if (field->vcol_info)
table->mark_virtual_col(field);
- if (thd->mark_used_columns == MARK_COLUMNS_READ)
+ if (thd->column_usage == MARK_COLUMNS_READ)
bitmap= table->read_set;
else
bitmap= table->write_set;
@@ -5304,13 +5588,13 @@ static void update_field_dependencies(THD *thd, Field *field, TABLE *table)
/*
The test-and-set mechanism in the bitmap is not reliable during
multi-UPDATE statements under MARK_COLUMNS_READ mode
- (thd->mark_used_columns == MARK_COLUMNS_READ), as this bitmap contains
+ (thd->column_usage == MARK_COLUMNS_READ), as this bitmap contains
only those columns that are used in the SET clause. I.e they are being
set here. See multi_update::prepare()
*/
if (bitmap_fast_test_and_set(bitmap, field->field_index))
{
- if (thd->mark_used_columns == MARK_COLUMNS_WRITE)
+ if (thd->column_usage == MARK_COLUMNS_WRITE)
{
DBUG_PRINT("warning", ("Found duplicated field"));
thd->dup_field= field;
@@ -5321,11 +5605,9 @@ static void update_field_dependencies(THD *thd, Field *field, TABLE *table)
}
DBUG_VOID_RETURN;
}
- if (table->get_fields_in_item_tree)
- field->flags|= GET_FIXED_FIELDS_FLAG;
table->used_fields++;
}
- else if (table->get_fields_in_item_tree)
+ if (table->get_fields_in_item_tree)
field->flags|= GET_FIXED_FIELDS_FLAG;
DBUG_VOID_RETURN;
}
@@ -5354,21 +5636,21 @@ static void update_field_dependencies(THD *thd, Field *field, TABLE *table)
static Field *
find_field_in_view(THD *thd, TABLE_LIST *table_list,
- const char *name, uint length,
+ const char *name, size_t length,
const char *item_name, Item **ref,
bool register_tree_change)
{
DBUG_ENTER("find_field_in_view");
DBUG_PRINT("enter",
("view: '%s', field name: '%s', item name: '%s', ref %p",
- table_list->alias, name, item_name, ref));
+ table_list->alias.str, name, item_name, ref));
Field_iterator_view field_it;
field_it.set(table_list);
Query_arena *arena= 0, backup;
for (; !field_it.end_of_fields(); field_it.next())
{
- if (!my_strcasecmp(system_charset_info, field_it.name(), name))
+ if (!my_strcasecmp(system_charset_info, field_it.name()->str, name))
{
// in PS use own arena or data will be freed after prepare
if (register_tree_change &&
@@ -5390,27 +5672,10 @@ find_field_in_view(THD *thd, TABLE_LIST *table_list,
*ref != NULL means that *ref contains the item that we need to
replace. If the item was aliased by the user, set the alias to
the replacing item.
- We need to set alias on both ref itself and on ref real item.
*/
if (*ref && !(*ref)->is_autogenerated_name)
- {
- if (register_tree_change)
- {
- item->set_name_for_rollback(thd, (*ref)->name,
- (*ref)->name_length,
- system_charset_info);
- item->real_item()->set_name_for_rollback(thd, (*ref)->name,
- (*ref)->name_length,
- system_charset_info);
- }
- else
- {
- item->set_name(thd, (*ref)->name, (*ref)->name_length,
- system_charset_info);
- item->real_item()->set_name(thd, (*ref)->name, (*ref)->name_length,
- system_charset_info);
- }
- }
+ item->set_name(thd, (*ref)->name.str, (*ref)->name.length,
+ system_charset_info);
if (register_tree_change)
thd->change_item_tree(ref, item);
else
@@ -5452,8 +5717,7 @@ find_field_in_view(THD *thd, TABLE_LIST *table_list,
*/
static Field *
-find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name,
- uint length, Item **ref, bool register_tree_change,
+find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name, size_t length, Item **ref, bool register_tree_change,
TABLE_LIST **actual_table)
{
List_iterator_fast<Natural_join_column>
@@ -5470,7 +5734,7 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name,
for (nj_col= NULL, curr_nj_col= field_it++; curr_nj_col;
curr_nj_col= field_it++)
{
- if (!my_strcasecmp(system_charset_info, curr_nj_col->name(), name))
+ if (!my_strcasecmp(system_charset_info, curr_nj_col->name()->str, name))
{
if (nj_col)
{
@@ -5500,15 +5764,10 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name,
*ref != NULL means that *ref contains the item that we need to
replace. If the item was aliased by the user, set the alias to
the replacing item.
- We need to set alias on both ref itself and on ref real item.
*/
if (*ref && !(*ref)->is_autogenerated_name)
- {
- item->set_name(thd, (*ref)->name, (*ref)->name_length,
+ item->set_name(thd, (*ref)->name.str, (*ref)->name.length,
system_charset_info);
- item->real_item()->set_name(thd, (*ref)->name, (*ref)->name_length,
- system_charset_info);
- }
if (register_tree_change && arena)
thd->restore_active_arena(arena, &backup);
@@ -5541,11 +5800,10 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name,
calls fix_fields on that item), it's just a check during table
reopening for columns that was dropped by the concurrent connection.
*/
- if (!nj_col->table_field->fixed &&
- nj_col->table_field->fix_fields(thd, &ref))
+ if (nj_col->table_field->fix_fields_if_needed(thd, &ref))
{
DBUG_PRINT("info", ("column '%s' was dropped by the concurrent connection",
- nj_col->table_field->name));
+ nj_col->table_field->name.str));
DBUG_RETURN(NULL);
}
DBUG_ASSERT(ref == 0); // Should not have changed
@@ -5581,10 +5839,10 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name,
*/
Field *
-find_field_in_table(THD *thd, TABLE *table, const char *name, uint length,
+find_field_in_table(THD *thd, TABLE *table, const char *name, size_t length,
bool allow_rowid, uint *cached_field_index_ptr)
{
- Field **field_ptr, *field;
+ Field *field;
uint cached_field_index= *cached_field_index_ptr;
DBUG_ENTER("find_field_in_table");
DBUG_PRINT("enter", ("table: '%s', field name: '%s'", table->alias.c_ptr(),
@@ -5593,34 +5851,24 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length,
/* We assume here that table->field < NO_CACHED_FIELD_INDEX = UINT_MAX */
if (cached_field_index < table->s->fields &&
!my_strcasecmp(system_charset_info,
- table->field[cached_field_index]->field_name, name))
- field_ptr= table->field + cached_field_index;
- else if (table->s->name_hash.records)
- {
- field_ptr= (Field**) my_hash_search(&table->s->name_hash, (uchar*) name,
- length);
- if (field_ptr)
- {
- /*
- field_ptr points to field in TABLE_SHARE. Convert it to the matching
- field in table
- */
- field_ptr= (table->field + (field_ptr - table->s->field));
- }
- }
+ table->field[cached_field_index]->field_name.str, name))
+ field= table->field[cached_field_index];
else
{
- if (!(field_ptr= table->field))
- DBUG_RETURN((Field *)0);
- for (; *field_ptr; ++field_ptr)
- if (!my_strcasecmp(system_charset_info, (*field_ptr)->field_name, name))
- break;
+ LEX_CSTRING fname= {name, length};
+ field= table->find_field_by_name(&fname);
}
- if (field_ptr && *field_ptr)
+ if (field)
{
- *cached_field_index_ptr= (uint)(field_ptr - table->field);
- field= *field_ptr;
+ if (field->invisible == INVISIBLE_FULL &&
+ DBUG_EVALUATE_IF("test_completely_invisible", 0, 1))
+ DBUG_RETURN((Field*)0);
+
+ if (field->invisible == INVISIBLE_SYSTEM &&
+ thd->column_usage != MARK_COLUMNS_READ &&
+ thd->column_usage != COLUMNS_READ)
+ DBUG_RETURN((Field*)0);
}
else
{
@@ -5630,6 +5878,7 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length,
DBUG_RETURN((Field*) 0);
field= table->field[table->s->rowid_field_offset-1];
}
+ *cached_field_index_ptr= field->field_index;
update_field_dependencies(thd, field, table);
@@ -5649,6 +5898,7 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length,
item_name [in] name of item if it will be created (VIEW)
db_name [in] optional database name that qualifies the
table_name [in] optional table name that qualifies the field
+ 0 for non-qualified field in natural joins
ref [in/out] if 'name' is resolved to a view field, ref
is set to point to the found view field
check_privileges [in] check privileges
@@ -5683,7 +5933,7 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length,
Field *
find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
- const char *name, uint length,
+ const char *name, size_t length,
const char *item_name, const char *db_name,
const char *table_name, Item **ref,
bool check_privileges, bool allow_rowid,
@@ -5692,12 +5942,12 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
{
Field *fld;
DBUG_ENTER("find_field_in_table_ref");
- DBUG_ASSERT(table_list->alias);
+ DBUG_ASSERT(table_list->alias.str);
DBUG_ASSERT(name);
DBUG_ASSERT(item_name);
DBUG_PRINT("enter",
("table: '%s' field name: '%s' item name: '%s' ref %p",
- table_list->alias, name, item_name, ref));
+ table_list->alias.str, name, item_name, ref));
/*
Check that the table and database that qualify the current field name
@@ -5713,9 +5963,13 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
inside the view, but we want to search directly in the view columns
which are represented as a 'field_translation'.
- TODO: Ensure that table_name, db_name and tables->db always points to
- something !
+ tables->db.str may be 0 if we are preparing a statement
+ db_name is 0 if item doesn't have a db name
+ table_name is 0 if item doesn't have a specified table_name
*/
+ if (db_name && !db_name[0])
+ db_name= 0; // Simpler test later
+
if (/* Exclude nested joins. */
(!table_list->nested_join ||
/* Include merge views and information schema tables. */
@@ -5725,12 +5979,19 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
to search.
*/
table_name && table_name[0] &&
- (my_strcasecmp(table_alias_charset, table_list->alias, table_name) ||
- (db_name && db_name[0] && (!table_list->db || !table_list->db[0])) ||
- (db_name && db_name[0] && table_list->db && table_list->db[0] &&
+ (my_strcasecmp(table_alias_charset, table_list->alias.str, table_name) ||
+ (db_name && (!table_list->db.str || !table_list->db.str[0])) ||
+ (db_name && table_list->db.str && table_list->db.str[0] &&
(table_list->schema_table ?
- my_strcasecmp(system_charset_info, db_name, table_list->db) :
- strcmp(db_name, table_list->db)))))
+ my_strcasecmp(system_charset_info, db_name, table_list->db.str) :
+ strcmp(db_name, table_list->db.str)))))
+ DBUG_RETURN(0);
+
+ /*
+ Don't allow usage of fields in sequence table that is opened as part of
+ NEXT VALUE for sequence_name
+ */
+ if (table_list->sequence)
DBUG_RETURN(0);
*actual_table= NULL;
@@ -5790,11 +6051,11 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/* Check if there are sufficient access rights to the found field. */
if (check_privileges &&
- check_column_grant_in_table_ref(thd, *actual_table, name, length))
+ check_column_grant_in_table_ref(thd, *actual_table, name, length, fld))
fld= WRONG_GRANT;
else
#endif
- if (thd->mark_used_columns != MARK_COLUMNS_NONE)
+ if (should_mark_column(thd->column_usage))
{
/*
Get rw_set correct for this field so that the handler
@@ -5811,7 +6072,7 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
field_to_set= ((Item_field*)it)->field;
else
{
- if (thd->mark_used_columns == MARK_COLUMNS_READ)
+ if (thd->column_usage == MARK_COLUMNS_READ)
it->walk(&Item::register_field_in_read_map, 0, 0);
else
it->walk(&Item::register_field_in_write_map, 0, 0);
@@ -5822,7 +6083,8 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
if (field_to_set)
{
TABLE *table= field_to_set->table;
- if (thd->mark_used_columns == MARK_COLUMNS_READ)
+ DBUG_ASSERT(table);
+ if (thd->column_usage == MARK_COLUMNS_READ)
bitmap_set_bit(table->read_set, field_to_set->field_index);
else
bitmap_set_bit(table->write_set, field_to_set->field_index);
@@ -5869,7 +6131,8 @@ Field *find_field_in_table_sef(TABLE *table, const char *name)
if (!(field_ptr= table->field))
return (Field *)0;
for (; *field_ptr; ++field_ptr)
- if (!my_strcasecmp(system_charset_info, (*field_ptr)->field_name, name))
+ if (!my_strcasecmp(system_charset_info, (*field_ptr)->field_name.str,
+ name))
break;
}
if (field_ptr)
@@ -5922,8 +6185,8 @@ find_field_in_tables(THD *thd, Item_ident *item,
Field *found=0;
const char *db= item->db_name;
const char *table_name= item->table_name;
- const char *name= item->field_name;
- uint length=(uint) strlen(name);
+ const char *name= item->field_name.str;
+ size_t length= item->field_name.length;
char name_buff[SAFE_NAME_LEN+1];
TABLE_LIST *cur_table= first_table;
TABLE_LIST *actual_table;
@@ -5965,12 +6228,12 @@ find_field_in_tables(THD *thd, Item_ident *item,
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/* Check if there are sufficient access rights to the found field. */
if (found && check_privileges &&
- check_column_grant_in_table_ref(thd, table_ref, name, length))
+ check_column_grant_in_table_ref(thd, table_ref, name, length, found))
found= WRONG_GRANT;
#endif
}
else
- found= find_field_in_table_ref(thd, table_ref, name, length, item->name,
+ found= find_field_in_table_ref(thd, table_ref, name, length, item->name.str,
NULL, NULL, ref, check_privileges,
TRUE, &(item->cached_field_index),
register_tree_change,
@@ -6038,7 +6301,7 @@ find_field_in_tables(THD *thd, Item_ident *item,
cur_table= cur_table->next_name_resolution_table)
{
Field *cur_field= find_field_in_table_ref(thd, cur_table, name, length,
- item->name, db, table_name, ref,
+ item->name.str, db, table_name, ref,
(thd->lex->sql_command ==
SQLCOM_SHOW_FIELDS)
? false : check_privileges,
@@ -6055,7 +6318,7 @@ find_field_in_tables(THD *thd, Item_ident *item,
thd->clear_error();
cur_field= find_field_in_table_ref(thd, cur_table, name, length,
- item->name, db, table_name, ref,
+ item->name.str, db, table_name, ref,
false,
allow_rowid,
&(item->cached_field_index),
@@ -6064,7 +6327,7 @@ find_field_in_tables(THD *thd, Item_ident *item,
if (cur_field)
{
Field *nf=new Field_null(NULL,0,Field::NONE,
- cur_field->field_name,
+ &cur_field->field_name,
&my_charset_bin);
nf->init(cur_table->table);
cur_field= nf;
@@ -6086,7 +6349,7 @@ find_field_in_tables(THD *thd, Item_ident *item,
if (db)
return cur_field;
- if (found)
+ if (unlikely(found))
{
if (report_error == REPORT_ALL_ERRORS ||
report_error == IGNORE_EXCEPT_NON_UNIQUE)
@@ -6098,7 +6361,7 @@ find_field_in_tables(THD *thd, Item_ident *item,
}
}
- if (found)
+ if (likely(found))
return found;
/*
@@ -6180,7 +6443,7 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter,
uint n_items= limit == 0 ? items.elements : limit;
Item **found=0, **found_unaliased= 0, *item;
const char *db_name=0;
- const char *field_name=0;
+ const LEX_CSTRING *field_name= 0;
const char *table_name=0;
bool found_unaliased_non_uniq= 0;
/*
@@ -6196,7 +6459,7 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter,
find->type() == Item::REF_ITEM);
if (is_ref_by_name)
{
- field_name= ((Item_ident*) find)->field_name;
+ field_name= &((Item_ident*) find)->field_name;
table_name= ((Item_ident*) find)->table_name;
db_name= ((Item_ident*) find)->db_name;
}
@@ -6204,7 +6467,7 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter,
for (uint i= 0; i < n_items; i++)
{
item= li++;
- if (field_name &&
+ if (field_name && field_name->str &&
(item->real_item()->type() == Item::FIELD_ITEM ||
((item->type() == Item::REF_ITEM) &&
(((Item_ref *)item)->ref_type() == Item_ref::VIEW_REF))))
@@ -6217,7 +6480,7 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter,
(if this field created from expression argument of group_concat()),
=> we have to check presence of name before compare
*/
- if (!item_field->name)
+ if (unlikely(!item_field->name.str))
continue;
if (table_name)
@@ -6238,9 +6501,9 @@ 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 && item_field->table_name &&
- !my_strcasecmp(system_charset_info, item_field->field_name,
- field_name) &&
+ if (item_field->field_name.str && item_field->table_name &&
+ !lex_string_cmp(system_charset_info, &item_field->field_name,
+ field_name) &&
!my_strcasecmp(table_alias_charset, item_field->table_name,
table_name) &&
(!db_name || (item_field->db_name &&
@@ -6269,11 +6532,11 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter,
}
else
{
- int fname_cmp= my_strcasecmp(system_charset_info,
- item_field->field_name,
- field_name);
- if (!my_strcasecmp(system_charset_info,
- item_field->name,field_name))
+ bool fname_cmp= lex_string_cmp(system_charset_info,
+ &item_field->field_name,
+ field_name);
+ if (!lex_string_cmp(system_charset_info,
+ &item_field->name, field_name))
{
/*
If table name was not given we should scan through aliases
@@ -6317,8 +6580,9 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter,
}
else if (!table_name)
{
- if (is_ref_by_name && find->name && item->name &&
- !my_strcasecmp(system_charset_info,item->name,find->name))
+ if (is_ref_by_name && find->name.str && item->name.str &&
+ find->name.length == item->name.length &&
+ !lex_string_cmp(system_charset_info, &item->name, &find->name))
{
found= li.ref();
*counter= i;
@@ -6334,24 +6598,27 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter,
}
}
}
- if (!found)
+
+ if (likely(found))
+ return found;
+
+ if (unlikely(found_unaliased_non_uniq))
+ {
+ if (report_error != IGNORE_ERRORS)
+ my_error(ER_NON_UNIQ_ERROR, MYF(0),
+ find->full_name(), current_thd->where);
+ return (Item **) 0;
+ }
+ if (found_unaliased)
{
- if (found_unaliased_non_uniq)
- {
- if (report_error != IGNORE_ERRORS)
- my_error(ER_NON_UNIQ_ERROR, MYF(0),
- find->full_name(), current_thd->where);
- return (Item **) 0;
- }
- if (found_unaliased)
- {
- found= found_unaliased;
- *counter= unaliased_counter;
- *resolution= RESOLVED_BEHIND_ALIAS;
- }
+ found= found_unaliased;
+ *counter= unaliased_counter;
+ *resolution= RESOLVED_BEHIND_ALIAS;
}
+
if (found)
return found;
+
if (report_error != REPORT_EXCEPT_NOT_FOUND)
{
if (report_error == REPORT_ALL_ERRORS)
@@ -6471,6 +6738,8 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2,
Query_arena *arena, backup;
bool result= TRUE;
bool first_outer_loop= TRUE;
+ Field *field_1;
+ field_visibility_t field_1_invisible, field_2_invisible;
/*
Leaf table references to which new natural join columns are added
if the leaves are != NULL.
@@ -6484,7 +6753,7 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2,
DBUG_ENTER("mark_common_columns");
DBUG_PRINT("info", ("operand_1: %s operand_2: %s",
- table_ref_1->alias, table_ref_2->alias));
+ table_ref_1->alias.str, table_ref_2->alias.str));
*found_using_fields= 0;
arena= thd->activate_stmt_arena_if_needed(&backup);
@@ -6492,17 +6761,29 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2,
for (it_1.set(table_ref_1); !it_1.end_of_fields(); it_1.next())
{
bool found= FALSE;
- const char *field_name_1;
+ const LEX_CSTRING *field_name_1;
+ Field *field_2= 0;
+
/* true if field_name_1 is a member of using_fields */
bool is_using_column_1;
if (!(nj_col_1= it_1.get_or_create_column_ref(thd, leaf_1)))
goto err;
+
+ field_1= nj_col_1->field();
+ field_1_invisible= field_1 ? field_1->invisible : VISIBLE;
+
+ if (field_1_invisible == INVISIBLE_FULL)
+ continue;
+
field_name_1= nj_col_1->name();
is_using_column_1= using_fields &&
- test_if_string_in_list(field_name_1, using_fields);
+ test_if_string_in_list(field_name_1->str, using_fields);
DBUG_PRINT ("info", ("field_name_1=%s.%s",
- nj_col_1->table_name() ? nj_col_1->table_name() : "",
- field_name_1));
+ nj_col_1->safe_table_name(),
+ field_name_1->str));
+
+ if (field_1_invisible && !is_using_column_1)
+ continue;
/*
Find a field with the same name in table_ref_2.
@@ -6515,14 +6796,20 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2,
for (it_2.set(table_ref_2); !it_2.end_of_fields(); it_2.next())
{
Natural_join_column *cur_nj_col_2;
- const char *cur_field_name_2;
+ const LEX_CSTRING *cur_field_name_2;
if (!(cur_nj_col_2= it_2.get_or_create_column_ref(thd, leaf_2)))
goto err;
+
+ field_2= cur_nj_col_2->field();
+ field_2_invisible= field_2 ? field_2->invisible : VISIBLE;
+
+ if (field_2_invisible == INVISIBLE_FULL)
+ continue;
+
cur_field_name_2= cur_nj_col_2->name();
DBUG_PRINT ("info", ("cur_field_name_2=%s.%s",
- cur_nj_col_2->table_name() ?
- cur_nj_col_2->table_name() : "",
- cur_field_name_2));
+ cur_nj_col_2->safe_table_name(),
+ cur_field_name_2->str));
/*
Compare the two columns and check for duplicate common fields.
@@ -6535,17 +6822,21 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2,
here. These columns must be checked only on unqualified reference
by name (e.g. in SELECT list).
*/
- if (!my_strcasecmp(system_charset_info, field_name_1, cur_field_name_2))
+ if (!lex_string_cmp(system_charset_info, field_name_1,
+ cur_field_name_2))
{
DBUG_PRINT ("info", ("match c1.is_common=%d", nj_col_1->is_common));
- if (cur_nj_col_2->is_common ||
- (found && (!using_fields || is_using_column_1)))
+ if (cur_nj_col_2->is_common || found)
{
- my_error(ER_NON_UNIQ_ERROR, MYF(0), field_name_1, thd->where);
+ my_error(ER_NON_UNIQ_ERROR, MYF(0), field_name_1->str, thd->where);
goto err;
}
- nj_col_2= cur_nj_col_2;
- found= TRUE;
+ if ((!using_fields && !field_2_invisible) || is_using_column_1)
+ {
+ DBUG_ASSERT(nj_col_2 == NULL);
+ nj_col_2= cur_nj_col_2;
+ found= TRUE;
+ }
}
}
if (first_outer_loop && leaf_2)
@@ -6565,7 +6856,7 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2,
clause (if present), mark them as common fields, and add a new
equi-join condition to the ON clause.
*/
- if (nj_col_2 && (!using_fields ||is_using_column_1))
+ if (nj_col_2)
{
/*
Create non-fixed fully qualified field and let fix_fields to
@@ -6573,8 +6864,6 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2,
*/
Item *item_1= nj_col_1->create_item(thd);
Item *item_2= nj_col_2->create_item(thd);
- Field *field_1= nj_col_1->field();
- Field *field_2= nj_col_2->field();
Item_ident *item_ident_1, *item_ident_2;
Item_func_eq *eq_cond;
@@ -6614,11 +6903,6 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2,
if (!(eq_cond= new (thd->mem_root) Item_func_eq(thd, item_ident_1, item_ident_2)))
goto err; /* Out of memory. */
- if (field_1 && field_1->vcol_info)
- field_1->table->mark_virtual_col(field_1);
- if (field_2 && field_2->vcol_info)
- field_2->table->mark_virtual_col(field_2);
-
/*
Add the new equi-join condition to the ON clause. Notice that
fix_fields() is applied to all ON conditions in setup_conds()
@@ -6630,27 +6914,15 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2,
nj_col_1->is_common= nj_col_2->is_common= TRUE;
DBUG_PRINT ("info", ("%s.%s and %s.%s are common",
- nj_col_1->table_name() ?
- nj_col_1->table_name() : "",
- nj_col_1->name(),
- nj_col_2->table_name() ?
- nj_col_2->table_name() : "",
- nj_col_2->name()));
+ nj_col_1->safe_table_name(),
+ nj_col_1->name()->str,
+ nj_col_2->safe_table_name(),
+ nj_col_2->name()->str));
if (field_1)
- {
- TABLE *table_1= nj_col_1->table_ref->table;
- /* Mark field_1 used for table cache. */
- bitmap_set_bit(table_1->read_set, field_1->field_index);
- table_1->covering_keys.intersect(field_1->part_of_key);
- }
+ update_field_dependencies(thd, field_1, field_1->table);
if (field_2)
- {
- TABLE *table_2= nj_col_2->table_ref->table;
- /* Mark field_2 used for table cache. */
- bitmap_set_bit(table_2->read_set, field_2->field_index);
- table_2->covering_keys.intersect(field_2->part_of_key);
- }
+ update_field_dependencies(thd, field_2, field_2->table);
if (using_fields != NULL)
++(*found_using_fields);
@@ -6774,7 +7046,7 @@ store_natural_using_join_columns(THD *thd, TABLE_LIST *natural_using_join,
goto err;
}
if (!my_strcasecmp(system_charset_info,
- common_field->name(), using_field_name_ptr))
+ common_field->name()->str, using_field_name_ptr))
break; // Found match
}
}
@@ -7114,8 +7386,7 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
while (wild_num && (item= it++))
{
if (item->type() == Item::FIELD_ITEM &&
- ((Item_field*) item)->field_name &&
- ((Item_field*) item)->field_name[0] == '*' &&
+ ((Item_field*) item)->field_name.str == star_clex_str.str &&
!((Item_field*) item)->field)
{
uint elem= fields.elements;
@@ -7182,12 +7453,12 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
****************************************************************************/
bool setup_fields(THD *thd, Ref_ptr_array ref_pointer_array,
- List<Item> &fields, enum_mark_columns mark_used_columns,
+ List<Item> &fields, enum_column_usage column_usage,
List<Item> *sum_func_list, List<Item> *pre_fix,
bool allow_sum_func)
{
Item *item;
- enum_mark_columns save_mark_used_columns= thd->mark_used_columns;
+ enum_column_usage saved_column_usage= thd->column_usage;
nesting_map save_allow_sum_func= thd->lex->allow_sum_func;
List_iterator<Item> it(fields);
bool save_is_item_list_lookup;
@@ -7195,17 +7466,16 @@ bool setup_fields(THD *thd, Ref_ptr_array ref_pointer_array,
DBUG_ENTER("setup_fields");
DBUG_PRINT("enter", ("ref_pointer_array: %p", ref_pointer_array.array()));
- thd->mark_used_columns= mark_used_columns;
- DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
+ thd->column_usage= column_usage;
+ DBUG_PRINT("info", ("thd->column_usage: %d", thd->column_usage));
if (allow_sum_func)
- thd->lex->allow_sum_func|=
- (nesting_map)1 << thd->lex->current_select->nest_level;
+ thd->lex->allow_sum_func.set_bit(thd->lex->current_select->nest_level);
thd->where= THD::DEFAULT_WHERE;
save_is_item_list_lookup= thd->lex->current_select->is_item_list_lookup;
thd->lex->current_select->is_item_list_lookup= 0;
/*
- To prevent fail on forward lookup we fill it with zerows,
+ To prevent fail on forward lookup we fill it with zeroes,
then if we got pointer on zero after find_item_in_list we will know
that it is forward lookup.
@@ -7244,15 +7514,15 @@ bool setup_fields(THD *thd, Ref_ptr_array ref_pointer_array,
if (make_pre_fix)
pre_fix->push_back(item, thd->stmt_arena->mem_root);
- if ((!item->fixed && item->fix_fields(thd, it.ref())) ||
- (item= *(it.ref()))->check_cols(1))
+ if (item->fix_fields_if_needed_for_scalar(thd, it.ref()))
{
thd->lex->current_select->is_item_list_lookup= save_is_item_list_lookup;
thd->lex->allow_sum_func= save_allow_sum_func;
- thd->mark_used_columns= save_mark_used_columns;
- DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
+ thd->column_usage= saved_column_usage;
+ DBUG_PRINT("info", ("thd->column_usage: %d", thd->column_usage));
DBUG_RETURN(TRUE); /* purecov: inspected */
}
+ item= *(it.ref()); // Item might have changed in fix_fields()
if (!ref.is_null())
{
ref[0]= item;
@@ -7277,8 +7547,8 @@ bool setup_fields(THD *thd, Ref_ptr_array ref_pointer_array,
thd->lex->current_select->cur_pos_in_select_list= UNDEF_POS;
thd->lex->allow_sum_func= save_allow_sum_func;
- thd->mark_used_columns= save_mark_used_columns;
- DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
+ thd->column_usage= saved_column_usage;
+ DBUG_PRINT("info", ("thd->column_usage: %d", thd->column_usage));
DBUG_RETURN(MY_TEST(thd->is_error()));
}
@@ -7565,7 +7835,7 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table,
0)
{
my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), name->c_ptr(),
- table->pos_in_table_list->alias);
+ table->pos_in_table_list->alias.str);
map->set_all();
return 1;
}
@@ -7636,8 +7906,8 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name,
DBUG_ASSERT(tables->is_leaf_for_name_resolution());
if ((table_name && my_strcasecmp(table_alias_charset, table_name,
- tables->alias)) ||
- (db_name && strcmp(tables->db,db_name)))
+ tables->alias.str)) ||
+ (db_name && strcmp(tables->db.str, db_name)))
continue;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -7699,6 +7969,14 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name,
for (; !field_iterator.end_of_fields(); field_iterator.next())
{
+ /*
+ field() is always NULL for views (see, e.g. Field_iterator_view or
+ Field_iterator_natural_join).
+ But view fields can never be invisible.
+ */
+ if ((field= field_iterator.field()) && field->invisible != VISIBLE)
+ continue;
+
Item *item;
if (!(item= field_iterator.create_item(thd)))
@@ -7740,7 +8018,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name,
!(fld->have_privileges=
(get_column_grant(thd, field_iterator.grant(),
field_iterator.get_db_name(),
- field_table_name, fld->field_name) &
+ field_table_name, fld->field_name.str) &
VIEW_ANY_ACL)))
{
my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), "ANY",
@@ -7811,13 +8089,13 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name,
*/
if (!table_name)
my_error(ER_NO_TABLES_USED, MYF(0));
- else if (!db_name && !thd->db)
+ else if (!db_name && !thd->db.str)
my_error(ER_NO_DB_ERROR, MYF(0));
else
{
char name[FN_REFLEN];
my_snprintf(name, sizeof(name), "%s.%s",
- db_name ? db_name : thd->db, table_name);
+ db_name ? db_name : thd->get_db(), table_name);
my_error(ER_BAD_TABLE_ERROR, MYF(0), name);
}
@@ -7871,9 +8149,8 @@ bool setup_on_expr(THD *thd, TABLE_LIST *table, bool is_update)
{
thd->where="on clause";
embedded->on_expr->mark_as_condition_AND_part(embedded);
- if ((!embedded->on_expr->fixed &&
- embedded->on_expr->fix_fields(thd, &embedded->on_expr)) ||
- embedded->on_expr->check_cols(1))
+ if (embedded->on_expr->fix_fields_if_needed_for_bool(thd,
+ &embedded->on_expr))
return TRUE;
}
/*
@@ -7883,7 +8160,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;
- if (!(*left_expr)->fixed && (*left_expr)->fix_fields(thd, left_expr))
+ if ((*left_expr)->fix_fields_if_needed(thd, left_expr))
return TRUE;
}
@@ -7950,14 +8227,10 @@ int setup_conds(THD *thd, TABLE_LIST *tables, List<TABLE_LIST> &leaves,
TABLE_LIST *derived= select_lex->master_unit()->derived;
DBUG_ENTER("setup_conds");
- /* Do not fix conditions for the derived tables that have been merged */
- if (derived && derived->merged)
- DBUG_RETURN(0);
-
select_lex->is_item_list_lookup= 0;
- thd->mark_used_columns= MARK_COLUMNS_READ;
- DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
+ thd->column_usage= MARK_COLUMNS_READ;
+ DBUG_PRINT("info", ("thd->column_usage: %d", thd->column_usage));
select_lex->cond_count= 0;
select_lex->between_count= 0;
select_lex->max_equal_elems= 0;
@@ -7985,8 +8258,7 @@ int setup_conds(THD *thd, TABLE_LIST *tables, List<TABLE_LIST> &leaves,
if ((*conds)->type() == Item::FIELD_ITEM && !derived)
wrap_ident(thd, conds);
(*conds)->mark_as_condition_AND_part(NO_JOIN_NEST);
- if ((!(*conds)->fixed && (*conds)->fix_fields(thd, conds)) ||
- (*conds)->check_cols(1))
+ if ((*conds)->fix_fields_if_needed_for_bool(thd, conds))
goto err_no_arena;
}
@@ -8051,6 +8323,9 @@ fill_record(THD *thd, TABLE *table_arg, List<Item> &fields, List<Item> &values,
List_iterator_fast<Item> f(fields),v(values);
Item *value, *fld;
Item_field *field;
+ Field *rfield;
+ TABLE *table;
+ bool only_unvers_fields= update && table_arg->versioned();
bool save_abort_on_warning= thd->abort_on_warning;
bool save_no_errors= thd->no_errors;
DBUG_ENTER("fill_record");
@@ -8061,53 +8336,71 @@ fill_record(THD *thd, TABLE *table_arg, List<Item> &fields, List<Item> &values,
only one row.
*/
if (fields.elements)
- {
- /*
- On INSERT or UPDATE fields are checked to be from the same table,
- thus we safely can take table from the first field.
- */
- fld= (Item_field*)f++;
- if (!(field= fld->field_for_view_update()))
- {
- my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), fld->name);
- goto err;
- }
- DBUG_ASSERT(field->field->table == table_arg);
table_arg->auto_increment_field_not_null= FALSE;
- f.rewind();
- }
while ((fld= f++))
{
if (!(field= fld->field_for_view_update()))
{
- my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), fld->name);
+ my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), fld->name.str);
goto err;
}
value=v++;
- Field *rfield= field->field;
- TABLE* table= rfield->table;
+ DBUG_ASSERT(value);
+ rfield= field->field;
+ table= rfield->table;
if (table->next_number_field &&
rfield->field_index == table->next_number_field->field_index)
table->auto_increment_field_not_null= TRUE;
- if (rfield->vcol_info &&
+ const bool skip_sys_field= rfield->vers_sys_field(); // TODO: && !thd->vers_modify_history() [MDEV-16546]
+ if ((rfield->vcol_info || skip_sys_field) &&
!value->vcol_assignment_allowed_value() &&
table->s->table_category != TABLE_CATEGORY_TEMPORARY)
{
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
- ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN,
- ER_THD(thd, ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN),
- rfield->field_name, table->s->table_name.str);
+ ER_WARNING_NON_DEFAULT_VALUE_FOR_GENERATED_COLUMN,
+ ER_THD(thd, ER_WARNING_NON_DEFAULT_VALUE_FOR_GENERATED_COLUMN),
+ rfield->field_name.str, table->s->table_name.str);
}
- if (rfield->stored_in_db() &&
- (value->save_in_field(rfield, 0)) < 0 && !ignore_errors)
+ if (only_unvers_fields && !rfield->vers_update_unversioned())
+ only_unvers_fields= false;
+
+ if (rfield->stored_in_db())
{
- my_message(ER_UNKNOWN_ERROR, ER_THD(thd, ER_UNKNOWN_ERROR), MYF(0));
- goto err;
+ if (!skip_sys_field &&
+ unlikely(value->save_in_field(rfield, 0) < 0) && !ignore_errors)
+ {
+ my_message(ER_UNKNOWN_ERROR, ER_THD(thd, ER_UNKNOWN_ERROR), MYF(0));
+ goto err;
+ }
+ /*
+ In sql MODE_SIMULTANEOUS_ASSIGNMENT,
+ move field pointer on value stored in record[1]
+ which contains row before update (see MDEV-13417)
+ */
+ if (update && thd->variables.sql_mode & MODE_SIMULTANEOUS_ASSIGNMENT)
+ rfield->move_field_offset((my_ptrdiff_t) (table->record[1] -
+ table->record[0]));
}
rfield->set_has_explicit_value();
}
+ if (update && thd->variables.sql_mode & MODE_SIMULTANEOUS_ASSIGNMENT)
+ {
+ // restore fields pointers on record[0]
+ f.rewind();
+ while ((fld= f++))
+ {
+ rfield= fld->field_for_view_update()->field;
+ if (rfield->stored_in_db())
+ {
+ table= rfield->table;
+ rfield->move_field_offset((my_ptrdiff_t) (table->record[0] -
+ table->record[1]));
+ }
+ }
+ }
+
if (update)
table_arg->evaluate_update_default_function();
else
@@ -8119,6 +8412,8 @@ fill_record(THD *thd, TABLE *table_arg, List<Item> &fields, List<Item> &values,
if (table_arg->vfield &&
table_arg->update_virtual_fields(table_arg->file, VCOL_UPDATE_FOR_WRITE))
goto err;
+ if (table_arg->versioned() && !only_unvers_fields)
+ table_arg->vers_update_fields();
thd->abort_on_warning= save_abort_on_warning;
thd->no_errors= save_no_errors;
DBUG_RETURN(thd->is_error());
@@ -8171,7 +8466,7 @@ void switch_defaults_to_nullable_trigger_fields(TABLE *table)
Field **trigger_field= table->field_to_fill();
/* True if we have NOT NULL fields and BEFORE triggers */
- if (trigger_field != table->field)
+ if (*trigger_field != *table->field)
{
for (Field **field_ptr= table->default_field; *field_ptr ; field_ptr++)
{
@@ -8242,7 +8537,7 @@ fill_record_n_invoke_before_triggers(THD *thd, TABLE *table,
List<Item> &values, bool ignore_errors,
enum trg_event_type event)
{
- bool result;
+ int result;
Table_triggers_list *triggers= table->triggers;
result= fill_record(thd, table, fields, values, ignore_errors,
@@ -8309,7 +8604,6 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List<Item> &values,
? table->next_number_field->field_index
: ~0U;
DBUG_ENTER("fill_record");
-
if (!*ptr)
{
/* No fields to update, quite strange!*/
@@ -8332,17 +8626,25 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List<Item> &values,
/* Ensure that all fields are from the same table */
DBUG_ASSERT(field->table == table);
- value=v++;
+ if (unlikely(field->invisible))
+ continue;
+ else
+ value=v++;
+
+ bool vers_sys_field= table->versioned() && field->vers_sys_field();
+
if (field->field_index == autoinc_index)
table->auto_increment_field_not_null= TRUE;
- if (field->vcol_info &&
+ if ((unlikely(field->vcol_info) || (vers_sys_field && !ignore_errors)) &&
!value->vcol_assignment_allowed_value() &&
table->s->table_category != TABLE_CATEGORY_TEMPORARY)
{
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
- ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN,
- ER_THD(thd, ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN),
- field->field_name, table->s->table_name.str);
+ ER_WARNING_NON_DEFAULT_VALUE_FOR_GENERATED_COLUMN,
+ ER_THD(thd, ER_WARNING_NON_DEFAULT_VALUE_FOR_GENERATED_COLUMN),
+ field->field_name.str, table->s->table_name.str);
+ if (vers_sys_field)
+ continue;
}
if (use_value)
@@ -8357,6 +8659,8 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List<Item> &values,
if (table->vfield &&
table->update_virtual_fields(table->file, VCOL_UPDATE_FOR_WRITE))
goto err;
+ if (table->versioned())
+ table->vers_update_fields();
thd->abort_on_warning= abort_on_warning_saved;
DBUG_RETURN(thd->is_error());
@@ -8447,8 +8751,8 @@ my_bool mysql_rm_tmp_tables(void)
if (!strncmp(file->name, tmp_file_prefix, tmp_file_prefix_length))
{
char *ext= fn_ext(file->name);
- uint ext_len= strlen(ext);
- uint filePath_len= my_snprintf(filePath, sizeof(filePath),
+ size_t ext_len= strlen(ext);
+ size_t filePath_len= my_snprintf(filePath, sizeof(filePath),
"%s%c%s", tmpdir, FN_LIBCHAR,
file->name);
if (!strcmp(reg_ext, ext))
@@ -8508,13 +8812,25 @@ int setup_ftfuncs(SELECT_LEX *select_lex)
}
+void cleanup_ftfuncs(SELECT_LEX *select_lex)
+{
+ List_iterator<Item_func_match> li(*(select_lex->ftfunc_list)),
+ lj(*(select_lex->ftfunc_list));
+ Item_func_match *ftf;
+
+ while ((ftf=li++))
+ {
+ ftf->cleanup();
+ }
+}
+
+
int init_ftfuncs(THD *thd, SELECT_LEX *select_lex, bool no_order)
{
if (select_lex->ftfunc_list->elements)
{
List_iterator<Item_func_match> li(*(select_lex->ftfunc_list));
Item_func_match *ifm;
- DBUG_PRINT("info",("Performing FULLTEXT search"));
while ((ifm=li++))
if (unlikely(!ifm->fixed))
@@ -8523,14 +8839,14 @@ int init_ftfuncs(THD *thd, SELECT_LEX *select_lex, bool no_order)
to remove the function from the list.
*/
li.remove();
- else
- ifm->init_search(thd, no_order);
+ else if (ifm->init_search(thd, no_order))
+ return 1;
}
return 0;
}
-bool is_equal(const LEX_STRING *a, const LEX_STRING *b)
+bool is_equal(const LEX_CSTRING *a, const LEX_CSTRING *b)
{
return a->length == b->length && !strncmp(a->str, b->str, a->length);
}
@@ -8713,7 +9029,7 @@ open_log_table(THD *thd, TABLE_LIST *one_table, Open_tables_backup *backup)
DBUG_ASSERT(table->s->table_category == TABLE_CATEGORY_LOG);
/* Make sure all columns get assigned to a default value */
table->use_all_columns();
- DBUG_ASSERT(table->no_replicate);
+ DBUG_ASSERT(table->s->no_replicate);
}
else
thd->restore_backup_open_tables_state(backup);
diff --git a/sql/sql_base.h b/sql/sql_base.h
index b67341bcbda..c9fb9bc6a62 100644
--- a/sql/sql_base.h
+++ b/sql/sql_base.h
@@ -18,7 +18,7 @@
#ifndef SQL_BASE_INCLUDED
#define SQL_BASE_INCLUDED
-#include "sql_class.h" /* enum_mark_columns */
+#include "sql_class.h" /* enum_column_usage */
#include "sql_trigger.h" /* trg_event_type */
#include "mysqld.h" /* key_map */
#include "table_cache.h"
@@ -107,6 +107,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update,
*/
#define MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK 0x1000
#define MYSQL_LOCK_NOT_TEMPORARY 0x2000
+#define MYSQL_LOCK_USE_MALLOC 0x4000
/**
Only check THD::killed if waits happen (e.g. wait on MDL, wait on
table flush, wait on thr_lock.c locks) while opening and locking table.
@@ -141,12 +142,12 @@ thr_lock_type read_lock_type_for_table(THD *thd,
my_bool mysql_rm_tmp_tables(void);
void close_tables_for_reopen(THD *thd, TABLE_LIST **tables,
const MDL_savepoint &start_of_statement_svp);
-bool table_already_fk_prelocked(TABLE_LIST *tl, LEX_STRING *db,
- LEX_STRING *table, thr_lock_type lock_type);
+bool table_already_fk_prelocked(TABLE_LIST *tl, LEX_CSTRING *db,
+ LEX_CSTRING *table, thr_lock_type lock_type);
TABLE_LIST *find_table_in_list(TABLE_LIST *table,
TABLE_LIST *TABLE_LIST::*link,
- const char *db_name,
- const char *table_name);
+ const LEX_CSTRING *db_name,
+ const LEX_CSTRING *table_name);
void close_thread_tables(THD *thd);
void switch_to_nullable_trigger_fields(List<Item> &items, TABLE *);
void switch_defaults_to_nullable_trigger_fields(TABLE *table);
@@ -169,7 +170,7 @@ void make_leaves_list(THD *thd, List<TABLE_LIST> &list, TABLE_LIST *tables,
int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
List<Item> *sum_func_list, uint wild_num, uint * hidden_bit_fields);
bool setup_fields(THD *thd, Ref_ptr_array ref_pointer_array,
- List<Item> &item, enum_mark_columns mark_used_columns,
+ List<Item> &item, enum_column_usage column_usage,
List<Item> *sum_func_list, List<Item> *pre_fix,
bool allow_sum_func);
void unfix_fields(List<Item> &items);
@@ -185,14 +186,14 @@ find_field_in_tables(THD *thd, Item_ident *item,
bool check_privileges, bool register_tree_change);
Field *
find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
- const char *name, uint length,
+ const char *name, size_t length,
const char *item_name, const char *db_name,
const char *table_name, Item **ref,
bool check_privileges, bool allow_rowid,
uint *cached_field_index_ptr,
bool register_tree_change, TABLE_LIST **actual_table);
Field *
-find_field_in_table(THD *thd, TABLE *table, const char *name, uint length,
+find_field_in_table(THD *thd, TABLE *table, const char *name, size_t length,
bool allow_rowid, uint *cached_field_index_ptr);
Field *
find_field_in_table_sef(TABLE *table, const char *name);
@@ -215,8 +216,8 @@ bool setup_tables_and_check_access(THD *thd,
bool wait_while_table_is_used(THD *thd, TABLE *table,
enum ha_extra_function function);
-void drop_open_table(THD *thd, TABLE *table, const char *db_name,
- const char *table_name);
+void drop_open_table(THD *thd, TABLE *table, const LEX_CSTRING *db_name,
+ const LEX_CSTRING *table_name);
void update_non_unique_table_error(TABLE_LIST *update,
const char *operation,
TABLE_LIST *duplicate);
@@ -224,6 +225,7 @@ int setup_conds(THD *thd, TABLE_LIST *tables, List<TABLE_LIST> &leaves,
COND **conds);
void wrap_ident(THD *thd, Item **conds);
int setup_ftfuncs(SELECT_LEX* select);
+void cleanup_ftfuncs(SELECT_LEX *select_lex);
int init_ftfuncs(THD *thd, SELECT_LEX* select, bool no_order);
bool lock_table_names(THD *thd, const DDL_options_st &options,
TABLE_LIST *table_list,
@@ -267,12 +269,15 @@ TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l,
Prelocking_strategy *prelocking_strategy);
bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags,
uint dt_phases);
+bool open_tables_only_view_structure(THD *thd, TABLE_LIST *tables,
+ bool can_deadlock);
+bool open_and_lock_internal_tables(TABLE *table, bool lock);
bool lock_tables(THD *thd, TABLE_LIST *tables, uint counter, uint flags);
int decide_logging_format(THD *thd, TABLE_LIST *tables);
void close_thread_table(THD *thd, TABLE **table_ptr);
TABLE_LIST *unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list,
uint check_flag);
-bool is_equal(const LEX_STRING *a, const LEX_STRING *b);
+bool is_equal(const LEX_CSTRING *a, const LEX_CSTRING *b);
class Open_tables_backup;
/* Functions to work with system tables. */
@@ -290,7 +295,7 @@ void close_performance_schema_table(THD *thd, Open_tables_state *backup);
bool close_cached_tables(THD *thd, TABLE_LIST *tables,
bool wait_for_refresh, ulong timeout);
-bool close_cached_connection_tables(THD *thd, LEX_STRING *connect_string);
+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);
@@ -353,31 +358,22 @@ inline void setup_table_map(TABLE *table, TABLE_LIST *table_list, uint tablenr)
}
inline TABLE_LIST *find_table_in_global_list(TABLE_LIST *table,
- const char *db_name,
- const char *table_name)
+ LEX_CSTRING *db_name,
+ LEX_CSTRING *table_name)
{
return find_table_in_list(table, &TABLE_LIST::next_global,
db_name, table_name);
}
-inline TABLE_LIST *find_table_in_local_list(TABLE_LIST *table,
- const char *db_name,
- const char *table_name)
-{
- return find_table_in_list(table, &TABLE_LIST::next_local,
- db_name, table_name);
-}
-
-
inline bool setup_fields_with_no_wrap(THD *thd, Ref_ptr_array ref_pointer_array,
List<Item> &item,
- enum_mark_columns mark_used_columns,
+ enum_column_usage column_usage,
List<Item> *sum_func_list,
bool allow_sum_func)
{
bool res;
thd->lex->select_lex.no_wrap_view_item= TRUE;
- res= setup_fields(thd, ref_pointer_array, item, mark_used_columns,
+ res= setup_fields(thd, ref_pointer_array, item, column_usage,
sum_func_list, NULL, allow_sum_func);
thd->lex->select_lex.no_wrap_view_item= FALSE;
return res;
diff --git a/sql/sql_binlog.cc b/sql/sql_binlog.cc
index 59d7e9cb028..1b8ba311a80 100644
--- a/sql/sql_binlog.cc
+++ b/sql/sql_binlog.cc
@@ -14,7 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "sql_binlog.h"
#include "sql_parse.h"
@@ -125,7 +125,7 @@ static int check_event_type(int type, Relay_log_info *rli)
int binlog_defragment(THD *thd)
{
user_var_entry *entry[2];
- LEX_STRING name[2]= { thd->lex->comment, thd->lex->ident };
+ LEX_CSTRING name[2]= { thd->lex->comment, thd->lex->ident };
/* compute the total size */
thd->lex->comment.str= NULL;
@@ -155,7 +155,7 @@ int binlog_defragment(THD *thd)
size_t gathered_length= 0;
for (uint k=0; k < 2; k++)
{
- memcpy(thd->lex->comment.str + gathered_length, entry[k]->value,
+ memcpy(const_cast<char*>(thd->lex->comment.str) + gathered_length, entry[k]->value,
entry[k]->length);
gathered_length += entry[k]->length;
}
@@ -242,7 +242,7 @@ void mysql_client_binlog_statement(THD* thd)
goto end;
}
- decoded_len= my_base64_needed_decoded_length(coded_len);
+ decoded_len= my_base64_needed_decoded_length((int)coded_len);
if (!(buf= (char *) my_malloc(decoded_len, MYF(MY_WME))))
{
my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), 1);
@@ -388,7 +388,7 @@ void mysql_client_binlog_statement(THD* thd)
end:
if (unlikely(is_fragmented))
- my_free(thd->lex->comment.str);
+ my_free(const_cast<char*>(thd->lex->comment.str));
thd->variables.option_bits= thd_options;
rgi->slave_close_thread_tables(thd);
my_free(buf);
diff --git a/sql/sql_bitmap.h b/sql/sql_bitmap.h
index fb40d003ccd..ca9ba0b67f4 100644
--- a/sql/sql_bitmap.h
+++ b/sql/sql_bitmap.h
@@ -141,16 +141,16 @@ public:
};
};
-/* An iterator to quickly walk over bits in unlonglong bitmap. */
+/* 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) {}
- int next_bit()
+ uint next_bit()
{
- static const char last_bit[16]= {32, 0, 1, 0,
+ static const uchar last_bit[16]= {32, 0, 1, 0,
2, 0, 1, 0,
3, 0, 1, 0,
2, 0, 1, 0};
@@ -162,10 +162,10 @@ public:
if (!bmp)
return BITMAP_END;
}
- bmp &= ~(1LL << bit);
+ bmp &= ~(1ULL << bit);
return no + bit;
}
- int operator++(int) { return next_bit(); }
+ uint operator++(int) { return next_bit(); }
enum { BITMAP_END= 64 };
};
@@ -201,7 +201,10 @@ public:
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(map,buf,16); return buf; }
+ char *print(char *buf) const {
+ longlong2str(longlong(map), buf, 16);
+ return buf;
+ }
ulonglong to_ulonglong() const { return map; }
class Iterator : public Table_map_iterator
{
diff --git a/sql/sql_bootstrap.cc b/sql/sql_bootstrap.cc
index 3db985f9d16..a8c930820a7 100644
--- a/sql/sql_bootstrap.cc
+++ b/sql/sql_bootstrap.cc
@@ -14,7 +14,7 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include <ctype.h>
#include <string.h>
#include "sql_bootstrap.h"
diff --git a/sql/sql_builtin.cc.in b/sql/sql_builtin.cc.in
index e666985eb86..5ac044afd5d 100644
--- a/sql/sql_builtin.cc.in
+++ b/sql/sql_builtin.cc.in
@@ -11,7 +11,13 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA
+*/
+
+/*
+ Note that sql_builtin.cc is automatically built by sql_bultin.cc.in
+ and cmake/plugin.cmake
+*/
#include <my_global.h>
#include <mysql/plugin.h>
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index 8555ae6ee3e..e2c98715b12 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -328,7 +328,7 @@ TODO list:
(This could be done with almost no speed penalty)
*/
-#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
#include "sql_basic_types.h"
#include "sql_cache.h"
@@ -345,7 +345,6 @@ TODO list:
#include "../storage/myisammrg/ha_myisammrg.h"
#include "../storage/myisammrg/myrg_def.h"
#include "probes_mysql.h"
-#include "log_slow.h"
#include "transaction.h"
#include "strfunc.h"
@@ -818,10 +817,10 @@ inline Query_cache_block * Query_cache_block_table::block()
Query_cache_block method(s)
*****************************************************************************/
-void Query_cache_block::init(ulong block_length)
+void Query_cache_block::init(size_t block_length)
{
DBUG_ENTER("Query_cache_block::init");
- DBUG_PRINT("qcache", ("init block: %p length: %lu", this,
+ DBUG_PRINT("qcache", ("init block: %p length: %zu", this,
block_length));
length = block_length;
used = 0;
@@ -962,7 +961,7 @@ inline void Query_cache_query::unlock_reading()
void Query_cache_query::init_n_lock()
{
DBUG_ENTER("Query_cache_query::init_n_lock");
- res=0; wri = 0; len = 0; ready= 0;
+ res=0; wri = 0; len = 0; ready= 0; hit_count = 0;
mysql_rwlock_init(key_rwlock_query_cache_query_lock, &lock);
lock_writing();
DBUG_PRINT("qcache", ("inited & locked query for block %p",
@@ -1038,7 +1037,7 @@ uchar *query_cache_query_get_key(const uchar *record, size_t *length,
/**
libmysql convenience wrapper to insert data into query cache.
*/
-void query_cache_insert(void *thd_arg, const char *packet, ulong length,
+void query_cache_insert(void *thd_arg, const char *packet, size_t length,
unsigned pkt_nr)
{
THD *thd= (THD*) thd_arg;
@@ -1050,11 +1049,11 @@ void query_cache_insert(void *thd_arg, const char *packet, ulong length,
called for this thread.
*/
- if (!thd)
+ if (unlikely(!thd))
return;
query_cache.insert(thd, &thd->query_cache_tls,
- packet, length,
+ packet, (size_t)length,
pkt_nr);
}
@@ -1065,7 +1064,7 @@ void query_cache_insert(void *thd_arg, const char *packet, ulong length,
void
Query_cache::insert(THD *thd, Query_cache_tls *query_cache_tls,
- const char *packet, ulong length,
+ const char *packet, size_t length,
unsigned pkt_nr)
{
DBUG_ENTER("Query_cache::insert");
@@ -1098,7 +1097,7 @@ Query_cache::insert(THD *thd, Query_cache_tls *query_cache_tls,
Query_cache_block *result= header->result();
DUMP(this);
- DBUG_PRINT("qcache", ("insert packet %lu bytes long",length));
+ DBUG_PRINT("qcache", ("insert packet %zu bytes long",length));
/*
On success, STRUCT_UNLOCK is done by append_result_data. Otherwise, we
@@ -1207,8 +1206,8 @@ void Query_cache::end_of_result(THD *thd)
BLOCK_LOCK_WR(query_block);
Query_cache_query *header= query_block->query();
Query_cache_block *last_result_block;
- ulong allign_size;
- ulong len;
+ size_t allign_size;
+ size_t len;
if (header->result() == 0)
{
@@ -1268,9 +1267,9 @@ void mysql_query_cache_invalidate4(THD *thd,
Query_cache methods
*****************************************************************************/
-Query_cache::Query_cache(ulong query_cache_limit_arg,
- ulong min_allocation_unit_arg,
- ulong min_result_data_size_arg,
+Query_cache::Query_cache(size_t query_cache_limit_arg,
+ size_t min_allocation_unit_arg,
+ size_t min_result_data_size_arg,
uint def_query_hash_size_arg,
uint def_table_hash_size_arg)
:query_cache_size(0),
@@ -1284,7 +1283,7 @@ Query_cache::Query_cache(ulong query_cache_limit_arg,
def_table_hash_size(ALIGN_SIZE(def_table_hash_size_arg)),
initialized(0)
{
- ulong min_needed= (ALIGN_SIZE(sizeof(Query_cache_block)) +
+ size_t min_needed= (ALIGN_SIZE(sizeof(Query_cache_block)) +
ALIGN_SIZE(sizeof(Query_cache_block_table)) +
ALIGN_SIZE(sizeof(Query_cache_query)) + 3);
set_if_bigger(min_allocation_unit,min_needed);
@@ -1293,11 +1292,11 @@ Query_cache::Query_cache(ulong query_cache_limit_arg,
}
-ulong Query_cache::resize(ulong query_cache_size_arg)
+size_t Query_cache::resize(size_t query_cache_size_arg)
{
- ulong new_query_cache_size;
+ size_t new_query_cache_size;
DBUG_ENTER("Query_cache::resize");
- DBUG_PRINT("qcache", ("from %lu to %lu",query_cache_size,
+ DBUG_PRINT("qcache", ("from %zu to %zu",query_cache_size,
query_cache_size_arg));
DBUG_ASSERT(initialized);
@@ -1351,7 +1350,7 @@ ulong Query_cache::resize(ulong query_cache_size_arg)
}
-ulong Query_cache::set_min_res_unit(ulong size)
+size_t Query_cache::set_min_res_unit(size_t size)
{
DBUG_ASSERT(size % 8 == 0);
if (size < min_allocation_unit)
@@ -1363,7 +1362,7 @@ ulong Query_cache::set_min_res_unit(ulong size)
void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
{
TABLE_COUNTER_TYPE local_tables;
- ulong tot_length;
+ size_t tot_length;
const char *query;
size_t query_length;
uint8 tables_type;
@@ -1451,8 +1450,8 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
DBUG_PRINT("qcache", ("\
long %d, 4.1: %d, eof: %d, bin_proto: %d, more results %d, pkt_nr: %d, \
CS client: %u, CS result: %u, CS conn: %u, limit: %llu, TZ: %p, \
-sql mode: 0x%llx, sort len: %llu, conncat len: %llu, div_precision: %lu, \
-def_week_frmt: %lu, in_trans: %d, autocommit: %d",
+sql mode: 0x%llx, sort len: %llu, conncat len: %llu, div_precision: %zu, \
+def_week_frmt: %zu, in_trans: %d, autocommit: %d",
(int)flags.client_long_flag,
(int)flags.client_protocol_41,
(int)flags.client_depr_eof,
@@ -1503,18 +1502,18 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
query_length= thd->base_query.length();
/* Key is query + database + flag */
- if (thd->db_length)
+ if (thd->db.length)
{
memcpy((char*) (query + query_length + 1 + QUERY_CACHE_DB_LENGTH_SIZE),
- thd->db, thd->db_length);
+ thd->db.str, thd->db.length);
DBUG_PRINT("qcache", ("database: %s length: %u",
- thd->db, (unsigned) thd->db_length));
+ thd->db.str, (unsigned) thd->db.length));
}
else
{
DBUG_PRINT("qcache", ("No active database"));
}
- tot_length= (query_length + thd->db_length + 1 +
+ tot_length= (query_length + thd->db.length + 1 +
QUERY_CACHE_DB_LENGTH_SIZE + QUERY_CACHE_FLAGS_SIZE);
/*
We should only copy structure (don't use it location directly)
@@ -1536,7 +1535,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
Query_cache_block::QUERY, local_tables);
if (query_block != 0)
{
- DBUG_PRINT("qcache", ("query block %p allocated, %lu",
+ DBUG_PRINT("qcache", ("query block %p allocated, %zu",
query_block, query_block->used));
Query_cache_query *header = query_block->query();
@@ -1616,7 +1615,7 @@ end:
@retval TRUE On error
*/
static bool
-send_data_in_chunks(NET *net, const uchar *packet, ulong len)
+send_data_in_chunks(NET *net, const uchar *packet, size_t len)
{
/*
On the client we may require more memory than max_allowed_packet
@@ -1632,7 +1631,7 @@ send_data_in_chunks(NET *net, const uchar *packet, ulong len)
for max_allowed_packet, but large enough to ensure there is no
unnecessary overhead from too many syscalls per result set.
*/
- static const ulong MAX_CHUNK_LENGTH= 1024*1024;
+ static const size_t MAX_CHUNK_LENGTH= 1024*1024;
while (len > MAX_CHUNK_LENGTH)
{
@@ -1700,7 +1699,7 @@ size_t build_normalized_name(char *buff, size_t bufflen,
In case of -1, no error is sent to the client.
*) The buffer must be allocated memory of size:
- tot_length= query_length + thd->db_length + 1 + QUERY_CACHE_FLAGS_SIZE;
+ tot_length= query_length + thd->db.length + 1 + QUERY_CACHE_FLAGS_SIZE;
*/
int
@@ -1713,7 +1712,7 @@ Query_cache::send_result_to_client(THD *thd, char *org_sql, uint query_length)
#endif
Query_cache_block *result_block;
Query_cache_block_table *block_table, *block_table_end;
- ulong tot_length;
+ size_t tot_length;
Query_cache_query_flags flags;
const char *sql, *sql_end, *found_brace= 0;
DBUG_ENTER("Query_cache::send_result_to_client");
@@ -1859,7 +1858,7 @@ Query_cache::send_result_to_client(THD *thd, char *org_sql, uint query_length)
as the previous one.
*/
size_t db_len= uint2korr(sql_end+1);
- if (thd->db_length != db_len)
+ if (thd->db.length != db_len)
{
/*
We should probably reallocate the buffer in this case,
@@ -1893,7 +1892,7 @@ Query_cache::send_result_to_client(THD *thd, char *org_sql, uint query_length)
if (found_brace)
sql= found_brace;
make_base_query(&thd->base_query, sql, (size_t) (sql_end - sql),
- thd->db_length + 1 + QUERY_CACHE_DB_LENGTH_SIZE +
+ thd->db.length + 1 + QUERY_CACHE_DB_LENGTH_SIZE +
QUERY_CACHE_FLAGS_SIZE);
sql= thd->base_query.ptr();
query_length= thd->base_query.length();
@@ -1905,14 +1904,14 @@ Query_cache::send_result_to_client(THD *thd, char *org_sql, uint query_length)
}
tot_length= (query_length + 1 + QUERY_CACHE_DB_LENGTH_SIZE +
- thd->db_length + QUERY_CACHE_FLAGS_SIZE);
+ thd->db.length + QUERY_CACHE_FLAGS_SIZE);
- if (thd->db_length)
+ if (thd->db.length)
{
memcpy((uchar*) sql + query_length + 1 + QUERY_CACHE_DB_LENGTH_SIZE,
- thd->db, thd->db_length);
+ thd->db.str, thd->db.length);
DBUG_PRINT("qcache", ("database: '%s' length: %u",
- thd->db, (uint) thd->db_length));
+ thd->db.str, (uint) thd->db.length));
}
else
{
@@ -1951,8 +1950,8 @@ Query_cache::send_result_to_client(THD *thd, char *org_sql, uint query_length)
DBUG_PRINT("qcache", ("\
long %d, 4.1: %d, eof: %d, bin_proto: %d, more results %d, pkt_nr: %d, \
CS client: %u, CS result: %u, CS conn: %u, limit: %llu, TZ: %p, \
-sql mode: 0x%llx, sort len: %llu, conncat len: %llu, div_precision: %lu, \
-def_week_frmt: %lu, in_trans: %d, autocommit: %d",
+sql mode: 0x%llx, sort len: %llu, conncat len: %llu, div_precision: %zu, \
+def_week_frmt: %zu, in_trans: %d, autocommit: %d",
(int)flags.client_long_flag,
(int)flags.client_protocol_41,
(int)flags.client_depr_eof,
@@ -2068,14 +2067,17 @@ lookup:
}
bzero((char*) &table_list,sizeof(table_list));
- table_list.db = table->db();
- table_list.alias= table_list.table_name= table->table();
+ table_list.db.str= table->db();
+ table_list.db.length= strlen(table_list.db.str);
+ table_list.alias.str= table_list.table_name.str= table->table();
+ table_list.alias.length= table_list.table_name.length= strlen(table->table());
+
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (check_table_access(thd,SELECT_ACL,&table_list, FALSE, 1,TRUE))
{
DBUG_PRINT("qcache",
("probably no SELECT access to %s.%s => return to normal processing",
- table_list.db, table_list.alias));
+ table_list.db.str, table_list.alias.str));
unlock();
thd->query_cache_is_applicable= 0; // Query can't be cached
thd->lex->safe_to_cache_query= 0; // For prepared statements
@@ -2085,7 +2087,7 @@ lookup:
if (table_list.grant.want_privilege)
{
DBUG_PRINT("qcache", ("Need to check column privileges for %s.%s",
- table_list.db, table_list.alias));
+ table_list.db.str, table_list.alias.str));
BLOCK_UNLOCK_RD(query_block);
thd->query_cache_is_applicable= 0; // Query can't be cached
thd->lex->safe_to_cache_query= 0; // For prepared statements
@@ -2096,7 +2098,7 @@ lookup:
if (table->callback())
{
char qcache_se_key_name[FN_REFLEN + 10];
- uint qcache_se_key_len, db_length= strlen(table->db());
+ size_t qcache_se_key_len, db_length= strlen(table->db());
engine_data= table->engine_data();
qcache_se_key_len= build_normalized_name(qcache_se_key_name,
@@ -2110,16 +2112,16 @@ lookup:
table->suffix_length());
if (!(*table->callback())(thd, qcache_se_key_name,
- qcache_se_key_len, &engine_data))
+ (uint)qcache_se_key_len, &engine_data))
{
DBUG_PRINT("qcache", ("Handler does not allow caching for %.*s",
- qcache_se_key_len, qcache_se_key_name));
+ (int)qcache_se_key_len, qcache_se_key_name));
BLOCK_UNLOCK_RD(query_block);
if (engine_data != table->engine_data())
{
DBUG_PRINT("qcache",
("Handler require invalidation queries of %.*s %llu-%llu",
- qcache_se_key_len, qcache_se_key_name,
+ (int)qcache_se_key_len, qcache_se_key_name,
engine_data, table->engine_data()));
invalidate_table_internal(thd,
(uchar *) table->db(),
@@ -2145,10 +2147,11 @@ lookup:
}
else
DBUG_PRINT("qcache", ("handler allow caching %s,%s",
- table_list.db, table_list.alias));
+ table_list.db.str, table_list.alias.str));
}
move_to_query_list_end(query_block);
hits++;
+ query->increment_hits();
unlock();
/*
@@ -2158,7 +2161,7 @@ lookup:
THD_STAGE_INFO(thd, stage_sending_cached_result_to_client);
do
{
- DBUG_PRINT("qcache", ("Results (len: %lu used: %lu headers: %u)",
+ DBUG_PRINT("qcache", ("Results (len: %zu used: %zu headers: %u)",
result_block->length, result_block->used,
(uint) (result_block->headers_len()+
ALIGN_SIZE(sizeof(Query_cache_result)))));
@@ -2320,7 +2323,7 @@ void Query_cache::invalidate(THD *thd, TABLE *table,
DBUG_VOID_RETURN;
}
-void Query_cache::invalidate(THD *thd, const char *key, uint32 key_length,
+void Query_cache::invalidate(THD *thd, const char *key, size_t key_length,
my_bool using_transactions)
{
DBUG_ENTER("Query_cache::invalidate (key)");
@@ -2341,13 +2344,13 @@ void Query_cache::invalidate(THD *thd, const char *key, uint32 key_length,
Remove all cached queries that uses the given database.
*/
-void Query_cache::invalidate(THD *thd, char *db)
+void Query_cache::invalidate(THD *thd, const char *db)
{
DBUG_ENTER("Query_cache::invalidate (db)");
if (is_disabled())
DBUG_VOID_RETURN;
- DBUG_ASSERT(ok_for_lower_case_names(db));
+ DBUG_SLOW_ASSERT(ok_for_lower_case_names(db));
bool restart= FALSE;
/*
@@ -2464,7 +2467,7 @@ void Query_cache::flush()
*/
-void Query_cache::pack(THD *thd, ulong join_limit, uint iteration_limit)
+void Query_cache::pack(THD *thd, size_t join_limit, uint iteration_limit)
{
DBUG_ENTER("Query_cache::pack");
@@ -2584,11 +2587,11 @@ void Query_cache::init()
}
-ulong Query_cache::init_cache()
+size_t Query_cache::init_cache()
{
- uint mem_bin_count, num, step;
- ulong mem_bin_size, prev_size, inc;
- ulong additional_data_size, max_mem_bin_size, approx_additional_data_size;
+ size_t mem_bin_count, num, step;
+ size_t mem_bin_size, prev_size, inc;
+ size_t max_mem_bin_size, approx_additional_data_size;
int align;
DBUG_ENTER("Query_cache::init_cache");
@@ -2653,8 +2656,15 @@ ulong Query_cache::init_cache()
if (!(cache= (uchar *)
my_malloc_lock(query_cache_size+additional_data_size, MYF(0))))
goto err;
+#if defined(DBUG_OFF) && defined(HAVE_MADVISE) && defined(MADV_DONTDUMP)
+ if (madvise(cache, query_cache_size+additional_data_size, MADV_DONTDUMP))
+ {
+ DBUG_PRINT("warning", ("coudn't mark query cache memory as " DONTDUMP_STR ": %s",
+ strerror(errno)));
+ }
+#endif
- DBUG_PRINT("qcache", ("cache length %lu, min unit %lu, %u bins",
+ DBUG_PRINT("qcache", ("cache length %zu, min unit %zu, %zu bins",
query_cache_size, min_allocation_unit, mem_bin_num));
steps = (Query_cache_memory_bin_step *) cache;
@@ -2678,9 +2688,9 @@ ulong Query_cache::init_cache()
mem_bin_size = max_mem_bin_size >> QUERY_CACHE_MEM_BIN_STEP_PWR2;
while (mem_bin_size > min_allocation_unit)
{
- ulong incr = (steps[step-1].size - mem_bin_size) / mem_bin_count;
- unsigned long size = mem_bin_size;
- for (uint i= mem_bin_count; i > 0; i--)
+ size_t incr = (steps[step-1].size - mem_bin_size) / mem_bin_count;
+ size_t size = mem_bin_size;
+ for (size_t i= mem_bin_count; i > 0; i--)
{
bins[num+i-1].init(size);
size += incr;
@@ -2703,9 +2713,9 @@ ulong Query_cache::init_cache()
steps[step].init(mem_bin_size, num + mem_bin_count - 1, inc);
{
- uint skiped = (min_allocation_unit - mem_bin_size)/inc;
- ulong size = mem_bin_size + inc*skiped;
- uint i = mem_bin_count - skiped;
+ size_t skiped = (min_allocation_unit - mem_bin_size)/inc;
+ size_t size = mem_bin_size + inc*skiped;
+ size_t i = mem_bin_count - skiped;
while (i-- > 0)
{
bins[num+i].init(size);
@@ -2815,6 +2825,13 @@ void Query_cache::free_cache()
} while (block != queries_blocks);
}
+#if defined(DBUG_OFF) && defined(HAVE_MADVISE) && defined(MADV_DODUMP)
+ if (madvise(cache, query_cache_size+additional_data_size, MADV_DODUMP))
+ {
+ DBUG_PRINT("warning", ("coudn't mark query cache memory as " DODUMP_STR ": %s",
+ strerror(errno)));
+ }
+#endif
my_free(cache);
make_disabled();
my_hash_free(&queries);
@@ -2917,7 +2934,7 @@ my_bool Query_cache::free_old_query()
void Query_cache::free_query_internal(Query_cache_block *query_block)
{
DBUG_ENTER("Query_cache::free_query_internal");
- DBUG_PRINT("qcache", ("free query %p %lu bytes result",
+ DBUG_PRINT("qcache", ("free query %p %zu bytes result",
query_block,
query_block->query()->length() ));
@@ -2987,7 +3004,7 @@ void Query_cache::free_query_internal(Query_cache_block *query_block)
void Query_cache::free_query(Query_cache_block *query_block)
{
DBUG_ENTER("Query_cache::free_query");
- DBUG_PRINT("qcache", ("free query %p %lu bytes result",
+ DBUG_PRINT("qcache", ("free query %p %zu bytes result",
query_block,
query_block->query()->length() ));
@@ -3002,18 +3019,18 @@ void Query_cache::free_query(Query_cache_block *query_block)
*****************************************************************************/
Query_cache_block *
-Query_cache::write_block_data(ulong data_len, uchar* data,
- ulong header_len,
+Query_cache::write_block_data(size_t data_len, uchar* data,
+ size_t header_len,
Query_cache_block::block_type type,
TABLE_COUNTER_TYPE ntab)
{
- ulong all_headers_len = (ALIGN_SIZE(sizeof(Query_cache_block)) +
+ size_t all_headers_len = (ALIGN_SIZE(sizeof(Query_cache_block)) +
ALIGN_SIZE(ntab*sizeof(Query_cache_block_table)) +
header_len);
- ulong len = data_len + all_headers_len;
- ulong align_len= ALIGN_SIZE(len);
+ size_t len = data_len + all_headers_len;
+ size_t align_len= ALIGN_SIZE(len);
DBUG_ENTER("Query_cache::write_block_data");
- DBUG_PRINT("qcache", ("data: %ld, header: %ld, all header: %ld",
+ DBUG_PRINT("qcache", ("data: %zd, header: %zd, all header: %zd",
data_len, header_len, all_headers_len));
Query_cache_block *block= allocate_block(MY_MAX(align_len,
min_allocation_unit),1, 0);
@@ -3031,33 +3048,33 @@ Query_cache::write_block_data(ulong data_len, uchar* data,
my_bool
Query_cache::append_result_data(Query_cache_block **current_block,
- ulong data_len, uchar* data,
+ size_t data_len, uchar* data,
Query_cache_block *query_block)
{
DBUG_ENTER("Query_cache::append_result_data");
- DBUG_PRINT("qcache", ("append %lu bytes to %p query",
+ DBUG_PRINT("qcache", ("append %zu bytes to %p query",
data_len, query_block));
if (query_block->query()->add(data_len) > query_cache_limit)
{
- DBUG_PRINT("qcache", ("size limit reached %lu > %lu",
+ DBUG_PRINT("qcache", ("size limit reached %zu > %zu",
query_block->query()->length(),
query_cache_limit));
DBUG_RETURN(0);
}
if (*current_block == 0)
{
- DBUG_PRINT("qcache", ("allocated first result data block %lu", data_len));
+ DBUG_PRINT("qcache", ("allocated first result data block %zu", data_len));
DBUG_RETURN(write_result_data(current_block, data_len, data, query_block,
Query_cache_block::RES_BEG));
}
Query_cache_block *last_block = (*current_block)->prev;
- DBUG_PRINT("qcache", ("lastblock %p len %lu used %lu",
+ DBUG_PRINT("qcache", ("lastblock %p len %zu used %zu",
last_block, last_block->length,
last_block->used));
my_bool success = 1;
- ulong last_block_free_space= last_block->length - last_block->used;
+ size_t last_block_free_space= last_block->length - last_block->used;
/*
We will first allocate and write the 'tail' of data, that doesn't fit
@@ -3066,8 +3083,8 @@ Query_cache::append_result_data(Query_cache_block **current_block,
*/
// Try join blocks if physically next block is free...
- ulong tail = data_len - last_block_free_space;
- ulong append_min = get_min_append_result_data_size();
+ size_t tail = data_len - last_block_free_space;
+ size_t append_min = get_min_append_result_data_size();
if (last_block_free_space < data_len &&
append_next_free_block(last_block,
MY_MAX(tail, append_min)))
@@ -3075,7 +3092,7 @@ Query_cache::append_result_data(Query_cache_block **current_block,
// If no space in last block (even after join) allocate new block
if (last_block_free_space < data_len)
{
- DBUG_PRINT("qcache", ("allocate new block for %lu bytes",
+ DBUG_PRINT("qcache", ("allocate new block for %zu bytes",
data_len-last_block_free_space));
Query_cache_block *new_block = 0;
success = write_result_data(&new_block, data_len-last_block_free_space,
@@ -3098,8 +3115,8 @@ Query_cache::append_result_data(Query_cache_block **current_block,
// Now finally write data to the last block
if (success && last_block_free_space > 0)
{
- ulong to_copy = MY_MIN(data_len,last_block_free_space);
- DBUG_PRINT("qcache", ("use free space %lub at block %p to copy %lub",
+ size_t to_copy = MY_MIN(data_len,last_block_free_space);
+ DBUG_PRINT("qcache", ("use free space %zub at block %p to copy %zub",
last_block_free_space,last_block, to_copy));
memcpy((uchar*) last_block + last_block->used, data, to_copy);
last_block->used+=to_copy;
@@ -3109,12 +3126,12 @@ Query_cache::append_result_data(Query_cache_block **current_block,
my_bool Query_cache::write_result_data(Query_cache_block **result_block,
- ulong data_len, uchar* data,
+ size_t data_len, uchar* data,
Query_cache_block *query_block,
Query_cache_block::block_type type)
{
DBUG_ENTER("Query_cache::write_result_data");
- DBUG_PRINT("qcache", ("data_len %lu",data_len));
+ DBUG_PRINT("qcache", ("data_len %zu",data_len));
/*
Reserve block(s) for filling
@@ -3140,8 +3157,8 @@ my_bool Query_cache::write_result_data(Query_cache_block **result_block,
do
{
block->type = type;
- ulong length = block->used - headers_len;
- DBUG_PRINT("qcache", ("write %lu byte in block %p",length,
+ size_t length = block->used - headers_len;
+ DBUG_PRINT("qcache", ("write %zu byte in block %p",length,
block));
memcpy((uchar*) block+headers_len, rest, length);
rest += length;
@@ -3181,16 +3198,16 @@ my_bool Query_cache::write_result_data(Query_cache_block **result_block,
DBUG_RETURN(success);
}
-inline ulong Query_cache::get_min_first_result_data_size()
+inline size_t Query_cache::get_min_first_result_data_size()
{
if (queries_in_cache < QUERY_CACHE_MIN_ESTIMATED_QUERIES_NUMBER)
return min_result_data_size;
- ulong avg_result = (query_cache_size - free_memory) / queries_in_cache;
+ size_t avg_result = (query_cache_size - free_memory) / queries_in_cache;
avg_result = MY_MIN(avg_result, query_cache_limit);
return MY_MAX(min_result_data_size, avg_result);
}
-inline ulong Query_cache::get_min_append_result_data_size()
+inline size_t Query_cache::get_min_append_result_data_size()
{
return min_result_data_size;
}
@@ -3199,25 +3216,25 @@ inline ulong Query_cache::get_min_append_result_data_size()
Allocate one or more blocks to hold data
*/
my_bool Query_cache::allocate_data_chain(Query_cache_block **result_block,
- ulong data_len,
+ size_t data_len,
Query_cache_block *query_block,
my_bool first_block_arg)
{
- ulong all_headers_len = (ALIGN_SIZE(sizeof(Query_cache_block)) +
+ size_t all_headers_len = (ALIGN_SIZE(sizeof(Query_cache_block)) +
ALIGN_SIZE(sizeof(Query_cache_result)));
- ulong min_size = (first_block_arg ?
+ size_t min_size = (first_block_arg ?
get_min_first_result_data_size():
get_min_append_result_data_size());
Query_cache_block *prev_block= NULL;
Query_cache_block *new_block;
DBUG_ENTER("Query_cache::allocate_data_chain");
- DBUG_PRINT("qcache", ("data_len %lu, all_headers_len %lu",
+ DBUG_PRINT("qcache", ("data_len %zu, all_headers_len %zu",
data_len, all_headers_len));
do
{
- ulong len= data_len + all_headers_len;
- ulong align_len= ALIGN_SIZE(len);
+ size_t len= data_len + all_headers_len;
+ size_t align_len= ALIGN_SIZE(len);
if (!(new_block= allocate_block(MY_MAX(min_size, align_len),
min_result_data_size == 0,
@@ -3234,7 +3251,7 @@ my_bool Query_cache::allocate_data_chain(Query_cache_block **result_block,
Query_cache_result *header = new_block->result();
header->parent(query_block);
- DBUG_PRINT("qcache", ("Block len %lu used %lu",
+ DBUG_PRINT("qcache", ("Block len %zu used %zu",
new_block->length, new_block->used));
if (prev_block)
@@ -3284,7 +3301,7 @@ void Query_cache::invalidate_table(THD *thd, TABLE *table)
table->s->table_cache_key.length);
}
-void Query_cache::invalidate_table(THD *thd, uchar * key, uint32 key_length)
+void Query_cache::invalidate_table(THD *thd, uchar * key, size_t key_length)
{
DEBUG_SYNC(thd, "wait_in_query_cache_invalidate1");
@@ -3312,7 +3329,7 @@ void Query_cache::invalidate_table(THD *thd, uchar * key, uint32 key_length)
*/
void
-Query_cache::invalidate_table_internal(THD *thd, uchar *key, uint32 key_length)
+Query_cache::invalidate_table_internal(THD *thd, uchar *key, size_t key_length)
{
Query_cache_block *table_block=
(Query_cache_block*)my_hash_search(&tables, key, key_length);
@@ -3415,7 +3432,7 @@ Query_cache::register_tables_from_list(THD *thd, TABLE_LIST *tables_used,
if (!insert_table(thd, tables_used->table->s->table_cache_key.length,
tables_used->table->s->table_cache_key.str,
(*block_table),
- tables_used->db_length, 0,
+ tables_used->db.length, 0,
tables_used->table->file->table_cache_type(),
tables_used->callback_func,
tables_used->engine_data,
@@ -3488,16 +3505,15 @@ my_bool Query_cache::register_all_tables(THD *thd,
*/
my_bool
-Query_cache::insert_table(THD *thd, uint key_len, const char *key,
- Query_cache_block_table *node,
- uint32 db_length, uint8 suffix_length_arg,
+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,
uint8 cache_type,
qc_engine_callback callback,
ulonglong engine_data,
my_bool hash)
{
DBUG_ENTER("Query_cache::insert_table");
- DBUG_PRINT("qcache", ("insert table node %p, len %d",
+ DBUG_PRINT("qcache", ("insert table node %p, len %zu",
node, key_len));
Query_cache_block *table_block=
@@ -3563,7 +3579,7 @@ Query_cache::insert_table(THD *thd, uint key_len, const char *key,
}
char *db= header->db();
header->table(db + db_length + 1);
- header->key_length(key_len);
+ header->key_length((uint32)key_len);
header->suffix_length(suffix_length_arg);
header->type(cache_type);
header->callback(callback);
@@ -3635,15 +3651,15 @@ void Query_cache::unlink_table(Query_cache_block_table *node)
*****************************************************************************/
Query_cache_block *
-Query_cache::allocate_block(ulong len, my_bool not_less, ulong min)
+Query_cache::allocate_block(size_t len, my_bool not_less, size_t min)
{
DBUG_ENTER("Query_cache::allocate_block");
- DBUG_PRINT("qcache", ("len %lu, not less %d, min %lu",
+ DBUG_PRINT("qcache", ("len %zu, not less %d, min %zu",
len, not_less,min));
if (len >= MY_MIN(query_cache_size, query_cache_limit))
{
- DBUG_PRINT("qcache", ("Query cache hase only %lu memory and limit %lu",
+ DBUG_PRINT("qcache", ("Query cache hase only %zu memory and limit %zu",
query_cache_size, query_cache_limit));
DBUG_RETURN(0); // in any case we don't have such piece of memory
}
@@ -3667,11 +3683,11 @@ Query_cache::allocate_block(ulong len, my_bool not_less, ulong min)
Query_cache_block *
-Query_cache::get_free_block(ulong len, my_bool not_less, ulong min)
+Query_cache::get_free_block(size_t len, my_bool not_less, size_t min)
{
Query_cache_block *block = 0, *first = 0;
DBUG_ENTER("Query_cache::get_free_block");
- DBUG_PRINT("qcache",("length %lu, not_less %d, min %lu", len,
+ DBUG_PRINT("qcache",("length %zu, not_less %d, min %zu", len,
(int)not_less, min));
/* Find block with minimal size > len */
@@ -3762,7 +3778,7 @@ void Query_cache::free_memory_block(Query_cache_block *block)
}
-void Query_cache::split_block(Query_cache_block *block, ulong len)
+void Query_cache::split_block(Query_cache_block *block, size_t len)
{
DBUG_ENTER("Query_cache::split_block");
Query_cache_block *new_block = (Query_cache_block*)(((uchar*) block)+len);
@@ -3783,7 +3799,7 @@ void Query_cache::split_block(Query_cache_block *block, ulong len)
else
free_memory_block(new_block);
- DBUG_PRINT("qcache", ("split %p (%lu) new %p",
+ DBUG_PRINT("qcache", ("split %p (%zu) new %p",
block, len, new_block));
DBUG_VOID_RETURN;
}
@@ -3816,16 +3832,16 @@ Query_cache::join_free_blocks(Query_cache_block *first_block_arg,
my_bool Query_cache::append_next_free_block(Query_cache_block *block,
- ulong add_size)
+ size_t add_size)
{
Query_cache_block *next_block = block->pnext;
DBUG_ENTER("Query_cache::append_next_free_block");
- DBUG_PRINT("enter", ("block %p, add_size %lu", block,
+ DBUG_PRINT("enter", ("block %p, add_size %zu", block,
add_size));
if (next_block != first_block && next_block->is_free())
{
- ulong old_len = block->length;
+ size_t old_len = block->length;
exclude_from_free_memory_list(next_block);
next_block->destroy();
total_blocks--;
@@ -3875,14 +3891,14 @@ void Query_cache::insert_into_free_memory_list(Query_cache_block *free_block)
DBUG_VOID_RETURN;
}
-uint Query_cache::find_bin(ulong size)
+uint Query_cache::find_bin(size_t size)
{
DBUG_ENTER("Query_cache::find_bin");
// Binary search
- int left = 0, right = mem_bin_steps;
+ size_t left = 0, right = mem_bin_steps;
do
{
- int middle = (left + right) / 2;
+ size_t middle = (left + right) / 2;
if (steps[middle].size > size)
left = middle+1;
else
@@ -3891,15 +3907,15 @@ uint Query_cache::find_bin(ulong size)
if (left == 0)
{
// first bin not subordinate of common rules
- DBUG_PRINT("qcache", ("first bin (# 0), size %lu",size));
+ DBUG_PRINT("qcache", ("first bin (# 0), size %zu",size));
DBUG_RETURN(0);
}
- uint bin = steps[left].idx -
- (uint)((size - steps[left].size)/steps[left].increment);
+ size_t bin = steps[left].idx -
+ ((size - steps[left].size)/steps[left].increment);
- DBUG_PRINT("qcache", ("bin %u step %u, size %lu step size %lu",
+ DBUG_PRINT("qcache", ("bin %zu step %zu, size %zu step size %zu",
bin, left, size, steps[left].size));
- DBUG_RETURN(bin);
+ DBUG_RETURN((uint)bin);
}
@@ -4077,41 +4093,35 @@ Query_cache::process_and_count_tables(THD *thd, TABLE_LIST *tables_used,
tables_used->view_name.str,
tables_used->view_db.str));
*tables_type|= HA_CACHE_TBL_NONTRANSACT;
+ continue;
}
- else
+ if (tables_used->derived)
{
- if (tables_used->derived)
- {
- DBUG_PRINT("qcache", ("table: %s", tables_used->alias));
- table_count--;
- DBUG_PRINT("qcache", ("derived table skipped"));
- continue;
- }
- DBUG_PRINT("qcache", ("table: %s db: %s type: %u",
- tables_used->table->s->table_name.str,
- tables_used->table->s->db.str,
- tables_used->table->s->db_type()->db_type));
- *tables_type|= tables_used->table->file->table_cache_type();
+ DBUG_PRINT("qcache", ("table: %s", tables_used->alias.str));
+ table_count--;
+ DBUG_PRINT("qcache", ("derived table skipped"));
+ continue;
+ }
- /*
- table_alias_charset used here because it depends of
- lower_case_table_names variable
- */
- table_count+= tables_used->table->file->
- count_query_cache_dependant_tables(tables_type);
-
- if (tables_used->table->s->tmp_table != NO_TMP_TABLE ||
- (*tables_type & HA_CACHE_TBL_NOCACHE) ||
- (tables_used->db_length == 5 &&
- my_strnncoll(table_alias_charset,
- (uchar*)tables_used->table->s->table_cache_key.str, 6,
- (uchar*)"mysql",6) == 0))
- {
- DBUG_PRINT("qcache",
- ("select not cacheable: temporary, system or "
- "other non-cacheable table(s)"));
- DBUG_RETURN(0);
- }
+ DBUG_PRINT("qcache", ("table: %s db: %s type: %u",
+ tables_used->table->s->table_name.str,
+ tables_used->table->s->db.str,
+ tables_used->table->s->db_type()->db_type));
+ *tables_type|= tables_used->table->file->table_cache_type();
+
+ /*
+ table_alias_charset used here because it depends of
+ lower_case_table_names variable
+ */
+ table_count+= tables_used->table->file->
+ count_query_cache_dependant_tables(tables_type);
+
+ if (tables_used->table->s->not_usable_by_query_cache)
+ {
+ DBUG_PRINT("qcache",
+ ("select not cacheable: temporary, system or "
+ "other non-cacheable table(s)"));
+ DBUG_RETURN(0);
}
}
DBUG_RETURN(table_count);
@@ -4203,7 +4213,7 @@ my_bool Query_cache::ask_handler_allowance(THD *thd,
handler= table->file;
if (!handler->register_query_cache_table(thd,
table->s->normalized_path.str,
- table->s->normalized_path.length,
+ (uint)table->s->normalized_path.length,
&tables_used->callback_func,
&tables_used->engine_data))
{
@@ -4229,7 +4239,7 @@ my_bool Query_cache::ask_handler_allowance(THD *thd,
/**
Rearrange all memory blocks so that free memory joins at the
'bottom' of the allocated memory block containing all cache data.
- @see Query_cache::pack(ulong join_limit, uint iteration_limit)
+ @see Query_cache::pack(size_t join_limit, uint iteration_limit)
*/
void Query_cache::pack_cache()
@@ -4240,7 +4250,7 @@ void Query_cache::pack_cache()
uchar *border = 0;
Query_cache_block *before = 0;
- ulong gap = 0;
+ size_t gap = 0;
my_bool ok = 1;
Query_cache_block *block = first_block;
DUMP(this);
@@ -4274,7 +4284,7 @@ void Query_cache::pack_cache()
my_bool Query_cache::move_by_type(uchar **border,
- Query_cache_block **before, ulong *gap,
+ Query_cache_block **before, size_t *gap,
Query_cache_block *block)
{
DBUG_ENTER("Query_cache::move_by_type");
@@ -4296,7 +4306,7 @@ my_bool Query_cache::move_by_type(uchar **border,
block->pnext->pprev=block->pprev;
block->destroy();
total_blocks--;
- DBUG_PRINT("qcache", ("added to gap (%lu)", *gap));
+ DBUG_PRINT("qcache", ("added to gap (%zu)", *gap));
break;
}
case Query_cache_block::TABLE:
@@ -4305,7 +4315,7 @@ my_bool Query_cache::move_by_type(uchar **border,
DBUG_PRINT("qcache", ("block %p TABLE", block));
if (*border == 0)
break;
- ulong len = block->length, used = block->used;
+ size_t len = block->length, used = block->used;
Query_cache_block_table *list_root = block->table(0);
Query_cache_block_table *tprev = list_root->prev,
*tnext = list_root->next;
@@ -4355,7 +4365,7 @@ my_bool Query_cache::move_by_type(uchar **border,
/* Fix hash to point at moved block */
my_hash_replace(&tables, &record_idx, (uchar*) new_block);
- DBUG_PRINT("qcache", ("moved %lu bytes to %p, new gap at %p",
+ DBUG_PRINT("qcache", ("moved %zu bytes to %p, new gap at %p",
len, new_block, *border));
break;
}
@@ -4366,7 +4376,7 @@ my_bool Query_cache::move_by_type(uchar **border,
if (*border == 0)
break;
BLOCK_LOCK_WR(block);
- ulong len = block->length, used = block->used;
+ size_t len = block->length, used = block->used;
TABLE_COUNTER_TYPE n_tables = block->n_tables;
Query_cache_block *prev = block->prev,
*next = block->next,
@@ -4448,7 +4458,7 @@ my_bool Query_cache::move_by_type(uchar **border,
}
/* Fix hash to point at moved block */
my_hash_replace(&queries, &record_idx, (uchar*) new_block);
- DBUG_PRINT("qcache", ("moved %lu bytes to %p, new gap at %p",
+ DBUG_PRINT("qcache", ("moved %zu bytes to %p, new gap at %p",
len, new_block, *border));
break;
}
@@ -4465,7 +4475,7 @@ my_bool Query_cache::move_by_type(uchar **border,
BLOCK_LOCK_WR(query_block);
Query_cache_block *next= block->next, *prev= block->prev;
Query_cache_block::block_type type= block->type;
- ulong len = block->length, used = block->used;
+ size_t len = block->length, used = block->used;
Query_cache_block *pprev = block->pprev,
*pnext = block->pnext,
*new_block =(Query_cache_block*) *border;
@@ -4483,7 +4493,7 @@ my_bool Query_cache::move_by_type(uchar **border,
*border += len;
*before = new_block;
/* If result writing complete && we have free space in block */
- ulong free_space= new_block->length - new_block->used;
+ size_t free_space= new_block->length - new_block->used;
free_space-= free_space % ALIGN_SIZE(1);
if (query->result()->type == Query_cache_block::RESULT &&
new_block->length > new_block->used &&
@@ -4493,11 +4503,11 @@ my_bool Query_cache::move_by_type(uchar **border,
*border-= free_space;
*gap+= free_space;
DBUG_PRINT("qcache",
- ("rest of result free space added to gap (%lu)", *gap));
+ ("rest of result free space added to gap (%zu)", *gap));
new_block->length -= free_space;
}
BLOCK_UNLOCK_WR(query_block);
- DBUG_PRINT("qcache", ("moved %lu bytes to %p, new gap at %p",
+ DBUG_PRINT("qcache", ("moved %zu bytes to %p, new gap at %p",
len, new_block, *border));
break;
}
@@ -4537,7 +4547,7 @@ void Query_cache::relink(Query_cache_block *oblock,
}
-my_bool Query_cache::join_results(ulong join_limit)
+my_bool Query_cache::join_results(size_t join_limit)
{
my_bool has_moving = 0;
DBUG_ENTER("Query_cache::join_results");
@@ -4561,7 +4571,7 @@ my_bool Query_cache::join_results(ulong join_limit)
{
has_moving = 1;
Query_cache_block *first_result = header->result();
- ulong new_len = (header->length() +
+ size_t new_len = (header->length() +
ALIGN_SIZE(sizeof(Query_cache_block)) +
ALIGN_SIZE(sizeof(Query_cache_result)));
if (new_result_block->length >
@@ -4574,7 +4584,7 @@ my_bool Query_cache::join_results(ulong join_limit)
new_result_block->used = new_len;
new_result_block->next = new_result_block->prev = new_result_block;
- DBUG_PRINT("qcache", ("new block %lu/%lu (%lu)",
+ DBUG_PRINT("qcache", ("new block %zu/%zu (%zu)",
new_result_block->length,
new_result_block->used,
header->length()));
@@ -4585,9 +4595,9 @@ my_bool Query_cache::join_results(ulong join_limit)
Query_cache_block *result_block = first_result;
do
{
- ulong len = (result_block->used - result_block->headers_len() -
+ size_t len = (result_block->used - result_block->headers_len() -
ALIGN_SIZE(sizeof(Query_cache_result)));
- DBUG_PRINT("loop", ("add block %lu/%lu (%lu)",
+ DBUG_PRINT("loop", ("add block %zu/%zu (%zu)",
result_block->length,
result_block->used,
len));
@@ -4689,14 +4699,14 @@ void Query_cache::bins_dump()
return;
}
- DBUG_PRINT("qcache", ("mem_bin_num=%u, mem_bin_steps=%u",
+ DBUG_PRINT("qcache", ("mem_bin_num=%zu, mem_bin_steps=%zu",
mem_bin_num, mem_bin_steps));
DBUG_PRINT("qcache", ("-------------------------"));
DBUG_PRINT("qcache", (" size idx step"));
DBUG_PRINT("qcache", ("-------------------------"));
for (i=0; i < mem_bin_steps; i++)
{
- DBUG_PRINT("qcache", ("%10lu %3d %10lu", steps[i].size, steps[i].idx,
+ DBUG_PRINT("qcache", ("%10zu %3zd %10zu", steps[i].size, steps[i].idx,
steps[i].increment));
}
DBUG_PRINT("qcache", ("-------------------------"));
@@ -4704,13 +4714,13 @@ void Query_cache::bins_dump()
DBUG_PRINT("qcache", ("-------------------------"));
for (i=0; i < mem_bin_num; i++)
{
- DBUG_PRINT("qcache", ("%10lu %3d %p", bins[i].size, bins[i].number,
+ DBUG_PRINT("qcache", ("%10zu %3d %p", bins[i].size, bins[i].number,
&(bins[i])));
if (bins[i].free_blocks)
{
Query_cache_block *block = bins[i].free_blocks;
do{
- DBUG_PRINT("qcache", ("\\-- %lu %p %p %p %p %p",
+ DBUG_PRINT("qcache", ("\\-- %zu %p %p %p %p %p",
block->length,block,
block->next,block->prev,
block->pnext,block->pprev));
@@ -4737,7 +4747,7 @@ void Query_cache::cache_dump()
do
{
DBUG_PRINT("qcache",
- ("%10lu %10lu %1d %2d %p %p %p %p %p",
+ ("%10zu %10zu %1d %2d %p %p %p %p %p",
i->length, i->used, (int)i->type,
i->n_tables,i,
i->next,i->prev,i->pnext,
@@ -4793,7 +4803,7 @@ void Query_cache::queries_dump()
Query_cache_block *result_beg = result_block;
do
{
- DBUG_PRINT("qcache", ("-r- %u %lu/%lu %p %p %p %p %p",
+ DBUG_PRINT("qcache", ("-r- %u %zu/%zu %p %p %p %p %p",
(uint) result_block->type,
result_block->length, result_block->used,
result_block,
@@ -4871,7 +4881,7 @@ my_bool Query_cache::check_integrity(bool locked)
}
DBUG_PRINT("qcache", ("physical address check ..."));
- ulong free=0, used=0;
+ size_t free=0, used=0;
Query_cache_block * block = first_block;
do
{
@@ -4936,7 +4946,7 @@ my_bool Query_cache::check_integrity(bool locked)
}
else
{
- int idx = (((uchar*)bin) - ((uchar*)bins)) /
+ size_t idx = (((uchar*)bin) - ((uchar*)bins)) /
sizeof(Query_cache_memory_bin);
if (in_list(bins[idx].free_blocks, block, "free memory"))
result = 1;
@@ -5009,7 +5019,7 @@ my_bool Query_cache::check_integrity(bool locked)
if (used + free != query_cache_size)
{
DBUG_PRINT("error",
- ("used memory (%lu) + free memory (%lu) != query_cache_size (%lu)",
+ ("used memory (%zu) + free memory (%zu) != query_cache_size (%zu)",
used, free, query_cache_size));
result = 1;
}
@@ -5017,7 +5027,7 @@ my_bool Query_cache::check_integrity(bool locked)
if (free != free_memory)
{
DBUG_PRINT("error",
- ("free memory (%lu) != free_memory (%lu)",
+ ("free memory (%zu) != free_memory (%zu)",
free, free_memory));
result = 1;
}
diff --git a/sql/sql_cache.h b/sql/sql_cache.h
index 6ad73bbb0a2..92635ecacc7 100644
--- a/sql/sql_cache.h
+++ b/sql/sql_cache.h
@@ -78,7 +78,7 @@ struct Query_cache_tls;
struct LEX;
class THD;
-typedef my_bool (*qc_engine_callback)(THD *thd, char *table_key,
+typedef my_bool (*qc_engine_callback)(THD *thd, const char *table_key,
uint key_length,
ulonglong *engine_data);
@@ -126,8 +126,8 @@ struct Query_cache_block
enum block_type {FREE, QUERY, RESULT, RES_CONT, RES_BEG,
RES_INCOMPLETE, TABLE, INCOMPLETE};
- ulong length; // length of all block
- ulong used; // length of data
+ size_t length; // length of all block
+ size_t used; // length of data
/*
Not used **pprev, **prev because really needed access to pervious block:
*pprev to join free blocks
@@ -139,7 +139,7 @@ struct Query_cache_block
TABLE_COUNTER_TYPE n_tables; // number of tables in query
inline bool is_free(void) { return type == FREE; }
- void init(ulong length);
+ void init(size_t length);
void destroy();
uint headers_len();
uchar* data(void);
@@ -155,10 +155,11 @@ struct Query_cache_query
mysql_rwlock_t lock;
Query_cache_block *res;
Query_cache_tls *wri;
- ulong len;
+ size_t len;
unsigned int last_pkt_nr;
uint8 tbls_type;
uint8 ready;
+ ulonglong hit_count;
Query_cache_query() {} /* Remove gcc warning */
inline void init_n_lock();
@@ -171,9 +172,9 @@ struct Query_cache_query
inline void writer(Query_cache_tls *p) { wri= p; }
inline uint8 tables_type() { return tbls_type; }
inline void tables_type(uint8 type) { tbls_type= type; }
- inline ulong length() { return len; }
- inline ulong add(ulong packet_len) { return(len+= packet_len); }
- inline void length(ulong length_arg) { len= length_arg; }
+ inline size_t length() { return len; }
+ inline size_t add(size_t packet_len) { return(len+= packet_len); }
+ inline void length(size_t length_arg) { len= length_arg; }
inline uchar* query()
{
return (((uchar*)this) + ALIGN_SIZE(sizeof(Query_cache_query)));
@@ -184,6 +185,8 @@ struct Query_cache_query
*/
inline void set_results_ready() { ready= 1; }
inline bool is_results_ready() { return ready; }
+ inline void increment_hits() { hit_count++; }
+ inline ulonglong hits() { return hit_count; }
void lock_writing();
void lock_reading();
bool try_lock_writing();
@@ -265,12 +268,12 @@ struct Query_cache_memory_bin
{
Query_cache_memory_bin() {} /* Remove gcc warning */
#ifndef DBUG_OFF
- ulong size;
+ size_t size;
#endif
uint number;
Query_cache_block *free_blocks;
- inline void init(ulong size_arg)
+ inline void init(size_t size_arg)
{
#ifndef DBUG_OFF
size = size_arg;
@@ -283,10 +286,10 @@ struct Query_cache_memory_bin
struct Query_cache_memory_bin_step
{
Query_cache_memory_bin_step() {} /* Remove gcc warning */
- ulong size;
- ulong increment;
- uint idx;
- inline void init(ulong size_arg, uint idx_arg, ulong increment_arg)
+ size_t size;
+ size_t increment;
+ size_t idx;
+ inline void init(size_t size_arg, size_t idx_arg, size_t increment_arg)
{
size = size_arg;
idx = idx_arg;
@@ -298,9 +301,9 @@ class Query_cache
{
public:
/* Info */
- ulong query_cache_size, query_cache_limit;
+ size_t query_cache_size, query_cache_limit;
/* statistics */
- ulong free_memory, queries_in_cache, hits, inserts, refused,
+ size_t free_memory, queries_in_cache, hits, inserts, refused,
free_memory_blocks, total_blocks, lowmem_prunes;
@@ -316,7 +319,7 @@ private:
Cache_staus m_cache_status;
void free_query_internal(Query_cache_block *point);
- void invalidate_table_internal(THD *thd, uchar *key, uint32 key_length);
+ void invalidate_table_internal(THD *thd, uchar *key, size_t key_length);
protected:
/*
@@ -334,6 +337,7 @@ protected:
till the end of a flush operation.
*/
mysql_mutex_t structure_guard_mutex;
+ size_t additional_data_size;
uchar *cache; // cache memory
Query_cache_block *first_block; // physical location block list
Query_cache_block *queries_blocks; // query list (LIFO)
@@ -343,10 +347,10 @@ protected:
Query_cache_memory_bin_step *steps; // bins spacing info
HASH queries, tables;
/* options */
- ulong min_allocation_unit, min_result_data_size;
+ size_t min_allocation_unit, min_result_data_size;
uint def_query_hash_size, def_table_hash_size;
- uint mem_bin_num, mem_bin_steps; // See at init_cache & find_bin
+ size_t mem_bin_num, mem_bin_steps; // See at init_cache & find_bin
bool initialized;
@@ -364,12 +368,12 @@ protected:
my_bool free_old_query();
void free_query(Query_cache_block *point);
my_bool allocate_data_chain(Query_cache_block **result_block,
- ulong data_len,
+ size_t data_len,
Query_cache_block *query_block,
my_bool first_block);
void invalidate_table(THD *thd, TABLE_LIST *table);
void invalidate_table(THD *thd, TABLE *table);
- void invalidate_table(THD *thd, uchar *key, uint32 key_length);
+ void invalidate_table(THD *thd, uchar *key, size_t key_length);
void invalidate_table(THD *thd, Query_cache_block *table_block);
void invalidate_query_block_list(THD *thd,
Query_cache_block_table *list_root);
@@ -382,19 +386,19 @@ protected:
TABLE_LIST *tables_used,
TABLE_COUNTER_TYPE tables);
void unlink_table(Query_cache_block_table *node);
- Query_cache_block *get_free_block (ulong len, my_bool not_less,
- ulong min);
+ Query_cache_block *get_free_block (size_t len, my_bool not_less,
+ size_t min);
void free_memory_block(Query_cache_block *point);
- void split_block(Query_cache_block *block, ulong len);
+ void split_block(Query_cache_block *block, size_t len);
Query_cache_block *join_free_blocks(Query_cache_block *first_block,
Query_cache_block *block_in_list);
my_bool append_next_free_block(Query_cache_block *block,
- ulong add_size);
+ size_t add_size);
void exclude_from_free_memory_list(Query_cache_block *free_block);
void insert_into_free_memory_list(Query_cache_block *new_block);
my_bool move_by_type(uchar **border, Query_cache_block **before,
- ulong *gap, Query_cache_block *i);
- uint find_bin(ulong size);
+ size_t *gap, Query_cache_block *i);
+ uint find_bin(size_t size);
void move_to_query_list_end(Query_cache_block *block);
void insert_into_free_memory_sorted_list(Query_cache_block *new_block,
Query_cache_block **list);
@@ -405,31 +409,31 @@ protected:
Query_cache_block *prev,
Query_cache_block *pnext,
Query_cache_block *pprev);
- my_bool join_results(ulong join_limit);
+ my_bool join_results(size_t join_limit);
/*
Following function control structure_guard_mutex
by themself or don't need structure_guard_mutex
*/
- ulong init_cache();
+ size_t init_cache();
void make_disabled();
void free_cache();
- Query_cache_block *write_block_data(ulong data_len, uchar* data,
- ulong header_len,
+ Query_cache_block *write_block_data(size_t data_len, uchar* data,
+ size_t header_len,
Query_cache_block::block_type type,
TABLE_COUNTER_TYPE ntab = 0);
my_bool append_result_data(Query_cache_block **result,
- ulong data_len, uchar* data,
+ size_t data_len, uchar* data,
Query_cache_block *parent);
my_bool write_result_data(Query_cache_block **result,
- ulong data_len, uchar* data,
+ size_t data_len, uchar* data,
Query_cache_block *parent,
Query_cache_block::block_type
type=Query_cache_block::RESULT);
- inline ulong get_min_first_result_data_size();
- inline ulong get_min_append_result_data_size();
- Query_cache_block *allocate_block(ulong len, my_bool not_less,
- ulong min);
+ inline size_t get_min_first_result_data_size();
+ inline size_t get_min_append_result_data_size();
+ Query_cache_block *allocate_block(size_t len, my_bool not_less,
+ size_t min);
/*
If query is cacheable return number tables in query
(query without tables not cached)
@@ -444,9 +448,9 @@ protected:
static my_bool ask_handler_allowance(THD *thd, TABLE_LIST *tables_used);
public:
- Query_cache(ulong query_cache_limit = ULONG_MAX,
- ulong min_allocation_unit = QUERY_CACHE_MIN_ALLOCATION_UNIT,
- ulong min_result_data_size = QUERY_CACHE_MIN_RESULT_DATA_SIZE,
+ Query_cache(size_t query_cache_limit = ULONG_MAX,
+ size_t min_allocation_unit = QUERY_CACHE_MIN_ALLOCATION_UNIT,
+ size_t min_result_data_size = QUERY_CACHE_MIN_RESULT_DATA_SIZE,
uint def_query_hash_size = QUERY_CACHE_DEF_QUERY_HASH_SIZE,
uint def_table_hash_size = QUERY_CACHE_DEF_TABLE_HASH_SIZE);
@@ -457,11 +461,11 @@ protected:
/* initialize cache (mutex) */
void init();
/* resize query cache (return real query size, 0 if disabled) */
- ulong resize(ulong query_cache_size);
+ size_t resize(size_t query_cache_size);
/* set limit on result size */
- inline void result_size_limit(ulong limit){query_cache_limit=limit;}
+ inline void result_size_limit(size_t limit){query_cache_limit=limit;}
/* set minimal result data allocation unit size */
- ulong set_min_res_unit(ulong size);
+ size_t set_min_res_unit(size_t size);
/* register query in cache */
void store_query(THD *thd, TABLE_LIST *used_tables);
@@ -478,29 +482,29 @@ protected:
void invalidate(THD *thd, CHANGED_TABLE_LIST *tables_used);
void invalidate_locked_for_write(THD *thd, TABLE_LIST *tables_used);
void invalidate(THD *thd, TABLE *table, my_bool using_transactions);
- void invalidate(THD *thd, const char *key, uint32 key_length,
+ void invalidate(THD *thd, const char *key, size_t key_length,
my_bool using_transactions);
/* Remove all queries that uses any of the tables in following database */
- void invalidate(THD *thd, char *db);
+ void invalidate(THD *thd, const char *db);
/* Remove all queries that uses any of the listed following table */
void invalidate_by_MyISAM_filename(const char *filename);
void flush();
void pack(THD *thd,
- ulong join_limit = QUERY_CACHE_PACK_LIMIT,
+ size_t join_limit = QUERY_CACHE_PACK_LIMIT,
uint iteration_limit = QUERY_CACHE_PACK_ITERATION);
void destroy();
void insert(THD *thd, Query_cache_tls *query_cache_tls,
const char *packet,
- ulong length,
+ size_t length,
unsigned pkt_nr);
- my_bool insert_table(THD *thd, uint key_len, const char *key,
+ my_bool insert_table(THD *thd, size_t key_len, const char *key,
Query_cache_block_table *node,
- uint32 db_length, uint8 suffix_length_arg,
+ size_t db_length, uint8 suffix_length_arg,
uint8 cache_type,
qc_engine_callback callback,
ulonglong engine_data,
@@ -559,8 +563,8 @@ struct Query_cache_query_flags
sql_mode_t sql_mode;
ulonglong max_sort_length;
ulonglong group_concat_max_len;
- ulong default_week_format;
- ulong div_precision_increment;
+ size_t default_week_format;
+ size_t div_precision_increment;
MY_LOCALE *lc_time_names;
};
#define QUERY_CACHE_FLAGS_SIZE sizeof(Query_cache_query_flags)
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 0a8c136e556..0c0a1ec98ae 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -28,7 +28,7 @@
#pragma implementation // gcc: Class implementation
#endif
-#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "mariadb.h"
#include "sql_priv.h"
#include "sql_class.h"
#include "sql_cache.h" // query_cache_abort
@@ -49,14 +49,16 @@
#include <m_ctype.h>
#include <sys/stat.h>
#include <thr_alarm.h>
-#ifdef __WIN__
+#ifdef __WIN__0
#include <io.h>
#endif
#include <mysys_err.h>
#include <limits.h>
+#include "sp_head.h"
#include "sp_rcontext.h"
#include "sp_cache.h"
+#include "sql_show.h" // append_identifier
#include "transaction.h"
#include "sql_select.h" /* declares create_tmp_table() */
#include "debug_sync.h"
@@ -101,11 +103,28 @@ extern "C" void free_user_var(user_var_entry *entry)
my_free(entry);
}
+/* Functions for last-value-from-sequence hash */
+
+extern "C" uchar *get_sequence_last_key(SEQUENCE_LAST_VALUE *entry,
+ size_t *length,
+ my_bool not_used
+ __attribute__((unused)))
+{
+ *length= entry->length;
+ return (uchar*) entry->key;
+}
+
+extern "C" void free_sequence_last(SEQUENCE_LAST_VALUE *entry)
+{
+ delete entry;
+}
+
+
bool Key_part_spec::operator==(const Key_part_spec& other) const
{
return length == other.length &&
- !my_strcasecmp(system_charset_info, field_name.str,
- other.field_name.str);
+ !lex_string_cmp(system_charset_info, &field_name,
+ &other.field_name);
}
/**
@@ -121,7 +140,7 @@ Key::Key(const Key &rhs, MEM_ROOT *mem_root)
columns(rhs.columns, mem_root),
name(rhs.name),
option_list(rhs.option_list),
- generated(rhs.generated)
+ generated(rhs.generated), invisible(false)
{
list_copy_and_replace_each_value(columns, mem_root);
}
@@ -232,9 +251,9 @@ bool Foreign_key::validate(List<Create_field> &table_fields)
{
it.rewind();
while ((sql_field= it++) &&
- my_strcasecmp(system_charset_info,
- column->field_name.str,
- sql_field->field_name)) {}
+ lex_string_cmp(system_charset_info,
+ &column->field_name,
+ &sql_field->field_name)) {}
if (!sql_field)
{
my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name.str);
@@ -318,24 +337,9 @@ ulong get_max_connections(void)
extern "C" int mysql_tmpfile(const char *prefix)
{
char filename[FN_REFLEN];
- File fd = create_temp_file(filename, mysql_tmpdir, prefix,
-#ifdef __WIN__
- O_BINARY | O_TRUNC | O_SEQUENTIAL |
- O_SHORT_LIVED |
-#endif /* __WIN__ */
- O_CREAT | O_EXCL | O_RDWR | O_TEMPORARY,
- MYF(MY_WME));
- if (fd >= 0) {
-#ifndef __WIN__
- /*
- This can be removed once the following bug is fixed:
- Bug #28903 create_temp_file() doesn't honor O_TEMPORARY option
- (file not removed) (Unix)
- */
- unlink(filename);
-#endif /* !__WIN__ */
- }
-
+ File fd= create_temp_file(filename, mysql_tmpdir, prefix,
+ O_BINARY | O_SEQUENTIAL,
+ MYF(MY_WME | MY_TEMPORARY));
return fd;
}
@@ -590,13 +594,10 @@ handle_condition(THD *thd,
extern "C" void thd_kill_timeout(THD* thd)
{
thd->status_var.max_statement_time_exceeded++;
- mysql_mutex_lock(&thd->LOCK_thd_data);
/* Kill queries that can't cause data corruptions */
thd->awake(KILL_TIMEOUT);
- mysql_mutex_unlock(&thd->LOCK_thd_data);
}
-
THD::THD(my_thread_id id, bool is_wsrep_applier)
:Statement(&main_lex, &main_mem_root, STMT_CONVENTIONAL_EXECUTION,
/* statement id */ 0),
@@ -665,6 +666,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
THD *old_THR_THD= current_thd;
set_current_thd(this);
status_var.local_memory_used= sizeof(THD);
+ status_var.max_local_memory_used= status_var.local_memory_used;
status_var.global_memory_used= 0;
variables.pseudo_thread_id= thread_id;
variables.max_mem_used= global_system_variables.max_mem_used;
@@ -677,8 +679,8 @@ 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, ALLOC_ROOT_MIN_BLOCK_SIZE, 0,
- MYF(MY_THREAD_SPECIFIC));
+ init_sql_alloc(&main_mem_root, "THD::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
@@ -698,7 +700,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
security_ctx= &main_security_ctx;
no_errors= 0;
password= 0;
- query_start_used= query_start_sec_part_used= 0;
+ query_start_sec_part_used= 0;
count_cuted_fields= CHECK_FIELD_IGNORE;
killed= NOT_KILLED;
killed_err= 0;
@@ -715,6 +717,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
// Must be reset to handle error with THD's created for init of mysqld
lex->current_select= 0;
start_utime= utime_after_query= 0;
+ system_time.start.val= system_time.sec= system_time.sec_part= 0;
utime_after_lock= 0L;
progress.arena= 0;
progress.report_to_client= 0;
@@ -735,7 +738,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
enable_slow_log= 0;
durability_property= HA_REGULAR_DURABILITY;
-#ifndef DBUG_OFF
+#ifdef DBUG_ASSERT_EXISTS
dbug_sentry=THD_SENTRY_MAGIC;
#endif
mysql_audit_init_thd(this);
@@ -802,9 +805,14 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
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, 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);
sp_proc_cache= NULL;
sp_func_cache= NULL;
+ sp_package_spec_cache= NULL;
+ sp_package_body_cache= NULL;
/* For user vars replication*/
if (opt_bin_log)
@@ -841,8 +849,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
}
m_binlog_invoker= INVOKER_NONE;
- memset(&invoker_user, 0, sizeof(invoker_user));
- memset(&invoker_host, 0, sizeof(invoker_host));
+ invoker.init();
prepare_derived_at_open= FALSE;
create_tmp_table_for_derived= FALSE;
save_prep_leaf_list= FALSE;
@@ -990,9 +997,10 @@ void THD::raise_note_printf(uint sql_errno, ...)
}
Sql_condition* THD::raise_condition(uint sql_errno,
- const char* sqlstate,
- Sql_condition::enum_warning_level level,
- const char* msg)
+ const char* sqlstate,
+ Sql_condition::enum_warning_level level,
+ const Sql_user_condition_identity &ucid,
+ const char* msg)
{
Diagnostics_area *da= get_stmt_da();
Sql_condition *cond= NULL;
@@ -1051,7 +1059,7 @@ Sql_condition* THD::raise_condition(uint sql_errno,
if (!da->is_error())
{
set_row_count_func(-1);
- da->set_error_status(sql_errno, msg, sqlstate, cond);
+ da->set_error_status(sql_errno, msg, sqlstate, ucid, cond);
}
}
@@ -1062,10 +1070,10 @@ Sql_condition* THD::raise_condition(uint sql_errno,
require memory allocation and therefore might fail. Non fatal out of
memory errors can occur if raised by SIGNAL/RESIGNAL statement.
*/
- if (!(is_fatal_error && (sql_errno == EE_OUTOFMEMORY ||
- sql_errno == ER_OUTOFMEMORY)))
+ if (likely(!(is_fatal_error && (sql_errno == EE_OUTOFMEMORY ||
+ sql_errno == ER_OUTOFMEMORY))))
{
- cond= da->push_warning(this, sql_errno, sqlstate, level, msg);
+ cond= da->push_warning(this, sql_errno, sqlstate, level, ucid, msg);
}
DBUG_RETURN(cond);
}
@@ -1095,11 +1103,11 @@ char *thd_strmake(MYSQL_THD thd, const char *str, size_t size)
}
extern "C"
-LEX_STRING *thd_make_lex_string(THD *thd, LEX_STRING *lex_str,
+LEX_CSTRING *thd_make_lex_string(THD *thd, LEX_CSTRING *lex_str,
const char *str, size_t size,
int allocate_lex_string)
{
- return allocate_lex_string ? thd->make_lex_string(str, size)
+ return allocate_lex_string ? thd->make_clex_string(str, size)
: thd->make_lex_string(lex_str, str, size);
}
@@ -1148,11 +1156,20 @@ extern "C" my_thread_id next_thread_id_noinline()
}
#endif
+
+const Type_handler *THD::type_handler_for_datetime() const
+{
+ if (opt_mysql56_temporal_format)
+ return &type_handler_datetime2;
+ return &type_handler_datetime;
+}
+
+
/*
Init common variables that has to be reset on start and on change_user
*/
-void THD::init(void)
+void THD::init()
{
DBUG_ENTER("thd::init");
mysql_mutex_lock(&LOCK_global_system_variables);
@@ -1165,10 +1182,9 @@ void THD::init(void)
variables.pseudo_thread_id= thread_id;
variables.default_master_connection.str= default_master_connection_buff;
- ::strmake(variables.default_master_connection.str,
+ ::strmake(default_master_connection_buff,
global_system_variables.default_master_connection.str,
variables.default_master_connection.length);
-
mysql_mutex_unlock(&LOCK_global_system_variables);
user_time.val= start_time= start_time_sec_part= 0;
@@ -1194,6 +1210,7 @@ void THD::init(void)
reset_current_stmt_binlog_format_row();
reset_binlog_local_stmt_filter();
set_status_var_init();
+ status_var.max_local_memory_used= status_var.local_memory_used;
bzero((char *) &org_status_var, sizeof(org_status_var));
status_in_global= 0;
start_bytes_received= 0;
@@ -1247,11 +1264,21 @@ void THD::init(void)
session_tracker.enable(this);
#endif //EMBEDDED_LIBRARY
- apc_target.init(&LOCK_thd_data);
+ apc_target.init(&LOCK_thd_kill);
DBUG_VOID_RETURN;
}
-
+
+bool THD::restore_from_local_lex_to_old_lex(LEX *oldlex)
+{
+ DBUG_ASSERT(lex->sphead);
+ if (lex->sphead->merge_lex(this, oldlex, lex))
+ return true;
+ lex= oldlex;
+ return false;
+}
+
+
/* Updates some status variables to be used by update_global_user_stats */
void THD::update_stats(void)
@@ -1350,8 +1377,82 @@ void THD::change_user(void)
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);
sp_cache_clear(&sp_proc_cache);
sp_cache_clear(&sp_func_cache);
+ sp_cache_clear(&sp_package_spec_cache);
+ sp_cache_clear(&sp_package_body_cache);
+}
+
+/**
+ Change default database
+
+ @note This is coded to have as few instructions as possible under
+ LOCK_thd_data
+*/
+
+bool THD::set_db(const LEX_CSTRING *new_db)
+{
+ bool result= 0;
+ /*
+ Acquiring mutex LOCK_thd_data as we either free the memory allocated
+ for the database and reallocating the memory for the new db or memcpy
+ the new_db to the db.
+ */
+ /* Do not reallocate memory if current chunk is big enough. */
+ if (db.str && new_db->str && db.length >= new_db->length)
+ {
+ mysql_mutex_lock(&LOCK_thd_data);
+ db.length= new_db->length;
+ memcpy((char*) db.str, new_db->str, new_db->length+1);
+ mysql_mutex_unlock(&LOCK_thd_data);
+ }
+ else
+ {
+ const char *org_db= db.str;
+ const char *tmp= NULL;
+ if (new_db->str)
+ {
+ if (!(tmp= my_strndup(new_db->str, new_db->length, MYF(MY_WME | ME_FATALERROR))))
+ result= 1;
+ }
+
+ mysql_mutex_lock(&LOCK_thd_data);
+ db.str= tmp;
+ db.length= tmp ? new_db->length : 0;
+ mysql_mutex_unlock(&LOCK_thd_data);
+ my_free((char*) org_db);
+ }
+ PSI_CALL_set_thread_db(db.str, (int) db.length);
+ return result;
+}
+
+
+/**
+ Set the current database
+
+ @param new_db a pointer to the new database name.
+ @param new_db_len length of the new database name.
+
+ @note This operation just sets {db, db_length}. Switching the current
+ database usually involves other actions, like switching other database
+ attributes including security context. In the future, this operation
+ will be made private and more convenient interface will be provided.
+*/
+
+void THD::reset_db(const LEX_CSTRING *new_db)
+{
+ if (new_db->str != db.str || new_db->length != db.length)
+ {
+ if (db.str != 0)
+ DBUG_PRINT("QQ", ("Overwriting: %p", db.str));
+ mysql_mutex_lock(&LOCK_thd_data);
+ db= *new_db;
+ mysql_mutex_unlock(&LOCK_thd_data);
+ PSI_CALL_set_thread_db(db.str, (int) db.length);
+ }
}
@@ -1407,8 +1508,11 @@ void THD::cleanup(void)
#endif /* defined(ENABLED_DEBUG_SYNC) */
my_hash_free(&user_vars);
+ my_hash_free(&sequences);
sp_cache_clear(&sp_proc_cache);
sp_cache_clear(&sp_func_cache);
+ sp_cache_clear(&sp_package_spec_cache);
+ sp_cache_clear(&sp_package_body_cache);
auto_inc_intervals_forced.empty();
auto_inc_intervals_in_cur_stmt_for_binlog.empty();
@@ -1437,8 +1541,8 @@ void THD::cleanup(void)
void THD::free_connection()
{
DBUG_ASSERT(free_connection_done == 0);
- my_free(db);
- db= NULL;
+ my_free((char*) db.str);
+ db= null_clex_str;
#ifndef EMBEDDED_LIBRARY
if (net.vio)
vio_delete(net.vio);
@@ -1508,9 +1612,13 @@ THD::~THD()
if (!status_in_global)
add_status_to_global();
- /* Ensure that no one is using THD */
- mysql_mutex_lock(&LOCK_thd_data);
- mysql_mutex_unlock(&LOCK_thd_data);
+ /*
+ Other threads may have a lock on LOCK_thd_kill to ensure that this
+ THD is not deleted while they access it. The following mutex_lock
+ ensures that no one else is using this THD and it's now safe to delete
+ */
+ mysql_mutex_lock(&LOCK_thd_kill);
+ mysql_mutex_unlock(&LOCK_thd_kill);
#ifdef WITH_WSREP
delete wsrep_rgi;
@@ -1525,7 +1633,7 @@ THD::~THD()
mysql_mutex_destroy(&LOCK_wakeup_ready);
mysql_mutex_destroy(&LOCK_thd_data);
mysql_mutex_destroy(&LOCK_thd_kill);
-#ifndef DBUG_OFF
+#ifdef DBUG_ASSERT_EXISTS
dbug_sentry= THD_SENTRY_GONE;
#endif
#ifndef EMBEDDED_LIBRARY
@@ -1557,7 +1665,7 @@ THD::~THD()
/* trick to make happy memory accounting system */
#ifndef EMBEDDED_LIBRARY
- session_tracker.deinit();
+ session_tracker.sysvars.deinit();
#endif //EMBEDDED_LIBRARY
if (status_var.local_memory_used != 0)
@@ -1606,6 +1714,9 @@ void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var)
to_var->binlog_bytes_written+= from_var->binlog_bytes_written;
to_var->cpu_time+= from_var->cpu_time;
to_var->busy_time+= from_var->busy_time;
+ to_var->table_open_cache_hits+= from_var->table_open_cache_hits;
+ to_var->table_open_cache_misses+= from_var->table_open_cache_misses;
+ to_var->table_open_cache_overflows+= from_var->table_open_cache_overflows;
/*
Update global_memory_used. We have to do this with atomic_add as the
@@ -1657,6 +1768,12 @@ void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var,
dec_var->binlog_bytes_written;
to_var->cpu_time+= from_var->cpu_time - dec_var->cpu_time;
to_var->busy_time+= from_var->busy_time - dec_var->busy_time;
+ to_var->table_open_cache_hits+= from_var->table_open_cache_hits -
+ dec_var->table_open_cache_hits;
+ to_var->table_open_cache_misses+= from_var->table_open_cache_misses -
+ dec_var->table_open_cache_misses;
+ to_var->table_open_cache_overflows+= from_var->table_open_cache_overflows -
+ dec_var->table_open_cache_overflows;
/*
We don't need to accumulate memory_used as these are not reset or used by
@@ -1680,17 +1797,17 @@ void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var,
This is normally called from another thread's THD object.
- @note Do always call this while holding LOCK_thd_data.
+ @note Do always call this while holding LOCK_thd_kill.
NOT_KILLED is used to awake a thread for a slave
*/
-void THD::awake(killed_state state_to_set)
+void THD::awake_no_mutex(killed_state state_to_set)
{
DBUG_ENTER("THD::awake");
DBUG_PRINT("enter", ("this: %p current_thd: %p state: %d",
this, current_thd, (int) state_to_set));
THD_CHECK_SENTRY(this);
- mysql_mutex_assert_owner(&LOCK_thd_data);
+ mysql_mutex_assert_owner(&LOCK_thd_kill);
print_aborted_warning(3, "KILLED");
@@ -1701,8 +1818,6 @@ void THD::awake(killed_state state_to_set)
if (killed >= KILL_CONNECTION)
state_to_set= killed;
- /* Set the 'killed' flag of 'this', which is the target THD object. */
- mysql_mutex_lock(&LOCK_thd_kill);
set_killed_no_mutex(state_to_set);
if (state_to_set >= KILL_CONNECTION || state_to_set == NOT_KILLED)
@@ -1789,7 +1904,6 @@ void THD::awake(killed_state state_to_set)
}
mysql_mutex_unlock(&mysys_var->mutex);
}
- mysql_mutex_unlock(&LOCK_thd_kill);
DBUG_VOID_RETURN;
}
@@ -1805,10 +1919,10 @@ void THD::disconnect()
{
Vio *vio= NULL;
- mysql_mutex_lock(&LOCK_thd_data);
-
set_killed(KILL_CONNECTION);
+ mysql_mutex_lock(&LOCK_thd_data);
+
#ifdef SIGNAL_WITH_VIO_CLOSE
/*
Since a active vio might might have not been set yet, in
@@ -1841,9 +1955,9 @@ bool THD::notify_shared_lock(MDL_context_owner *ctx_in_use,
{
/* This code is similar to kill_delayed_threads() */
DBUG_PRINT("info", ("kill delayed thread"));
- mysql_mutex_lock(&in_use->LOCK_thd_data);
+ mysql_mutex_lock(&in_use->LOCK_thd_kill);
if (in_use->killed < KILL_CONNECTION)
- in_use->set_killed(KILL_CONNECTION);
+ in_use->set_killed_no_mutex(KILL_CONNECTION);
if (in_use->mysys_var)
{
mysql_mutex_lock(&in_use->mysys_var->mutex);
@@ -1854,7 +1968,7 @@ bool THD::notify_shared_lock(MDL_context_owner *ctx_in_use,
in_use->mysys_var->abort= 1;
mysql_mutex_unlock(&in_use->mysys_var->mutex);
}
- mysql_mutex_unlock(&in_use->LOCK_thd_data);
+ mysql_mutex_unlock(&in_use->LOCK_thd_kill);
signalled= TRUE;
}
@@ -1945,6 +2059,23 @@ int THD::killed_errno()
}
+void THD::reset_killed()
+{
+ /*
+ Resetting killed has to be done under a mutex to ensure
+ its not done during an awake() call.
+ */
+ DBUG_ENTER("reset_killed");
+ if (killed != NOT_KILLED)
+ {
+ mysql_mutex_lock(&LOCK_thd_kill);
+ killed= NOT_KILLED;
+ killed_err= 0;
+ mysql_mutex_unlock(&LOCK_thd_kill);
+ }
+ DBUG_VOID_RETURN;
+}
+
/*
Remember the location of thread info, the structure needed for
the structure for the net buffer
@@ -1962,7 +2093,7 @@ bool THD::store_globals()
return 1;
/*
mysys_var is concurrently readable by a killer thread.
- It is protected by LOCK_thd_data, it is not needed to lock while the
+ It is protected by LOCK_thd_kill, it is not needed to lock while the
pointer is changing from NULL not non-NULL. If the kill thread reads
NULL it doesn't refer to anything, but if it is non-NULL we need to
ensure that the thread doesn't proceed to assign another thread to
@@ -2013,9 +2144,9 @@ bool THD::store_globals()
void THD::reset_globals()
{
- mysql_mutex_lock(&LOCK_thd_data);
+ mysql_mutex_lock(&LOCK_thd_kill);
mysys_var= 0;
- mysql_mutex_unlock(&LOCK_thd_data);
+ mysql_mutex_unlock(&LOCK_thd_kill);
/* Undocking the thread specific data. */
set_current_thd(0);
@@ -2141,18 +2272,18 @@ void THD::cleanup_after_query()
*/
bool THD::convert_string(LEX_STRING *to, CHARSET_INFO *to_cs,
- const char *from, uint from_length,
+ const char *from, size_t from_length,
CHARSET_INFO *from_cs)
{
DBUG_ENTER("THD::convert_string");
size_t new_length= to_cs->mbmaxlen * from_length;
uint errors;
- if (alloc_lex_string(to, new_length + 1))
+ if (unlikely(alloc_lex_string(to, new_length + 1)))
DBUG_RETURN(true); // EOM
to->length= copy_and_convert((char*) to->str, new_length, to_cs,
from, from_length, from_cs, &errors);
to->str[to->length]= 0; // Safety
- if (errors && lex->parse_vcol_expr)
+ if (unlikely(errors) && lex->parse_vcol_expr)
{
my_error(ER_BAD_DATA, MYF(0),
ErrConvString(from, from_length, from_cs).ptr(),
@@ -2168,7 +2299,7 @@ bool THD::convert_string(LEX_STRING *to, CHARSET_INFO *to_cs,
dstcs and srccs cannot be &my_charset_bin.
*/
bool THD::convert_fix(CHARSET_INFO *dstcs, LEX_STRING *dst,
- CHARSET_INFO *srccs, const char *src, uint src_length,
+ CHARSET_INFO *srccs, const char *src, size_t src_length,
String_copier *status)
{
DBUG_ENTER("THD::convert_fix");
@@ -2186,7 +2317,7 @@ bool THD::convert_fix(CHARSET_INFO *dstcs, LEX_STRING *dst,
Copy or convert a string.
*/
bool THD::copy_fix(CHARSET_INFO *dstcs, LEX_STRING *dst,
- CHARSET_INFO *srccs, const char *src, uint src_length,
+ CHARSET_INFO *srccs, const char *src, size_t src_length,
String_copier *status)
{
DBUG_ENTER("THD::copy_fix");
@@ -2203,7 +2334,7 @@ bool THD::copy_fix(CHARSET_INFO *dstcs, LEX_STRING *dst,
class String_copier_with_error: public String_copier
{
public:
- bool check_errors(CHARSET_INFO *srccs, const char *src, uint src_length)
+ bool check_errors(CHARSET_INFO *srccs, const char *src, size_t src_length)
{
if (most_important_error_pos())
{
@@ -2218,7 +2349,7 @@ public:
bool THD::convert_with_error(CHARSET_INFO *dstcs, LEX_STRING *dst,
CHARSET_INFO *srccs,
- const char *src, uint src_length)
+ const char *src, size_t src_length)
{
String_copier_with_error status;
return convert_fix(dstcs, dst, srccs, src, src_length, &status) ||
@@ -2228,7 +2359,7 @@ bool THD::convert_with_error(CHARSET_INFO *dstcs, LEX_STRING *dst,
bool THD::copy_with_error(CHARSET_INFO *dstcs, LEX_STRING *dst,
CHARSET_INFO *srccs,
- const char *src, uint src_length)
+ const char *src, size_t src_length)
{
String_copier_with_error status;
return copy_fix(dstcs, dst, srccs, src, src_length, &status) ||
@@ -2254,7 +2385,8 @@ bool THD::copy_with_error(CHARSET_INFO *dstcs, LEX_STRING *dst,
bool THD::convert_string(String *s, CHARSET_INFO *from_cs, CHARSET_INFO *to_cs)
{
uint dummy_errors;
- if (convert_buffer.copy(s->ptr(), s->length(), from_cs, to_cs, &dummy_errors))
+ if (unlikely(convert_buffer.copy(s->ptr(), s->length(), from_cs, to_cs,
+ &dummy_errors)))
return TRUE;
/* If convert_buffer >> s copying is more efficient long term */
if (convert_buffer.alloced_length() >= convert_buffer.length() * 2 ||
@@ -2267,6 +2399,85 @@ bool THD::convert_string(String *s, CHARSET_INFO *from_cs, CHARSET_INFO *to_cs)
}
+bool THD::check_string_for_wellformedness(const char *str,
+ size_t length,
+ CHARSET_INFO *cs) const
+{
+ size_t wlen= Well_formed_prefix(cs, str, length).length();
+ if (wlen < length)
+ {
+ ErrConvString err(str, length, &my_charset_bin);
+ my_error(ER_INVALID_CHARACTER_STRING, MYF(0), cs->csname, err.ptr());
+ return true;
+ }
+ return false;
+}
+
+
+bool THD::to_ident_sys_alloc(Lex_ident_sys_st *to, const Lex_ident_cli_st *ident)
+{
+ if (ident->is_quoted())
+ {
+ LEX_CSTRING unquoted;
+ if (quote_unescape(&unquoted, ident, ident->quote()))
+ return true;
+ return charset_is_system_charset ?
+ to->copy_sys(this, &unquoted) :
+ to->convert(this, &unquoted, charset());
+ }
+ return charset_is_system_charset ?
+ to->copy_sys(this, ident) :
+ to->copy_or_convert(this, ident, charset());
+}
+
+
+Item_basic_constant *
+THD::make_string_literal(const char *str, size_t length, uint repertoire)
+{
+ if (!length && (variables.sql_mode & MODE_EMPTY_STRING_IS_NULL))
+ return new (mem_root) Item_null(this, 0, variables.collation_connection);
+ if (!charset_is_collation_connection &&
+ (repertoire != MY_REPERTOIRE_ASCII ||
+ !my_charset_is_ascii_based(variables.collation_connection)))
+ {
+ LEX_STRING to;
+ if (convert_string(&to, variables.collation_connection,
+ str, length, variables.character_set_client))
+ return NULL;
+ str= to.str;
+ length= to.length;
+ }
+ return new (mem_root) Item_string(this, str, (uint)length,
+ variables.collation_connection,
+ DERIVATION_COERCIBLE, repertoire);
+}
+
+
+Item_basic_constant *
+THD::make_string_literal_nchar(const Lex_string_with_metadata_st &str)
+{
+ DBUG_ASSERT(my_charset_is_ascii_based(national_charset_info));
+ if (!str.length && (variables.sql_mode & MODE_EMPTY_STRING_IS_NULL))
+ return new (mem_root) Item_null(this, 0, national_charset_info);
+
+ return new (mem_root) Item_string(this, str.str, (uint)str.length,
+ national_charset_info,
+ DERIVATION_COERCIBLE,
+ str.repertoire());
+}
+
+
+Item_basic_constant *
+THD::make_string_literal_charset(const Lex_string_with_metadata_st &str,
+ CHARSET_INFO *cs)
+{
+ 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);
+}
+
+
/*
Update some cache variables when character set changes
*/
@@ -2318,7 +2529,7 @@ void THD::add_changed_table(TABLE *table)
}
-void THD::add_changed_table(const char *key, long key_length)
+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;
@@ -2331,7 +2542,7 @@ void THD::add_changed_table(const char *key, long key_length)
{
list_include(prev_changed, curr, changed_table_dup(key, key_length));
DBUG_PRINT("info",
- ("key_length: %ld %u", key_length,
+ ("key_length: %zu %zu", key_length,
(*prev_changed)->key_length));
DBUG_VOID_RETURN;
}
@@ -2342,7 +2553,7 @@ void THD::add_changed_table(const char *key, long key_length)
{
list_include(prev_changed, curr, changed_table_dup(key, key_length));
DBUG_PRINT("info",
- ("key_length: %ld %u", key_length,
+ ("key_length: %zu %zu", key_length,
(*prev_changed)->key_length));
DBUG_VOID_RETURN;
}
@@ -2354,13 +2565,13 @@ void THD::add_changed_table(const char *key, long key_length)
}
}
*prev_changed = changed_table_dup(key, key_length);
- DBUG_PRINT("info", ("key_length: %ld %u", key_length,
+ DBUG_PRINT("info", ("key_length: %zu %zu", key_length,
(*prev_changed)->key_length));
DBUG_VOID_RETURN;
}
-CHANGED_TABLE_LIST* THD::changed_table_dup(const char *key, long key_length)
+CHANGED_TABLE_LIST* THD::changed_table_dup(const char *key, size_t key_length)
{
CHANGED_TABLE_LIST* new_table =
(CHANGED_TABLE_LIST*) trans_alloc(ALIGN_SIZE(sizeof(CHANGED_TABLE_LIST))+
@@ -2645,7 +2856,7 @@ static String default_field_term("\t",default_charset_info);
static String default_enclosed_and_line_start("", default_charset_info);
static String default_xml_row_term("<row>", default_charset_info);
-sql_exchange::sql_exchange(char *name, bool flag,
+sql_exchange::sql_exchange(const char *name, bool flag,
enum enum_filetype filetype_arg)
:file_name(name), opt_enclosed(0), dumpfile(flag), skip_lines(0)
{
@@ -2658,7 +2869,7 @@ sql_exchange::sql_exchange(char *name, bool flag,
cs= NULL;
}
-bool sql_exchange::escaped_given(void)
+bool sql_exchange::escaped_given(void) const
{
return escaped != &default_escaped;
}
@@ -2749,7 +2960,7 @@ bool select_send::send_eof()
Don't send EOF if we're in error condition (which implies we've already
sent or are sending an error)
*/
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
return TRUE;
::my_eof(thd);
is_result_set_started= 0;
@@ -2764,10 +2975,11 @@ bool select_send::send_eof()
bool select_to_file::send_eof()
{
int error= MY_TEST(end_io_cache(&cache));
- if (mysql_file_close(file, MYF(MY_WME)) || thd->is_error())
+ if (unlikely(mysql_file_close(file, MYF(MY_WME))) ||
+ unlikely(thd->is_error()))
error= true;
- if (!error && !suppress_my_ok)
+ if (likely(!error) && !suppress_my_ok)
{
::my_ok(thd,row_count);
}
@@ -2838,8 +3050,7 @@ static File create_file(THD *thd, char *path, sql_exchange *exchange,
if (!dirname_length(exchange->file_name))
{
- strxnmov(path, FN_REFLEN-1, mysql_real_data_home, thd->db ? thd->db : "",
- NullS);
+ strxnmov(path, FN_REFLEN-1, mysql_real_data_home, thd->get_db(), NullS);
(void) fn_format(path, exchange->file_name, path, "", option);
}
else
@@ -3030,7 +3241,7 @@ int select_export::send_data(List<Item> &items)
res->charset(),
res->ptr(), res->length());
error_pos= copier.most_important_error_pos();
- if (error_pos)
+ if (unlikely(error_pos))
{
/*
TODO:
@@ -3044,12 +3255,12 @@ int select_export::send_data(List<Item> &items)
ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
ER_THD(thd, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
"string", printable_buff,
- item->name, static_cast<long>(row_count));
+ item->name.str, static_cast<long>(row_count));
*/
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
ER_THD(thd, WARN_DATA_TRUNCATED),
- item->name, static_cast<long>(row_count));
+ item->name.str, static_cast<long>(row_count));
}
else if (copier.source_end_pos() < res->ptr() + res->length())
{
@@ -3332,7 +3543,7 @@ int select_max_min_finder_subselect::send_data(List<Item> &items)
{
if (!cache)
{
- cache= Item_cache::get_cache(thd, val_item);
+ cache= val_item->get_cache(thd);
switch (val_item->result_type()) {
case REAL_RESULT:
op= &select_max_min_finder_subselect::cmp_real;
@@ -3454,15 +3665,30 @@ int select_exists_subselect::send_data(List<Item> &items)
int select_dumpvar::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
{
+ my_var_sp *mvsp;
unit= u;
-
- if (var_list.elements != list.elements)
+ m_var_sp_row= NULL;
+
+ if (var_list.elements == 1 &&
+ (mvsp= var_list.head()->get_my_var_sp()) &&
+ mvsp->type_handler() == &type_handler_row)
{
- my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT,
- ER_THD(thd, ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT), MYF(0));
- return 1;
- }
- return 0;
+ // SELECT INTO row_type_sp_variable
+ if (mvsp->get_rcontext(thd->spcont)->get_variable(mvsp->offset)->cols() !=
+ list.elements)
+ goto error;
+ m_var_sp_row= mvsp;
+ return 0;
+ }
+
+ // SELECT INTO variable list
+ if (var_list.elements == list.elements)
+ return 0;
+
+error:
+ my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT,
+ ER_THD(thd, ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT), MYF(0));
+ return 1;
}
@@ -3523,12 +3749,11 @@ Statement::Statement(LEX *lex_arg, MEM_ROOT *mem_root_arg,
enum enum_state state_arg, ulong id_arg)
:Query_arena(mem_root_arg, state_arg),
id(id_arg),
- mark_used_columns(MARK_COLUMNS_READ),
+ column_usage(MARK_COLUMNS_READ),
lex(lex_arg),
- db(NULL),
- db_length(0)
+ db(null_clex_str)
{
- name.str= NULL;
+ name= null_clex_str;
}
@@ -3541,7 +3766,7 @@ Query_arena::Type Statement::type() const
void Statement::set_statement(Statement *stmt)
{
id= stmt->id;
- mark_used_columns= stmt->mark_used_columns;
+ column_usage= stmt->column_usage;
lex= stmt->lex;
query_string= stmt->query_string;
}
@@ -3594,7 +3819,7 @@ void THD::set_n_backup_active_arena(Query_arena *set, Query_arena *backup)
backup->set_query_arena(this);
set_query_arena(set);
-#ifndef DBUG_OFF
+#ifdef DBUG_ASSERT_EXISTS
backup->is_backup_arena= TRUE;
#endif
DBUG_VOID_RETURN;
@@ -3613,7 +3838,7 @@ void THD::restore_active_arena(Query_arena *set, Query_arena *backup)
DBUG_ASSERT(backup->is_backup_arena);
set->set_query_arena(this);
set_query_arena(backup);
-#ifndef DBUG_OFF
+#ifdef DBUG_ASSERT_EXISTS
backup->is_backup_arena= FALSE;
#endif
DBUG_VOID_RETURN;
@@ -3785,22 +4010,48 @@ Statement_map::~Statement_map()
bool my_var_user::set(THD *thd, Item *item)
{
- Item_func_set_user_var *suv= new (thd->mem_root) Item_func_set_user_var(thd, name, item);
+ Item_func_set_user_var *suv= new (thd->mem_root) Item_func_set_user_var(thd, &name, item);
suv->save_item_result(item);
return suv->fix_fields(thd, 0) || suv->update();
}
+
+sp_rcontext *my_var_sp::get_rcontext(sp_rcontext *local_ctx) const
+{
+ return m_rcontext_handler->get_rcontext(local_ctx);
+}
+
+
bool my_var_sp::set(THD *thd, Item *item)
{
- return thd->spcont->set_variable(thd, offset, &item);
+ return get_rcontext(thd->spcont)->set_variable(thd, offset, &item);
}
-int select_dumpvar::send_data(List<Item> &items)
+bool my_var_sp_row_field::set(THD *thd, Item *item)
{
+ return get_rcontext(thd->spcont)->
+ set_variable_row_field(thd, offset, m_field_offset, &item);
+}
+
+
+bool select_dumpvar::send_data_to_var_list(List<Item> &items)
+{
+ DBUG_ENTER("select_dumpvar::send_data_to_var_list");
List_iterator_fast<my_var> var_li(var_list);
List_iterator<Item> it(items);
Item *item;
my_var *mv;
+ while ((mv= var_li++) && (item= it++))
+ {
+ if (mv->set(thd, item))
+ DBUG_RETURN(true);
+ }
+ DBUG_RETURN(false);
+}
+
+
+int select_dumpvar::send_data(List<Item> &items)
+{
DBUG_ENTER("select_dumpvar::send_data");
if (unit->offset_limit_cnt)
@@ -3813,11 +4064,12 @@ int select_dumpvar::send_data(List<Item> &items)
my_message(ER_TOO_MANY_ROWS, ER_THD(thd, ER_TOO_MANY_ROWS), MYF(0));
DBUG_RETURN(1);
}
- while ((mv= var_li++) && (item= it++))
- {
- if (mv->set(thd, item))
- DBUG_RETURN(1);
- }
+ if (m_var_sp_row ?
+ m_var_sp_row->get_rcontext(thd->spcont)->
+ set_variable_row(thd, m_var_sp_row->offset, items) :
+ send_data_to_var_list(items))
+ DBUG_RETURN(1);
+
DBUG_RETURN(thd->is_error());
}
@@ -3830,7 +4082,7 @@ bool select_dumpvar::send_eof()
Don't send EOF if we're in error condition (which implies we've already
sent or are sending an error)
*/
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
return true;
if (!suppress_my_ok)
@@ -3845,9 +4097,10 @@ bool
select_materialize_with_stats::
create_result_table(THD *thd_arg, List<Item> *column_types,
bool is_union_distinct, ulonglong options,
- const char *table_alias, bool bit_fields_as_long,
+ const LEX_CSTRING *table_alias, bool bit_fields_as_long,
bool create_table,
- bool keep_row_order)
+ bool keep_row_order,
+ uint hidden)
{
DBUG_ASSERT(table == 0);
tmp_table_param.field_count= column_types->elements;
@@ -3855,7 +4108,7 @@ create_result_table(THD *thd_arg, List<Item> *column_types,
if (! (table= create_tmp_table(thd_arg, &tmp_table_param, *column_types,
(ORDER*) 0, is_union_distinct, 1,
- options, HA_POS_ERROR, (char*) table_alias,
+ options, HA_POS_ERROR, table_alias,
!create_table, keep_row_order)))
return TRUE;
@@ -3882,12 +4135,12 @@ void select_materialize_with_stats::reset()
void select_materialize_with_stats::cleanup()
{
reset();
- select_union::cleanup();
+ select_unit::cleanup();
}
/**
- Override select_union::send_data to analyze each row for NULLs and to
+ Override select_unit::send_data to analyze each row for NULLs and to
update null_statistics before sending data to the client.
@return TRUE if fatal error when sending data to the client
@@ -3902,7 +4155,7 @@ int select_materialize_with_stats::send_data(List<Item> &items)
uint nulls_in_row= 0;
int res;
- if ((res= select_union::send_data(items)))
+ if ((res= select_unit::send_data(items)))
return res;
if (table->null_catch_flags & REJECT_ROW_DUE_TO_NULL_FIELDS)
{
@@ -3956,7 +4209,7 @@ void TMP_TABLE_PARAM::init()
}
-void thd_increment_bytes_sent(void *thd, ulong length)
+void thd_increment_bytes_sent(void *thd, size_t length)
{
/* thd == 0 when close_connection() calls net_send_error() */
if (likely(thd != 0))
@@ -3965,22 +4218,16 @@ void thd_increment_bytes_sent(void *thd, ulong length)
}
}
-my_bool thd_net_is_killed()
+my_bool thd_net_is_killed(THD *thd)
{
- THD *thd= current_thd;
return thd && thd->killed ? 1 : 0;
}
-void thd_increment_bytes_received(void *thd, ulong length)
+void thd_increment_bytes_received(void *thd, size_t length)
{
- ((THD*) thd)->status_var.bytes_received+= length;
-}
-
-
-void thd_increment_net_big_packet_count(void *thd, ulong length)
-{
- ((THD*) thd)->status_var.net_big_packet_count+= length;
+ if (thd != NULL) // MDEV-13073 Ack collector having NULL
+ ((THD*) thd)->status_var.bytes_received+= length;
}
@@ -3988,6 +4235,12 @@ void THD::set_status_var_init()
{
bzero((char*) &status_var, offsetof(STATUS_VAR,
last_cleared_system_status_var));
+ /*
+ Session status for Threads_running is always 1. It can only be queried
+ by thread itself via INFORMATION_SCHEMA.SESSION_STATUS or SHOW [SESSION]
+ STATUS. And at this point thread is guaranteed to be running.
+ */
+ status_var.threads_running= 1;
}
@@ -4014,7 +4267,7 @@ void Security_context::destroy()
}
if (user != delayed_user)
{
- my_free(user);
+ my_free((char*) user);
user= NULL;
}
@@ -4024,7 +4277,7 @@ void Security_context::destroy()
external_user= NULL;
}
- my_free(ip);
+ my_free((char*) ip);
ip= NULL;
}
@@ -4040,7 +4293,7 @@ void Security_context::skip_grants()
bool Security_context::set_user(char *user_arg)
{
- my_free(user);
+ my_free((char*) user);
user= my_strdup(user_arg, MYF(0));
return user == 0;
}
@@ -4098,9 +4351,9 @@ bool Security_context::set_user(char *user_arg)
bool
Security_context::
change_security_context(THD *thd,
- LEX_STRING *definer_user,
- LEX_STRING *definer_host,
- LEX_STRING *db,
+ LEX_CSTRING *definer_user,
+ LEX_CSTRING *definer_host,
+ LEX_CSTRING *db,
Security_context **backup)
{
bool needs_change;
@@ -4423,8 +4676,10 @@ TABLE *open_purge_table(THD *thd, const char *db, size_t dblen,
Open_table_context ot_ctx(thd, 0);
TABLE_LIST *tl= (TABLE_LIST*)thd->alloc(sizeof(TABLE_LIST));
+ LEX_CSTRING db_name= {db, dblen };
+ LEX_CSTRING table_name= { tb, tblen };
- tl->init_one_table(db, dblen, tb, tblen, tb, TL_READ);
+ tl->init_one_table(&db_name, &table_name, 0, TL_READ);
tl->i_s_requested_object= OPEN_TABLE_ONLY;
bool error= open_table(thd, tl, &ot_ctx);
@@ -4432,7 +4687,7 @@ TABLE *open_purge_table(THD *thd, const char *db, size_t dblen,
/* we don't recover here */
DBUG_ASSERT(!error || !ot_ctx.can_recover_from_failed_open());
- if (error)
+ if (unlikely(error))
close_thread_tables(thd);
DBUG_RETURN(error ? NULL : tl->table);
@@ -4454,7 +4709,7 @@ TABLE *find_fk_open_table(THD *thd, const char *db, size_t db_len,
{
if (t->s->db.length == db_len && t->s->table_name.length == table_len &&
!strcmp(t->s->db.str, db) && !strcmp(t->s->table_name.str, table) &&
- t->pos_in_table_list->prelocking_placeholder == TABLE_LIST::FK)
+ t->pos_in_table_list->prelocking_placeholder == TABLE_LIST::PRELOCK_FK)
return t;
}
return NULL;
@@ -4479,7 +4734,6 @@ void destroy_thd(MYSQL_THD thd)
thd->add_status_to_global();
unlink_not_visible_thd(thd);
delete thd;
- dec_thread_running();
}
void reset_thd(MYSQL_THD thd)
@@ -4561,18 +4815,14 @@ extern "C" int thd_slave_thread(const MYSQL_THD thd)
return(thd->slave_thread);
}
-/* Returns true for a worker thread in parallel replication. */
-extern "C" int thd_rpl_is_parallel(const MYSQL_THD thd)
-{
- return thd->rgi_slave && thd->rgi_slave->is_parallel_exec;
-}
+
/* Returns high resolution timestamp for the start
of the current query. */
extern "C" unsigned long long thd_start_utime(const MYSQL_THD thd)
{
- return thd->start_utime;
+ return thd->start_time * 1000000 + thd->start_time_sec_part;
}
@@ -4605,7 +4855,7 @@ thd_need_wait_reports(const MYSQL_THD thd)
}
/*
- Used by storage engines (currently TokuDB and InnoDB/XtraDB) to report that
+ Used by storage engines (currently TokuDB and InnoDB) to report that
one transaction THD is about to go to wait for a transactional lock held by
another transactions OTHER_THD.
@@ -4627,7 +4877,7 @@ thd_need_wait_reports(const MYSQL_THD thd)
transaction, and later re-try it, to resolve the deadlock.
This call need only receive reports about waits for locks that will remain
- until the holding transaction commits. InnoDB/XtraDB auto-increment locks,
+ until the holding transaction commits. InnoDB auto-increment locks,
for example, are released earlier, and so need not be reported. (Such false
positives are not harmful, but could lead to unnecessary kill and retry, so
best avoided).
@@ -4680,7 +4930,7 @@ thd_rpl_deadlock_check(MYSQL_THD thd, MYSQL_THD other_thd)
}
/*
- This function is called from InnoDB/XtraDB to check if the commit order of
+ This function is called from InnoDB to check if the commit order of
two transactions has already been decided by the upper layer. This happens
in parallel replication, where the commit order is forced to be the same on
the slave as it was originally on the master.
@@ -4710,7 +4960,7 @@ thd_rpl_deadlock_check(MYSQL_THD thd, MYSQL_THD other_thd)
If this function returns true, normal locking should be done as required by
the binlogging and transaction isolation level in effect. But if it returns
- false, the correct order will be enforced anyway, and InnoDB/XtraDB can
+ false, the correct order will be enforced anyway, and InnoDB can
avoid taking the gap lock, preventing the lock conflict.
Calling this function is just an optimisation to avoid unnecessary
@@ -4748,66 +4998,6 @@ thd_need_ordering_with(const MYSQL_THD thd, const MYSQL_THD other_thd)
return 0;
}
-
-/*
- If the storage engine detects a deadlock, and needs to choose a victim
- transaction to roll back, it can call this function to ask the upper
- server layer for which of two possible transactions is prefered to be
- aborted and rolled back.
-
- In parallel replication, if two transactions are running in parallel and
- one is fixed to commit before the other, then the one that commits later
- will be prefered as the victim - chosing the early transaction as a victim
- will not resolve the deadlock anyway, as the later transaction still needs
- to wait for the earlier to commit.
-
- Otherwise, a transaction that uses only transactional tables, and can thus
- be safely rolled back, will be prefered as a deadlock victim over a
- transaction that also modified non-transactional (eg. MyISAM) tables.
-
- The return value is -1 if the first transaction is prefered as a deadlock
- victim, 1 if the second transaction is prefered, or 0 for no preference (in
- which case the storage engine can make the choice as it prefers).
-*/
-extern "C" int
-thd_deadlock_victim_preference(const MYSQL_THD thd1, const MYSQL_THD thd2)
-{
- rpl_group_info *rgi1, *rgi2;
- bool nontrans1, nontrans2;
-
- if (!thd1 || !thd2)
- return 0;
-
- /*
- If the transactions are participating in the same replication domain in
- parallel replication, then request to select the one that will commit
- later (in the fixed commit order from the master) as the deadlock victim.
- */
- rgi1= thd1->rgi_slave;
- rgi2= thd2->rgi_slave;
- if (rgi1 && rgi2 &&
- rgi1->is_parallel_exec &&
- rgi1->rli == rgi2->rli &&
- rgi1->current_gtid.domain_id == rgi2->current_gtid.domain_id)
- return rgi1->gtid_sub_id < rgi2->gtid_sub_id ? 1 : -1;
-
- /*
- If one transaction has modified non-transactional tables (so that it
- cannot be safely rolled back), and the other has not, then prefer to
- select the purely transactional one as the victim.
- */
- nontrans1= thd1->transaction.all.modified_non_trans_table;
- nontrans2= thd2->transaction.all.modified_non_trans_table;
- if (nontrans1 && !nontrans2)
- return 1;
- else if (!nontrans1 && nontrans2)
- return -1;
-
- /* No preferences, let the storage engine decide. */
- return 0;
-}
-
-
extern "C" int thd_non_transactional_update(const MYSQL_THD thd)
{
return(thd->transaction.all.modified_non_trans_table);
@@ -4833,7 +5023,7 @@ extern "C" void thd_mark_transaction_to_rollback(MYSQL_THD thd, bool all)
extern "C" bool thd_binlog_filter_ok(const MYSQL_THD thd)
{
- return binlog_filter->db_ok(thd->db);
+ return binlog_filter->db_ok(thd->db.str);
}
/*
@@ -4888,6 +5078,20 @@ extern "C" bool thd_is_strict_mode(const MYSQL_THD thd)
}
+/**
+ Get query start time as SQL field data.
+ Needed by InnoDB.
+ @param thd Thread object
+ @param buf Buffer to hold start time data
+*/
+void thd_get_query_start_data(THD *thd, char *buf)
+{
+ LEX_CSTRING field_name;
+ Field_timestampf f((uchar *)buf, NULL, 0, Field::NONE, &field_name, NULL, 6);
+ f.store_TIME(thd->query_start(), thd->query_start_sec_part());
+}
+
+
/*
Interface for MySQL Server, plugins and storage engines to report
when they are going to sleep/stall.
@@ -4989,10 +5193,7 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup,
backup->count_cuted_fields= count_cuted_fields;
backup->in_sub_stmt= in_sub_stmt;
backup->enable_slow_log= enable_slow_log;
- backup->query_plan_flags= query_plan_flags;
backup->limit_found_rows= limit_found_rows;
- backup->examined_row_count= m_examined_row_count;
- backup->sent_row_count= m_sent_row_count;
backup->cuted_fields= cuted_fields;
backup->client_capabilities= client_capabilities;
backup->savepoints= transaction.savepoints;
@@ -5000,6 +5201,7 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup,
first_successful_insert_id_in_prev_stmt;
backup->first_successful_insert_id_in_cur_stmt=
first_successful_insert_id_in_cur_stmt;
+ store_slow_query_state(backup);
if ((!lex->requires_prelocking() || is_update_query(lex->sql_command)) &&
!is_current_stmt_binlog_format_row())
@@ -5015,14 +5217,12 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup,
/* Disable result sets */
client_capabilities &= ~CLIENT_MULTI_RESULTS;
in_sub_stmt|= new_state;
- m_examined_row_count= 0;
- m_sent_row_count= 0;
cuted_fields= 0;
transaction.savepoints= 0;
first_successful_insert_id_in_cur_stmt= 0;
+ reset_slow_query_state();
}
-
void THD::restore_sub_statement_state(Sub_statement_state *backup)
{
DBUG_ENTER("THD::restore_sub_statement_state");
@@ -5057,7 +5257,6 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup)
variables.option_bits= backup->option_bits;
in_sub_stmt= backup->in_sub_stmt;
enable_slow_log= backup->enable_slow_log;
- query_plan_flags= backup->query_plan_flags;
first_successful_insert_id_in_prev_stmt=
backup->first_successful_insert_id_in_prev_stmt;
first_successful_insert_id_in_cur_stmt=
@@ -5065,6 +5264,10 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup)
limit_found_rows= backup->limit_found_rows;
set_sent_row_count(backup->sent_row_count);
client_capabilities= backup->client_capabilities;
+
+ /* Restore statistic needed for slow log */
+ add_slow_query_state(backup);
+
/*
If we've left sub-statement mode, reset the fatal error flag.
Otherwise keep the current value, to propagate it up the sub-statement
@@ -5089,6 +5292,56 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup)
DBUG_VOID_RETURN;
}
+/*
+ Store slow query state at start of a stored procedure statment
+*/
+
+void THD::store_slow_query_state(Sub_statement_state *backup)
+{
+ backup->affected_rows= affected_rows;
+ backup->bytes_sent_old= bytes_sent_old;
+ backup->examined_row_count= m_examined_row_count;
+ backup->query_plan_flags= query_plan_flags;
+ backup->query_plan_fsort_passes= query_plan_fsort_passes;
+ backup->sent_row_count= m_sent_row_count;
+ backup->tmp_tables_disk_used= tmp_tables_disk_used;
+ backup->tmp_tables_size= tmp_tables_size;
+ backup->tmp_tables_used= tmp_tables_used;
+}
+
+/* Reset variables related to slow query log */
+
+void THD::reset_slow_query_state()
+{
+ affected_rows= 0;
+ bytes_sent_old= status_var.bytes_sent;
+ m_examined_row_count= 0;
+ m_sent_row_count= 0;
+ query_plan_flags= QPLAN_INIT;
+ query_plan_fsort_passes= 0;
+ tmp_tables_disk_used= 0;
+ tmp_tables_size= 0;
+ tmp_tables_used= 0;
+}
+
+/*
+ Add back the stored values to the current counters to be able to get
+ right status for 'call procedure_name'
+*/
+
+void THD::add_slow_query_state(Sub_statement_state *backup)
+{
+ affected_rows+= backup->affected_rows;
+ bytes_sent_old= backup->bytes_sent_old;
+ m_examined_row_count+= backup->examined_row_count;
+ m_sent_row_count+= backup->sent_row_count;
+ query_plan_flags|= backup->query_plan_flags;
+ query_plan_fsort_passes+= backup->query_plan_fsort_passes;
+ tmp_tables_disk_used+= backup->tmp_tables_disk_used;
+ tmp_tables_size+= backup->tmp_tables_size;
+ tmp_tables_used+= backup->tmp_tables_used;
+}
+
void THD::set_statement(Statement *stmt)
{
@@ -5123,6 +5376,8 @@ void THD::inc_examined_row_count(ha_rows count)
void THD::inc_status_created_tmp_disk_tables()
{
+ tmp_tables_disk_used++;
+ query_plan_flags|= QPLAN_TMP_DISK;
status_var_increment(status_var.created_tmp_disk_tables_);
#ifdef HAVE_PSI_STATEMENT_INTERFACE
PSI_STATEMENT_CALL(inc_statement_created_tmp_disk_tables)(m_statement_psi, 1);
@@ -5131,6 +5386,8 @@ void THD::inc_status_created_tmp_disk_tables()
void THD::inc_status_created_tmp_tables()
{
+ tmp_tables_used++;
+ query_plan_flags|= QPLAN_TMP_TABLE;
status_var_increment(status_var.created_tmp_tables_);
#ifdef HAVE_PSI_STATEMENT_INTERFACE
PSI_STATEMENT_CALL(inc_statement_created_tmp_tables)(m_statement_psi, 1);
@@ -5240,9 +5497,9 @@ void THD::set_query_and_id(char *query_arg, uint32 query_length_arg,
/** Assign a new value to thd->mysys_var. */
void THD::set_mysys_var(struct st_my_thread_var *new_mysys_var)
{
- mysql_mutex_lock(&LOCK_thd_data);
+ mysql_mutex_lock(&LOCK_thd_kill);
mysys_var= new_mysys_var;
- mysql_mutex_unlock(&LOCK_thd_data);
+ mysql_mutex_unlock(&LOCK_thd_kill);
}
/**
@@ -5284,8 +5541,8 @@ void THD::get_definer(LEX_USER *definer, bool role)
if (slave_thread && has_invoker())
#endif
{
- definer->user = invoker_user;
- definer->host= invoker_host;
+ definer->user= invoker.user;
+ definer->host= invoker.host;
definer->reset_auth();
}
else
@@ -5375,7 +5632,7 @@ public:
MY_MEMORY_ORDER_RELAXED))
{
old&= ACQUIRED | RECOVERED;
- (void) LF_BACKOFF;
+ (void) LF_BACKOFF();
}
}
bool acquire_recovered()
@@ -5388,7 +5645,7 @@ public:
if (!(old & RECOVERED) || (old & ACQUIRED))
return false;
old= RECOVERED;
- (void) LF_BACKOFF;
+ (void) LF_BACKOFF();
}
return true;
}
@@ -5667,7 +5924,7 @@ int xid_cache_iterate(THD *thd, my_hash_walk_action action, void *arg)
int THD::decide_logging_format(TABLE_LIST *tables)
{
DBUG_ENTER("THD::decide_logging_format");
- DBUG_PRINT("info", ("Query: %s", query()));
+ DBUG_PRINT("info", ("Query: %.*s", (uint) query_length(), query()));
DBUG_PRINT("info", ("variables.binlog_format: %lu",
variables.binlog_format));
DBUG_PRINT("info", ("lex->get_stmt_unsafe_flags(): 0x%x",
@@ -5682,7 +5939,7 @@ int THD::decide_logging_format(TABLE_LIST *tables)
*/
if (mysql_bin_log.is_open() && (variables.option_bits & OPTION_BIN_LOG) &&
!(wsrep_binlog_format() == BINLOG_FORMAT_STMT &&
- !binlog_filter->db_ok(db)))
+ !binlog_filter->db_ok(db.str)))
{
if (is_bulk_op())
@@ -5777,17 +6034,33 @@ int THD::decide_logging_format(TABLE_LIST *tables)
Get the capabilities vector for all involved storage engines and
mask out the flags for the binary log.
*/
- for (TABLE_LIST *table= tables; table; table= table->next_global)
+ for (TABLE_LIST *tbl= tables; tbl; tbl= tbl->next_global)
{
- if (table->placeholder())
+ TABLE *table;
+ TABLE_SHARE *share;
+ handler::Table_flags flags;
+ if (tbl->placeholder())
continue;
- handler::Table_flags const flags= table->table->file->ha_table_flags();
+ table= tbl->table;
+ share= table->s;
+ flags= table->file->ha_table_flags();
+ if (!share->table_creation_was_logged)
+ {
+ /*
+ This is a temporary table which was not logged in the binary log.
+ Disable statement logging to enforce row level logging.
+ */
+ DBUG_ASSERT(share->tmp_table);
+ flags&= ~HA_BINLOG_STMT_CAPABLE;
+ /* We can only use row logging */
+ set_current_stmt_binlog_format_row();
+ }
DBUG_PRINT("info", ("table: %s; ha_table_flags: 0x%llx",
- table->table_name, flags));
+ tbl->table_name.str, flags));
- if (table->table->no_replicate)
+ if (share->no_replicate)
{
/*
The statement uses a table that is not replicated.
@@ -5805,43 +6078,44 @@ int THD::decide_logging_format(TABLE_LIST *tables)
*/
lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_TABLE);
- if (table->lock_type >= TL_WRITE_ALLOW_WRITE)
+ if (tbl->lock_type >= TL_WRITE_ALLOW_WRITE)
{
non_replicated_tables_count++;
continue;
}
}
- if (table == lex->first_not_own_table())
+ if (tbl == lex->first_not_own_table())
found_first_not_own_table= true;
replicated_tables_count++;
- if (table->prelocking_placeholder != TABLE_LIST::FK)
+ if (tbl->prelocking_placeholder != TABLE_LIST::PRELOCK_FK)
{
- if (table->lock_type <= TL_READ_NO_INSERT)
+ if (tbl->lock_type <= TL_READ_NO_INSERT)
has_read_tables= true;
- else if (table->table->found_next_number_field &&
- (table->lock_type >= TL_WRITE_ALLOW_WRITE))
+ else if (table->found_next_number_field &&
+ (tbl->lock_type >= TL_WRITE_ALLOW_WRITE))
{
has_auto_increment_write_tables= true;
has_auto_increment_write_tables_not_first= found_first_not_own_table;
- if (table->table->s->next_number_keypart != 0)
+ if (share->next_number_keypart != 0)
has_write_table_auto_increment_not_first_in_pk= true;
}
}
- if (table->lock_type >= TL_WRITE_ALLOW_WRITE)
+ if (tbl->lock_type >= TL_WRITE_ALLOW_WRITE)
{
bool trans;
if (prev_write_table && prev_write_table->file->ht !=
- table->table->file->ht)
+ table->file->ht)
multi_write_engine= TRUE;
- if (table->table->s->non_determinstic_insert)
+ if (share->non_determinstic_insert &&
+ !(sql_command_flags[lex->sql_command] & CF_SCHEMA_CHANGE))
has_write_tables_with_unsafe_statements= true;
- trans= table->table->file->has_transactions();
+ trans= table->file->has_transactions();
- if (table->table->s->tmp_table)
+ if (share->tmp_table)
lex->set_stmt_accessed_table(trans ? LEX::STMT_WRITES_TEMP_TRANS_TABLE :
LEX::STMT_WRITES_TEMP_NON_TRANS_TABLE);
else
@@ -5852,17 +6126,16 @@ int THD::decide_logging_format(TABLE_LIST *tables)
flags_write_some_set |= flags;
is_write= TRUE;
- prev_write_table= table->table;
+ prev_write_table= table;
}
flags_access_some_set |= flags;
- if (lex->sql_command != SQLCOM_CREATE_TABLE ||
- (lex->sql_command == SQLCOM_CREATE_TABLE && lex->tmp_table()))
+ if (lex->sql_command != SQLCOM_CREATE_TABLE || lex->tmp_table())
{
- my_bool trans= table->table->file->has_transactions();
+ my_bool trans= table->file->has_transactions();
- if (table->table->s->tmp_table)
+ if (share->tmp_table)
lex->set_stmt_accessed_table(trans ? LEX::STMT_READS_TEMP_TRANS_TABLE :
LEX::STMT_READS_TEMP_NON_TRANS_TABLE);
else
@@ -5871,10 +6144,10 @@ int THD::decide_logging_format(TABLE_LIST *tables)
}
if (prev_access_table && prev_access_table->file->ht !=
- table->table->file->ht)
+ table->file->ht)
multi_access_engine= TRUE;
- prev_access_table= table->table;
+ prev_access_table= table;
}
if (wsrep_binlog_format() != BINLOG_FORMAT_ROW)
@@ -6003,9 +6276,16 @@ int THD::decide_logging_format(TABLE_LIST *tables)
{
/*
5. Error: Cannot modify table that uses a storage engine
- limited to row-logging when binlog_format = STATEMENT
+ limited to row-logging when binlog_format = STATEMENT, except
+ if all tables that are updated are temporary tables
*/
- if (IF_WSREP((!WSREP(this) || wsrep_exec_mode == LOCAL_STATE),1))
+ if (!lex->stmt_writes_to_non_temp_table())
+ {
+ /* As all updated tables are temporary, nothing will be logged */
+ set_current_stmt_binlog_format_row();
+ }
+ else if (IF_WSREP((!WSREP(this) ||
+ wsrep_exec_mode == LOCAL_STATE),1))
{
my_error((error= ER_BINLOG_STMT_MODE_AND_ROW_ENGINE), MYF(0), "");
}
@@ -6064,7 +6344,8 @@ int THD::decide_logging_format(TABLE_LIST *tables)
clear_binlog_local_stmt_filter();
}
- if (error) {
+ if (unlikely(error))
+ {
DBUG_PRINT("info", ("decision: no logging since an error was generated"));
DBUG_RETURN(-1);
}
@@ -6073,10 +6354,8 @@ int THD::decide_logging_format(TABLE_LIST *tables)
"ROW" : "STATEMENT"));
if (variables.binlog_format == BINLOG_FORMAT_ROW &&
- (lex->sql_command == SQLCOM_UPDATE ||
- lex->sql_command == SQLCOM_UPDATE_MULTI ||
- lex->sql_command == SQLCOM_DELETE ||
- lex->sql_command == SQLCOM_DELETE_MULTI))
+ (sql_command_flags[lex->sql_command] &
+ (CF_UPDATES_DATA | CF_DELETES_DATA)))
{
String table_names;
/*
@@ -6090,14 +6369,14 @@ 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->table_name);
table_names.append(",");
}
}
if (!table_names.is_empty())
{
- bool is_update= (lex->sql_command == SQLCOM_UPDATE ||
- lex->sql_command == SQLCOM_UPDATE_MULTI);
+ bool is_update= MY_TEST(sql_command_flags[lex->sql_command] &
+ CF_UPDATES_DATA);
/*
Replace the last ',' with '.' for table_names
*/
@@ -6122,7 +6401,7 @@ int THD::decide_logging_format(TABLE_LIST *tables)
mysql_bin_log.is_open(),
(variables.option_bits & OPTION_BIN_LOG),
(uint) wsrep_binlog_format(),
- binlog_filter->db_ok(db)));
+ binlog_filter->db_ok(db.str)));
#endif
DBUG_RETURN(0);
@@ -6344,7 +6623,7 @@ CPP_UNNAMED_NS_START
{
DBUG_ASSERT(s < sizeof(m_ptr)/sizeof(*m_ptr));
DBUG_ASSERT(m_ptr[s] != 0);
- DBUG_ASSERT(m_alloc_checked == TRUE);
+ DBUG_SLOW_ASSERT(m_alloc_checked == TRUE);
return m_ptr[s];
}
@@ -6859,6 +7138,15 @@ static bool protect_against_unsafe_warning_flood(int unsafe_type)
DBUG_RETURN(unsafe_warning_suppression_active[unsafe_type]);
}
+MYSQL_TIME THD::query_start_TIME()
+{
+ MYSQL_TIME res;
+ variables.time_zone->gmt_sec_to_TIME(&res, query_start());
+ res.second_part= query_start_sec_part();
+ time_zone_used= 1;
+ return res;
+}
+
/**
Auxiliary method used by @c binlog_query() to raise warnings.
@@ -6922,11 +7210,12 @@ void THD::issue_unsafe_warnings()
@see decide_logging_format
+ @retval < 0 No logging of query (ok)
@retval 0 Success
-
- @retval nonzero If there is a failure when writing the query (e.g.,
- write failure), then the error code is returned.
+ @retval > 0 If there is a failure when writing the query (e.g.,
+ write failure), then the error code is returned.
*/
+
int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
ulong query_len, bool is_trans, bool direct,
bool suppress_use, int errcode)
@@ -6952,7 +7241,7 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
The current statement is to be ignored, and not written to
the binlog. Do not call issue_unsafe_warnings().
*/
- DBUG_RETURN(0);
+ DBUG_RETURN(-1);
}
/*
@@ -6965,8 +7254,14 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
top-most close_thread_tables().
*/
if (this->locked_tables_mode <= LTM_LOCK_TABLES)
- if (int error= binlog_flush_pending_rows_event(TRUE, is_trans))
+ {
+ int error;
+ if (unlikely(error= binlog_flush_pending_rows_event(TRUE, is_trans)))
+ {
+ DBUG_ASSERT(error > 0);
DBUG_RETURN(error);
+ }
+ }
/*
Warnings for unsafe statements logged in statement format are
@@ -7008,7 +7303,7 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
("is_current_stmt_binlog_format_row: %d",
is_current_stmt_binlog_format_row()));
if (is_current_stmt_binlog_format_row())
- DBUG_RETURN(0);
+ DBUG_RETURN(-1);
/* Fall through */
/*
@@ -7049,7 +7344,7 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
}
binlog_table_maps= 0;
- DBUG_RETURN(error);
+ DBUG_RETURN(error >= 0 ? error : 1);
}
case THD::QUERY_TYPE_COUNT:
@@ -7084,12 +7379,12 @@ void THD::set_last_commit_gtid(rpl_gtid &gtid)
#endif
m_last_commit_gtid= gtid;
#ifndef EMBEDDED_LIBRARY
- if (changed_gtid &&
- session_tracker.get_tracker(SESSION_SYSVARS_TRACKER)->is_enabled())
+ if (changed_gtid && session_tracker.sysvars.is_enabled())
{
- session_tracker.get_tracker(SESSION_SYSVARS_TRACKER)->
+ DBUG_ASSERT(current_thd == this);
+ session_tracker.sysvars.
mark_as_changed(this, (LEX_CSTRING*)Sys_last_gtid_ptr);
- }
+ }
#endif
}
@@ -7249,7 +7544,7 @@ wait_for_commit::wait_for_prior_commit2(THD *thd)
thd->ENTER_COND(&COND_wait_commit, &LOCK_wait_commit,
&stage_waiting_for_prior_transaction_to_commit,
&old_stage);
- while ((loc_waitee= this->waitee) && !thd->check_killed())
+ while ((loc_waitee= this->waitee) && likely(!thd->check_killed(1)))
mysql_cond_wait(&COND_wait_commit, &LOCK_wait_commit);
if (!loc_waitee)
{
@@ -7439,4 +7734,105 @@ bool Discrete_intervals_list::append(Discrete_interval *new_interval)
DBUG_RETURN(0);
}
+
+void AUTHID::copy(MEM_ROOT *mem_root, const LEX_CSTRING *user_name,
+ const LEX_CSTRING *host_name)
+{
+ user.str= strmake_root(mem_root, user_name->str, user_name->length);
+ user.length= user_name->length;
+
+ host.str= strmake_root(mem_root, host_name->str, host_name->length);
+ host.length= host_name->length;
+}
+
+
+/*
+ Set from a string in 'user@host' format.
+ This method resebmles parse_user(),
+ but does not need temporary buffers.
+*/
+void AUTHID::parse(const char *str, size_t length)
+{
+ const char *p= strrchr(str, '@');
+ if (!p)
+ {
+ user.str= str;
+ user.length= length;
+ host= null_clex_str;
+ }
+ else
+ {
+ user.str= str;
+ user.length= (size_t) (p - str);
+ host.str= p + 1;
+ host.length= (size_t) (length - user.length - 1);
+ if (user.length && !host.length)
+ host= host_not_specified; // 'user@' -> 'user@%'
+ }
+ if (user.length > USERNAME_LENGTH)
+ user.length= USERNAME_LENGTH;
+ if (host.length > HOSTNAME_LENGTH)
+ host.length= HOSTNAME_LENGTH;
+}
+
+
+void Database_qualified_name::copy(MEM_ROOT *mem_root,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &name)
+{
+ m_db.length= db.length;
+ m_db.str= strmake_root(mem_root, db.str, db.length);
+ m_name.length= name.length;
+ m_name.str= strmake_root(mem_root, name.str, name.length);
+}
+
+
+bool Table_ident::append_to(THD *thd, String *str) const
+{
+ return (db.length &&
+ (append_identifier(thd, str, db.str, db.length) ||
+ str->append('.'))) ||
+ append_identifier(thd, str, table.str, table.length);
+}
+
+
+bool Qualified_column_ident::append_to(THD *thd, String *str) const
+{
+ return Table_ident::append_to(thd, str) || str->append('.') ||
+ append_identifier(thd, str, m_column.str, m_column.length);
+}
+
+
#endif /* !defined(MYSQL_CLIENT) */
+
+
+Query_arena_stmt::Query_arena_stmt(THD *_thd) :
+ thd(_thd)
+{
+ arena= thd->activate_stmt_arena_if_needed(&backup);
+}
+
+Query_arena_stmt::~Query_arena_stmt()
+{
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+}
+
+
+bool THD::timestamp_to_TIME(MYSQL_TIME *ltime, my_time_t ts,
+ ulong sec_part, ulonglong fuzzydate)
+{
+ time_zone_used= 1;
+ if (ts == 0 && sec_part == 0)
+ {
+ if (fuzzydate & TIME_NO_ZERO_DATE)
+ return 1;
+ set_zero_time(ltime, MYSQL_TIMESTAMP_DATETIME);
+ }
+ else
+ {
+ variables.time_zone->gmt_sec_to_TIME(ltime, ts);
+ ltime->second_part= sec_part;
+ }
+ return 0;
+}
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 8d8ab779d56..5ced820a34d 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1,6 +1,6 @@
/*
Copyright (c) 2000, 2016, 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
@@ -20,7 +20,6 @@
/* Classes in mysql */
-#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
#include "dur_prop.h"
#include <waiting_threads.h>
#include "sql_const.h"
@@ -38,6 +37,7 @@
#include "thr_lock.h" /* thr_lock_type, THR_LOCK_DATA, THR_LOCK_INFO */
#include "thr_timer.h"
#include "thr_malloc.h"
+#include "log_slow.h" /* LOG_SLOW_DISABLE_... */
#include <my_tree.h>
#include "sql_digest_stream.h" // sql_digest_state
@@ -93,8 +93,25 @@ enum enum_slave_run_triggers_for_rbr { SLAVE_RUN_TRIGGERS_FOR_RBR_NO,
SLAVE_RUN_TRIGGERS_FOR_RBR_LOGGING};
enum enum_slave_type_conversions { SLAVE_TYPE_CONVERSIONS_ALL_LOSSY,
SLAVE_TYPE_CONVERSIONS_ALL_NON_LOSSY};
-enum enum_mark_columns
-{ MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE};
+
+/*
+ MARK_COLUMNS_READ: A column is goind to be read.
+ MARK_COLUMNS_WRITE: A column is going to be written to.
+ MARK_COLUMNS_READ: A column is goind to be read.
+ A bit in read set is set to inform handler that the field
+ is to be read. If field list contains duplicates, then
+ thd->dup_field is set to point to the last found
+ duplicate.
+ MARK_COLUMNS_WRITE: A column is going to be written to.
+ A bit is set in write set to inform handler that it needs
+ to update this field in write_row and update_row.
+*/
+enum enum_column_usage
+{ COLUMNS_READ, COLUMNS_WRITE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE};
+
+static inline bool should_mark_column(enum_column_usage column_usage)
+{ return column_usage >= MARK_COLUMNS_READ; }
+
enum enum_filetype { FILETYPE_CSV, FILETYPE_XML };
enum enum_binlog_row_image {
@@ -140,6 +157,8 @@ enum enum_binlog_row_image {
#define MODE_HIGH_NOT_PRECEDENCE (1ULL << 29)
#define MODE_NO_ENGINE_SUBSTITUTION (1ULL << 30)
#define MODE_PAD_CHAR_TO_FULL_LENGTH (1ULL << 31)
+#define MODE_EMPTY_STRING_IS_NULL (1ULL << 32)
+#define MODE_SIMULTANEOUS_ASSIGNMENT (1ULL << 33)
/* Bits for different old style modes */
#define OLD_MODE_NO_DUP_KEY_WARNINGS_WITH_IGNORE (1 << 0)
@@ -148,7 +167,6 @@ enum enum_binlog_row_image {
extern char internal_table_name[2];
extern char empty_c_string[1];
-extern LEX_STRING EMPTY_STR;
extern MYSQL_PLUGIN_IMPORT const char **errmesg;
extern bool volatile shutdown_in_progress;
@@ -195,7 +213,7 @@ typedef struct st_user_var_events
{
user_var_entry *user_var_event;
char *value;
- ulong length;
+ size_t length;
Item_result type;
uint charset_number;
bool unsigned_flag;
@@ -234,14 +252,11 @@ typedef struct st_copy_info {
class Key_part_spec :public Sql_alloc {
public:
- LEX_STRING field_name;
+ LEX_CSTRING field_name;
uint length;
- Key_part_spec(const LEX_STRING &name, uint len)
- : field_name(name), length(len)
+ Key_part_spec(const LEX_CSTRING *name, uint len)
+ : field_name(*name), length(len)
{}
- Key_part_spec(const char *name, const size_t name_len, uint len)
- : length(len)
- { field_name.str= (char *)name; field_name.length= name_len; }
bool operator==(const Key_part_spec& other) const;
/**
Construct a copy of this Key_part_spec. field_name is copied
@@ -287,8 +302,9 @@ class Alter_column :public Sql_alloc {
public:
const char *name;
Virtual_column_info *default_value;
- Alter_column(const char *par_name, Virtual_column_info *expr)
- :name(par_name), default_value(expr) {}
+ 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) {}
/**
Used to make a clone of this object for ALTER/CREATE TABLE
@sa comment for Key_part_spec::clone
@@ -304,37 +320,29 @@ public:
enum Keytype type;
KEY_CREATE_INFO key_create_info;
List<Key_part_spec> columns;
- LEX_STRING name;
+ LEX_CSTRING name;
engine_option_value *option_list;
bool generated;
+ bool invisible;
- Key(enum Keytype type_par, const LEX_STRING &name_arg,
+ 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)
+ name(*name_arg), option_list(NULL), generated(generated_arg),
+ invisible(false)
{
key_create_info.algorithm= algorithm_arg;
- }
- Key(enum Keytype type_par, const LEX_STRING &name_arg,
+ }
+ Key(enum Keytype type_par, const LEX_CSTRING *name_arg,
KEY_CREATE_INFO *key_info_arg,
- bool generated_arg, List<Key_part_spec> &cols,
+ bool generated_arg, List<Key_part_spec> *cols,
engine_option_value *create_opt, DDL_options_st ddl_options)
: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)
+ type(type_par), key_create_info(*key_info_arg), columns(*cols),
+ name(*name_arg), option_list(create_opt), generated(generated_arg),
+ invisible(false)
{}
- Key(enum Keytype type_par, const char *name_arg, size_t name_len_arg,
- KEY_CREATE_INFO *key_info_arg, bool generated_arg,
- List<Key_part_spec> &cols,
- engine_option_value *create_opt, DDL_options_st ddl_options)
- :DDL_options(ddl_options),
- type(type_par), key_create_info(*key_info_arg), columns(cols),
- option_list(create_opt), generated(generated_arg)
- {
- name.str= (char *)name_arg;
- name.length= name_len_arg;
- }
Key(const Key &rhs, MEM_ROOT *mem_root);
virtual ~Key() {}
/* Equality comparison of keys (ignoring name) */
@@ -352,19 +360,20 @@ class Foreign_key: public Key {
public:
enum fk_match_opt { FK_MATCH_UNDEF, FK_MATCH_FULL,
FK_MATCH_PARTIAL, FK_MATCH_SIMPLE};
-
- LEX_STRING ref_db;
- LEX_STRING ref_table;
+ LEX_CSTRING ref_db;
+ LEX_CSTRING ref_table;
List<Key_part_spec> ref_columns;
- uint delete_opt, update_opt, match_opt;
- Foreign_key(const LEX_STRING &name_arg, List<Key_part_spec> &cols,
- const LEX_STRING &ref_db_arg, const LEX_STRING &ref_table_arg,
- List<Key_part_spec> &ref_cols,
- uint delete_opt_arg, uint update_opt_arg, uint match_opt_arg,
+ 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 *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,
+ fk_match_opt match_opt_arg,
DDL_options ddl_options)
:Key(FOREIGN_KEY, name_arg, &default_key_create_info, 0, cols, NULL,
ddl_options),
- ref_db(ref_db_arg), ref_table(ref_table_arg), ref_columns(ref_cols),
+ 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)
{
@@ -385,8 +394,9 @@ public:
typedef struct st_mysql_lock
{
TABLE **table;
- uint table_count,lock_count;
THR_LOCK_DATA **locks;
+ uint table_count,lock_count;
+ uint flags;
} MYSQL_LOCK;
@@ -454,7 +464,7 @@ typedef enum enum_diag_condition_item_name
Name of each diagnostic condition item.
This array is indexed by Diag_condition_item_name.
*/
-extern const LEX_STRING Diag_condition_item_names[];
+extern const LEX_CSTRING Diag_condition_item_names[];
/**
These states are bit coded with HARD. For each state there must be a pair
@@ -552,6 +562,8 @@ typedef struct system_variables
ulonglong join_buff_space_limit;
ulonglong log_slow_filter;
ulonglong log_slow_verbosity;
+ ulonglong log_slow_disabled_statements;
+ ulonglong log_disabled_statements;
ulonglong bulk_insert_buff_size;
ulonglong join_buff_size;
ulonglong sortbuff_size;
@@ -581,6 +593,7 @@ typedef struct system_variables
ulong saved_auto_increment_increment, saved_auto_increment_offset;
#endif /* WITH_WSREP */
uint eq_range_index_dive_limit;
+ ulong column_compression_zlib_strategy;
ulong lock_wait_timeout;
ulong join_cache_level;
ulong max_allowed_packet;
@@ -631,6 +644,7 @@ typedef struct system_variables
ulong query_cache_type;
ulong tx_isolation;
ulong updatable_views_with_limit;
+ ulong alter_algorithm;
int max_user_connections;
ulong server_id;
/**
@@ -654,7 +668,6 @@ typedef struct system_variables
my_bool keep_files_on_create;
my_bool old_mode;
- my_bool old_alter_table;
my_bool old_passwords;
my_bool big_tables;
my_bool only_standard_compliant_cte;
@@ -668,6 +681,7 @@ typedef struct system_variables
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;
plugin_ref table_plugin;
plugin_ref tmp_table_plugin;
@@ -684,7 +698,7 @@ typedef struct system_variables
CHARSET_INFO *collation_connection;
/* Names. These will be allocated in buffers in thd */
- LEX_STRING default_master_connection;
+ LEX_CSTRING default_master_connection;
/* Error messages */
MY_LOCALE *lc_messages;
@@ -717,6 +731,16 @@ typedef struct system_variables
my_bool session_track_state_change;
ulong threadpool_priority;
+
+ uint idle_transaction_timeout;
+ uint idle_readonly_transaction_timeout;
+ uint idle_write_transaction_timeout;
+ uint column_compression_threshold;
+ uint column_compression_zlib_level;
+ uint in_subquery_conversion_threshold;
+
+ vers_asof_timestamp_t vers_asof_timestamp;
+ ulong vers_alter_history;
} SV;
/**
@@ -727,6 +751,8 @@ typedef struct system_variables
typedef struct system_status_var
{
+ ulong column_compressions;
+ ulong column_decompressions;
ulong com_stat[(uint) SQLCOM_END];
ulong com_create_tmp_table;
ulong com_drop_tmp_table;
@@ -771,6 +797,7 @@ typedef struct system_status_var
/* The following are for internal temporary tables */
ulong ha_tmp_update_count;
ulong ha_tmp_write_count;
+ ulong ha_tmp_delete_count;
ulong ha_prepare_count;
ulong ha_icp_attempts;
ulong ha_icp_match;
@@ -779,7 +806,6 @@ typedef struct system_status_var
ulong ha_savepoint_rollback_count;
ulong ha_external_lock_count;
- ulong net_big_packet_count;
ulong opened_tables;
ulong opened_shares;
ulong opened_views; /* +1 opening a view */
@@ -800,11 +826,16 @@ typedef struct system_status_var
ulong filesort_pq_sorts_;
/* Features used */
+ ulong feature_custom_aggregate_functions; /* +1 when custom aggregate
+ functions are used */
ulong feature_dynamic_columns; /* +1 when creating a dynamic column */
ulong feature_fulltext; /* +1 when MATCH is used */
ulong feature_gis; /* +1 opening a table with GIS features */
+ ulong feature_invisible_columns; /* +1 opening a table with invisible column */
+ ulong feature_json; /* +1 when JSON function appears in the statement */
ulong feature_locale; /* +1 when LOCALE is set */
ulong feature_subquery; /* +1 when subqueries are used */
+ ulong feature_system_versioning; /* +1 opening a table WITH SYSTEM VERSIONING */
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 */
@@ -835,10 +866,15 @@ typedef struct system_status_var
ulonglong rows_sent;
ulonglong rows_tmp_read;
ulonglong binlog_bytes_written;
+ ulonglong table_open_cache_hits;
+ ulonglong table_open_cache_misses;
+ ulonglong table_open_cache_overflows;
double last_query_cost;
double cpu_time, busy_time;
+ uint32 threads_running;
/* Don't initialize */
/* Memory used for thread local storage */
+ int64 max_local_memory_used;
volatile int64 local_memory_used;
/* Memory allocated for global usage */
volatile int64 global_memory_used;
@@ -916,7 +952,7 @@ void free_tmp_table(THD *thd, TABLE *entry);
/* The following macro is to make init of Query_arena simpler */
-#ifndef DBUG_OFF
+#ifdef DBUG_ASSERT_EXISTS
#define INIT_ARENA_DBUG_INFO is_backup_arena= 0; is_reprepared= FALSE;
#else
#define INIT_ARENA_DBUG_INFO
@@ -931,7 +967,7 @@ public:
*/
Item *free_list;
MEM_ROOT *mem_root; // Pointer to current memroot
-#ifndef DBUG_OFF
+#ifdef DBUG_ASSERT_EXISTS
bool is_backup_arena; /* True if this arena is used for backup. */
bool is_reprepared;
#endif
@@ -950,6 +986,7 @@ public:
enum_state state;
+public:
/* We build without RTTI, so dynamic_cast can't be used. */
enum Type
{
@@ -982,7 +1019,7 @@ public:
inline void* calloc(size_t size)
{
void *ptr;
- if ((ptr=alloc_root(mem_root,size)))
+ if (likely((ptr=alloc_root(mem_root,size))))
bzero(ptr, size);
return ptr;
}
@@ -992,10 +1029,10 @@ public:
{ return strmake_root(mem_root,str,size); }
inline void *memdup(const void *str, size_t size)
{ return memdup_root(mem_root,str,size); }
- inline void *memdup_w_gap(const void *str, size_t size, uint gap)
+ inline void *memdup_w_gap(const void *str, size_t size, size_t gap)
{
void *ptr;
- if ((ptr= alloc_root(mem_root,size+gap)))
+ if (likely((ptr= alloc_root(mem_root,size+gap))))
memcpy(ptr,str,size);
return ptr;
}
@@ -1021,6 +1058,22 @@ public:
};
+class Query_arena_stmt
+{
+ THD *thd;
+ Query_arena backup;
+ Query_arena *arena;
+
+public:
+ Query_arena_stmt(THD *_thd);
+ ~Query_arena_stmt();
+ bool arena_replaced()
+ {
+ return arena != NULL;
+ }
+};
+
+
class Server_side_cursor;
/**
@@ -1050,20 +1103,9 @@ public:
*/
ulong id;
- /*
- MARK_COLUMNS_NONE: Means mark_used_colums is not set and no indicator to
- handler of fields used is set
- MARK_COLUMNS_READ: Means a bit in read set is set to inform handler
- that the field is to be read. If field list contains
- duplicates, then thd->dup_field is set to point
- to the last found duplicate.
- MARK_COLUMNS_WRITE: Means a bit is set in write set to inform handler
- that it needs to update this field in write_row
- and update_row.
- */
- enum enum_mark_columns mark_used_columns;
-
- LEX_STRING name; /* name for named prepared statements */
+ enum enum_column_usage column_usage;
+
+ LEX_CSTRING name; /* name for named prepared statements */
LEX *lex; // parse tree descriptor
/*
Points to the query associated with this statement. It's const, but
@@ -1094,6 +1136,10 @@ public:
{
return static_cast<uint32>(query_string.length());
}
+ inline char *query_end() const
+ {
+ return query_string.str() + query_string.length();
+ }
CHARSET_INFO *query_charset() const { return query_string.charset(); }
void set_query_inner(const CSET_STRING &string_arg)
{
@@ -1111,18 +1157,13 @@ public:
/**
Name of the current (default) database.
- If there is the current (default) database, "db" contains its name. If
- there is no current (default) database, "db" is NULL and "db_length" is
- 0. In other words, "db", "db_length" must either be NULL, or contain a
+ If there is the current (default) database, "db.str" contains its name. If
+ there is no current (default) database, "db.str" is NULL and "db.length" is
+ 0. In other words, db must either be NULL, or contain a
valid database name.
-
- @note this attribute is set and alloced by the slave SQL thread (for
- the THD of that thread); that thread is (and must remain, for now) the
- only responsible for freeing this member.
*/
- char *db;
- size_t db_length;
+ LEX_CSTRING db;
/* This is set to 1 of last call to send_result_to_client() was ok */
my_bool query_cache_is_applicable;
@@ -1161,7 +1202,7 @@ public:
int insert(THD *thd, Statement *statement);
- Statement *find_by_name(LEX_STRING *name)
+ Statement *find_by_name(LEX_CSTRING *name)
{
Statement *stmt;
stmt= (Statement*)my_hash_search(&names_hash, (uchar*)name->str,
@@ -1267,7 +1308,7 @@ public:
ip - client IP
*/
const char *host;
- char *user, *ip;
+ const char *user, *ip;
char priv_user[USERNAME_LENGTH];
char proxy_user[USERNAME_LENGTH + MAX_HOSTNAME + 5];
/* The host privilege we are using */
@@ -1294,9 +1335,9 @@ public:
#ifndef NO_EMBEDDED_ACCESS_CHECKS
bool
change_security_context(THD *thd,
- LEX_STRING *definer_user,
- LEX_STRING *definer_host,
- LEX_STRING *db,
+ LEX_CSTRING *definer_user,
+ LEX_CSTRING *definer_host,
+ LEX_CSTRING *db,
Security_context **backup);
void
@@ -1594,19 +1635,25 @@ public:
class Sub_statement_state
{
public:
+ Discrete_interval auto_inc_interval_for_cur_row;
+ Discrete_intervals_list auto_inc_intervals_forced;
+ SAVEPOINT *savepoints;
ulonglong option_bits;
ulonglong first_successful_insert_id_in_prev_stmt;
ulonglong first_successful_insert_id_in_cur_stmt, insert_id_for_cur_row;
- Discrete_interval auto_inc_interval_for_cur_row;
- Discrete_intervals_list auto_inc_intervals_forced;
ulonglong limit_found_rows;
- ha_rows cuted_fields, sent_row_count, examined_row_count;
+ ulonglong tmp_tables_size;
ulonglong client_capabilities;
+ ulonglong cuted_fields, sent_row_count, examined_row_count;
+ ulonglong affected_rows;
+ ulonglong bytes_sent_old;
+ ulong tmp_tables_used;
+ ulong tmp_tables_disk_used;
+ ulong query_plan_fsort_passes;
ulong query_plan_flags;
uint in_sub_stmt; /* 0, SUB_STMT_TRIGGER or SUB_STMT_FUNCTION */
bool enable_slow_log;
bool last_insert_id_used;
- SAVEPOINT *savepoints;
enum enum_check_fields count_cuted_fields;
};
@@ -1622,7 +1669,8 @@ enum enum_thread_type
SYSTEM_THREAD_EVENT_WORKER= 16,
SYSTEM_THREAD_BINLOG_BACKGROUND= 32,
SYSTEM_THREAD_SLAVE_BACKGROUND= 64,
- SYSTEM_THREAD_GENERIC= 128
+ SYSTEM_THREAD_GENERIC= 128,
+ SYSTEM_THREAD_SEMISYNC_MASTER_BACKGROUND= 256
};
inline char const *
@@ -1638,6 +1686,7 @@ show_system_thread(enum_thread_type thread)
RETURN_NAME_AS_STRING(SYSTEM_THREAD_EVENT_SCHEDULER);
RETURN_NAME_AS_STRING(SYSTEM_THREAD_EVENT_WORKER);
RETURN_NAME_AS_STRING(SYSTEM_THREAD_SLAVE_BACKGROUND);
+ RETURN_NAME_AS_STRING(SYSTEM_THREAD_SEMISYNC_MASTER_BACKGROUND);
default:
sprintf(buf, "<UNKNOWN SYSTEM THREAD: %d>", thread);
return buf;
@@ -1818,8 +1867,9 @@ private:
class Locked_tables_list
{
-private:
+public:
MEM_ROOT m_locked_tables_root;
+private:
TABLE_LIST *m_locked_tables;
TABLE_LIST **m_locked_tables_last;
/** An auxiliary array used only in reopen_tables(). */
@@ -1841,7 +1891,8 @@ public:
m_locked_tables_count(0),
some_table_marked_for_reopen(0)
{
- init_sql_alloc(&m_locked_tables_root, MEM_ROOT_BLOCK_SIZE, 0,
+ init_sql_alloc(&m_locked_tables_root, "Locked_tables_list",
+ MEM_ROOT_BLOCK_SIZE, 0,
MYF(MY_THREAD_SPECIFIC));
}
void unlock_locked_tables(THD *thd);
@@ -2101,14 +2152,8 @@ struct wait_for_commit
void reinit();
};
-
extern "C" void my_message_sql(uint error, const char *str, myf MyFlags);
-class THD;
-#ifndef DBUG_OFF
-void dbug_serve_apcs(THD *thd, int n_calls);
-#endif
-
/**
@class THD
For each client connection we create a separate thread with THD serving as
@@ -2218,12 +2263,16 @@ public:
Protects THD data accessed from other threads:
- thd->query and thd->query_length (used by SHOW ENGINE
INNODB STATUS and SHOW PROCESSLIST
- - thd->db and thd->db_length (used in SHOW PROCESSLIST)
- - thd->mysys_var (used by KILL statement and shutdown).
+ - thd->db (used in SHOW PROCESSLIST)
Is locked when THD is deleted.
*/
mysql_mutex_t LOCK_thd_data;
- /* Protect kill information */
+ /*
+ Protects:
+ - kill information
+ - mysys_var (used by KILL statement and shutdown).
+ - Also ensures that THD is not deleted while mutex is hold
+ */
mysql_mutex_t LOCK_thd_kill;
/* all prepared statements and cursors of this connection */
@@ -2285,12 +2334,13 @@ public:
const char *calling_file,
const unsigned int calling_line)
{
- DBUG_PRINT("THD::enter_stage", ("%s:%d", calling_file, calling_line));
+ DBUG_PRINT("THD::enter_stage", ("%s at %s:%d", stage->m_name,
+ calling_file, calling_line));
DBUG_ASSERT(stage);
m_current_stage_key= stage->m_key;
proc_info= stage->m_name;
#if defined(ENABLED_PROFILING)
- profiling.status_change(stage->m_name, calling_func, calling_file,
+ profiling.status_change(proc_info, calling_func, calling_file,
calling_line);
#endif
#ifdef HAVE_PSI_THREAD_INTERFACE
@@ -2316,7 +2366,8 @@ public:
/* Needed by MariaDB semi sync replication */
Trans_binlog_info *semisync_info;
-
+ /* If this is a semisync slave connection. */
+ bool semi_sync_slave;
ulonglong client_capabilities; /* What the client supports */
ulong max_client_packet_length;
@@ -2327,7 +2378,9 @@ public:
chapter 'Miscellaneous functions', for functions GET_LOCK, RELEASE_LOCK.
*/
HASH ull_hash;
-#ifndef DBUG_OFF
+ /* Hash of used seqeunces (for PREVIOUS value) */
+ HASH sequences;
+#ifdef DBUG_ASSERT_EXISTS
uint dbug_sentry; // watch out for memory corruption
#endif
struct st_my_thread_var *mysys_var;
@@ -2534,6 +2587,8 @@ private:
uint binlog_table_maps;
public:
void issue_unsafe_warnings();
+ void reset_unsafe_warnings()
+ { binlog_unsafe_warning_flags= 0; }
uint get_binlog_table_maps() const {
return binlog_table_maps;
@@ -2554,6 +2609,15 @@ public:
WT_THD wt; ///< for deadlock detection
Rows_log_event *m_pending_rows_event;
+ struct st_trans_time : public timeval
+ {
+ void reset(THD *thd)
+ {
+ tv_sec= thd->query_start();
+ tv_usec= (long) thd->query_start_sec_part();
+ }
+ } start_time;
+
/*
Tables changed in transaction (that must be invalidated in query cache).
List contain only transactional tables, that not invalidated in query
@@ -2585,7 +2649,8 @@ public:
{
bzero((char*)this, sizeof(*this));
xid_state.xid.null();
- init_sql_alloc(&mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0,
+ init_sql_alloc(&mem_root, "THD::transactions",
+ ALLOC_ROOT_MIN_BLOCK_SIZE, 0,
MYF(MY_THREAD_SPECIFIC));
}
} transaction;
@@ -2801,6 +2866,14 @@ public:
{
m_row_count_func= row_count_func;
}
+ inline void set_affected_rows(longlong row_count_func)
+ {
+ /*
+ We have to add to affected_rows (used by slow log), as otherwise
+ information for 'call' will be wrong
+ */
+ affected_rows+= (row_count_func >= 0 ? row_count_func : 0);
+ }
ha_rows cuted_fields;
@@ -2830,6 +2903,9 @@ public:
ha_rows get_examined_row_count() const
{ return m_examined_row_count; }
+ ulonglong get_affected_rows() const
+ { return affected_rows; }
+
void set_sent_row_count(ha_rows count);
void set_examined_row_count(ha_rows count);
@@ -2907,8 +2983,16 @@ public:
/* Statement id is thread-wide. This counter is used to generate ids */
ulong statement_id_counter;
ulong rand_saved_seed1, rand_saved_seed2;
+
+ /* The following variables are used when printing to slow log */
ulong query_plan_flags;
ulong query_plan_fsort_passes;
+ ulong tmp_tables_used;
+ ulong tmp_tables_disk_used;
+ ulonglong tmp_tables_size;
+ ulonglong bytes_sent_old;
+ ulonglong affected_rows; /* Number of changed rows */
+
pthread_t real_id; /* For debugging */
my_thread_id thread_id, thread_dbug_id;
uint32 os_thread_id;
@@ -2975,10 +3059,14 @@ public:
} *killed_err;
/* See also thd_killed() */
- inline bool check_killed()
+ inline bool check_killed(bool dont_send_error_message= 0)
{
- if (killed)
+ if (unlikely(killed))
+ {
+ if (!dont_send_error_message)
+ send_kill_message();
return TRUE;
+ }
if (apc_target.have_apc_requests())
apc_target.process_apc_requests();
return FALSE;
@@ -2991,13 +3079,12 @@ public:
If this is a slave, the name of the connection stored here.
This is used for taging error messages in the log files.
*/
- LEX_STRING connection_name;
+ LEX_CSTRING connection_name;
char default_master_connection_buff[MAX_CONNECTION_NAME+1];
uint8 password; /* 0, 1 or 2 */
uint8 failed_com_change_user;
bool slave_thread;
bool extra_port; /* If extra connection */
-
bool no_errors;
/**
@@ -3026,7 +3113,7 @@ public:
Reset to FALSE when we leave the sub-statement mode.
*/
bool is_fatal_sub_stmt_error;
- bool query_start_used, rand_used, time_zone_used;
+ bool rand_used, time_zone_used;
bool query_start_sec_part_used;
/* for IS NULL => = last_insert_id() fix in remove_eq_conds() */
bool substitute_null_with_insert_id;
@@ -3039,9 +3126,11 @@ public:
is set if a statement accesses a temporary table created through
CREATE TEMPORARY TABLE.
*/
- bool charset_is_system_charset, charset_is_collation_connection;
+private:
+ bool charset_is_system_charset, charset_is_collation_connection;
bool charset_is_character_set_filesystem;
- bool enable_slow_log; /* enable slow log for current statement */
+public:
+ bool enable_slow_log; /* Enable slow log for current statement */
bool abort_on_warning;
bool got_warning; /* Set on call to push_warning() */
/* set during loop of derived table processing */
@@ -3073,10 +3162,13 @@ public:
the query. 0 if no error on the master.
*/
int slave_expected_error;
+ enum_sql_command last_sql_command; // Last sql_command exceuted in mysql_execute_command()
sp_rcontext *spcont; // SP runtime context
sp_cache *sp_proc_cache;
sp_cache *sp_func_cache;
+ sp_cache *sp_package_spec_cache;
+ sp_cache *sp_package_body_cache;
/** number of name_const() substitutions, see sp_head.cc:subst_spvars() */
uint query_name_consts;
@@ -3175,11 +3267,15 @@ public:
/* Debug Sync facility. See debug_sync.cc. */
struct st_debug_sync_control *debug_sync_control;
#endif /* defined(ENABLED_DEBUG_SYNC) */
+ /**
+ @param id thread identifier
+ @param is_wsrep_applier thread type
+ */
THD(my_thread_id id, bool is_wsrep_applier= false);
~THD();
- void init(void);
+ void init();
/*
Initialize memory roots necessary for query processing and (!)
pre-allocate memory for it. We can't do that in THD constructor because
@@ -3214,7 +3310,13 @@ public:
}
void close_active_vio();
#endif
- void awake(killed_state state_to_set);
+ void awake_no_mutex(killed_state state_to_set);
+ void awake(killed_state state_to_set)
+ {
+ mysql_mutex_lock(&LOCK_thd_kill);
+ awake_no_mutex(state_to_set);
+ mysql_mutex_unlock(&LOCK_thd_kill);
+ }
/** Disconnect the associated communication endpoint. */
void disconnect();
@@ -3320,45 +3422,109 @@ public:
{
return !MY_TEST(variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES);
}
- inline my_time_t query_start() { query_start_used=1; return start_time; }
+ const Type_handler *type_handler_for_datetime() const;
+ bool timestamp_to_TIME(MYSQL_TIME *ltime, my_time_t ts,
+ ulong sec_part, ulonglong fuzzydate);
+ inline my_time_t query_start() { return start_time; }
inline ulong query_start_sec_part()
{ query_start_sec_part_used=1; return start_time_sec_part; }
- inline void set_current_time()
+ MYSQL_TIME query_start_TIME();
+
+private:
+ struct {
+ my_hrtime_t start;
+ my_time_t sec;
+ ulong sec_part;
+ } system_time;
+
+ void set_system_time()
{
my_hrtime_t hrtime= my_hrtime();
- start_time= hrtime_to_my_time(hrtime);
- start_time_sec_part= hrtime_sec_part(hrtime);
-#ifdef HAVE_PSI_THREAD_INTERFACE
- PSI_THREAD_CALL(set_thread_start_time)(start_time);
-#endif
+ my_time_t sec= hrtime_to_my_time(hrtime);
+ ulong sec_part= hrtime_sec_part(hrtime);
+ if (sec > system_time.sec ||
+ (sec == system_time.sec && sec_part > system_time.sec_part) ||
+ hrtime.val < system_time.start.val)
+ {
+ system_time.sec= sec;
+ system_time.sec_part= sec_part;
+ system_time.start= hrtime;
+ }
+ else
+ {
+ if (system_time.sec_part < TIME_MAX_SECOND_PART)
+ system_time.sec_part++;
+ else
+ {
+ system_time.sec++;
+ system_time.sec_part= 0;
+ }
+ }
}
+
+public:
+ timeval transaction_time()
+ {
+ if (!in_multi_stmt_transaction_mode())
+ transaction.start_time.reset(this);
+ return transaction.start_time;
+ }
+
inline void set_start_time()
{
if (user_time.val)
{
start_time= hrtime_to_my_time(user_time);
start_time_sec_part= hrtime_sec_part(user_time);
-#ifdef HAVE_PSI_THREAD_INTERFACE
- PSI_THREAD_CALL(set_thread_start_time)(start_time);
-#endif
}
else
- set_current_time();
+ {
+ set_system_time();
+ start_time= system_time.sec;
+ start_time_sec_part= system_time.sec_part;
+ }
+ PSI_CALL_set_thread_start_time(start_time);
}
inline void set_time()
{
set_start_time();
start_utime= utime_after_lock= microsecond_interval_timer();
}
+ /* only used in SET @@timestamp=... */
inline void set_time(my_hrtime_t t)
{
user_time= t;
set_time();
}
+ /*
+ this is only used by replication and BINLOG command.
+ usecs > TIME_MAX_SECOND_PART means "was not in binlog"
+ */
inline void set_time(my_time_t t, ulong sec_part)
{
- my_hrtime_t hrtime= { hrtime_from_time(t) + sec_part };
- set_time(hrtime);
+ if (opt_secure_timestamp > (slave_thread ? SECTIME_REPL : SECTIME_SUPER))
+ set_time(); // note that BINLOG itself requires SUPER
+ else
+ {
+ if (sec_part <= TIME_MAX_SECOND_PART)
+ {
+ start_time= system_time.sec= t;
+ start_time_sec_part= system_time.sec_part= sec_part;
+ }
+ else if (t != system_time.sec)
+ {
+ start_time= system_time.sec= t;
+ start_time_sec_part= system_time.sec_part= 0;
+ }
+ else
+ {
+ start_time= t;
+ start_time_sec_part= ++system_time.sec_part;
+ }
+ user_time.val= hrtime_from_time(start_time) + start_time_sec_part;
+ PSI_CALL_set_thread_start_time(start_time);
+ start_utime= utime_after_lock= microsecond_interval_timer();
+ }
}
void set_time_after_lock()
{
@@ -3384,7 +3550,7 @@ public:
void update_server_status()
{
set_time_for_next_stage();
- if (utime_after_query > utime_after_lock + variables.long_query_time)
+ if (utime_after_query >= utime_after_lock + variables.long_query_time)
server_status|= SERVER_QUERY_WAS_SLOW;
}
inline ulonglong found_rows(void)
@@ -3463,45 +3629,109 @@ public:
{
return !stmt_arena->is_stmt_prepare();
}
- inline void* trans_alloc(unsigned int size)
+ inline void* trans_alloc(size_t size)
{
return alloc_root(&transaction.mem_root,size);
}
- LEX_STRING *make_lex_string(LEX_STRING *lex_str, const char* str, uint length)
+ LEX_STRING *make_lex_string(LEX_STRING *lex_str, const char* str, size_t length)
+ {
+ if (!(lex_str->str= strmake_root(mem_root, str, length)))
+ {
+ lex_str->length= 0;
+ return 0;
+ }
+ lex_str->length= length;
+ return lex_str;
+ }
+ LEX_CSTRING *make_lex_string(LEX_CSTRING *lex_str, const char* str, size_t length)
{
if (!(lex_str->str= strmake_root(mem_root, str, length)))
+ {
+ lex_str->length= 0;
return 0;
+ }
lex_str->length= length;
return lex_str;
}
+ // Remove double quotes: aaa""bbb -> aaa"bbb
+ bool quote_unescape(LEX_CSTRING *dst, const LEX_CSTRING *src, char quote)
+ {
+ const char *tmp= src->str;
+ const char *tmpend= src->str + src->length;
+ char *to;
+ if (!(dst->str= to= (char *) alloc(src->length + 1)))
+ {
+ dst->length= 0; // Safety
+ return true;
+ }
+ for ( ; tmp < tmpend; )
+ {
+ if ((*to++= *tmp++) == quote)
+ tmp++; // Skip double quotes
+ }
+ *to= 0; // End null for safety
+ dst->length= to - dst->str;
+ return false;
+ }
- LEX_STRING *make_lex_string(const char* str, uint length)
+ LEX_CSTRING *make_clex_string(const char* str, size_t length)
{
- LEX_STRING *lex_str;
- if (!(lex_str= (LEX_STRING *)alloc_root(mem_root, sizeof(LEX_STRING))))
+ LEX_CSTRING *lex_str;
+ char *tmp;
+ if (unlikely(!(lex_str= (LEX_CSTRING *)alloc_root(mem_root,
+ sizeof(LEX_CSTRING) +
+ length+1))))
return 0;
- return make_lex_string(lex_str, str, length);
+ tmp= (char*) (lex_str+1);
+ lex_str->str= tmp;
+ memcpy(tmp, str, length);
+ tmp[length]= 0;
+ lex_str->length= length;
+ return lex_str;
+ }
+ LEX_CSTRING *make_clex_string(const LEX_CSTRING from)
+ {
+ return make_clex_string(from.str, from.length);
}
// Allocate LEX_STRING for character set conversion
- bool alloc_lex_string(LEX_STRING *dst, uint length)
+ bool alloc_lex_string(LEX_STRING *dst, size_t length)
{
- if ((dst->str= (char*) alloc(length)))
+ if (likely((dst->str= (char*) alloc(length))))
return false;
dst->length= 0; // Safety
return true; // EOM
}
bool convert_string(LEX_STRING *to, CHARSET_INFO *to_cs,
- const char *from, uint from_length,
+ const char *from, size_t from_length,
CHARSET_INFO *from_cs);
+ bool convert_string(LEX_CSTRING *to, CHARSET_INFO *to_cs,
+ const char *from, size_t from_length,
+ CHARSET_INFO *from_cs)
+ {
+ LEX_STRING tmp;
+ bool rc= convert_string(&tmp, to_cs, from, from_length, from_cs);
+ to->str= tmp.str;
+ to->length= tmp.length;
+ return rc;
+ }
+ bool convert_string(LEX_CSTRING *to, CHARSET_INFO *tocs,
+ const LEX_CSTRING *from, CHARSET_INFO *fromcs,
+ bool simple_copy_is_possible)
+ {
+ if (!simple_copy_is_possible)
+ return unlikely(convert_string(to, tocs, from->str, from->length, fromcs));
+ *to= *from;
+ return false;
+ }
/*
Convert a strings between character sets.
Uses my_convert_fix(), which uses an mb_wc .. mc_mb loop internally.
dstcs and srccs cannot be &my_charset_bin.
*/
bool convert_fix(CHARSET_INFO *dstcs, LEX_STRING *dst,
- CHARSET_INFO *srccs, const char *src, uint src_length,
+ CHARSET_INFO *srccs, const char *src, size_t src_length,
String_copier *status);
/*
@@ -3510,15 +3740,14 @@ public:
*/
bool convert_with_error(CHARSET_INFO *dstcs, LEX_STRING *dst,
CHARSET_INFO *srccs,
- const char *src, uint src_length);
-
+ 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().
Otherwise, performs Unicode conversion using convert_fix().
*/
bool copy_fix(CHARSET_INFO *dstcs, LEX_STRING *dst,
- CHARSET_INFO *srccs, const char *src, uint src_length,
+ CHARSET_INFO *srccs, const char *src, size_t src_length,
String_copier *status);
/*
@@ -3526,13 +3755,58 @@ public:
in case of bad byte sequences or Unicode conversion problems.
*/
bool copy_with_error(CHARSET_INFO *dstcs, LEX_STRING *dst,
- CHARSET_INFO *srccs, const char *src, uint src_length);
+ CHARSET_INFO *srccs, const char *src, size_t src_length);
bool convert_string(String *s, CHARSET_INFO *from_cs, CHARSET_INFO *to_cs);
+ /*
+ Check if the string is wellformed, raise an error if not wellformed.
+ @param str - The string to check.
+ @param length - the string length.
+ */
+ bool check_string_for_wellformedness(const char *str,
+ size_t length,
+ CHARSET_INFO *cs) const;
+
+ bool to_ident_sys_alloc(Lex_ident_sys_st *to, const Lex_ident_cli_st *from);
+
+ /*
+ Create a string literal with optional client->connection conversion.
+ @param str - the string in the client character set
+ @param length - length of the string
+ @param repertoire - the repertoire of the string
+ */
+ Item_basic_constant *make_string_literal(const char *str, size_t length,
+ uint repertoire);
+ Item_basic_constant *make_string_literal(const Lex_string_with_metadata_st &str)
+ {
+ uint 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);
+ Item_basic_constant *make_string_literal_charset(const Lex_string_with_metadata_st &str,
+ CHARSET_INFO *cs);
+ bool make_text_string_sys(LEX_CSTRING *to,
+ const Lex_string_with_metadata_st *from)
+ {
+ return convert_string(to, system_charset_info,
+ from, charset(), charset_is_system_charset);
+ }
+ bool make_text_string_connection(LEX_CSTRING *to,
+ const Lex_string_with_metadata_st *from)
+ {
+ return convert_string(to, variables.collation_connection,
+ from, charset(), charset_is_collation_connection);
+ }
+ bool make_text_string_filesystem(LEX_CSTRING *to,
+ const Lex_string_with_metadata_st *from)
+ {
+ return convert_string(to, variables.character_set_filesystem,
+ from, charset(), charset_is_character_set_filesystem);
+ }
void add_changed_table(TABLE *table);
- void add_changed_table(const char *key, long key_length);
- CHANGED_TABLE_LIST * changed_table_dup(const char *key, long key_length);
+ void add_changed_table(const char *key, size_t key_length);
+ CHANGED_TABLE_LIST * changed_table_dup(const char *key, size_t key_length);
int prepare_explain_fields(select_result *result, List<Item> *field_list,
uint8 explain_flags, bool is_analyze);
int send_explain_fields(select_result *result, uint8 explain_flags,
@@ -3621,7 +3895,7 @@ public:
void set_stmt_da(Diagnostics_area *da)
{ m_stmt_da= da; }
- inline CHARSET_INFO *charset() { return variables.character_set_client; }
+ inline CHARSET_INFO *charset() const { return variables.character_set_client; }
void update_charset();
void update_charset(CHARSET_INFO *character_set_client,
CHARSET_INFO *collation_connection)
@@ -3737,7 +4011,7 @@ public:
a suboptimal error message.
*/
killed_err= (err_info*) alloc_root(&main_mem_root, sizeof(*killed_err));
- if (killed_err)
+ if (likely(killed_err))
{
killed_err->no= killed_errno_arg;
::strmake((char*) killed_err->msg, killed_err_msg_arg,
@@ -3747,20 +4021,7 @@ public:
}
}
int killed_errno();
- inline void reset_killed()
- {
- /*
- Resetting killed has to be done under a mutex to ensure
- its not done during an awake() call.
- */
- if (killed != NOT_KILLED)
- {
- mysql_mutex_lock(&LOCK_thd_kill);
- killed= NOT_KILLED;
- killed_err= 0;
- mysql_mutex_unlock(&LOCK_thd_kill);
- }
- }
+ void reset_killed();
inline void reset_kill_query()
{
if (killed < KILL_CONNECTION)
@@ -3790,6 +4051,9 @@ public:
void restore_backup_open_tables_state(Open_tables_backup *backup);
void reset_sub_statement_state(Sub_statement_state *backup, uint new_state);
void restore_sub_statement_state(Sub_statement_state *backup);
+ void store_slow_query_state(Sub_statement_state *backup);
+ void reset_slow_query_state();
+ void add_slow_query_state(Sub_statement_state *backup);
void set_n_backup_active_arena(Query_arena *set, Query_arena *backup);
void restore_active_arena(Query_arena *set, Query_arena *backup);
@@ -3848,6 +4112,11 @@ public:
DBUG_VOID_RETURN;
}
+ inline void set_current_stmt_binlog_format(enum_binlog_format format)
+ {
+ current_stmt_binlog_format= format;
+ }
+
inline void set_current_stmt_binlog_format_row()
{
DBUG_ENTER("set_current_stmt_binlog_format_row");
@@ -3922,67 +4191,21 @@ public:
@retval FALSE Success
@retval TRUE Out-of-memory error
*/
- bool set_db(const char *new_db, size_t new_db_len)
- {
- /*
- Acquiring mutex LOCK_thd_data as we either free the memory allocated
- for the database and reallocating the memory for the new db or memcpy
- the new_db to the db.
- */
- mysql_mutex_lock(&LOCK_thd_data);
- /* Do not reallocate memory if current chunk is big enough. */
- if (db && new_db && db_length >= new_db_len)
- memcpy(db, new_db, new_db_len+1);
- else
- {
- my_free(db);
- if (new_db)
- db= my_strndup(new_db, new_db_len, MYF(MY_WME | ME_FATALERROR));
- else
- db= NULL;
- }
- db_length= db ? new_db_len : 0;
- bool result= new_db && !db;
- mysql_mutex_unlock(&LOCK_thd_data);
-#ifdef HAVE_PSI_THREAD_INTERFACE
- if (result)
- PSI_THREAD_CALL(set_thread_db)(new_db, (int) new_db_len);
-#endif
- return result;
- }
+ bool set_db(const LEX_CSTRING *new_db);
- /**
- Set the current database; use shallow copy of C-string.
+ /** Set the current database, without copying */
+ void reset_db(const LEX_CSTRING *new_db);
- @param new_db a pointer to the new database name.
- @param new_db_len length of the new database name.
-
- @note This operation just sets {db, db_length}. Switching the current
- database usually involves other actions, like switching other database
- attributes including security context. In the future, this operation
- will be made private and more convenient interface will be provided.
- */
- void reset_db(char *new_db, size_t new_db_len)
- {
- if (new_db != db || new_db_len != db_length)
- {
- mysql_mutex_lock(&LOCK_thd_data);
- db= new_db;
- db_length= new_db_len;
- mysql_mutex_unlock(&LOCK_thd_data);
-#ifdef HAVE_PSI_THREAD_INTERFACE
- PSI_THREAD_CALL(set_thread_db)(new_db, (int) new_db_len);
-#endif
- }
- }
/*
Copy the current database to the argument. Use the current arena to
allocate memory for a deep copy: current database may be freed after
a statement is parsed but before it's executed.
+
+ Can only be called by owner of thd (no mutex protection)
*/
- bool copy_db_to(char **p_db, size_t *p_db_length)
+ bool copy_db_to(LEX_CSTRING *to)
{
- if (db == NULL)
+ if (db.str == NULL)
{
/*
No default database is set. In this case if it's guaranteed that
@@ -3998,16 +4221,19 @@ public:
return TRUE;
}
/* This will allow to throw an error later for non-CTE references */
- *p_db= NULL;
- *p_db_length= 0;
+ to->str= NULL;
+ to->length= 0;
+ return FALSE;
}
- else
- {
- *p_db= strmake(db, db_length);
- *p_db_length= db_length;
- }
- return FALSE;
+
+ to->str= strmake(db.str, db.length);
+ to->length= db.length;
+ return to->str == NULL; /* True on error */
}
+ /* Get db name or "". Use for printing current db */
+ const char *get_db()
+ { return safe_str(db.str); }
+
thd_scheduler event_scheduler;
public:
@@ -4078,6 +4304,39 @@ public:
*/
void raise_note_printf(uint code, ...);
+ /**
+ @brief Push an error message into MySQL error stack with line
+ and position information.
+
+ This function provides semantic action implementers with a way
+ to push the famous "You have a syntax error near..." error
+ message into the error stack, which is normally produced only if
+ a parse error is discovered internally by the Bison generated
+ parser.
+ */
+ void parse_error(const char *err_text, const char *yytext)
+ {
+ Lex_input_stream *lip= &m_parser_state->m_lip;
+ if (!yytext && !(yytext= lip->get_tok_start()))
+ yytext= "";
+ /* Push an error into the error stack */
+ ErrConvString err(yytext, strlen(yytext), variables.character_set_client);
+ my_printf_error(ER_PARSE_ERROR, ER_THD(this, ER_PARSE_ERROR), MYF(0),
+ err_text, err.ptr(), lip->yylineno);
+ }
+ void parse_error(uint err_number, const char *yytext= 0)
+ {
+ parse_error(ER_THD(this, err_number), yytext);
+ }
+ void parse_error()
+ {
+ parse_error(ER_SYNTAX_ERROR);
+ }
+#ifdef mysqld_error_find_printf_error_used
+ void parse_error(const char *t)
+ {
+ }
+#endif
private:
/*
Only the implementation of the SIGNAL and RESIGNAL statements
@@ -4104,8 +4363,42 @@ private:
raise_condition(uint sql_errno,
const char* sqlstate,
Sql_condition::enum_warning_level level,
+ const char* msg)
+ {
+ return raise_condition(sql_errno, sqlstate, level,
+ Sql_user_condition_identity(), msg);
+ }
+
+ /**
+ Raise a generic or a user defined SQL condition.
+ @param ucid - the user condition identity
+ (or an empty identity if not a user condition)
+ @param sql_errno - the condition error number
+ @param sqlstate - the condition SQLSTATE
+ @param level - the condition level
+ @param msg - the condition message text
+ @return The condition raised, or NULL
+ */
+ Sql_condition*
+ raise_condition(uint sql_errno,
+ const char* sqlstate,
+ Sql_condition::enum_warning_level level,
+ const Sql_user_condition_identity &ucid,
const char* msg);
+ Sql_condition*
+ raise_condition(const Sql_condition *cond)
+ {
+ Sql_condition *raised= raise_condition(cond->get_sql_errno(),
+ cond->get_sqlstate(),
+ cond->get_level(),
+ *cond/*Sql_user_condition_identity*/,
+ cond->get_message_text());
+ if (raised)
+ raised->copy_opt_attributes(cond);
+ return raised;
+ }
+
public:
/** Overloaded to guard query/query_length fields */
virtual void set_statement(Statement *stmt);
@@ -4123,12 +4416,12 @@ public:
Assign a new value to thd->query and thd->query_id and mysys_var.
Protected with LOCK_thd_data mutex.
*/
- void set_query(char *query_arg, uint32 query_length_arg,
+ void set_query(char *query_arg, size_t query_length_arg,
CHARSET_INFO *cs_arg)
{
set_query(CSET_STRING(query_arg, query_length_arg, cs_arg));
}
- void set_query(char *query_arg, uint32 query_length_arg) /*Mutex protected*/
+ void set_query(char *query_arg, size_t query_length_arg) /*Mutex protected*/
{
set_query(CSET_STRING(query_arg, query_length_arg, charset()));
}
@@ -4138,9 +4431,7 @@ public:
set_query_inner(string_arg);
mysql_mutex_unlock(&LOCK_thd_data);
-#ifdef HAVE_PSI_THREAD_INTERFACE
- PSI_THREAD_CALL(set_thread_info)(query(), query_length());
-#endif
+ PSI_CALL_set_thread_info(query(), query_length());
}
void reset_query() /* Mutex protected */
{ set_query(CSET_STRING()); }
@@ -4195,14 +4486,14 @@ public:
void binlog_invoker(bool role) { m_binlog_invoker= role ? INVOKER_ROLE : INVOKER_USER; }
enum need_invoker need_binlog_invoker() { return m_binlog_invoker; }
void get_definer(LEX_USER *definer, bool role);
- void set_invoker(const LEX_STRING *user, const LEX_STRING *host)
+ void set_invoker(const LEX_CSTRING *user, const LEX_CSTRING *host)
{
- invoker_user= *user;
- invoker_host= *host;
+ invoker.user= *user;
+ invoker.host= *host;
}
- LEX_STRING get_invoker_user() { return invoker_user; }
- LEX_STRING get_invoker_host() { return invoker_host; }
- bool has_invoker() { return invoker_user.length > 0; }
+ LEX_CSTRING get_invoker_user() { return invoker.user; }
+ LEX_CSTRING get_invoker_host() { return invoker.host; }
+ bool has_invoker() { return invoker.user.length > 0; }
void print_aborted_warning(uint threshold, const char *reason)
{
@@ -4210,7 +4501,7 @@ public:
{
Security_context *sctx= &main_security_ctx;
sql_print_warning(ER_THD(this, ER_NEW_ABORTING_CONNECTION),
- thread_id, (db ? db : "unconnected"),
+ thread_id, (db.str ? db.str : "unconnected"),
sctx->user ? sctx->user : "unauthenticated",
sctx->host_or_ip, reason);
}
@@ -4301,8 +4592,7 @@ private:
TRIGGER or VIEW statements or current user in account management
statements if it is not NULL.
*/
- LEX_STRING invoker_user;
- LEX_STRING invoker_host;
+ AUTHID invoker;
public:
#ifndef EMBEDDED_LIBRARY
@@ -4349,7 +4639,8 @@ public:
const char *path,
const char *db,
const char *table_name,
- bool open_in_engine);
+ bool open_in_engine,
+ bool open_internal_tables);
TABLE *find_temporary_table(const char *db, const char *table_name,
Temporary_table_state state= TMP_TABLE_IN_USE);
@@ -4361,14 +4652,14 @@ public:
TMP_TABLE_SHARE *find_tmp_table_share(const char *db,
const char *table_name);
TMP_TABLE_SHARE *find_tmp_table_share(const TABLE_LIST *tl);
- TMP_TABLE_SHARE *find_tmp_table_share(const char *key, uint key_length);
+ TMP_TABLE_SHARE *find_tmp_table_share(const char *key, size_t key_length);
bool open_temporary_table(TABLE_LIST *tl);
bool open_temporary_tables(TABLE_LIST *tl);
bool close_temporary_tables();
- bool rename_temporary_table(TABLE *table, const char *db,
- const char *table_name);
+ bool rename_temporary_table(TABLE *table, const LEX_CSTRING *db,
+ const LEX_CSTRING *table_name);
bool drop_temporary_table(TABLE *table, bool *is_trans, bool delete_table);
bool rm_temporary_table(handlerton *hton, const char *path);
void mark_tmp_tables_as_free_for_reuse();
@@ -4474,6 +4765,7 @@ public:
/* Handling of timeouts for commands */
thr_timer_t query_timer;
+
public:
void set_query_timer()
{
@@ -4527,6 +4819,57 @@ public:
current_linfo= 0;
mysql_mutex_unlock(&LOCK_thread_count);
}
+
+
+ uint get_net_wait_timeout()
+ {
+ if (in_active_multi_stmt_transaction())
+ {
+ if (transaction.all.is_trx_read_write())
+ {
+ if (variables.idle_write_transaction_timeout > 0)
+ return variables.idle_write_transaction_timeout;
+ }
+ else
+ {
+ if (variables.idle_readonly_transaction_timeout > 0)
+ return variables.idle_readonly_transaction_timeout;
+ }
+
+ if (variables.idle_transaction_timeout > 0)
+ return variables.idle_transaction_timeout;
+ }
+
+ return variables.net_wait_timeout;
+ }
+
+ /**
+ Switch to a sublex, to parse a substatement or an expression.
+ */
+ void set_local_lex(sp_lex_local *sublex)
+ {
+ DBUG_ASSERT(lex->sphead);
+ lex= sublex;
+ /* Reset part of parser state which needs this. */
+ m_parser_state->m_yacc.reset_before_substatement();
+ }
+
+ /**
+ Switch back from a sublex (currently pointed by this->lex) to the old lex.
+ Sublex is merged to "oldlex" and this->lex is set to "oldlex".
+
+ This method is called after parsing a substatement or an expression.
+ set_local_lex() must be previously called.
+ @param oldlex - The old lex which was active before set_local_lex().
+ @returns - false on success, true on error (failed to merge LEX's).
+
+ See also sp_head::merge_lex().
+ */
+ bool restore_from_local_lex_to_old_lex(LEX *oldlex);
+
+ Item *sp_fix_func_item(Item **it_addr);
+ Item *sp_prepare_func_item(Item **it_addr, uint cols= 1);
+ bool sp_eval_expr(Field *result_field, Item **expr_item_ptr);
};
inline void add_to_active_threads(THD *thd)
@@ -4553,11 +4896,12 @@ inline void unlink_not_visible_thd(THD *thd)
/** A short cut for thd->get_stmt_da()->set_ok_status(). */
inline void
-my_ok(THD *thd, ulonglong affected_rows= 0, ulonglong id= 0,
+my_ok(THD *thd, ulonglong affected_rows_arg= 0, ulonglong id= 0,
const char *message= NULL)
{
- thd->set_row_count_func(affected_rows);
- thd->get_stmt_da()->set_ok_status(affected_rows, id, message);
+ thd->set_row_count_func(affected_rows_arg);
+ thd->set_affected_rows(affected_rows_arg);
+ thd->get_stmt_da()->set_ok_status(affected_rows_arg, id, message);
}
@@ -4598,15 +4942,15 @@ class sql_exchange :public Sql_alloc
{
public:
enum enum_filetype filetype; /* load XML, Added by Arnold & Erik */
- char *file_name;
+ const char *file_name;
String *field_term,*enclosed,*line_term,*line_start,*escaped;
bool opt_enclosed;
bool dumpfile;
ulong skip_lines;
CHARSET_INFO *cs;
- sql_exchange(char *name, bool dumpfile_flag,
+ sql_exchange(const char *name, bool dumpfile_flag,
enum_filetype filetype_arg= FILETYPE_CSV);
- bool escaped_given(void);
+ bool escaped_given(void) const;
};
/*
@@ -4627,6 +4971,7 @@ public:
*/
virtual int send_data(List<Item> &items)=0;
virtual ~select_result_sink() {};
+ void reset(THD *thd_arg) { thd= thd_arg; }
};
class select_result_interceptor;
@@ -4654,7 +4999,8 @@ protected:
SELECT_LEX_UNIT *unit;
/* Something used only by the parser: */
public:
- select_result(THD *thd_arg): select_result_sink(thd_arg) {}
+ ha_rows est_records; /* estimated number of records in the result */
+ 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() {};
/**
@@ -4679,7 +5025,7 @@ public:
unit= u;
return 0;
}
- virtual int prepare2(void) { return 0; }
+ virtual int prepare2(JOIN *join) { return 0; }
/*
Because of peculiarities of prepared statements protocol
we need to know number of columns in the result set (if
@@ -4688,7 +5034,7 @@ public:
virtual uint field_count(List<Item> &fields) const
{ return fields.elements; }
virtual bool send_result_set_metadata(List<Item> &list, uint flags)=0;
- virtual bool initialize_tables (JOIN *join=0) { return 0; }
+ virtual bool initialize_tables (JOIN *join) { return 0; }
virtual bool send_eof()=0;
/**
Check if this query returns a result set and therefore is allowed in
@@ -4705,6 +5051,11 @@ public:
*/
virtual void cleanup();
void set_thd(THD *thd_arg) { thd= thd_arg; }
+ void reset(THD *thd_arg)
+ {
+ select_result_sink::reset(thd_arg);
+ unit= NULL;
+ }
#ifdef EMBEDDED_LIBRARY
virtual void begin_dataset() {}
#else
@@ -4726,6 +5077,14 @@ public:
- this if the output is set elsewhere (a file, @variable, or table).
*/
virtual select_result_interceptor *result_interceptor()=0;
+
+ /*
+ This method is used to distinguish an normal SELECT from the cursor
+ structure discovery for cursor%ROWTYPE routine variables.
+ If this method returns "true", then a SELECT execution performs only
+ all preparation stages, but does not fetch any rows.
+ */
+ virtual bool view_structure_only() const { return false; }
};
@@ -4800,11 +5159,117 @@ public:
elsewhere. (this is used by ANALYZE $stmt feature).
*/
void disable_my_ok_calls() { suppress_my_ok= true; }
+ void reset(THD *thd_arg)
+ {
+ select_result::reset(thd_arg);
+ suppress_my_ok= false;
+ }
protected:
bool suppress_my_ok;
};
+class sp_cursor_statistics
+{
+protected:
+ ulonglong m_fetch_count; // Number of FETCH commands since last OPEN
+ ulonglong m_row_count; // Number of successful FETCH since last OPEN
+ bool m_found; // If last FETCH fetched a row
+public:
+ sp_cursor_statistics()
+ :m_fetch_count(0),
+ m_row_count(0),
+ m_found(false)
+ { }
+ bool found() const
+ { return m_found; }
+
+ ulonglong row_count() const
+ { return m_row_count; }
+
+ ulonglong fetch_count() const
+ { return m_fetch_count; }
+ void reset() { *this= sp_cursor_statistics(); }
+};
+
+
+/* A mediator between stored procedures and server side cursors */
+class sp_lex_keeper;
+class sp_cursor: public sp_cursor_statistics
+{
+private:
+ /// An interceptor of cursor result set used to implement
+ /// FETCH <cname> INTO <varlist>.
+ class Select_fetch_into_spvars: public select_result_interceptor
+ {
+ List<sp_variable> *spvar_list;
+ uint field_count;
+ bool m_view_structure_only;
+ bool send_data_to_variable_list(List<sp_variable> &vars, List<Item> &items);
+ public:
+ Select_fetch_into_spvars(THD *thd_arg, bool view_structure_only)
+ :select_result_interceptor(thd_arg),
+ m_view_structure_only(view_structure_only)
+ {}
+ void reset(THD *thd_arg)
+ {
+ select_result_interceptor::reset(thd_arg);
+ spvar_list= NULL;
+ field_count= 0;
+ }
+ uint get_field_count() { return field_count; }
+ void set_spvar_list(List<sp_variable> *vars) { spvar_list= vars; }
+
+ virtual bool send_eof() { return FALSE; }
+ virtual int send_data(List<Item> &items);
+ virtual int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
+ virtual bool view_structure_only() const { return m_view_structure_only; }
+};
+
+public:
+ sp_cursor()
+ :result(NULL, false),
+ m_lex_keeper(NULL),
+ server_side_cursor(NULL)
+ { }
+ sp_cursor(THD *thd_arg, sp_lex_keeper *lex_keeper, bool view_structure_only)
+ :result(thd_arg, view_structure_only),
+ m_lex_keeper(lex_keeper),
+ server_side_cursor(NULL)
+ {}
+
+ virtual ~sp_cursor()
+ { destroy(); }
+
+ sp_lex_keeper *get_lex_keeper() { return m_lex_keeper; }
+
+ int open(THD *thd);
+
+ int close(THD *thd);
+
+ my_bool is_open()
+ { return MY_TEST(server_side_cursor); }
+
+ int fetch(THD *, List<sp_variable> *vars, bool error_on_no_data);
+
+ bool export_structure(THD *thd, Row_definition_list *list);
+
+ void reset(THD *thd_arg, sp_lex_keeper *lex_keeper)
+ {
+ sp_cursor_statistics::reset();
+ result.reset(thd_arg);
+ m_lex_keeper= lex_keeper;
+ server_side_cursor= NULL;
+ }
+
+private:
+ Select_fetch_into_spvars result;
+ sp_lex_keeper *m_lex_keeper;
+ Server_side_cursor *server_side_cursor;
+ void destroy();
+};
+
+
class select_send :public select_result {
/**
True if we have sent result set metadata to the client.
@@ -4922,7 +5387,7 @@ class select_insert :public select_result_interceptor {
enum_duplicates duplic, bool ignore);
~select_insert();
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
- virtual int prepare2(void);
+ virtual int prepare2(JOIN *join);
virtual int send_data(List<Item> &items);
virtual void store_values(List<Item> &values);
virtual bool can_rollback_data() { return 0; }
@@ -4974,7 +5439,13 @@ public:
// Needed for access from local class MY_HOOKS in prepare(), since thd is proteted.
const THD *get_thd(void) { return thd; }
const HA_CREATE_INFO *get_create_info() { return create_info; };
- int prepare2(void) { return 0; }
+ int prepare2(JOIN *join) { return 0; }
+
+private:
+ TABLE *create_table_from_items(THD *thd,
+ List<Item> *items,
+ MYSQL_LOCK **lock,
+ TABLEOP_HOOKS *hooks);
};
#include <myisam.h>
@@ -5102,18 +5573,23 @@ public:
};
-class select_union :public select_result_interceptor
+class select_unit :public select_result_interceptor
{
+ uint curr_step, prev_step, curr_sel;
+ enum sub_select_type step;
public:
+ Item_int *intersect_mark;
TMP_TABLE_PARAM tmp_table_param;
int write_err; /* Error code from the last send_data->ha_write_row call. */
-public:
TABLE *table;
- ha_rows records;
- select_union(THD *thd_arg):
- select_result_interceptor(thd_arg), write_err(0), table(0), records(0)
- { tmp_table_param.init(); }
+ select_unit(THD *thd_arg):
+ select_result_interceptor(thd_arg),
+ intersect_mark(0), table(0)
+ {
+ init();
+ tmp_table_param.init();
+ }
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
/**
Do prepare() and prepare2() if they have been postponed until
@@ -5131,15 +5607,23 @@ public:
void cleanup();
virtual bool create_result_table(THD *thd, List<Item> *column_types,
bool is_distinct, ulonglong options,
- const char *alias,
+ const LEX_CSTRING *alias,
bool bit_fields_as_long,
bool create_table,
- bool keep_row_order= FALSE);
+ bool keep_row_order,
+ uint hidden);
TMP_TABLE_PARAM *get_tmp_table_param() { return &tmp_table_param; }
+ void init()
+ {
+ curr_step= prev_step= 0;
+ curr_sel= UINT_MAX;
+ step= UNION_TYPE;
+ write_err= 0;
+ }
+ void change_select();
};
-
-class select_union_recursive :public select_union
+class select_union_recursive :public select_unit
{
public:
/* The temporary table with the new records generated by one iterative step */
@@ -5156,16 +5640,17 @@ class select_union_recursive :public select_union
uint cleanup_count;
select_union_recursive(THD *thd_arg):
- select_union(thd_arg),
+ select_unit(thd_arg),
incr_table(0), first_rec_table_to_update(0), cleanup_count(0) {};
int send_data(List<Item> &items);
bool create_result_table(THD *thd, List<Item> *column_types,
bool is_distinct, ulonglong options,
- const char *alias,
+ const LEX_CSTRING *alias,
bool bit_fields_as_long,
bool create_table,
- bool keep_row_order= FALSE);
+ bool keep_row_order,
+ uint hidden);
void cleanup();
};
@@ -5182,7 +5667,7 @@ class select_union_recursive :public select_union
have a global ORDER BY clause. @see st_select_lex_unit::prepare().
*/
-class select_union_direct :public select_union
+class select_union_direct :public select_unit
{
private:
/* Result object that receives all rows */
@@ -5208,7 +5693,7 @@ public:
ha_rows send_records;
select_union_direct(THD *thd_arg, select_result *result_arg,
SELECT_LEX *last_select_lex_arg):
- select_union(thd_arg), result(result_arg),
+ select_unit(thd_arg), result(result_arg),
last_select_lex(last_select_lex_arg),
done_send_result_set_metadata(false), done_initialize_tables(false),
limit_found_rows(0)
@@ -5223,7 +5708,7 @@ public:
bool postponed_prepare(List<Item> &types);
bool send_result_set_metadata(List<Item> &list, uint flags);
int send_data(List<Item> &items);
- bool initialize_tables (JOIN *join= NULL);
+ bool initialize_tables (JOIN *join);
bool send_eof();
bool flush() { return false; }
bool check_simple_select() const
@@ -5291,7 +5776,7 @@ public:
about NULLs.
*/
-class select_materialize_with_stats : public select_union
+class select_materialize_with_stats : public select_unit
{
protected:
class Column_statistics
@@ -5324,14 +5809,15 @@ protected:
void reset();
public:
- select_materialize_with_stats(THD *thd_arg): select_union(thd_arg)
+ select_materialize_with_stats(THD *thd_arg): select_unit(thd_arg)
{ tmp_table_param.init(); }
bool create_result_table(THD *thd, List<Item> *column_types,
bool is_distinct, ulonglong options,
- const char *alias,
+ const LEX_CSTRING *alias,
bool bit_fields_as_long,
bool create_table,
- bool keep_row_order= FALSE);
+ bool keep_row_order,
+ uint hidden);
bool init_result_table(ulonglong select_options);
int send_data(List<Item> &items);
void cleanup();
@@ -5384,8 +5870,6 @@ public:
};
-
-
/*
Optimizer and executor structure for the materialized semi-join info. This
structure contains
@@ -5400,7 +5884,10 @@ public:
uint tables; /* Number of tables in the sj-nest */
- /* Expected #rows in the materialized table */
+ /* Number of rows in the materialized table, before the de-duplication */
+ double rows_with_duplicates;
+
+ /* Expected #rows in the materialized table, after de-duplication */
double rows;
/*
@@ -5473,22 +5960,23 @@ typedef struct st_sort_buffer {
class Table_ident :public Sql_alloc
{
public:
- LEX_STRING db;
- LEX_STRING table;
+ LEX_CSTRING db;
+ LEX_CSTRING table;
SELECT_LEX_UNIT *sel;
- inline Table_ident(THD *thd, LEX_STRING db_arg, LEX_STRING table_arg,
+ inline Table_ident(THD *thd, const LEX_CSTRING *db_arg,
+ const LEX_CSTRING *table_arg,
bool force)
- :table(table_arg), sel((SELECT_LEX_UNIT *)0)
+ :table(*table_arg), sel((SELECT_LEX_UNIT *)0)
{
if (!force && (thd->client_capabilities & CLIENT_NO_SCHEMA))
- db.str=0;
+ db= null_clex_str;
else
- db= db_arg;
+ db= *db_arg;
}
- inline Table_ident(LEX_STRING table_arg)
- :table(table_arg), sel((SELECT_LEX_UNIT *)0)
+ inline Table_ident(const LEX_CSTRING *table_arg)
+ :table(*table_arg), sel((SELECT_LEX_UNIT *)0)
{
- db.str=0;
+ db= null_clex_str;
}
/*
This constructor is used only for the case when we create a derived
@@ -5505,21 +5993,49 @@ public:
table.length=1;
}
bool is_derived_table() const { return MY_TEST(sel); }
- inline void change_db(char *db_name)
+ inline void change_db(LEX_CSTRING *db_name)
{
- db.str= db_name; db.length= (uint) strlen(db_name);
+ db= *db_name;
}
+ bool resolve_table_rowtype_ref(THD *thd, Row_definition_list &defs);
+ bool append_to(THD *thd, String *to) const;
+};
+
+
+class Qualified_column_ident: public Table_ident
+{
+public:
+ LEX_CSTRING m_column;
+public:
+ Qualified_column_ident(const LEX_CSTRING *column)
+ :Table_ident(&null_clex_str),
+ m_column(*column)
+ { }
+ Qualified_column_ident(const LEX_CSTRING *table, const LEX_CSTRING *column)
+ :Table_ident(table),
+ m_column(*column)
+ { }
+ Qualified_column_ident(THD *thd,
+ const LEX_CSTRING *db,
+ const LEX_CSTRING *table,
+ const LEX_CSTRING *column)
+ :Table_ident(thd, db, table, false),
+ m_column(*column)
+ { }
+ bool resolve_type_ref(THD *thd, Column_definition *def);
+ bool append_to(THD *thd, String *to) const;
};
+
// this is needed for user_vars hash
class user_var_entry
{
CHARSET_INFO *m_charset;
public:
user_var_entry() {} /* Remove gcc warning */
- LEX_STRING name;
+ LEX_CSTRING name;
char *value;
- ulong length;
+ size_t length;
query_id_t update_query_id, used_query_id;
Item_result type;
bool unsigned_flag;
@@ -5532,7 +6048,7 @@ class user_var_entry
void set_charset(CHARSET_INFO *cs) { m_charset= cs; }
};
-user_var_entry *get_variable(HASH *hash, LEX_STRING &name,
+user_var_entry *get_variable(HASH *hash, LEX_CSTRING *name,
bool create_if_not_exists);
class SORT_INFO;
@@ -5574,7 +6090,7 @@ class multi_update :public select_result_interceptor
{
TABLE_LIST *all_tables; /* query/update command tables */
List<TABLE_LIST> *leaves; /* list of leves of join table tree */
- TABLE_LIST *update_tables, *table_being_updated;
+ TABLE_LIST *update_tables;
TABLE **tmp_tables, *main_table, *table_to_update;
TMP_TABLE_PARAM *tmp_table_param;
ha_rows updated, found;
@@ -5600,6 +6116,12 @@ class multi_update :public select_result_interceptor
/* Need this to protect against multiple prepare() calls */
bool prepared;
+
+ // For System Versioning (may need to insert new fields to a table).
+ ha_rows updated_sys_ver;
+
+ bool has_vers_fields;
+
public:
multi_update(THD *thd_arg, TABLE_LIST *ut, List<TABLE_LIST> *leaves_list,
List<Item> *fields, List<Item> *values,
@@ -5608,6 +6130,7 @@ public:
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
int send_data(List<Item> &items);
bool initialize_tables (JOIN *join);
+ int prepare2(JOIN *join);
int do_updates();
bool send_eof();
inline ha_rows num_found() const { return found; }
@@ -5617,34 +6140,62 @@ public:
void prepare_to_read_rows();
};
+class my_var_sp;
class my_var : public Sql_alloc {
public:
- const LEX_STRING name;
+ const LEX_CSTRING name;
enum type { SESSION_VAR, LOCAL_VAR, PARAM_VAR };
type scope;
- my_var(const LEX_STRING& j, enum type s) : name(j), scope(s) { }
+ my_var(const LEX_CSTRING *j, enum type s) : name(*j), scope(s) { }
virtual ~my_var() {}
virtual bool set(THD *thd, Item *val) = 0;
+ virtual my_var_sp *get_my_var_sp() { return NULL; }
};
class my_var_sp: public my_var {
+ const Sp_rcontext_handler *m_rcontext_handler;
+ const Type_handler *m_type_handler;
public:
uint offset;
- enum_field_types type;
/*
Routine to which this Item_splocal belongs. Used for checking if correct
runtime context is used for variable handling.
*/
sp_head *sp;
- my_var_sp(const LEX_STRING& j, uint o, enum_field_types t, sp_head *s)
- : my_var(j, LOCAL_VAR), offset(o), type(t), sp(s) { }
+ my_var_sp(const Sp_rcontext_handler *rcontext_handler,
+ const LEX_CSTRING *j, uint o, const Type_handler *type_handler,
+ sp_head *s)
+ : my_var(j, LOCAL_VAR),
+ m_rcontext_handler(rcontext_handler),
+ m_type_handler(type_handler), offset(o), sp(s) { }
~my_var_sp() { }
bool set(THD *thd, Item *val);
+ my_var_sp *get_my_var_sp() { return this; }
+ const Type_handler *type_handler() const { return m_type_handler; }
+ sp_rcontext *get_rcontext(sp_rcontext *local_ctx) const;
+};
+
+/*
+ This class handles fields of a ROW SP variable when it's used as a OUT
+ parameter in a stored procedure.
+*/
+class my_var_sp_row_field: public my_var_sp
+{
+ uint m_field_offset;
+public:
+ my_var_sp_row_field(const Sp_rcontext_handler *rcontext_handler,
+ const LEX_CSTRING *varname, const LEX_CSTRING *fieldname,
+ uint var_idx, uint field_idx, sp_head *s)
+ :my_var_sp(rcontext_handler, varname, var_idx,
+ &type_handler_double/*Not really used*/, s),
+ m_field_offset(field_idx)
+ { }
+ bool set(THD *thd, Item *val);
};
class my_var_user: public my_var {
public:
- my_var_user(const LEX_STRING& j)
+ my_var_user(const LEX_CSTRING *j)
: my_var(j, SESSION_VAR) { }
~my_var_user() { }
bool set(THD *thd, Item *val);
@@ -5652,10 +6203,13 @@ public:
class select_dumpvar :public select_result_interceptor {
ha_rows row_count;
+ my_var_sp *m_var_sp_row; // Not NULL if SELECT INTO row_type_sp_variable
+ bool send_data_to_var_list(List<Item> &items);
public:
List<my_var> var_list;
- select_dumpvar(THD *thd_arg): select_result_interceptor(thd_arg)
- { var_list.empty(); row_count= 0; }
+ select_dumpvar(THD *thd_arg)
+ :select_result_interceptor(thd_arg), row_count(0), m_var_sp_row(NULL)
+ { var_list.empty(); }
~select_dumpvar() {}
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
int send_data(List<Item> &items);
@@ -5787,10 +6341,23 @@ public:
SP Bulk execution optimized
*/
#define CF_SP_BULK_OPTIMIZED (1U << 21)
+/**
+ If command creates or drops a table
+*/
+#define CF_SCHEMA_CHANGE (1U << 22)
+/**
+ If command creates or drops a database
+*/
+#define CF_DB_CHANGE (1U << 23)
/* Bits in server_command_flags */
/**
+ Statement that deletes existing rows (DELETE, DELETE_MULTI)
+*/
+#define CF_DELETES_DATA (1U << 24)
+
+/**
Skip the increase of the global query id counter. Commonly set for
commands that are stateless (won't cause any change on the server
internal states).
@@ -5902,6 +6469,17 @@ inline int handler::ha_write_tmp_row(uchar *buf)
return error;
}
+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,
+ { error= delete_row(buf); })
+ MYSQL_DELETE_ROW_DONE(error);
+ return error;
+}
+
inline int handler::ha_update_tmp_row(const uchar *old_data, uchar *new_data)
{
int error;
@@ -5961,7 +6539,7 @@ void thd_exit_cond(MYSQL_THD thd, const PSI_stage_info *stage,
#define THD_EXIT_COND(P1, P2) \
thd_exit_cond(P1, P2, __func__, __FILE__, __LINE__)
-inline bool binlog_should_compress(ulong len)
+inline bool binlog_should_compress(size_t len)
{
return opt_bin_log_compress &&
len >= opt_bin_log_compress_min_len;
@@ -5999,6 +6577,240 @@ class Switch_to_definer_security_ctx
Security_context *m_sctx;
};
-#endif /* MYSQL_SERVER */
+/**
+ This class resembles the SQL Standard schema qualified object name:
+ <schema qualified name> ::= [ <schema name> <period> ] <qualified identifier>
+*/
+class Database_qualified_name
+{
+public:
+ LEX_CSTRING m_db;
+ LEX_CSTRING m_name;
+ Database_qualified_name(const LEX_CSTRING *db, const LEX_CSTRING *name)
+ :m_db(*db), m_name(*name)
+ { }
+ Database_qualified_name(const LEX_CSTRING &db, const LEX_CSTRING &name)
+ :m_db(db), m_name(name)
+ { }
+ Database_qualified_name(const char *db, size_t db_length,
+ const char *name, size_t name_length)
+ {
+ m_db.str= db;
+ m_db.length= db_length;
+ m_name.str= name;
+ m_name.length= name_length;
+ }
+
+ bool eq(const Database_qualified_name *other) const
+ {
+ CHARSET_INFO *cs= lower_case_table_names ?
+ &my_charset_utf8_general_ci :
+ &my_charset_utf8_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);
+ }
+ void copy(MEM_ROOT *mem_root, const LEX_CSTRING &db,
+ const LEX_CSTRING &name);
+ // Export db and name as a qualified name string: 'db.name'
+ size_t make_qname(char *dst, size_t dstlen) const
+ {
+ return my_snprintf(dst, dstlen, "%.*s.%.*s",
+ (int) m_db.length, m_db.str,
+ (int) m_name.length, m_name.str);
+ }
+ // Export db and name as a qualified name string, allocate on mem_root.
+ bool make_qname(MEM_ROOT *mem_root, LEX_CSTRING *dst) const
+ {
+ const uint dot= !!m_db.length;
+ char *tmp;
+ /* format: [database + dot] + name + '\0' */
+ dst->length= m_db.length + dot + m_name.length;
+ if (unlikely(!(dst->str= tmp= (char*) alloc_root(mem_root,
+ dst->length + 1))))
+ return true;
+ sprintf(tmp, "%.*s%.*s%.*s",
+ (int) m_db.length, (m_db.length ? m_db.str : ""),
+ dot, ".",
+ (int) m_name.length, m_name.str);
+ DBUG_SLOW_ASSERT(ok_for_lower_case_names(m_db.str));
+ return false;
+ }
+
+ bool make_package_routine_name(MEM_ROOT *mem_root,
+ const LEX_CSTRING &package,
+ const LEX_CSTRING &routine)
+ {
+ char *tmp;
+ size_t length= package.length + 1 + routine.length + 1;
+ if (unlikely(!(tmp= (char *) alloc_root(mem_root, length))))
+ return true;
+ m_name.length= my_snprintf(tmp, length, "%.*s.%.*s",
+ (int) package.length, package.str,
+ (int) routine.length, routine.str);
+ m_name.str= tmp;
+ return false;
+ }
+
+ bool make_package_routine_name(MEM_ROOT *mem_root,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &package,
+ const LEX_CSTRING &routine)
+ {
+ if (unlikely(make_package_routine_name(mem_root, package, routine)))
+ return true;
+ if (unlikely(!(m_db.str= strmake_root(mem_root, db.str, db.length))))
+ return true;
+ m_db.length= db.length;
+ return false;
+ }
+};
+
+
+class ErrConvDQName: public ErrConv
+{
+ const Database_qualified_name *m_name;
+public:
+ ErrConvDQName(const Database_qualified_name *name)
+ :m_name(name)
+ { }
+ const char *ptr() const
+ {
+ m_name->make_qname(err_buffer, sizeof(err_buffer));
+ return err_buffer;
+ }
+};
+
+class Type_holder: public Sql_alloc,
+ public Item_args,
+ public Type_handler_hybrid_field_type,
+ public Type_all_attributes,
+ public Type_geometry_attributes
+{
+ TYPELIB *m_typelib;
+ bool m_maybe_null;
+public:
+ Type_holder()
+ :m_typelib(NULL),
+ m_maybe_null(false)
+ { }
+
+ void set_maybe_null(bool maybe_null_arg) { m_maybe_null= maybe_null_arg; }
+ bool get_maybe_null() const { return m_maybe_null; }
+
+ uint decimal_precision() const
+ {
+ /*
+ Type_holder is not used directly to create fields, so
+ its virtual decimal_precision() is never called.
+ We should eventually extend create_result_table() to accept
+ an array of Type_holders directly, without having to allocate
+ Item_type_holder's and put them into List<Item>.
+ */
+ 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)
+ {
+ m_typelib= typelib;
+ }
+ TYPELIB *get_typelib() const
+ {
+ return m_typelib;
+ }
+
+ bool aggregate_attributes(THD *thd)
+ {
+ for (uint i= 0; i < arg_count; i++)
+ m_maybe_null|= args[i]->maybe_null;
+ return
+ type_handler()->Item_hybrid_func_fix_attributes(thd,
+ "UNION", this, this,
+ args, arg_count);
+ }
+};
+
+
+/*
+ A helper class to set THD flags to emit warnings/errors in case of
+ overflow/type errors during assigning values into the SP variable fields.
+ Saves original flags values in constructor.
+ Restores original flags in destructor.
+*/
+class Sp_eval_expr_state
+{
+ THD *m_thd;
+ enum_check_fields m_count_cuted_fields;
+ bool m_abort_on_warning;
+ bool m_stmt_modified_non_trans_table;
+ void start()
+ {
+ 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;
+ }
+ 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_stmt_modified_non_trans_table;
+ }
+public:
+ Sp_eval_expr_state(THD *thd)
+ :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.
+ modified_non_trans_table)
+ {
+ start();
+ }
+ ~Sp_eval_expr_state()
+ {
+ stop();
+ }
+};
+
+
+#ifndef DBUG_OFF
+void dbug_serve_apcs(THD *thd, int n_calls);
+#endif
+
+class ScopedStatementReplication
+{
+public:
+ ScopedStatementReplication(THD *thd) :
+ saved_binlog_format(thd
+ ? thd->set_current_stmt_binlog_format_stmt()
+ : BINLOG_FORMAT_MIXED),
+ thd(thd)
+ {}
+ ~ScopedStatementReplication()
+ {
+ if (thd)
+ thd->restore_stmt_binlog_format(saved_binlog_format);
+ }
+
+private:
+ const enum_binlog_format saved_binlog_format;
+ THD *const thd;
+};
+
+#endif /* MYSQL_SERVER */
#endif /* SQL_CLASS_INCLUDED */
diff --git a/sql/sql_client.cc b/sql/sql_client.cc
index bd60b17ea95..b4a22c34e99 100644
--- a/sql/sql_client.cc
+++ b/sql/sql_client.cc
@@ -18,7 +18,7 @@
This files defines some MySQL C API functions that are server specific
*/
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "sql_class.h" // system_variables
diff --git a/sql/sql_cmd.h b/sql/sql_cmd.h
index 4f6673ae615..f5df73216d8 100644
--- a/sql/sql_cmd.h
+++ b/sql/sql_cmd.h
@@ -96,6 +96,18 @@ enum enum_sql_command {
SQLCOM_ALTER_USER,
SQLCOM_SHOW_CREATE_USER,
SQLCOM_EXECUTE_IMMEDIATE,
+ SQLCOM_CREATE_SEQUENCE,
+ SQLCOM_DROP_SEQUENCE,
+ SQLCOM_ALTER_SEQUENCE,
+ SQLCOM_CREATE_PACKAGE,
+ SQLCOM_DROP_PACKAGE,
+ SQLCOM_CREATE_PACKAGE_BODY,
+ SQLCOM_DROP_PACKAGE_BODY,
+ SQLCOM_SHOW_CREATE_PACKAGE,
+ SQLCOM_SHOW_CREATE_PACKAGE_BODY,
+ SQLCOM_SHOW_STATUS_PACKAGE,
+ SQLCOM_SHOW_STATUS_PACKAGE_BODY,
+ SQLCOM_SHOW_PACKAGE_BODY_CODE,
/*
When a command is added here, be sure it's also added in mysqld.cc
@@ -127,6 +139,7 @@ public:
bool resolve_storage_engine_with_error(THD *thd,
handlerton **ha,
bool tmp_table);
+ bool is_set() { return m_storage_engine_name.str != NULL; }
};
@@ -194,15 +207,54 @@ protected:
}
};
-
-class Sql_cmd_create_table: public Sql_cmd,
- public Storage_engine_name
+class Sql_cmd_create_table_like: public Sql_cmd,
+ public Storage_engine_name
{
public:
- enum_sql_command sql_command_code() const { return SQLCOM_CREATE_TABLE; }
Storage_engine_name *option_storage_engine_name() { return this; }
bool execute(THD *thd);
};
+class Sql_cmd_create_table: public Sql_cmd_create_table_like
+{
+public:
+ enum_sql_command sql_command_code() const { return SQLCOM_CREATE_TABLE; }
+};
+
+class Sql_cmd_create_sequence: public Sql_cmd_create_table_like
+{
+public:
+ enum_sql_command sql_command_code() const { return SQLCOM_CREATE_SEQUENCE; }
+};
+
+
+/**
+ Sql_cmd_call represents the CALL statement.
+*/
+class Sql_cmd_call : public Sql_cmd
+{
+public:
+ class sp_name *m_name;
+ const class Sp_handler *m_handler;
+ Sql_cmd_call(class sp_name *name, const class Sp_handler *handler)
+ :m_name(name),
+ m_handler(handler)
+ {}
+
+ virtual ~Sql_cmd_call()
+ {}
+
+ /**
+ Execute a CALL statement at runtime.
+ @param thd the current thread.
+ @return false on success.
+ */
+ bool execute(THD *thd);
+
+ virtual enum_sql_command sql_command_code() const
+ {
+ return SQLCOM_CALL;
+ }
+};
#endif // SQL_CMD_INCLUDED
diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc
index b2900a20b28..37d2b0e902f 100644
--- a/sql/sql_connect.cc
+++ b/sql/sql_connect.cc
@@ -20,7 +20,8 @@
Functions to autenticate and handle reqests for a connection
*/
-#include <my_global.h>
+#include "mariadb.h"
+#include "mysqld.h"
#include "sql_priv.h"
#ifndef __WIN__
#include <netdb.h> // getservbyname, servent
@@ -37,6 +38,7 @@
#include "sql_acl.h" // acl_getroot, NO_ACCESS, SUPER_ACL
#include "sql_callback.h"
#include "wsrep_mysqld.h"
+#include "proxy_protocol.h"
HASH global_user_stats, global_client_stats, global_table_stats;
HASH global_index_stats;
@@ -44,6 +46,7 @@ HASH global_index_stats;
extern mysql_mutex_t LOCK_global_user_client_stats;
extern mysql_mutex_t LOCK_global_table_stats;
extern mysql_mutex_t LOCK_global_index_stats;
+extern vio_keepalive_opts opt_vio_keepalive;
/*
Get structure for logging connection data for the current user
@@ -83,7 +86,7 @@ int get_or_create_user_conn(THD *thd, const char *user,
uc->user=(char*) (uc+1);
memcpy(uc->user,temp_user,temp_len+1);
uc->host= uc->user + user_len + 1;
- uc->len= temp_len;
+ uc->len= (uint)temp_len;
uc->connections= uc->questions= uc->updates= uc->conn_per_hour= 0;
uc->user_resources= *mqh;
uc->reset_utime= thd->thr_create_utime;
@@ -164,7 +167,7 @@ int check_for_max_user_connections(THD *thd, USER_CONN *uc)
error= 0;
end:
- if (error)
+ if (unlikely(error))
{
uc->connections--; // no need for decrease_user_connections() here
/*
@@ -175,7 +178,7 @@ end:
thd->user_connect= NULL;
}
mysql_mutex_unlock(&LOCK_user_conn);
- if (error)
+ if (unlikely(error))
{
inc_host_errors(thd->main_security_ctx.ip, &errors);
}
@@ -336,7 +339,7 @@ void reset_mqh(LEX_USER *lu, bool get_them= 0)
if (lu) // for GRANT
{
USER_CONN *uc;
- uint temp_len=lu->user.length+lu->host.length+2;
+ size_t temp_len=lu->user.length+lu->host.length+2;
char temp_user[USER_HOST_BUFF_SIZE];
memcpy(temp_user,lu->user.str,lu->user.length);
@@ -378,7 +381,7 @@ void reset_mqh(LEX_USER *lu, bool get_them= 0)
static const char mysql_system_user[]= "#mysql_system#";
// Returns 'user' if it's not NULL. Returns 'mysql_system_user' otherwise.
-static const char * get_valid_user_string(char* user)
+static const char * get_valid_user_string(const char* user)
{
return user ? user : mysql_system_user;
}
@@ -442,7 +445,7 @@ void init_user_stats(USER_STATS *user_stats,
user_length= MY_MIN(user_length, sizeof(user_stats->user)-1);
memcpy(user_stats->user, user, user_length);
user_stats->user[user_length]= 0;
- user_stats->user_name_length= user_length;
+ user_stats->user_name_length= (uint)user_length;
strmake_buf(user_stats->priv_user, priv_user);
user_stats->total_connections= total_connections;
@@ -818,6 +821,113 @@ bool init_new_connection_handler_thread()
return 0;
}
+/**
+ Set client address during authentication.
+
+ Initializes THD::main_security_ctx and THD::peer_port.
+ Optionally does ip to hostname translation.
+
+ @param thd current THD handle
+ @param addr peer address (can be NULL, if 'ip' is set)
+ @param ip peer address as string (can be NULL if 'addr' is set)
+ @param port peer port
+ @param check_proxy_networks if true, and host is in
+ 'proxy_protocol_networks' list, skip
+ "host not privileged" check
+ @param[out] host_errors - number of connect
+ errors for this host
+
+ @retval 0 ok, 1 error
+*/
+int thd_set_peer_addr(THD *thd,
+ sockaddr_storage *addr,
+ const char *ip,
+ uint port,
+ bool check_proxy_networks,
+ uint *host_errors)
+{
+ *host_errors= 0;
+
+ thd->peer_port= port;
+
+ char ip_string[128];
+ if (!ip)
+ {
+ void *addr_data;
+ if (addr->ss_family == AF_UNIX)
+ {
+ /* local connection */
+ my_free((void *)thd->main_security_ctx.ip);
+ thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host = my_localhost;
+ thd->main_security_ctx.ip= 0;
+ return 0;
+ }
+ else if (addr->ss_family == AF_INET)
+ addr_data= &((struct sockaddr_in *)addr)->sin_addr;
+ else
+ addr_data= &((struct sockaddr_in6 *)addr)->sin6_addr;
+ if (!inet_ntop(addr->ss_family,addr_data, ip_string, sizeof(ip_string)))
+ {
+ DBUG_ASSERT(0);
+ return 1;
+ }
+ ip= ip_string;
+ }
+
+ my_free((void *)thd->main_security_ctx.ip);
+ if (!(thd->main_security_ctx.ip = my_strdup(ip, MYF(MY_WME))))
+ {
+ /*
+ No error accounting per IP in host_cache,
+ this is treated as a global server OOM error.
+ TODO: remove the need for my_strdup.
+ */
+ statistic_increment(aborted_connects, &LOCK_status);
+ statistic_increment(connection_errors_internal, &LOCK_status);
+ return 1; /* The error is set by my_strdup(). */
+ }
+ thd->main_security_ctx.host_or_ip = thd->main_security_ctx.ip;
+ if (!(specialflag & SPECIAL_NO_RESOLVE))
+ {
+ int rc;
+
+ rc = ip_to_hostname(addr,
+ thd->main_security_ctx.ip,
+ &thd->main_security_ctx.host,
+ host_errors);
+
+ /* Cut very long hostnames to avoid possible overflows */
+ if (thd->main_security_ctx.host)
+ {
+ if (thd->main_security_ctx.host != my_localhost)
+ ((char*)thd->main_security_ctx.host)[MY_MIN(strlen(thd->main_security_ctx.host),
+ HOSTNAME_LENGTH)] = 0;
+ thd->main_security_ctx.host_or_ip = thd->main_security_ctx.host;
+ }
+
+ if (rc == RC_BLOCKED_HOST)
+ {
+ /* HOST_CACHE stats updated by ip_to_hostname(). */
+ my_error(ER_HOST_IS_BLOCKED, MYF(0), thd->main_security_ctx.host_or_ip);
+ return 1;
+ }
+ }
+ DBUG_PRINT("info", ("Host: %s ip: %s",
+ (thd->main_security_ctx.host ?
+ thd->main_security_ctx.host : "unknown host"),
+ (thd->main_security_ctx.ip ?
+ thd->main_security_ctx.ip : "unknown ip")));
+ if ((!check_proxy_networks || !is_proxy_protocol_allowed((struct sockaddr *) addr))
+ && acl_check_host(thd->main_security_ctx.host, thd->main_security_ctx.ip))
+ {
+ /* HOST_CACHE stats updated by acl_check_host(). */
+ my_error(ER_HOST_NOT_PRIVILEGED, MYF(0),
+ thd->main_security_ctx.host_or_ip);
+ return 1;
+ }
+ return 0;
+}
+
/*
Perform handshake, authorize client and update thd ACL variables.
@@ -847,8 +957,9 @@ static int check_connection(THD *thd)
{
my_bool peer_rc;
char ip[NI_MAXHOST];
+ uint16 peer_port;
- peer_rc= vio_peer_addr(net->vio, ip, &thd->peer_port, NI_MAXHOST);
+ peer_rc= vio_peer_addr(net->vio, ip, &peer_port, NI_MAXHOST);
/*
===========================================================================
@@ -869,7 +980,7 @@ static int check_connection(THD *thd)
struct in_addr *ip4= &((struct sockaddr_in *) sa)->sin_addr;
/* See RFC 5737, 192.0.2.0/24 is reserved. */
const char* fake= "192.0.2.4";
- ip4->s_addr= inet_addr(fake);
+ inet_pton(AF_INET,fake, ip4);
strcpy(ip, fake);
peer_rc= 0;
}
@@ -923,55 +1034,10 @@ static int check_connection(THD *thd)
my_error(ER_BAD_HOST_ERROR, MYF(0));
return 1;
}
- if (!(thd->main_security_ctx.ip= my_strdup(ip,MYF(MY_WME))))
- {
- /*
- No error accounting per IP in host_cache,
- this is treated as a global server OOM error.
- TODO: remove the need for my_strdup.
- */
- statistic_increment(aborted_connects,&LOCK_status);
- statistic_increment(connection_errors_internal, &LOCK_status);
- return 1; /* The error is set by my_strdup(). */
- }
- thd->main_security_ctx.host_or_ip= thd->main_security_ctx.ip;
- if (!(specialflag & SPECIAL_NO_RESOLVE))
- {
- int rc;
-
- rc= ip_to_hostname(&net->vio->remote,
- thd->main_security_ctx.ip,
- &thd->main_security_ctx.host,
- &connect_errors);
-
- /* Cut very long hostnames to avoid possible overflows */
- if (thd->main_security_ctx.host)
- {
- if (thd->main_security_ctx.host != my_localhost)
- ((char*) thd->main_security_ctx.host)[MY_MIN(strlen(thd->main_security_ctx.host),
- HOSTNAME_LENGTH)]= 0;
- thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host;
- }
-
- if (rc == RC_BLOCKED_HOST)
- {
- /* HOST_CACHE stats updated by ip_to_hostname(). */
- my_error(ER_HOST_IS_BLOCKED, MYF(0), thd->main_security_ctx.host_or_ip);
- return 1;
- }
- }
- DBUG_PRINT("info",("Host: %s ip: %s",
- (thd->main_security_ctx.host ?
- thd->main_security_ctx.host : "unknown host"),
- (thd->main_security_ctx.ip ?
- thd->main_security_ctx.ip : "unknown ip")));
- if (acl_check_host(thd->main_security_ctx.host, thd->main_security_ctx.ip))
- {
- /* HOST_CACHE stats updated by acl_check_host(). */
- my_error(ER_HOST_NOT_PRIVILEGED, MYF(0),
- thd->main_security_ctx.host_or_ip);
+
+ if (thd_set_peer_addr(thd, &net->vio->remote, ip, peer_port,
+ true, &connect_errors))
return 1;
- }
}
else /* Hostname given means that the connection was on a socket */
{
@@ -982,8 +1048,9 @@ static int check_connection(THD *thd)
bzero((char*) &net->vio->remote, sizeof(net->vio->remote));
}
vio_keepalive(net->vio, TRUE);
-
- if (thd->packet.alloc(thd->variables.net_buffer_length))
+ vio_set_keepalive_options(net->vio, &opt_vio_keepalive);
+
+ if (unlikely(thd->packet.alloc(thd->variables.net_buffer_length)))
{
/*
Important note:
@@ -1073,7 +1140,7 @@ bool login_connection(THD *thd)
error= check_connection(thd);
thd->protocol->end_statement();
- if (error)
+ if (unlikely(error))
{ // Wrong permissions
#ifdef _WIN32
if (vio_type(net->vio) == VIO_TYPE_NAMEDPIPE)
@@ -1139,13 +1206,13 @@ void end_connection(THD *thd)
thd->user_connect= NULL;
}
- if (thd->killed || (net->error && net->vio != 0))
+ if (unlikely(thd->killed) || (net->error && net->vio != 0))
{
statistic_increment(aborted_threads,&LOCK_status);
status_var_increment(thd->status_var.lost_connections);
}
- if (!thd->killed && (net->error && net->vio != 0))
+ if (likely(!thd->killed) && (net->error && net->vio != 0))
thd->print_aborted_warning(1, thd->get_stmt_da()->is_error()
? thd->get_stmt_da()->message() : ER_THD(thd, ER_UNKNOWN_ERROR));
}
@@ -1174,7 +1241,7 @@ void prepare_new_connection_state(THD* thd)
if (opt_init_connect.length && !(sctx->master_access & SUPER_ACL))
{
execute_init_command(thd, &opt_init_connect, &LOCK_sys_init_connect);
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
{
Host_errors errors;
thd->set_killed(KILL_CONNECTION);
@@ -1198,7 +1265,7 @@ void prepare_new_connection_state(THD* thd)
if (packet_length != packet_error)
my_error(ER_NEW_ABORTING_CONNECTION, MYF(0),
thd->thread_id,
- thd->db ? thd->db : "unconnected",
+ thd->db.str ? thd->db.str : "unconnected",
sctx->user ? sctx->user : "unauthenticated",
sctx->host_or_ip, "init_connect command failed");
thd->server_status&= ~SERVER_STATUS_CLEAR_SET;
@@ -1263,9 +1330,9 @@ bool thd_prepare_connection(THD *thd)
bool thd_is_connection_alive(THD *thd)
{
NET *net= &thd->net;
- if (!net->error &&
- net->vio != 0 &&
- thd->killed < KILL_CONNECTION)
+ if (likely(!net->error &&
+ net->vio != 0 &&
+ thd->killed < KILL_CONNECTION))
return TRUE;
return FALSE;
}
@@ -1444,7 +1511,7 @@ THD *CONNECT::create_thd(THD *thd)
res= my_net_init(&thd->net, vio, thd, MYF(MY_THREAD_SPECIFIC));
vio= 0; // Vio now handled by thd
- if (res || thd->is_error())
+ if (unlikely(res || thd->is_error()))
{
if (!thd_reused)
delete thd;
diff --git a/sql/sql_connect.h b/sql/sql_connect.h
index c87dc4e6377..82ab4423b37 100644
--- a/sql/sql_connect.h
+++ b/sql/sql_connect.h
@@ -16,7 +16,7 @@
#ifndef SQL_CONNECT_INCLUDED
#define SQL_CONNECT_INCLUDED
-#include "my_sys.h" /* pthread_handler_t */
+#include <my_sys.h> /* pthread_handler_t */
#include "mysql_com.h" /* enum_server_command */
#include "structs.h"
#include <mysql/psi/mysql_socket.h>
@@ -57,7 +57,6 @@ public:
class THD;
-typedef struct st_lex_user LEX_USER;
typedef struct user_conn USER_CONN;
void init_max_user_conn(void);
@@ -86,6 +85,10 @@ bool thd_init_client_charset(THD *thd, uint cs_number);
bool 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,
+ const char *ip, uint port,
+ bool check_proxy_networks,
+ uint *host_errors);
bool login_connection(THD *thd);
void prepare_new_connection_state(THD* thd);
diff --git a/sql/sql_const.h b/sql/sql_const.h
index 65fe94135ec..e33d8b73626 100644
--- a/sql/sql_const.h
+++ b/sql/sql_const.h
@@ -64,7 +64,7 @@
CREATE TABLE t1 (c VARBINARY(65534));
CREATE TABLE t1 (c VARBINARY(65535));
Like VARCHAR(65536), they will be converted to BLOB automatically
- in non-sctict mode.
+ in non-strict mode.
*/
#define MAX_FIELD_VARCHARLENGTH (65535-2-1)
#define MAX_FIELD_BLOBLENGTH UINT_MAX32 /* cf field_blob::get_length() */
@@ -94,7 +94,7 @@
#define MAX_FIELDS 4096 /* Limit in the .frm file */
#define MAX_PARTITIONS 8192
-#define MAX_SELECT_NESTING (sizeof(nesting_map)*8-1)
+#define MAX_SELECT_NESTING (SELECT_NESTING_MAP_SIZE - 1)
#define MAX_SORT_MEMORY 2048*1024
#define MIN_SORT_MEMORY 1024
@@ -136,6 +136,7 @@
#define MAX_ACCEPT_RETRY 10 // Test accept this many times
#define MAX_FIELDS_BEFORE_HASH 32
#define USER_VARS_HASH_SIZE 16
+#define SEQUENCES_HASH_SIZE 16
#define TABLE_OPEN_CACHE_MIN 200
#define TABLE_OPEN_CACHE_DEFAULT 2000
#define TABLE_DEF_CACHE_DEFAULT 400
@@ -184,6 +185,11 @@
#define TABLE_ALLOC_BLOCK_SIZE 1024
#define WARN_ALLOC_BLOCK_SIZE 2048
#define WARN_ALLOC_PREALLOC_SIZE 1024
+/*
+ Note that if we are using 32K or less, then TCmalloc will use a local
+ heap without locks!
+*/
+#define SHOW_ALLOC_BLOCK_SIZE (32768-MALLOC_OVERHEAD)
/*
The following parameters is to decide when to use an extra cache to
@@ -235,6 +241,7 @@
*/
#define HEAP_TEMPTABLE_LOOKUP_COST 0.05
#define DISK_TEMPTABLE_LOOKUP_COST 1.0
+#define SORT_INDEX_CMP_COST 0.02
#define COST_MAX (DBL_MAX * (1.0 - DBL_EPSILON))
diff --git a/sql/sql_crypt.cc b/sql/sql_crypt.cc
index f913cd3ebfa..edff85a0f9d 100644
--- a/sql/sql_crypt.cc
+++ b/sql/sql_crypt.cc
@@ -26,7 +26,7 @@
#pragma implementation // gcc: Class implementation
#endif
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "sql_crypt.h"
#include "password.h"
diff --git a/sql/sql_crypt.h b/sql/sql_crypt.h
index e077452e527..3c90550c944 100644
--- a/sql/sql_crypt.h
+++ b/sql/sql_crypt.h
@@ -21,7 +21,7 @@
#pragma interface /* gcc class implementation */
#endif
-#include "sql_list.h" /* Sql_alloc */
+#include "sql_alloc.h" /* Sql_alloc */
#include "my_rnd.h" /* rand_struct */
class SQL_CRYPT :public Sql_alloc
diff --git a/sql/sql_cte.cc b/sql/sql_cte.cc
index fe8e0de71b4..a952f3b84b1 100644
--- a/sql/sql_cte.cc
+++ b/sql/sql_cte.cc
@@ -1,3 +1,20 @@
+/*
+ Copyright (c) 2016, 2017 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 */
+
+#include "mariadb.h"
#include "sql_class.h"
#include "sql_lex.h"
#include "sql_cte.h"
@@ -112,8 +129,8 @@ bool With_clause::check_dependencies()
elem != with_elem;
elem= elem->next)
{
- if (my_strcasecmp(system_charset_info, with_elem->query_name->str,
- elem->query_name->str) == 0)
+ if (lex_string_cmp(system_charset_info, with_elem->query_name,
+ elem->query_name) == 0)
{
my_error(ER_DUP_QUERY_NAME, MYF(0), with_elem->query_name->str);
return true;
@@ -223,7 +240,7 @@ With_element *With_clause::find_table_def(TABLE_LIST *table,
with_elem= with_elem->next)
{
if (my_strcasecmp(system_charset_info, with_elem->query_name->str,
- table->table_name) == 0 &&
+ table->table_name.str) == 0 &&
!table->is_fqtn)
{
table->set_derived();
@@ -743,17 +760,15 @@ bool With_clause::prepare_unreferenced_elements(THD *thd)
*/
bool With_element::set_unparsed_spec(THD *thd, char *spec_start, char *spec_end,
- uint spec_offset)
+ my_ptrdiff_t spec_offset)
{
stmt_prepare_mode= thd->m_parser_state->m_lip.stmt_prepare_mode;
unparsed_spec.length= spec_end - spec_start;
+
if (stmt_prepare_mode || !thd->lex->sphead)
unparsed_spec.str= spec_start;
else
- {
- unparsed_spec.str= (char*) thd->memdup(spec_start, unparsed_spec.length+1);
- unparsed_spec.str[unparsed_spec.length]= '\0';
- }
+ unparsed_spec.str= thd->strmake(spec_start, unparsed_spec.length);
unparsed_spec_offset= spec_offset;
if (!unparsed_spec.str)
@@ -825,8 +840,8 @@ st_select_lex_unit *With_element::clone_parsed_spec(THD *thd,
st_select_lex *with_select;
char save_end= unparsed_spec.str[unparsed_spec.length];
- unparsed_spec.str[unparsed_spec.length]= '\0';
- if (parser_state.init(thd, unparsed_spec.str, unparsed_spec.length))
+ ((char*) &unparsed_spec.str[unparsed_spec.length])[0]= '\0';
+ if (parser_state.init(thd, (char*) unparsed_spec.str, (unsigned int)unparsed_spec.length))
goto err;
parser_state.m_lip.stmt_prepare_mode= stmt_prepare_mode;
parser_state.m_lip.multi_statements= false;
@@ -844,7 +859,7 @@ st_select_lex_unit *With_element::clone_parsed_spec(THD *thd,
with_select= &lex->select_lex;
with_select->select_number= ++thd->lex->stmt_lex->current_select_number;
parse_status= parse_sql(thd, &parser_state, 0);
- unparsed_spec.str[unparsed_spec.length]= save_end;
+ ((char*) &unparsed_spec.str[unparsed_spec.length])[0]= save_end;
if (parse_status)
goto err;
@@ -932,9 +947,9 @@ 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_STRING> nm(column_list);
+ List_iterator_fast<LEX_CSTRING> nm(column_list);
Item *item;
- LEX_STRING *name;
+ LEX_CSTRING *name;
if (column_list.elements != select->item_list.elements)
{
@@ -993,7 +1008,7 @@ bool With_element::prepare_unreferenced(THD *thd)
thd->lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_DERIVED;
if (!spec->prepared &&
- (spec->prepare(thd, 0, 0) ||
+ (spec->prepare(spec->derived, 0, 0) ||
rename_columns_of_derived_unit(thd, spec) ||
check_duplicate_names(thd, first_sl->item_list, 1)))
rc= true;
@@ -1338,32 +1353,29 @@ bool With_element::check_unrestricted_recursive(st_select_lex *sel,
bool st_select_lex::check_subqueries_with_recursive_references()
{
- st_select_lex_unit *sl_master= master_unit();
List_iterator<TABLE_LIST> ti(leaf_tables);
TABLE_LIST *tbl;
while ((tbl= ti++))
{
- if (!(tbl->is_with_table_recursive_reference() && sl_master->item))
+ if (!(tbl->is_with_table_recursive_reference()))
continue;
- With_element *with_elem= tbl->with;
- bool check_embedding_materialized_derived= true;
+ With_element *rec_elem= tbl->with;
+ st_select_lex_unit *sl_master;
for (st_select_lex *sl= this; sl; sl= sl_master->outer_select())
- {
+ {
sl_master= sl->master_unit();
- if (with_elem->get_owner() == sl_master->with_clause)
- check_embedding_materialized_derived= false;
- if (check_embedding_materialized_derived && !sl_master->with_element &&
- sl_master->derived && sl_master->derived->is_materialized_derived())
+ if (sl_master->with_element &&
+ sl_master->with_element->get_owner() == rec_elem->get_owner())
+ break;
+ sl->uncacheable|= UNCACHEABLE_DEPENDENT;
+ sl_master->uncacheable|= UNCACHEABLE_DEPENDENT;
+ if (sl_master->derived)
+ sl_master->derived->register_as_derived_with_rec_ref(rec_elem);
+ if (sl_master->item)
{
- my_error(ER_REF_TO_RECURSIVE_WITH_TABLE_IN_DERIVED,
- MYF(0), with_elem->query_name->str);
- return true;
+ Item_subselect *subq= (Item_subselect *) (sl_master->item);
+ subq->register_as_with_rec_ref(rec_elem);
}
- if (!sl_master->item)
- continue;
- Item_subselect *subq= (Item_subselect *) sl_master->item;
- subq->with_recursive_reference= true;
- subq->register_as_with_rec_ref(tbl->with);
}
}
return false;
@@ -1419,6 +1431,22 @@ void With_clause::print(String *str, enum_query_type query_type)
void With_element::print(String *str, enum_query_type query_type)
{
str->append(query_name);
+ if (column_list.elements)
+ {
+ List_iterator_fast<LEX_CSTRING> 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(',');
+ }
+ }
str->append(STRING_WITH_LEN(" as "));
str->append('(');
spec->print(str, query_type);
diff --git a/sql/sql_cte.h b/sql/sql_cte.h
index 58f371d936b..bda62271649 100644
--- a/sql/sql_cte.h
+++ b/sql/sql_cte.h
@@ -1,10 +1,26 @@
+/*
+ Copyright (c) 2016, 2017 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_CTE_INCLUDED
#define SQL_CTE_INCLUDED
#include "sql_list.h"
#include "sql_lex.h"
#include "sql_select.h"
-class select_union;
+class select_unit;
struct st_unit_ctxt_elem;
@@ -73,9 +89,9 @@ private:
Unparsed specification of the query that specifies this element.
It used to build clones of the specification if they are needed.
*/
- LEX_STRING unparsed_spec;
+ LEX_CSTRING unparsed_spec;
/* Offset of the specification in the input string */
- uint unparsed_spec_offset;
+ my_ptrdiff_t unparsed_spec_offset;
/* True if the with element is used a prepared statement */
bool stmt_prepare_mode;
@@ -88,14 +104,14 @@ public:
The name of the table introduced by this with elememt. The name
can be used in FROM lists of the queries in the scope of the element.
*/
- LEX_STRING *query_name;
+ LEX_CSTRING *query_name;
/*
Optional list of column names to name the columns of the table introduced
by this with element. It is used in the case when the names are not
inherited from the query that specified the table. Otherwise the list is
always empty.
*/
- List <LEX_STRING> column_list;
+ List <LEX_CSTRING> column_list;
/* The query that specifies the table introduced by this with element */
st_select_lex_unit *spec;
/*
@@ -142,10 +158,12 @@ public:
select_union_recursive *rec_result;
/* List of Item_subselects containing recursive references to this CTE */
- SQL_I_List<Item_subselect> sq_with_rec_ref;
+ SQL_I_List<Item_subselect> sq_with_rec_ref;
+ /* List of derived tables containing recursive references to this CTE */
+ SQL_I_List<TABLE_LIST> derived_with_rec_ref;
- With_element(LEX_STRING *name,
- List <LEX_STRING> list,
+ With_element(LEX_CSTRING *name,
+ List <LEX_CSTRING> 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),
@@ -180,7 +198,7 @@ public:
TABLE_LIST *find_first_sq_rec_ref_in_select(st_select_lex *sel);
bool set_unparsed_spec(THD *thd, char *spec_start, char *spec_end,
- uint spec_offset);
+ my_ptrdiff_t spec_offset);
st_select_lex_unit *clone_parsed_spec(THD *thd, TABLE_LIST *with_table);
diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc
index 475e388718e..8f41fe7c70d 100644
--- a/sql/sql_cursor.cc
+++ b/sql/sql_cursor.cc
@@ -17,7 +17,7 @@
#pragma implementation /* gcc class implementation */
#endif
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#include "sql_cursor.h"
@@ -53,6 +53,10 @@ public:
virtual int open(JOIN *join __attribute__((unused)));
virtual void fetch(ulong num_rows);
virtual void close();
+ bool export_structure(THD *thd, Row_definition_list *defs)
+ {
+ return table->export_structure(thd, defs);
+ }
virtual ~Materialized_cursor();
void on_table_fill_finished();
@@ -68,13 +72,13 @@ public:
create a Materialized_cursor.
*/
-class Select_materialize: public select_union
+class Select_materialize: public select_unit
{
select_result *result; /**< the result object of the caller (PS or SP) */
public:
Materialized_cursor *materialized_cursor;
Select_materialize(THD *thd_arg, select_result *result_arg):
- select_union(thd_arg), result(result_arg), materialized_cursor(0) {}
+ select_unit(thd_arg), result(result_arg), materialized_cursor(0) {}
virtual bool send_result_set_metadata(List<Item> &list, uint flags);
bool send_eof()
{
@@ -88,6 +92,11 @@ public:
if (materialized_cursor)
materialized_cursor->on_table_fill_finished();
}
+
+ bool view_structure_only() const
+ {
+ return result->view_structure_only();
+ }
};
@@ -128,7 +137,7 @@ int mysql_open_cursor(THD *thd, select_result *result,
MYSQL_QUERY_EXEC_START(thd->query(),
thd->thread_id,
- (char *) (thd->db ? thd->db : ""),
+ thd->get_db(),
&thd->security_ctx->priv_user[0],
(char *) thd->security_ctx->host_or_ip,
2);
@@ -277,7 +286,7 @@ int Materialized_cursor::send_result_set_metadata(
{
Send_field send_field;
Item_ident *ident= static_cast<Item_ident *>(item_dst);
- item_org->make_field(thd, &send_field);
+ item_org->make_send_field(thd, &send_field);
ident->db_name= thd->strdup(send_field.db_name);
ident->table_name= thd->strdup(send_field.table_name);
@@ -436,7 +445,7 @@ bool Select_materialize::send_result_set_metadata(List<Item> &list, uint flags)
if (create_result_table(unit->thd, unit->get_column_types(true),
FALSE,
thd->variables.option_bits | TMP_TABLE_ALL_COLUMNS,
- "", FALSE, TRUE, TRUE))
+ &empty_clex_str, FALSE, TRUE, TRUE, 0))
return TRUE;
materialized_cursor= new (&table->mem_root)
diff --git a/sql/sql_cursor.h b/sql/sql_cursor.h
index e07cb9973d1..00b9cd4e67a 100644
--- a/sql/sql_cursor.h
+++ b/sql/sql_cursor.h
@@ -54,9 +54,15 @@ public:
virtual int open(JOIN *top_level_join)= 0;
virtual void fetch(ulong num_rows)= 0;
virtual void close()= 0;
+ virtual bool export_structure(THD *thd, Row_definition_list *defs)
+ {
+ DBUG_ASSERT(0);
+ return true;
+ }
virtual ~Server_side_cursor();
static void operator delete(void *ptr, size_t size);
+ static void operator delete(void *, MEM_ROOT *){}
};
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index bbc333632ae..7b1ca52239c 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -18,7 +18,7 @@
/* create and drop of databases */
-#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
#include "unireg.h"
#include "sql_db.h"
@@ -50,19 +50,19 @@
#define MAX_DROP_TABLE_Q_LEN 1024
const char *del_exts[]= {".BAK", ".opt", NullS};
-static TYPELIB deletable_extentions=
+static TYPELIB deletable_extensions=
{array_elements(del_exts)-1,"del_exts", del_exts, NULL};
-static bool find_db_tables_and_rm_known_files(THD *, MY_DIR *, char *,
+static bool find_db_tables_and_rm_known_files(THD *, MY_DIR *, const char *,
const char *, TABLE_LIST **);
long mysql_rm_arc_files(THD *thd, MY_DIR *dirp, const char *org_path);
static my_bool rm_dir_w_symlink(const char *org_path, my_bool send_error);
static void mysql_change_db_impl(THD *thd,
- LEX_STRING *new_db_name,
+ LEX_CSTRING *new_db_name,
ulong new_db_access,
CHARSET_INFO *new_db_charset);
-static bool mysql_rm_db_internal(THD *thd, char *db,
+static bool mysql_rm_db_internal(THD *thd, const LEX_CSTRING *db,
bool if_exists, bool silent);
@@ -94,12 +94,12 @@ typedef struct my_dbopt_st
*/
static inline bool
-cmp_db_names(const char *db1_name,
- const char *db2_name)
+cmp_db_names(LEX_CSTRING *db1_name, const LEX_CSTRING *db2_name)
{
- return ((!db1_name && !db2_name) ||
- (db1_name && db2_name &&
- my_strcasecmp(table_alias_charset, db1_name, db2_name) == 0));
+ return (db1_name->length == db2_name->length &&
+ (db1_name->length == 0 ||
+ my_strcasecmp(table_alias_charset,
+ db1_name->str, db2_name->str) == 0));
}
@@ -122,12 +122,12 @@ uchar* dboptions_get_key(my_dbopt_t *opt, size_t *length,
Helper function to write a query to binlog used by mysql_rm_db()
*/
-static inline int write_to_binlog(THD *thd, char *query, uint q_len,
- char *db, uint db_len)
+static inline int write_to_binlog(THD *thd, const char *query, size_t q_len,
+ const char *db, size_t db_len)
{
Query_log_event qinfo(thd, query, q_len, FALSE, TRUE, FALSE, 0);
qinfo.db= db;
- qinfo.db_len= db_len;
+ qinfo.db_len= (uint32)db_len;
return mysql_bin_log.write(&qinfo);
}
@@ -293,7 +293,7 @@ static my_bool put_dbopt(const char *dbname, Schema_specification_st *create)
strmov(opt->name, dbname);
opt->name_length= length;
- if ((error= my_hash_insert(&dboptions, (uchar*) opt)))
+ if (unlikely((error= my_hash_insert(&dboptions, (uchar*) opt))))
{
my_free(opt);
goto end;
@@ -388,7 +388,7 @@ bool load_db_opt(THD *thd, const char *path, Schema_specification_st *create)
char buf[256];
DBUG_ENTER("load_db_opt");
bool error=1;
- uint nbytes;
+ size_t nbytes;
bzero((char*) create,sizeof(*create));
create->default_table_charset= thd->variables.collation_server;
@@ -523,7 +523,7 @@ CHARSET_INFO *get_default_db_collation(THD *thd, const char *db_name)
{
Schema_specification_st db_info;
- if (thd->db != NULL && strcmp(db_name, thd->db) == 0)
+ if (thd->db.str != NULL && strcmp(db_name, thd->db.str) == 0)
return thd->db_charset;
load_db_opt_by_name(thd, db_name, &db_info);
@@ -566,7 +566,7 @@ CHARSET_INFO *get_default_db_collation(THD *thd, const char *db_name)
*/
static int
-mysql_create_db_internal(THD *thd, char *db,
+mysql_create_db_internal(THD *thd, const LEX_CSTRING *db,
const DDL_options_st &options,
Schema_specification_st *create_info,
bool silent)
@@ -579,25 +579,18 @@ mysql_create_db_internal(THD *thd, char *db,
/* do not create 'information_schema' db */
if (is_infoschema_db(db))
{
- my_error(ER_DB_CREATE_EXISTS, MYF(0), db);
+ my_error(ER_DB_CREATE_EXISTS, MYF(0), db->str);
DBUG_RETURN(-1);
}
- char db_tmp[SAFE_NAME_LEN], *dbnorm;
- if (lower_case_table_names)
- {
- strmake_buf(db_tmp, db);
- my_casedn_str(system_charset_info, db_tmp);
- dbnorm= db_tmp;
- }
- else
- dbnorm= db;
+ char db_tmp[SAFE_NAME_LEN+1];
+ const char *dbnorm= normalize_db_name(db->str, db_tmp, sizeof(db_tmp));
if (lock_schema_name(thd, dbnorm))
DBUG_RETURN(-1);
/* Check directory */
- path_len= build_table_filename(path, sizeof(path) - 1, db, "", "", 0);
+ path_len= build_table_filename(path, sizeof(path) - 1, db->str, "", "", 0);
path[path_len-1]= 0; // Remove last '/' from path
long affected_rows= 1;
@@ -625,20 +618,20 @@ mysql_create_db_internal(THD *thd, char *db,
{
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_DB_CREATE_EXISTS, ER_THD(thd, ER_DB_CREATE_EXISTS),
- db);
+ db->str);
affected_rows= 0;
goto not_silent;
}
else
{
- my_error(ER_DB_CREATE_EXISTS, MYF(0), db);
+ my_error(ER_DB_CREATE_EXISTS, MYF(0), db->str);
DBUG_RETURN(-1);
}
if (my_mkdir(path, 0777, MYF(0)) < 0)
{
- my_error(ER_CANT_CREATE_DB, MYF(0), db, my_errno);
+ my_error(ER_CANT_CREATE_DB, MYF(0), db->str, my_errno);
DBUG_RETURN(-1);
}
@@ -694,8 +687,8 @@ not_silent:
USE sisyfos; # Will give error on slave since
# database does not exist.
*/
- qinfo.db = db;
- qinfo.db_len = strlen(db);
+ qinfo.db = db->str;
+ qinfo.db_len = (uint32)db->length;
/*
These DDL methods and logging are protected with the exclusive
@@ -714,7 +707,7 @@ not_silent:
/* db-name is already validated when we come here */
static bool
-mysql_alter_db_internal(THD *thd, const char *db,
+mysql_alter_db_internal(THD *thd, const LEX_CSTRING *db,
Schema_specification_st *create_info)
{
char path[FN_REFLEN+16];
@@ -722,7 +715,7 @@ mysql_alter_db_internal(THD *thd, const char *db,
int error= 0;
DBUG_ENTER("mysql_alter_db");
- if (lock_schema_name(thd, db))
+ if (lock_schema_name(thd, db->str))
DBUG_RETURN(TRUE);
/*
@@ -730,13 +723,13 @@ mysql_alter_db_internal(THD *thd, const char *db,
We pass MY_DB_OPT_FILE as "extension" to avoid
"table name to file name" encoding.
*/
- build_table_filename(path, sizeof(path) - 1, db, "", MY_DB_OPT_FILE, 0);
- if ((error=write_db_opt(thd, path, create_info)))
+ build_table_filename(path, sizeof(path) - 1, db->str, "", MY_DB_OPT_FILE, 0);
+ if (unlikely((error=write_db_opt(thd, path, create_info))))
goto exit;
/* Change options if current database is being altered. */
- if (thd->db && !strcmp(thd->db,db))
+ if (thd->db.str && !cmp(&thd->db, db))
{
thd->db_charset= create_info->default_table_charset ?
create_info->default_table_charset :
@@ -754,14 +747,14 @@ mysql_alter_db_internal(THD *thd, const char *db,
database" and not the threads current database, which is the
default.
*/
- qinfo.db = db;
- qinfo.db_len = strlen(db);
+ qinfo.db= db->str;
+ qinfo.db_len= (uint)db->length;
/*
These DDL methods and logging are protected with the exclusive
metadata lock on the schema.
*/
- if ((error= mysql_bin_log.write(&qinfo)))
+ if (unlikely((error= mysql_bin_log.write(&qinfo))))
goto exit;
}
my_ok(thd, result);
@@ -771,7 +764,7 @@ exit:
}
-int mysql_create_db(THD *thd, char *db, DDL_options_st options,
+int mysql_create_db(THD *thd, const LEX_CSTRING *db, DDL_options_st options,
const Schema_specification_st *create_info)
{
/*
@@ -786,7 +779,7 @@ int mysql_create_db(THD *thd, char *db, DDL_options_st options,
}
-bool mysql_alter_db(THD *thd, const char *db,
+bool mysql_alter_db(THD *thd, const LEX_CSTRING *db,
const Schema_specification_st *create_info)
{
/*
@@ -814,7 +807,7 @@ bool mysql_alter_db(THD *thd, const char *db,
*/
static bool
-mysql_rm_db_internal(THD *thd,char *db, bool if_exists, bool silent)
+mysql_rm_db_internal(THD *thd, const LEX_CSTRING *db, bool if_exists, bool silent)
{
ulong deleted_tables= 0;
bool error= true, rm_mysql_schema;
@@ -826,20 +819,13 @@ mysql_rm_db_internal(THD *thd,char *db, bool if_exists, bool silent)
Drop_table_error_handler err_handler;
DBUG_ENTER("mysql_rm_db");
- char db_tmp[SAFE_NAME_LEN], *dbnorm;
- if (lower_case_table_names)
- {
- strmake_buf(db_tmp, db);
- my_casedn_str(system_charset_info, db_tmp);
- dbnorm= db_tmp;
- }
- else
- dbnorm= db;
+ char db_tmp[SAFE_NAME_LEN+1];
+ const char *dbnorm= normalize_db_name(db->str, db_tmp, sizeof(db_tmp));
if (lock_schema_name(thd, dbnorm))
DBUG_RETURN(true);
- length= build_table_filename(path, sizeof(path) - 1, db, "", "", 0);
+ length= build_table_filename(path, sizeof(path) - 1, db->str, "", "", 0);
strmov(path+length, MY_DB_OPT_FILE); // Append db option file name
del_dbopt(path); // Remove dboption hash entry
/*
@@ -862,14 +848,14 @@ mysql_rm_db_internal(THD *thd,char *db, bool if_exists, bool silent)
{
if (!if_exists)
{
- my_error(ER_DB_DROP_EXISTS, MYF(0), db);
+ my_error(ER_DB_DROP_EXISTS, MYF(0), db->str);
DBUG_RETURN(true);
}
else
{
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_DB_DROP_EXISTS, ER_THD(thd, ER_DB_DROP_EXISTS),
- db);
+ db->str);
error= false;
goto update_binlog;
}
@@ -883,7 +869,7 @@ mysql_rm_db_internal(THD *thd,char *db, bool if_exists, bool silent)
This check is only needed if we are dropping the "mysql" database.
*/
if ((rm_mysql_schema=
- (my_strcasecmp(system_charset_info, MYSQL_SCHEMA_NAME.str, db) == 0)))
+ (my_strcasecmp(system_charset_info, MYSQL_SCHEMA_NAME.str, db->str) == 0)))
{
for (table= tables; table; table= table->next_local)
if (check_if_log_table(table, TRUE, "DROP"))
@@ -900,11 +886,9 @@ mysql_rm_db_internal(THD *thd,char *db, bool if_exists, bool silent)
{
for (table= tables; table; table= table->next_local)
{
- LEX_STRING db_name= { table->db, table->db_length };
- LEX_STRING table_name= { table->table_name, table->table_name_length };
if (table->open_type == OT_BASE_ONLY ||
!thd->find_temporary_table(table))
- (void) delete_statistics_for_table(thd, &db_name, &table_name);
+ (void) delete_statistics_for_table(thd, &table->db, &table->table_name);
}
}
@@ -918,7 +902,8 @@ mysql_rm_db_internal(THD *thd,char *db, bool if_exists, bool silent)
thd->push_internal_handler(&err_handler);
if (!thd->killed &&
!(tables &&
- mysql_rm_table_no_locks(thd, tables, true, false, true, true, false)))
+ mysql_rm_table_no_locks(thd, tables, true, false, true, false, true,
+ false)))
{
/*
We temporarily disable the binary log while dropping the objects
@@ -955,7 +940,7 @@ mysql_rm_db_internal(THD *thd,char *db, bool if_exists, bool silent)
thd->pop_internal_handler();
update_binlog:
- if (!silent && !error)
+ if (!silent && likely(!error))
{
const char *query;
ulong query_length;
@@ -974,8 +959,8 @@ update_binlog:
database" and not the threads current database, which is the
default.
*/
- qinfo.db = db;
- qinfo.db_len = strlen(db);
+ qinfo.db = db->str;
+ qinfo.db_len = (uint32)db->length;
/*
These DDL methods and logging are protected with the exclusive
@@ -995,32 +980,31 @@ update_binlog:
{
char *query, *query_pos, *query_end, *query_data_start;
TABLE_LIST *tbl;
- uint db_len;
if (!(query= (char*) thd->alloc(MAX_DROP_TABLE_Q_LEN)))
goto exit; /* not much else we can do */
query_pos= query_data_start= strmov(query,"DROP TABLE IF EXISTS ");
query_end= query + MAX_DROP_TABLE_Q_LEN;
- db_len= strlen(db);
for (tbl= tables; tbl; tbl= tbl->next_local)
{
- uint tbl_name_len;
+ size_t tbl_name_len;
char quoted_name[FN_REFLEN+3];
// Only write drop table to the binlog for tables that no longer exist.
- if (ha_table_exists(thd, tbl->db, tbl->table_name))
+ if (ha_table_exists(thd, &tbl->db, &tbl->table_name))
continue;
- my_snprintf(quoted_name, sizeof(quoted_name), "%`s", tbl->table_name);
- tbl_name_len= strlen(quoted_name) + 1; /* +1 for the comma */
+ tbl_name_len= my_snprintf(quoted_name, sizeof(quoted_name), "%`s",
+ tbl->table_name.str);
+ tbl_name_len++; /* +1 for the comma */
if (query_pos + tbl_name_len + 1 >= query_end)
{
/*
These DDL methods and logging are protected with the exclusive
metadata lock on the schema.
*/
- if (write_to_binlog(thd, query, (uint)(query_pos -1 - query), db, db_len))
+ if (write_to_binlog(thd, query, (uint)(query_pos -1 - query), db->str, db->length))
{
error= true;
goto exit;
@@ -1038,7 +1022,7 @@ update_binlog:
These DDL methods and logging are protected with the exclusive
metadata lock on the schema.
*/
- if (write_to_binlog(thd, query, (uint)(query_pos -1 - query), db, db_len))
+ if (write_to_binlog(thd, query, (uint)(query_pos -1 - query), db->str, db->length))
{
error= true;
goto exit;
@@ -1053,7 +1037,7 @@ exit:
SELECT DATABASE() in the future). For this we free() thd->db and set
it to 0.
*/
- if (thd->db && cmp_db_names(thd->db, db) && !error)
+ 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);
@@ -1063,7 +1047,7 @@ exit:
}
-bool mysql_rm_db(THD *thd,char *db, bool if_exists)
+bool mysql_rm_db(THD *thd, const LEX_CSTRING *db, bool if_exists)
{
if (thd->slave_thread &&
slave_ddl_exec_mode_options == SLAVE_EXEC_MODE_IDEMPOTENT)
@@ -1073,18 +1057,18 @@ bool mysql_rm_db(THD *thd,char *db, bool if_exists)
static bool find_db_tables_and_rm_known_files(THD *thd, MY_DIR *dirp,
- char *dbname,
+ const char *dbname,
const char *path,
TABLE_LIST **tables)
{
char filePath[FN_REFLEN];
- LEX_STRING db= { dbname, strlen(dbname) };
+ LEX_CSTRING db= { dbname, strlen(dbname) };
TABLE_LIST *tot_list=0, **tot_list_next_local, **tot_list_next_global;
DBUG_ENTER("find_db_tables_and_rm_known_files");
DBUG_PRINT("enter",("path: %s", path));
/* first, get the list of tables */
- Dynamic_array<LEX_STRING*> files(dirp->number_of_files);
+ Dynamic_array<LEX_CSTRING*> files(dirp->number_of_files);
Discovered_table_list tl(thd, &files);
if (ha_discover_table_names(thd, &db, dirp, &tl, true))
DBUG_RETURN(1);
@@ -1094,17 +1078,15 @@ static bool find_db_tables_and_rm_known_files(THD *thd, MY_DIR *dirp,
for (size_t idx=0; idx < files.elements(); idx++)
{
- LEX_STRING *table= files.at(idx);
+ LEX_CSTRING *table= files.at(idx);
/* Drop the table nicely */
TABLE_LIST *table_list=(TABLE_LIST*)thd->calloc(sizeof(*table_list));
if (!table_list)
DBUG_RETURN(true);
- table_list->db= db.str;
- table_list->db_length= db.length;
- table_list->table_name= table->str;
- table_list->table_name_length= table->length;
+ table_list->db= db;
+ table_list->table_name= *table;
table_list->open_type= OT_BASE_ONLY;
/*
@@ -1113,12 +1095,12 @@ static bool find_db_tables_and_rm_known_files(THD *thd, MY_DIR *dirp,
as well to look up the cache properly.
*/
if (lower_case_file_system)
- table_list->table_name_length= my_casedn_str(files_charset_info,
- table_list->table_name);
+ table_list->table_name.length= my_casedn_str(files_charset_info,
+ (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,
- table_list->table_name, MDL_EXCLUSIVE,
+ table_list->mdl_request.init(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;
@@ -1158,7 +1140,7 @@ static bool find_db_tables_and_rm_known_files(THD *thd, MY_DIR *dirp,
}
if (!(extension= strrchr(file->name, '.')))
extension= strend(file->name);
- if (find_type(extension, &deletable_extentions, FIND_TYPE_NO_PREFIX) > 0)
+ if (find_type(extension, &deletable_extensions, FIND_TYPE_NO_PREFIX) > 0)
{
strxmov(filePath, path, "/", file->name, NullS);
/*
@@ -1205,9 +1187,9 @@ static my_bool rm_dir_w_symlink(const char *org_path, my_bool send_error)
if (pos > path && pos[-1] == FN_LIBCHAR)
*--pos=0;
- if ((error= my_readlink(tmp2_path, path, MYF(MY_WME))) < 0)
+ if (unlikely((error= my_readlink(tmp2_path, path, MYF(MY_WME))) < 0))
DBUG_RETURN(1);
- if (!error)
+ if (likely(!error))
{
if (mysql_file_delete(key_file_misc, path, MYF(send_error ? MY_WME : 0)))
{
@@ -1222,7 +1204,7 @@ static my_bool rm_dir_w_symlink(const char *org_path, my_bool send_error)
if (pos > path && pos[-1] == FN_LIBCHAR)
*--pos=0;
- if (rmdir(path) < 0 && send_error)
+ if (unlikely(rmdir(path) < 0 && send_error))
{
my_error(ER_DB_DROP_RMDIR, MYF(0), path, errno);
DBUG_RETURN(1);
@@ -1320,7 +1302,7 @@ err:
*/
static void mysql_change_db_impl(THD *thd,
- LEX_STRING *new_db_name,
+ LEX_CSTRING *new_db_name,
ulong new_db_access,
CHARSET_INFO *new_db_charset)
{
@@ -1333,16 +1315,16 @@ static void mysql_change_db_impl(THD *thd,
sets the new one.
*/
- thd->set_db(NULL, 0);
+ thd->set_db(&null_clex_str);
}
- else if (new_db_name == &INFORMATION_SCHEMA_NAME)
+ else if (new_db_name->str == INFORMATION_SCHEMA_NAME.str)
{
/*
Here we must use THD::set_db(), because we want to copy
INFORMATION_SCHEMA_NAME constant.
*/
- thd->set_db(INFORMATION_SCHEMA_NAME.str, INFORMATION_SCHEMA_NAME.length);
+ thd->set_db(&INFORMATION_SCHEMA_NAME);
}
else
{
@@ -1351,8 +1333,8 @@ static void mysql_change_db_impl(THD *thd,
we just call THD::reset_db(). Since THD::reset_db() does not releases
the previous database name, we should do it explicitly.
*/
- thd->set_db(NULL, 0);
- thd->reset_db(new_db_name->str, new_db_name->length);
+ thd->set_db(&null_clex_str);
+ thd->reset_db(new_db_name);
}
/* 2. Update security context. */
@@ -1387,17 +1369,17 @@ static void mysql_change_db_impl(THD *thd,
static void backup_current_db_name(THD *thd,
LEX_STRING *saved_db_name)
{
- if (!thd->db)
+ DBUG_ASSERT(saved_db_name->length >= SAFE_NAME_LEN +1);
+ if (!thd->db.str)
{
/* No current (default) database selected. */
-
- saved_db_name->str= NULL;
+ saved_db_name->str= 0;
saved_db_name->length= 0;
}
else
{
- strmake(saved_db_name->str, thd->db, saved_db_name->length - 1);
- saved_db_name->length= thd->db_length;
+ memcpy(saved_db_name->str, thd->db.str, thd->db.length + 1);
+ saved_db_name->length= thd->db.length;
}
}
@@ -1464,9 +1446,10 @@ static void backup_current_db_name(THD *thd,
@retval >0 Error
*/
-uint mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
+uint mysql_change_db(THD *thd, const LEX_CSTRING *new_db_name,
+ bool force_switch)
{
- LEX_STRING new_db_file_name;
+ LEX_CSTRING new_db_file_name;
Security_context *sctx= thd->security_ctx;
ulong db_access= sctx->db_access;
@@ -1499,7 +1482,7 @@ uint mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
}
DBUG_PRINT("enter",("name: '%s'", new_db_name->str));
- if (is_infoschema_db(new_db_name->str, new_db_name->length))
+ if (is_infoschema_db(new_db_name))
{
/* Switch the current database to INFORMATION_SCHEMA. */
@@ -1529,12 +1512,13 @@ uint mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
It's next to impossible however to get this error when we are called
from sp_head::execute(). But let's switch the current database to NULL
in this case to be sure.
+ The cast below ok here as new_db_file_name was just allocated
*/
- if (check_db_name(&new_db_file_name))
+ if (check_db_name((LEX_STRING*) &new_db_file_name))
{
my_error(ER_WRONG_DB_NAME, MYF(0), new_db_file_name.str);
- my_free(new_db_file_name.str);
+ my_free(const_cast<char*>(new_db_file_name.str));
if (force_switch)
mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server);
@@ -1568,7 +1552,7 @@ uint mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
new_db_file_name.str);
general_log_print(thd, COM_INIT_DB, ER_THD(thd, ER_DBACCESS_DENIED_ERROR),
sctx->priv_user, sctx->priv_host, new_db_file_name.str);
- my_free(new_db_file_name.str);
+ my_free(const_cast<char*>(new_db_file_name.str));
DBUG_RETURN(ER_DBACCESS_DENIED_ERROR);
}
#endif
@@ -1585,7 +1569,7 @@ uint mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
ER_BAD_DB_ERROR, ER_THD(thd, ER_BAD_DB_ERROR),
new_db_file_name.str);
- my_free(new_db_file_name.str);
+ my_free(const_cast<char*>(new_db_file_name.str));
/* Change db to NULL. */
@@ -1599,7 +1583,7 @@ uint mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
/* Report an error and free new_db_file_name. */
my_error(ER_BAD_DB_ERROR, MYF(0), new_db_file_name.str);
- my_free(new_db_file_name.str);
+ my_free(const_cast<char*>(new_db_file_name.str));
/* The operation failed. */
@@ -1644,12 +1628,12 @@ done:
*/
bool mysql_opt_change_db(THD *thd,
- const LEX_STRING *new_db_name,
+ const LEX_CSTRING *new_db_name,
LEX_STRING *saved_db_name,
bool force_switch,
bool *cur_db_changed)
{
- *cur_db_changed= !cmp_db_names(thd->db, new_db_name->str);
+ *cur_db_changed= !cmp_db_names(&thd->db, new_db_name);
if (!*cur_db_changed)
return FALSE;
@@ -1674,16 +1658,17 @@ bool mysql_opt_change_db(THD *thd,
@param old_db 5.0 database name, in #mysql50#name format
@return 0 on success, 1 on error
*/
-bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db)
+
+bool mysql_upgrade_db(THD *thd, const LEX_CSTRING *old_db)
{
- int error= 0, change_to_newdb= 0;
+ bool error= 0, change_to_newdb= 0;
char path[FN_REFLEN+16];
uint length;
Schema_specification_st create_info;
MY_DIR *dirp;
TABLE_LIST *table_list;
SELECT_LEX *sl= thd->lex->current_select;
- LEX_STRING new_db;
+ LEX_CSTRING new_db;
DBUG_ENTER("mysql_upgrade_db");
if ((old_db->length <= MYSQL50_TABLE_NAME_PREFIX_LENGTH) ||
@@ -1709,7 +1694,7 @@ bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db)
Let's remember if we should do "USE newdb" afterwards.
thd->db will be cleared in mysql_rename_db()
*/
- if (thd->db && !strcmp(thd->db, old_db->str))
+ if (thd->db.str && !cmp(&thd->db, old_db))
change_to_newdb= 1;
build_table_filename(path, sizeof(path)-1,
@@ -1720,15 +1705,16 @@ bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db)
length= build_table_filename(path, sizeof(path)-1, old_db->str, "", "", 0);
if (length && path[length-1] == FN_LIBCHAR)
path[length-1]=0; // remove ending '\'
- if ((error= my_access(path,F_OK)))
+ if (unlikely((error= my_access(path,F_OK))))
{
my_error(ER_BAD_DB_ERROR, MYF(0), old_db->str);
goto exit;
}
/* Step1: Create the new database */
- if ((error= mysql_create_db_internal(thd, new_db.str,
- DDL_options(), &create_info, 1)))
+ if (unlikely((error= mysql_create_db_internal(thd, &new_db,
+ DDL_options(), &create_info,
+ 1))))
goto exit;
/* Step2: Move tables to the new database */
@@ -1739,12 +1725,11 @@ bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db)
{
FILEINFO *file= dirp->dir_entry + idx;
char *extension, tname[FN_REFLEN + 1];
- LEX_STRING table_str;
+ LEX_CSTRING table_str;
DBUG_PRINT("info",("Examining: %s", file->name));
/* skiping non-FRM files */
- if (my_strcasecmp(files_charset_info,
- (extension= fn_rext(file->name)), reg_ext))
+ if (!(extension= (char*) fn_frm_ext(file->name)))
continue;
/* A frm file found, add the table info rename list */
@@ -1753,8 +1738,8 @@ bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db)
table_str.length= filename_to_tablename(file->name,
tname, sizeof(tname)-1);
table_str.str= (char*) thd->memdup(tname, table_str.length + 1);
- Table_ident *old_ident= new Table_ident(thd, *old_db, table_str, 0);
- Table_ident *new_ident= new Table_ident(thd, new_db, table_str, 0);
+ Table_ident *old_ident= new Table_ident(thd, old_db, &table_str, 0);
+ Table_ident *new_ident= new Table_ident(thd, &new_db, &table_str, 0);
if (!old_ident || !new_ident ||
!sl->add_table_to_list(thd, old_ident, NULL,
TL_OPTION_UPDATING, TL_IGNORE,
@@ -1852,7 +1837,7 @@ bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db)
to execute them again.
mysql_rm_db() also "unuses" if we drop the current database.
*/
- error= mysql_rm_db_internal(thd, old_db->str, 0, true);
+ error= mysql_rm_db_internal(thd, old_db, 0, true);
/* Step8: logging */
if (mysql_bin_log.is_open())
@@ -1866,7 +1851,7 @@ bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db)
/* Step9: Let's do "use newdb" if we renamed the current database */
if (change_to_newdb)
- error|= mysql_change_db(thd, & new_db, FALSE);
+ error|= mysql_change_db(thd, & new_db, FALSE) != 0;
exit:
DBUG_RETURN(error);
@@ -1901,3 +1886,14 @@ bool check_db_dir_existence(const char *db_name)
return my_access(db_dir_path, F_OK);
}
+
+
+const char *normalize_db_name(const char *db, char *buffer, size_t buffer_size)
+{
+ DBUG_ASSERT(buffer_size > 1);
+ if (!lower_case_table_names)
+ return db;
+ strmake(buffer, db, buffer_size - 1);
+ my_casedn_str(system_charset_info, buffer);
+ return buffer;
+}
diff --git a/sql/sql_db.h b/sql/sql_db.h
index 5841de63514..c9f1ed068e6 100644
--- a/sql/sql_db.h
+++ b/sql/sql_db.h
@@ -20,17 +20,17 @@
class THD;
-int mysql_create_db(THD *thd, char *db, DDL_options_st options,
+int mysql_create_db(THD *thd, const LEX_CSTRING *db, DDL_options_st options,
const Schema_specification_st *create);
-bool mysql_alter_db(THD *thd, const char *db,
+bool mysql_alter_db(THD *thd, const LEX_CSTRING *db,
const Schema_specification_st *create);
-bool mysql_rm_db(THD *thd, char *db, bool if_exists);
-bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db);
-uint mysql_change_db(THD *thd, const LEX_STRING *new_db_name,
+bool mysql_rm_db(THD *thd, const LEX_CSTRING *db, bool if_exists);
+bool mysql_upgrade_db(THD *thd, const LEX_CSTRING *old_db);
+uint mysql_change_db(THD *thd, const LEX_CSTRING *new_db_name,
bool force_switch);
bool mysql_opt_change_db(THD *thd,
- const LEX_STRING *new_db_name,
+ const LEX_CSTRING *new_db_name,
LEX_STRING *saved_db_name,
bool force_switch,
bool *cur_db_changed);
@@ -44,6 +44,9 @@ CHARSET_INFO *get_default_db_collation(THD *thd, const char *db_name);
bool my_dbopt_init(void);
void my_dbopt_cleanup(void);
+const char *normalize_db_name(const char *db, char *buffer,
+ size_t buffer_size);
+
#define MY_DB_OPT_FILE "db.opt"
#endif /* SQL_DB_INCLUDED */
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index e2d4cd47580..929455977e6 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -21,7 +21,7 @@
Multi-table deletes were introduced by Monty and Sinisa
*/
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#include "sql_delete.h"
@@ -45,6 +45,8 @@
// end_read_record
#include "sql_partition.h" // make_used_partitions_str
+#define MEM_STRIP_BUF_SIZE ((size_t) thd->variables.sortbuff_size)
+
/*
@brief
Print query plan of a single-table DELETE command
@@ -59,6 +61,8 @@ Explain_delete* Delete_plan::save_explain_delete_data(MEM_ROOT *mem_root, THD *t
Explain_query *query= thd->lex->explain;
Explain_delete *explain=
new (mem_root) Explain_delete(mem_root, thd->lex->analyze_stmt);
+ if (!explain)
+ return 0;
if (deleting_all_rows)
{
@@ -69,8 +73,9 @@ Explain_delete* Delete_plan::save_explain_delete_data(MEM_ROOT *mem_root, THD *t
else
{
explain->deleting_all_rows= false;
- Update_plan::save_explain_data_intern(mem_root, explain,
- thd->lex->analyze_stmt);
+ if (Update_plan::save_explain_data_intern(mem_root, explain,
+ thd->lex->analyze_stmt))
+ return 0;
}
query->add_upd_del_plan(explain);
@@ -84,18 +89,21 @@ Update_plan::save_explain_update_data(MEM_ROOT *mem_root, THD *thd)
Explain_query *query= thd->lex->explain;
Explain_update* explain=
new (mem_root) Explain_update(mem_root, thd->lex->analyze_stmt);
- save_explain_data_intern(mem_root, explain, thd->lex->analyze_stmt);
+ if (!explain)
+ return 0;
+ if (save_explain_data_intern(mem_root, explain, thd->lex->analyze_stmt))
+ return 0;
query->add_upd_del_plan(explain);
return explain;
}
-void Update_plan::save_explain_data_intern(MEM_ROOT *mem_root,
+bool Update_plan::save_explain_data_intern(MEM_ROOT *mem_root,
Explain_update *explain,
bool is_analyze)
{
explain->select_type= "SIMPLE";
- explain->table_name.append(table->pos_in_table_list->alias);
+ explain->table_name.append(&table->pos_in_table_list->alias);
explain->impossible_where= false;
explain->no_partitions= false;
@@ -103,13 +111,13 @@ void Update_plan::save_explain_data_intern(MEM_ROOT *mem_root,
if (impossible_where)
{
explain->impossible_where= true;
- return;
+ return 0;
}
if (no_partitions)
{
explain->no_partitions= true;
- return;
+ return 0;
}
if (is_analyze)
@@ -160,7 +168,8 @@ void Update_plan::save_explain_data_intern(MEM_ROOT *mem_root,
explain->where_cond= select? select->cond: NULL;
if (using_filesort)
- explain->filesort_tracker= new (mem_root) Filesort_tracker(is_analyze);
+ if (!(explain->filesort_tracker= new (mem_root) Filesort_tracker(is_analyze)))
+ return 1;
explain->using_io_buffer= using_io_buffer;
append_possible_keys(mem_root, explain->possible_keys, table,
@@ -209,6 +218,35 @@ void Update_plan::save_explain_data_intern(MEM_ROOT *mem_root,
if (!(unit->item && unit->item->eliminated))
explain->add_child(unit->first_select()->select_number);
}
+ return 0;
+}
+
+
+static bool record_should_be_deleted(THD *thd, TABLE *table, SQL_SELECT *sel,
+ Explain_delete *explain, bool truncate_history)
+{
+ explain->tracker.on_record_read();
+ thd->inc_examined_row_count(1);
+ if (table->vfield)
+ (void) table->update_virtual_fields(table->file, VCOL_UPDATE_FOR_DELETE);
+ if (!sel || sel->skip_record(thd) > 0)
+ {
+ explain->tracker.on_record_after_where();
+ return true;
+ }
+ return false;
+}
+
+
+inline
+int TABLE::delete_row()
+{
+ if (!versioned(VERS_TIMESTAMP) || !vers_end_field()->is_max())
+ return file->ha_delete_row(record[0]);
+
+ store_record(this, record[1]);
+ vers_update_end();
+ return file->ha_update_row(record[1], record[0]);
}
@@ -224,7 +262,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
SQL_I_List<ORDER> *order_list, ha_rows limit,
ulonglong options, select_result *result)
{
- bool will_batch;
+ bool will_batch= FALSE;
int error, loc_error;
TABLE *table;
SQL_SELECT *select=0;
@@ -236,22 +274,31 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
bool return_error= 0;
ha_rows deleted= 0;
bool reverse= FALSE;
+ bool has_triggers;
ORDER *order= (ORDER *) ((order_list && order_list->elements) ?
order_list->first : NULL);
SELECT_LEX *select_lex= &thd->lex->select_lex;
killed_state killed_status= NOT_KILLED;
THD::enum_binlog_query_type query_type= THD::ROW_QUERY_TYPE;
+ bool 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;
+ bool delete_record, delete_while_scanning;
+ DBUG_ENTER("mysql_delete");
+
query_plan.index= MAX_KEY;
query_plan.using_filesort= FALSE;
- DBUG_ENTER("mysql_delete");
create_explain_query(thd->lex, thd->mem_root);
if (open_and_lock_tables(thd, table_list, TRUE, 0))
DBUG_RETURN(TRUE);
+ THD_STAGE_INFO(thd, stage_init_update);
+
+ const bool delete_history= table_list->vers_conditions.delete_history;
+
if (thd->lex->handle_list_of_derived(table_list, DT_MERGE_FOR_INSERT))
DBUG_RETURN(TRUE);
if (thd->lex->handle_list_of_derived(table_list, DT_PREPARE))
@@ -259,7 +306,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (!table_list->single_table_updatable())
{
- my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "DELETE");
+ my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "DELETE");
DBUG_RETURN(TRUE);
}
if (!(table= table_list->table) || !table->is_created())
@@ -268,16 +315,19 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
table_list->view_db.str, table_list->view_name.str);
DBUG_RETURN(TRUE);
}
- THD_STAGE_INFO(thd, stage_init);
table->map=1;
query_plan.select_lex= &thd->lex->select_lex;
query_plan.table= table;
query_plan.updating_a_view= MY_TEST(table_list->view);
if (mysql_prepare_delete(thd, table_list, select_lex->with_wild,
- select_lex->item_list, &conds))
+ select_lex->item_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);
@@ -320,7 +370,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
}
const_cond_result= const_cond && (!conds || conds->val_int());
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
{
/* Error evaluating val_int(). */
DBUG_RETURN(TRUE);
@@ -341,9 +391,13 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
- We should not be binlogging this statement in row-based, and
- there should be no delete triggers associated with the table.
*/
+
+ has_triggers= (table->triggers &&
+ table->triggers->has_delete_triggers());
if (!with_select && !using_limit && const_cond_result &&
(!thd->is_current_stmt_binlog_format_row() &&
- !(table->triggers && table->triggers->has_delete_triggers())))
+ !has_triggers)
+ && !table->versioned(VERS_TIMESTAMP))
{
/* Update the table->file->stats.records number */
table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
@@ -354,7 +408,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (thd->lex->describe)
goto produce_explain_and_leave;
- if (!(error=table->file->ha_delete_all_rows()))
+ if (likely(!(error=table->file->ha_delete_all_rows())))
{
/*
If delete_all_rows() is used, it is not possible to log the
@@ -363,7 +417,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
query_type= THD::STMT_QUERY_TYPE;
error= -1;
deleted= maybe_deleted;
- query_plan.save_explain_delete_data(thd->mem_root, thd);
+ if (!query_plan.save_explain_delete_data(thd->mem_root, thd))
+ error= 1;
goto cleanup;
}
if (error != HA_ERR_WRONG_COMMAND)
@@ -409,7 +464,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
table->quick_keys.clear_all(); // Can't use 'only index'
select=make_select(table, 0, 0, conds, (SORT_INFO*) 0, 0, &error);
- if (error)
+ if (unlikely(error))
DBUG_RETURN(TRUE);
if ((select && select->check_quick(thd, safe_update, limit)) || !limit)
{
@@ -425,7 +480,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
Currently they rely on the user checking DA for
errors when unwinding the stack after calling Item::val_xxx().
*/
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
DBUG_RETURN(TRUE);
my_ok(thd, 0);
DBUG_RETURN(0); // Nothing to delete
@@ -481,7 +536,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (thd->lex->describe)
goto produce_explain_and_leave;
- explain= query_plan.save_explain_delete_data(thd->mem_root, thd);
+ if (!(explain= query_plan.save_explain_delete_data(thd->mem_root, thd)))
+ goto got_error;
ANALYZE_START_TRACKING(&explain->command_tracker);
DBUG_EXECUTE_IF("show_explain_probe_delete_exec_start",
@@ -490,14 +546,59 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (!(select && select->quick))
status_var_increment(thd->status_var.delete_scan_count);
- if (query_plan.using_filesort)
+ binlog_is_row= thd->is_current_stmt_binlog_format_row();
+ DBUG_PRINT("info", ("binlog_is_row: %s", binlog_is_row ? "TRUE" : "FALSE"));
+
+ /*
+ We can use direct delete (delete that is done silently in the handler)
+ if none of the following conditions are true:
+ - There are triggers
+ - There is binary logging
+ - There is a virtual not stored column in the WHERE clause
+ - ORDER BY or LIMIT
+ - As this requires the rows to be deleted in a specific order
+ - Note that Spider can handle ORDER BY and LIMIT in a cluster with
+ one data node. These conditions are therefore checked in
+ direct_delete_rows_init().
+
+ Direct delete does not require a WHERE clause
+
+ Later we also ensure that we are only using one table (no sub queries)
+ */
+
+ if ((table->file->ha_table_flags() & HA_CAN_DIRECT_UPDATE_AND_DELETE) &&
+ !has_triggers && !binlog_is_row && !with_select)
{
+ table->mark_columns_needed_for_delete();
+ if (!table->check_virtual_columns_marked_for_read())
+ {
+ DBUG_PRINT("info", ("Trying direct delete"));
+ if (select && select->cond &&
+ (select->cond->used_tables() == table->map))
+ {
+ DBUG_ASSERT(!table->file->pushed_cond);
+ if (!table->file->cond_push(select->cond))
+ table->file->pushed_cond= select->cond;
+ }
+ if (!table->file->direct_delete_rows_init())
+ {
+ /* Direct deleting is supported */
+ DBUG_PRINT("info", ("Using direct delete"));
+ THD_STAGE_INFO(thd, stage_updating);
+ if (!(error= table->file->ha_direct_delete_rows(&deleted)))
+ error= -1;
+ goto terminate_delete;
+ }
+ }
+ }
+ if (query_plan.using_filesort)
+ {
{
Filesort fsort(order, HA_POS_ERROR, true, select);
DBUG_ASSERT(query_plan.index == MAX_KEY);
- Filesort_tracker *fs_tracker=
+ Filesort_tracker *fs_tracker=
thd->lex->explain->get_upd_del_plan()->filesort_tracker;
if (!(file_sort= filesort(thd, table, &fsort, fs_tracker)))
@@ -530,43 +631,79 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
else
error= init_read_record_idx(&info, thd, table, 1, query_plan.index,
reverse);
- if (error)
+ if (unlikely(error))
goto got_error;
- init_ftfuncs(thd, select_lex, 1);
- THD_STAGE_INFO(thd, stage_updating);
-
- if (table->prepare_triggers_for_delete_stmt_or_event())
- {
- will_batch= FALSE;
- }
- else
- will_batch= !table->file->start_bulk_delete();
+ if (unlikely(init_ftfuncs(thd, select_lex, 1)))
+ goto got_error;
table->mark_columns_needed_for_delete();
+ if ((table->file->ha_table_flags() & HA_CAN_FORCE_BULK_DELETE) &&
+ !table->prepare_triggers_for_delete_stmt_or_event())
+ will_batch= !table->file->start_bulk_delete();
+
if (with_select)
{
- if (result->send_result_set_metadata(select_lex->item_list,
- Protocol::SEND_NUM_ROWS |
- Protocol::SEND_EOF))
+ if (unlikely(result->send_result_set_metadata(select_lex->item_list,
+ Protocol::SEND_NUM_ROWS |
+ Protocol::SEND_EOF)))
goto cleanup;
}
explain= (Explain_delete*)thd->lex->explain->get_upd_del_plan();
explain->tracker.on_scan_init();
- while (!(error=info.read_record(&info)) && !thd->killed &&
- ! thd->is_error())
+ if (!delete_while_scanning)
{
- explain->tracker.on_record_read();
- thd->inc_examined_row_count(1);
- if (table->vfield)
- (void) table->update_virtual_fields(table->file, VCOL_UPDATE_FOR_DELETE);
- if (!select || select->skip_record(thd) > 0)
+ /*
+ The table we are going to delete appears in subqueries in the where
+ clause. Instead of deleting the rows, first mark them deleted.
+ */
+ ha_rows tmplimit=limit;
+ deltempfile= new (thd->mem_root) Unique (refpos_order_cmp, table->file,
+ table->file->ref_length,
+ MEM_STRIP_BUF_SIZE);
+
+ THD_STAGE_INFO(thd, stage_searching_rows_for_update);
+ while (!(error=info.read_record()) && !thd->killed &&
+ ! thd->is_error())
{
- explain->tracker.on_record_after_where();
- if (table->triggers &&
+ if (record_should_be_deleted(thd, table, select, explain, delete_history))
+ {
+ table->file->position(table->record[0]);
+ if (unlikely((error=
+ deltempfile->unique_add((char*) table->file->ref))))
+ {
+ error= 1;
+ goto terminate_delete;
+ }
+ if (!--tmplimit && using_limit)
+ break;
+ }
+ }
+ end_read_record(&info);
+ if (unlikely(deltempfile->get(table)) ||
+ unlikely(table->file->ha_index_or_rnd_end()) ||
+ unlikely(init_read_record(&info, thd, table, 0, &deltempfile->sort, 0,
+ 1, false)))
+ {
+ error= 1;
+ goto terminate_delete;
+ }
+ delete_record= true;
+ }
+
+ THD_STAGE_INFO(thd, stage_updating);
+ while (likely(!(error=info.read_record())) && likely(!thd->killed) &&
+ likely(!thd->is_error()))
+ {
+ if (delete_while_scanning)
+ delete_record= record_should_be_deleted(thd, table, select, explain,
+ delete_history);
+ if (delete_record)
+ {
+ if (!delete_history && table->triggers &&
table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
TRG_ACTION_BEFORE, FALSE))
{
@@ -580,10 +717,11 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
break;
}
- if (!(error= table->file->ha_delete_row(table->record[0])))
+ error= table->delete_row();
+ if (likely(!error))
{
deleted++;
- if (table->triggers &&
+ if (!delete_history && table->triggers &&
table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
TRG_ACTION_AFTER, FALSE))
{
@@ -611,15 +749,17 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
Don't try unlocking the row if skip_record reported an error since in
this case the transaction might have been rolled back already.
*/
- else if (!thd->is_error())
+ else if (likely(!thd->is_error()))
table->file->unlock_row(); // Row failed selection, release lock on it
else
break;
}
+
+terminate_delete:
killed_status= thd->killed;
- if (killed_status != NOT_KILLED || thd->is_error())
+ if (unlikely(killed_status != NOT_KILLED || thd->is_error()))
error= 1; // Aborted
- if (will_batch && (loc_error= table->file->end_bulk_delete()))
+ if (will_batch && unlikely((loc_error= table->file->end_bulk_delete())))
{
if (error != 1)
table->file->print_error(loc_error,MYF(0));
@@ -647,6 +787,8 @@ cleanup:
thd->lex->current_select->first_cond_optimization= 0;
}
+ delete deltempfile;
+ deltempfile=NULL;
delete select;
select= NULL;
transactional_table= table->file->has_transactions();
@@ -656,7 +798,7 @@ cleanup:
thd->transaction.all.modified_non_trans_table= TRUE;
/* See similar binlogging code in sql_update.cc, for comments */
- if ((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())
{
@@ -666,6 +808,8 @@ cleanup:
else
errcode= query_error_code(thd, killed_status == NOT_KILLED);
+ ScopedStatementReplication scoped_stmt_rpl(
+ table->versioned(VERS_TRX_ID) ? thd : NULL);
/*
[binlog]: If 'handler::delete_all_rows()' was called and the
storage engine does not inject the rows itself, we replicate
@@ -677,7 +821,7 @@ cleanup:
transactional_table, FALSE, FALSE,
errcode);
- if (log_result)
+ if (log_result > 0)
{
error=1;
}
@@ -685,7 +829,7 @@ cleanup:
}
DBUG_ASSERT(transactional_table || !deleted || thd->transaction.stmt.modified_non_trans_table);
- if (error < 0 ||
+ if (likely(error < 0) ||
(thd->lex->ignore && !thd->is_error() && !thd->is_fatal_error))
{
if (thd->lex->analyze_stmt)
@@ -699,6 +843,8 @@ cleanup:
}
delete file_sort;
free_underlaid_joins(thd, select_lex);
+ if (table->file->pushed_cond)
+ table->file->cond_pop();
DBUG_RETURN(error >= 0 || thd->is_error());
/* Special exits */
@@ -707,7 +853,8 @@ produce_explain_and_leave:
We come here for various "degenerate" query plans: impossible WHERE,
no-partitions-used, impossible-range, etc.
*/
- query_plan.save_explain_delete_data(thd->mem_root, thd);
+ if (!(query_plan.save_explain_delete_data(thd->mem_root, thd)))
+ goto got_error;
send_nothing_and_leave:
/*
@@ -719,7 +866,8 @@ send_nothing_and_leave:
delete select;
delete file_sort;
free_underlaid_joins(thd, select_lex);
- //table->set_keyread(false);
+ if (table->file->pushed_cond)
+ table->file->cond_pop();
DBUG_ASSERT(!return_error || thd->is_error() || thd->killed);
DBUG_RETURN((return_error || thd->is_error() || thd->killed) ? 1 : 0);
@@ -740,26 +888,44 @@ got_error:
wild_num - number of wildcards used in optional SELECT clause
field_list - list of items in optional SELECT clause
conds - conditions
-l
+
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,
+ uint wild_num, List<Item> &field_list, Item **conds,
+ bool *delete_while_scanning)
{
Item *fake_conds= 0;
SELECT_LEX *select_lex= &thd->lex->select_lex;
DBUG_ENTER("mysql_prepare_delete");
List<Item> all_fields;
- thd->lex->allow_sum_func= 0;
+ *delete_while_scanning= true;
+ thd->lex->allow_sum_func.clear_all();
if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context,
&thd->lex->select_lex.top_join_list,
table_list,
select_lex->leaf_tables, FALSE,
DELETE_ACL, SELECT_ACL, TRUE))
DBUG_RETURN(TRUE);
+
+ if (table_list->vers_conditions.is_set() && table_list->is_view_or_derived())
+ {
+ my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str);
+ DBUG_RETURN(true);
+ }
+
+ DBUG_ASSERT(table_list->table);
+ // conds could be cached from previous SP call
+ DBUG_ASSERT(!table_list->vers_conditions.need_setup() ||
+ !*conds || thd->stmt_arena->is_stmt_execute());
+ if (select_lex->vers_setup_conds(thd, table_list))
+ DBUG_RETURN(TRUE);
+
+ *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(),
@@ -770,17 +936,12 @@ l
if (!table_list->single_table_updatable() ||
check_key_in_view(thd, table_list))
{
- my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "DELETE");
+ my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "DELETE");
DBUG_RETURN(TRUE);
}
- {
- TABLE_LIST *duplicate;
- if ((duplicate= unique_table(thd, table_list, table_list->next_global, 0)))
- {
- update_non_unique_table_error(table_list, "DELETE", duplicate);
- DBUG_RETURN(TRUE);
- }
- }
+
+ if (unique_table(thd, table_list, table_list->next_global, 0))
+ *delete_while_scanning= false;
if (select_lex->inner_refs_list.elements &&
fix_inner_refs(thd, all_fields, select_lex, select_lex->ref_pointer_array))
@@ -795,7 +956,6 @@ l
Delete multiple tables from join
***************************************************************************/
-#define MEM_STRIP_BUF_SIZE (size_t)(current_thd->variables.sortbuff_size)
extern "C" int refpos_order_cmp(void* arg, const void *a,const void *b)
{
@@ -868,7 +1028,7 @@ int mysql_multi_delete_prepare(THD *thd)
check_key_in_view(thd, target_tbl->correspondent_table))
{
my_error(ER_NON_UPDATABLE_TABLE, MYF(0),
- target_tbl->table_name, "DELETE");
+ target_tbl->table_name.str, "DELETE");
DBUG_RETURN(TRUE);
}
/*
@@ -941,11 +1101,12 @@ multi_delete::initialize_tables(JOIN *join)
Unique **tempfiles_ptr;
DBUG_ENTER("initialize_tables");
- if ((thd->variables.option_bits & OPTION_SAFE_UPDATES) && error_if_full_join(join))
+ if (unlikely((thd->variables.option_bits & OPTION_SAFE_UPDATES) &&
+ error_if_full_join(join)))
DBUG_RETURN(1);
table_map tables_to_delete_from=0;
- delete_while_scanning= 1;
+ delete_while_scanning= true;
for (walk= delete_tables; walk; walk= walk->next_local)
{
TABLE_LIST *tbl= walk->correspondent_table->find_table_for_update();
@@ -958,7 +1119,7 @@ multi_delete::initialize_tables(JOIN *join)
in join, we need to defer delete. So the delete
doesn't interfers with the scaning of results.
*/
- delete_while_scanning= 0;
+ delete_while_scanning= false;
}
}
@@ -994,7 +1155,7 @@ multi_delete::initialize_tables(JOIN *join)
case send_data() shouldn't delete any rows a we may touch
the rows in the deleted table many times
*/
- delete_while_scanning= 0;
+ delete_while_scanning= false;
}
}
walk= delete_tables;
@@ -1007,13 +1168,12 @@ multi_delete::initialize_tables(JOIN *join)
for (;walk ;walk= walk->next_local)
{
TABLE *table=walk->table;
- *tempfiles_ptr++= new Unique (refpos_order_cmp,
- (void *) table->file,
- table->file->ref_length,
- MEM_STRIP_BUF_SIZE);
+ *tempfiles_ptr++= new (thd->mem_root) Unique (refpos_order_cmp, table->file,
+ table->file->ref_length,
+ MEM_STRIP_BUF_SIZE);
}
init_ftfuncs(thd, thd->lex->current_select, 1);
- DBUG_RETURN(thd->is_fatal_error != 0);
+ DBUG_RETURN(thd->is_fatal_error);
}
@@ -1066,7 +1226,9 @@ int multi_delete::send_data(List<Item> &values)
TRG_ACTION_BEFORE, FALSE))
DBUG_RETURN(1);
table->status|= STATUS_DELETED;
- if (!(error=table->file->ha_delete_row(table->record[0])))
+
+ error= table->delete_row();
+ if (likely(!error))
{
deleted++;
if (!table->file->has_transactions())
@@ -1089,7 +1251,7 @@ int multi_delete::send_data(List<Item> &values)
else
{
error=tempfiles[secure_counter]->unique_add((char*) table->file->ref);
- if (error)
+ if (unlikely(error))
{
error= 1; // Fatal error
DBUG_RETURN(1);
@@ -1185,19 +1347,19 @@ int multi_delete::do_deletes()
{
TABLE *table = table_being_deleted->table;
int local_error;
- if (tempfiles[counter]->get(table))
+ if (unlikely(tempfiles[counter]->get(table)))
DBUG_RETURN(1);
local_error= do_table_deletes(table, &tempfiles[counter]->sort,
thd->lex->ignore);
- if (thd->killed && !local_error)
+ if (unlikely(thd->killed) && likely(!local_error))
DBUG_RETURN(1);
- if (local_error == -1) // End of file
- local_error = 0;
+ if (unlikely(local_error == -1)) // End of file
+ local_error= 0;
- if (local_error)
+ if (unlikely(local_error))
DBUG_RETURN(local_error);
}
DBUG_RETURN(0);
@@ -1227,27 +1389,23 @@ int multi_delete::do_table_deletes(TABLE *table, SORT_INFO *sort_info,
ha_rows last_deleted= deleted;
DBUG_ENTER("do_deletes_for_table");
- if (init_read_record(&info, thd, table, NULL, sort_info, 0, 1, FALSE))
+ if (unlikely(init_read_record(&info, thd, table, NULL, sort_info, 0, 1,
+ FALSE)))
DBUG_RETURN(1);
- /*
- Ignore any rows not found in reference tables as they may already have
- been deleted by foreign key handling
- */
- info.ignore_not_found_rows= 1;
bool will_batch= !table->file->start_bulk_delete();
- while (!(local_error= info.read_record(&info)) && !thd->killed)
+ while (likely(!(local_error= info.read_record())) && likely(!thd->killed))
{
if (table->triggers &&
- table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
- TRG_ACTION_BEFORE, FALSE))
+ unlikely(table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
+ TRG_ACTION_BEFORE, FALSE)))
{
local_error= 1;
break;
}
-
- local_error= table->file->ha_delete_row(table->record[0]);
- if (local_error && !ignore)
+
+ local_error= table->delete_row();
+ if (unlikely(local_error) && !ignore)
{
table->file->print_error(local_error, MYF(0));
break;
@@ -1258,7 +1416,7 @@ int multi_delete::do_table_deletes(TABLE *table, SORT_INFO *sort_info,
during ha_delete_row.
Also, don't execute the AFTER trigger if the row operation failed.
*/
- if (!local_error)
+ if (unlikely(!local_error))
{
deleted++;
if (table->triggers &&
@@ -1273,7 +1431,7 @@ int multi_delete::do_table_deletes(TABLE *table, SORT_INFO *sort_info,
if (will_batch)
{
int tmp_error= table->file->end_bulk_delete();
- if (tmp_error && !local_error)
+ if (unlikely(tmp_error) && !local_error)
{
local_error= tmp_error;
table->file->print_error(local_error, MYF(0));
@@ -1321,29 +1479,31 @@ bool multi_delete::send_eof()
{
query_cache_invalidate3(thd, delete_tables, 1);
}
- if ((local_error == 0) || thd->transaction.stmt.modified_non_trans_table)
+ if (likely((local_error == 0) ||
+ thd->transaction.stmt.modified_non_trans_table))
{
if(WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open())
{
int errcode= 0;
- if (local_error == 0)
+ if (likely(local_error == 0))
thd->clear_error();
else
errcode= query_error_code(thd, killed_status == NOT_KILLED);
thd->thread_specific_used= TRUE;
- if (thd->binlog_query(THD::ROW_QUERY_TYPE,
- thd->query(), thd->query_length(),
- transactional_tables, FALSE, FALSE, errcode) &&
+ if (unlikely(thd->binlog_query(THD::ROW_QUERY_TYPE,
+ thd->query(), thd->query_length(),
+ transactional_tables, FALSE, FALSE,
+ errcode) > 0) &&
!normal_tables)
{
local_error=1; // Log write failed: roll back the SQL statement
}
}
}
- if (local_error != 0)
+ if (unlikely(local_error != 0))
error_handled= TRUE; // to force early leave from ::abort_result_set()
- if (!local_error && !thd->lex->analyze_stmt)
+ if (likely(!local_error && !thd->lex->analyze_stmt))
{
::my_ok(thd, deleted);
}
diff --git a/sql/sql_delete.h b/sql/sql_delete.h
index 5975598f657..7af8564abf9 100644
--- a/sql/sql_delete.h
+++ b/sql/sql_delete.h
@@ -27,7 +27,8 @@ 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);
+ uint wild_num, List<Item> &field_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,
ulonglong options, select_result *result);
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index 39499e6895f..11d3a13b8b9 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -1,6 +1,6 @@
/*
Copyright (c) 2002, 2011, 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
@@ -22,7 +22,7 @@
*/
-#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
#include "unireg.h"
#include "sql_derived.h"
@@ -175,10 +175,11 @@ mysql_handle_single_derived(LEX *lex, TABLE_LIST *derived, uint phases)
DBUG_ENTER("mysql_handle_single_derived");
DBUG_PRINT("enter", ("phases: 0x%x allowed: 0x%x alias: '%s'",
phases, allowed_phases,
- (derived->alias ? derived->alias : "<NULL>")));
+ (derived->alias.str ? derived->alias.str : "<NULL>")));
if (!lex->derived_tables)
DBUG_RETURN(FALSE);
+ derived->select_lex->changed_elements|= TOUCHED_SEL_DERIVED;
lex->thd->derived_tables_processing= TRUE;
for (uint phase= 0; phase < DT_PHASES; phase++)
@@ -336,7 +337,7 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived)
Query_arena *arena, backup;
DBUG_ENTER("mysql_derived_merge");
DBUG_PRINT("enter", ("Alias: '%s' Unit: %p",
- (derived->alias ? derived->alias : "<NULL>"),
+ (derived->alias.str ? derived->alias.str : "<NULL>"),
derived->get_unit()));
if (derived->merged)
@@ -436,9 +437,7 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived)
derived->prep_on_expr= expr->copy_andor_structure(thd);
}
if (derived->on_expr &&
- ((!derived->on_expr->fixed &&
- derived->on_expr->fix_fields(thd, &derived->on_expr)) ||
- derived->on_expr->check_cols(1)))
+ derived->on_expr->fix_fields_if_needed_for_bool(thd, &derived->on_expr))
{
res= TRUE; /* purecov: inspected */
goto exit_merge;
@@ -489,7 +488,7 @@ bool mysql_derived_merge_for_insert(THD *thd, LEX *lex, TABLE_LIST *derived)
{
DBUG_ENTER("mysql_derived_merge_for_insert");
DBUG_PRINT("enter", ("Alias: '%s' Unit: %p",
- (derived->alias ? derived->alias : "<NULL>"),
+ (derived->alias.str ? derived->alias.str : "<NULL>"),
derived->get_unit()));
DBUG_PRINT("info", ("merged_for_insert: %d is_materialized_derived: %d "
"is_multitable: %d single_table_updatable: %d "
@@ -547,7 +546,7 @@ bool mysql_derived_init(THD *thd, LEX *lex, TABLE_LIST *derived)
SELECT_LEX_UNIT *unit= derived->get_unit();
DBUG_ENTER("mysql_derived_init");
DBUG_PRINT("enter", ("Alias: '%s' Unit: %p",
- (derived->alias ? derived->alias : "<NULL>"),
+ (derived->alias.str ? derived->alias.str : "<NULL>"),
derived->get_unit()));
// Skip already prepared views/DT
@@ -622,11 +621,10 @@ bool mysql_derived_init(THD *thd, LEX *lex, TABLE_LIST *derived)
bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
{
SELECT_LEX_UNIT *unit= derived->get_unit();
- DBUG_ENTER("mysql_derived_prepare");
bool res= FALSE;
- DBUG_PRINT("enter", ("Alias: '%s' Unit: %p",
- (derived->alias ? derived->alias : "<NULL>"),
- unit));
+ DBUG_ENTER("mysql_derived_prepare");
+ DBUG_PRINT("enter", ("unit: %p table_list: %p alias: '%s'",
+ unit, derived, derived->alias.str));
if (!unit)
DBUG_RETURN(FALSE);
@@ -646,7 +644,8 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
table reference from a subquery for this.
*/
DBUG_ASSERT(derived->with->get_sq_rec_ref());
- if (mysql_derived_prepare(lex->thd, lex, derived->with->get_sq_rec_ref()))
+ if (unlikely(mysql_derived_prepare(lex->thd, lex,
+ derived->with->get_sq_rec_ref())))
DBUG_RETURN(TRUE);
}
@@ -659,7 +658,7 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
specification has been already prepared (a secondary recursive table
reference.
*/
- if (!(derived->derived_result= new (thd->mem_root) select_union(thd)))
+ if (!(derived->derived_result= new (thd->mem_root) select_unit(thd)))
DBUG_RETURN(TRUE); // out of memory
thd->create_tmp_table_for_derived= TRUE;
res= derived->derived_result->create_result_table(
@@ -667,10 +666,10 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
(first_select->options |
thd->variables.option_bits |
TMP_TABLE_ALL_COLUMNS),
- derived->alias, FALSE, FALSE);
+ &derived->alias, FALSE, FALSE, FALSE, 0);
thd->create_tmp_table_for_derived= FALSE;
- if (!res && !derived->table)
+ if (likely(!res) && !derived->table)
{
derived->derived_result->set_unit(unit);
derived->table= derived->derived_result->table;
@@ -690,7 +689,39 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
!(derived->is_multitable() &&
(thd->lex->sql_command == SQLCOM_UPDATE_MULTI ||
thd->lex->sql_command == SQLCOM_DELETE_MULTI))))
+ {
+ /*
+ System versioned tables may still require to get versioning conditions
+ when modifying view (see vers_setup_conds()). Only UPDATE and DELETE are
+ affected because they use WHERE condition.
+ */
+ if (!unit->prepared &&
+ derived->table->versioned() &&
+ derived->merge_underlying_list &&
+ /* choose only those merged views that do not select from other views */
+ !derived->merge_underlying_list->merge_underlying_list)
+ {
+ switch (thd->lex->sql_command)
+ {
+ case SQLCOM_DELETE:
+ case SQLCOM_DELETE_MULTI:
+ case SQLCOM_UPDATE:
+ case SQLCOM_UPDATE_MULTI:
+ if ((res= first_select->vers_setup_conds(thd,
+ derived->merge_underlying_list)))
+ goto exit;
+ if (derived->merge_underlying_list->where)
+ {
+ Query_arena_stmt on_stmt_arena(thd);
+ derived->where= and_items(thd, derived->where,
+ derived->merge_underlying_list->where);
+ }
+ default:
+ break;
+ }
+ }
DBUG_RETURN(FALSE);
+ }
/* prevent name resolving out of derived table */
for (SELECT_LEX *sl= first_select; sl; sl= sl->next_select())
@@ -719,8 +750,6 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
}
}
- unit->derived= derived;
-
/*
Above cascade call of prepare is important for PS protocol, but after it
is called we can check if we really need prepare for this derived
@@ -733,12 +762,12 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
derived->fill_me= FALSE;
- if (!(derived->derived_result= new (thd->mem_root) select_union(thd)))
+ if (!(derived->derived_result= new (thd->mem_root) select_unit(thd)))
DBUG_RETURN(TRUE); // out of memory
lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_DERIVED;
// st_select_lex_unit::prepare correctly work for single select
- if ((res= unit->prepare(thd, derived->derived_result, 0)))
+ if ((res= unit->prepare(derived, derived->derived_result, 0)))
goto exit;
if (derived->with &&
(res= derived->with->rename_columns_of_derived_unit(thd, unit)))
@@ -761,7 +790,7 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
As 'distinct' parameter we always pass FALSE (0), because underlying
query will control distinct condition by itself. Correct test of
- distinct underlying query will be is_union &&
+ distinct underlying query will be is_unit_op &&
!unit->union_distinct->next_select() (i.e. it is union and last distinct
SELECT is last SELECT of UNION).
*/
@@ -771,8 +800,9 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
(first_select->options |
thd->variables.option_bits |
TMP_TABLE_ALL_COLUMNS),
- derived->alias,
- FALSE, FALSE, FALSE))
+ &derived->alias,
+ FALSE, FALSE, FALSE,
+ 0))
{
thd->create_tmp_table_for_derived= FALSE;
goto exit;
@@ -795,8 +825,8 @@ exit:
thd->get_stmt_da()->sql_errno() == ER_SP_DOES_NOT_EXIST))
{
thd->clear_error();
- my_error(ER_VIEW_INVALID, MYF(0), derived->db,
- derived->table_name);
+ my_error(ER_VIEW_INVALID, MYF(0), derived->db.str,
+ derived->table_name.str);
}
}
@@ -809,7 +839,7 @@ exit:
{
if (!derived->is_with_table_recursive_reference())
{
- if (derived->table)
+ if (derived->table && derived->table->s->tmp_table)
free_tmp_table(thd, derived->table);
delete derived->derived_result;
}
@@ -866,11 +896,10 @@ bool mysql_derived_optimize(THD *thd, LEX *lex, TABLE_LIST *derived)
SELECT_LEX_UNIT *unit= derived->get_unit();
SELECT_LEX *first_select= unit->first_select();
SELECT_LEX *save_current_select= lex->current_select;
-
bool res= FALSE;
DBUG_ENTER("mysql_derived_optimize");
DBUG_PRINT("enter", ("Alias: '%s' Unit: %p",
- (derived->alias ? derived->alias : "<NULL>"),
+ (derived->alias.str ? derived->alias.str : "<NULL>"),
derived->get_unit()));
if (derived->merged)
{
@@ -878,12 +907,12 @@ bool mysql_derived_optimize(THD *thd, LEX *lex, TABLE_LIST *derived)
DBUG_RETURN(FALSE);
}
- if (unit->optimized)
- DBUG_RETURN(FALSE);
lex->current_select= first_select;
- if (unit->is_union())
+ if (unit->is_unit_op())
{
+ if (unit->optimized)
+ DBUG_RETURN(FALSE);
// optimize union without execution
res= unit->optimize();
}
@@ -893,7 +922,20 @@ bool mysql_derived_optimize(THD *thd, LEX *lex, TABLE_LIST *derived)
{
JOIN *join= first_select->join;
unit->set_limit(unit->global_parameters());
- unit->optimized= TRUE;
+ if (join &&
+ join->optimization_state == JOIN::OPTIMIZATION_PHASE_1_DONE &&
+ join->with_two_phase_optimization)
+ {
+ if (unit->optimized_2)
+ DBUG_RETURN(FALSE);
+ unit->optimized_2= TRUE;
+ }
+ else
+ {
+ if (unit->optimized)
+ DBUG_RETURN(FALSE);
+ unit->optimized= TRUE;
+ }
if ((res= join->optimize()))
goto err;
if (join->table_count == join->const_tables)
@@ -943,14 +985,14 @@ bool mysql_derived_create(THD *thd, LEX *lex, TABLE_LIST *derived)
{
DBUG_ENTER("mysql_derived_create");
DBUG_PRINT("enter", ("Alias: '%s' Unit: %p",
- (derived->alias ? derived->alias : "<NULL>"),
+ (derived->alias.str ? derived->alias.str : "<NULL>"),
derived->get_unit()));
TABLE *table= derived->table;
SELECT_LEX_UNIT *unit= derived->get_unit();
if (table->is_created())
DBUG_RETURN(FALSE);
- select_union *result= derived->derived_result;
+ select_unit *result= derived->derived_result;
if (table->s->db_type() == TMP_ENGINE_HTON)
{
result->tmp_table_param.keyinfo= table->s->key_info;
@@ -969,6 +1011,20 @@ bool mysql_derived_create(THD *thd, LEX *lex, TABLE_LIST *derived)
}
+void TABLE_LIST::register_as_derived_with_rec_ref(With_element *rec_elem)
+{
+ rec_elem->derived_with_rec_ref.link_in_list(this, &this->next_with_rec_ref);
+ is_derived_with_recursive_reference= true;
+ get_unit()->uncacheable|= UNCACHEABLE_DEPENDENT;
+}
+
+
+bool TABLE_LIST::is_nonrecursive_derived_with_rec_ref()
+{
+ return is_derived_with_recursive_reference;
+}
+
+
/**
@brief
Fill the recursive with table
@@ -1044,7 +1100,7 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived)
bool res= FALSE;
DBUG_ENTER("mysql_derived_fill");
DBUG_PRINT("enter", ("Alias: '%s' Unit: %p",
- (derived->alias ? derived->alias : "<NULL>"),
+ (derived->alias.str ? derived->alias.str : "<NULL>"),
derived->get_unit()));
if (unit->executed && !unit->uncacheable && !unit->describe &&
@@ -1052,8 +1108,24 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived)
DBUG_RETURN(FALSE);
/*check that table creation passed without problems. */
DBUG_ASSERT(derived->table && derived->table->is_created());
- select_union *derived_result= derived->derived_result;
+ select_unit *derived_result= derived->derived_result;
SELECT_LEX *save_current_select= lex->current_select;
+
+ if (unit->executed && !derived_is_recursive &&
+ (unit->uncacheable & UNCACHEABLE_DEPENDENT))
+ {
+ if ((res= derived->table->file->ha_delete_all_rows()))
+ goto err;
+ JOIN *join= unit->first_select()->join;
+ join->first_record= false;
+ for (uint i= join->top_join_tab_count;
+ i < join->top_join_tab_count + join->aggr_tables;
+ i++)
+ {
+ if ((res= join->join_tab[i].table->file->ha_delete_all_rows()))
+ goto err;
+ }
+ }
if (derived_is_recursive)
{
@@ -1068,7 +1140,7 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived)
res= derived->fill_recursive(thd);
}
}
- else if (unit->is_union())
+ else if (unit->is_unit_op())
{
// execute union without clean up
res= unit->exec();
@@ -1120,8 +1192,8 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived)
}
}
}
-
- if (res || (!lex->describe && !derived_is_recursive))
+err:
+ if (res || (!derived_is_recursive && !lex->describe && !unit->uncacheable))
unit->cleanup();
lex->current_select= save_current_select;
@@ -1150,7 +1222,7 @@ bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived)
{
DBUG_ENTER("mysql_derived_reinit");
DBUG_PRINT("enter", ("Alias: '%s' Unit: %p",
- (derived->alias ? derived->alias : "<NULL>"),
+ (derived->alias.str ? derived->alias.str : "<NULL>"),
derived->get_unit()));
st_select_lex_unit *unit= derived->get_unit();
@@ -1248,15 +1320,51 @@ bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived)
st_select_lex *save_curr_select= thd->lex->current_select;
for (; sl; sl= sl->next_select())
{
+ Item *extracted_cond_copy;
if (!sl->cond_pushdown_is_allowed())
continue;
thd->lex->current_select= sl;
+ if (sl->have_window_funcs())
+ {
+ if (sl->join->group_list || sl->join->implicit_grouping)
+ continue;
+ ORDER *common_partition_fields=
+ sl->find_common_window_func_partition_fields(thd);
+ if (!common_partition_fields)
+ continue;
+ extracted_cond_copy= !sl->next_select() ?
+ extracted_cond :
+ extracted_cond->build_clone(thd);
+ if (!extracted_cond_copy)
+ continue;
+
+ Item *cond_over_partition_fields;;
+ sl->collect_grouping_fields(thd, common_partition_fields);
+ sl->check_cond_extraction_for_grouping_fields(extracted_cond_copy,
+ derived);
+ cond_over_partition_fields=
+ sl->build_cond_for_grouping_fields(thd, extracted_cond_copy, true);
+ if (cond_over_partition_fields)
+ cond_over_partition_fields= cond_over_partition_fields->transform(thd,
+ &Item::derived_grouping_field_transformer_for_where,
+ (uchar*) sl);
+ if (cond_over_partition_fields)
+ {
+ cond_over_partition_fields->walk(
+ &Item::cleanup_excluding_const_fields_processor, 0, 0);
+ sl->cond_pushed_into_where= cond_over_partition_fields;
+ }
+
+ continue;
+ }
+
/*
For each select of the unit except the last one
create a clone of extracted_cond
*/
- Item *extracted_cond_copy= !sl->next_select() ? extracted_cond :
- extracted_cond->build_clone(thd, thd->mem_root);
+ extracted_cond_copy= !sl->next_select() ?
+ extracted_cond :
+ extracted_cond->build_clone(thd);
if (!extracted_cond_copy)
continue;
@@ -1281,7 +1389,7 @@ bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived)
that could be pushed into the where clause of sl
*/
Item *cond_over_grouping_fields;
- sl->collect_grouping_fields(thd);
+ sl->collect_grouping_fields(thd, sl->join->group_list);
sl->check_cond_extraction_for_grouping_fields(extracted_cond_copy,
derived);
cond_over_grouping_fields=
@@ -1329,4 +1437,3 @@ bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived)
thd->lex->current_select= save_curr_select;
DBUG_RETURN(false);
}
-
diff --git a/sql/sql_derived.h b/sql/sql_derived.h
index a3657758c5a..093191e62a7 100644
--- a/sql/sql_derived.h
+++ b/sql/sql_derived.h
@@ -24,20 +24,6 @@ bool mysql_handle_derived(LEX *lex, uint phases);
bool mysql_handle_single_derived(LEX *lex, TABLE_LIST *derived, uint phases);
bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived);
-/**
- Cleans up the SELECT_LEX_UNIT for the derived table (if any).
-
- @param thd Thread handler
- @param lex LEX for this thread
- @param derived TABLE_LIST for the derived table
-
- @retval false Success
- @retval true Failure
-*/
-bool mysql_derived_cleanup(THD *thd, LEX *lex, TABLE_LIST *derived);
-
-Item *delete_not_needed_parts(THD *thd, Item *cond);
-
bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived);
#endif /* SQL_DERIVED_INCLUDED */
diff --git a/sql/sql_digest.cc b/sql/sql_digest.cc
index 47ed8fd4ecb..10a9547d80f 100644
--- a/sql/sql_digest.cc
+++ b/sql/sql_digest.cc
@@ -18,7 +18,7 @@
This code needs extra visibility in the lexer structures
*/
-#include "my_global.h"
+#include "mariadb.h"
#include "my_md5.h"
#include "unireg.h"
@@ -31,13 +31,8 @@
#include "sql_get_diagnostics.h"
-#ifdef NEVER
-#include "my_sys.h"
-#include "sql_signal.h"
-#endif
-
/* Generated code */
-#include "sql_yacc.h"
+#include "sql_yacc.hh"
#define LEX_TOKEN_WITH_DEFINITION
#include "lex_token.h"
@@ -154,7 +149,7 @@ inline void store_token_identifier(sql_digest_storage* digest_storage,
/* Write the string data */
if (id_length > 0)
memcpy((char *)(dest + 4), id_name, id_length);
- digest_storage->m_byte_count+= bytes_needed;
+ digest_storage->m_byte_count+= (uint)bytes_needed;
}
else
{
@@ -572,7 +567,7 @@ sql_digest_state* digest_add_token(sql_digest_state *state,
case IDENT_QUOTED:
{
YYSTYPE *lex_token= yylval;
- char *yytext= lex_token->lex_str.str;
+ const char *yytext= lex_token->lex_str.str;
size_t yylen= lex_token->lex_str.length;
/*
diff --git a/sql/sql_digest.h b/sql/sql_digest.h
index 9f3f75ab4eb..cc786a3b6fa 100644
--- a/sql/sql_digest.h
+++ b/sql/sql_digest.h
@@ -54,10 +54,10 @@ struct sql_digest_storage
reset(NULL, 0);
}
- inline void reset(unsigned char *token_array, uint length)
+ inline void reset(unsigned char *token_array, size_t length)
{
m_token_array= token_array;
- m_token_array_length= length;
+ m_token_array_length= (uint)length;
reset();
}
diff --git a/sql/sql_do.cc b/sql/sql_do.cc
index f4420552a25..eab0e57b1d2 100644
--- a/sql/sql_do.cc
+++ b/sql/sql_do.cc
@@ -16,7 +16,7 @@
/* Execute DO statement */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "transaction.h"
#include "unireg.h"
@@ -29,13 +29,13 @@ bool mysql_do(THD *thd, List<Item> &values)
List_iterator<Item> li(values);
Item *value;
DBUG_ENTER("mysql_do");
- if (setup_fields(thd, Ref_ptr_array(), values, MARK_COLUMNS_NONE, 0, NULL, 0))
+ if (setup_fields(thd, Ref_ptr_array(), values, COLUMNS_READ, 0, NULL, 0))
DBUG_RETURN(TRUE);
while ((value = li++))
(void) value->is_null();
free_underlaid_joins(thd, &thd->lex->select_lex);
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
{
/*
Rollback the effect of the statement, since next instruction
diff --git a/sql/sql_error.cc b/sql/sql_error.cc
index d2436d5fcb4..da6a12137f7 100644
--- a/sql/sql_error.cc
+++ b/sql/sql_error.cc
@@ -41,7 +41,7 @@ This file contains the implementation of error and warnings related
***********************************************************************/
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#include "sql_error.h"
@@ -172,70 +172,6 @@ This file contains the implementation of error and warnings related
See WL#2111 (Stored Procedures: Implement GET DIAGNOSTICS)
*/
-Sql_condition::Sql_condition()
- : Sql_alloc(),
- m_class_origin((const char*) NULL, 0, & my_charset_utf8_bin),
- m_subclass_origin((const char*) NULL, 0, & my_charset_utf8_bin),
- 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_message_text(),
- m_sql_errno(0),
- m_level(Sql_condition::WARN_LEVEL_ERROR),
- m_mem_root(NULL)
-{
- memset(m_returned_sqlstate, 0, sizeof(m_returned_sqlstate));
-}
-
-void Sql_condition::init(MEM_ROOT *mem_root)
-{
- DBUG_ASSERT(mem_root != NULL);
- DBUG_ASSERT(m_mem_root == NULL);
- m_mem_root= mem_root;
-}
-
-void Sql_condition::clear()
-{
- m_class_origin.length(0);
- m_subclass_origin.length(0);
- m_constraint_catalog.length(0);
- m_constraint_schema.length(0);
- m_constraint_name.length(0);
- m_catalog_name.length(0);
- m_schema_name.length(0);
- m_table_name.length(0);
- m_column_name.length(0);
- m_cursor_name.length(0);
- m_message_text.length(0);
- m_sql_errno= 0;
- m_level= Sql_condition::WARN_LEVEL_ERROR;
-}
-
-Sql_condition::Sql_condition(MEM_ROOT *mem_root)
- : Sql_alloc(),
- m_class_origin((const char*) NULL, 0, & my_charset_utf8_bin),
- m_subclass_origin((const char*) NULL, 0, & my_charset_utf8_bin),
- 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_message_text(),
- m_sql_errno(0),
- m_level(Sql_condition::WARN_LEVEL_ERROR),
- m_mem_root(mem_root)
-{
- DBUG_ASSERT(mem_root != NULL);
- memset(m_returned_sqlstate, 0, sizeof(m_returned_sqlstate));
-}
static void copy_string(MEM_ROOT *mem_root, String* dst, const String* src)
{
@@ -270,21 +206,6 @@ Sql_condition::copy_opt_attributes(const Sql_condition *cond)
copy_string(m_mem_root, & m_cursor_name, & cond->m_cursor_name);
}
-void
-Sql_condition::set(uint sql_errno, const char* sqlstate,
- Sql_condition::enum_warning_level level, const char* msg)
-{
- DBUG_ASSERT(sql_errno != 0);
- DBUG_ASSERT(sqlstate != NULL);
- DBUG_ASSERT(msg != NULL);
-
- m_sql_errno= sql_errno;
- memcpy(m_returned_sqlstate, sqlstate, SQLSTATE_LENGTH);
- m_returned_sqlstate[SQLSTATE_LENGTH]= '\0';
-
- set_builtin_message_text(msg);
- m_level= level;
-}
void
Sql_condition::set_builtin_message_text(const char* str)
@@ -312,13 +233,46 @@ Sql_condition::get_message_octet_length() const
return m_message_text.length();
}
-void
-Sql_condition::set_sqlstate(const char* sqlstate)
+
+void Sql_state_errno_level::assign_defaults(const Sql_state_errno *from)
{
- memcpy(m_returned_sqlstate, sqlstate, SQLSTATE_LENGTH);
- m_returned_sqlstate[SQLSTATE_LENGTH]= '\0';
+ DBUG_ASSERT(from);
+ int sqlerrno= from->get_sql_errno();
+ /*
+ SIGNAL is restricted in sql_yacc.yy to only signal SQLSTATE conditions.
+ */
+ DBUG_ASSERT(from->has_sql_state());
+ set_sqlstate(from);
+ /* SQLSTATE class "00": illegal, rejected in the parser. */
+ DBUG_ASSERT(m_sqlstate[0] != '0' || get_sqlstate()[1] != '0');
+
+ if (Sql_state::is_warning()) /* SQLSTATE class "01": warning. */
+ {
+ m_level= Sql_condition::WARN_LEVEL_WARN;
+ m_sql_errno= sqlerrno ? sqlerrno : ER_SIGNAL_WARN;
+ }
+ else if (Sql_state::is_not_found()) /* SQLSTATE class "02": not found. */
+ {
+ m_level= Sql_condition::WARN_LEVEL_ERROR;
+ m_sql_errno= sqlerrno ? sqlerrno : ER_SIGNAL_NOT_FOUND;
+ }
+ else /* other SQLSTATE classes : error. */
+ {
+ m_level= Sql_condition::WARN_LEVEL_ERROR;
+ m_sql_errno= sqlerrno ? sqlerrno : ER_SIGNAL_EXCEPTION;
+ }
}
+
+void Sql_condition::assign_defaults(THD *thd, const Sql_state_errno *from)
+{
+ if (from)
+ Sql_state_errno_level::assign_defaults(from);
+ if (!get_message_text())
+ set_builtin_message_text(ER(get_sql_errno()));
+}
+
+
Diagnostics_area::Diagnostics_area(bool initialize)
: is_bulk_execution(0), m_main_wi(0, false, initialize)
{
@@ -353,7 +307,8 @@ Diagnostics_area::reset_diagnostics_area()
m_can_overwrite_status= FALSE;
/** Don't take chances in production */
m_message[0]= '\0';
- m_sql_errno= 0;
+ Sql_state_errno::clear();
+ Sql_user_condition_identity::clear();
m_affected_rows= 0;
m_last_insert_id= 0;
m_statement_warn_count= 0;
@@ -382,7 +337,7 @@ Diagnostics_area::set_ok_status(ulonglong affected_rows,
In production, refuse to overwrite an error or a custom response
with an OK packet.
*/
- if (is_error() || is_disabled())
+ if (unlikely(is_error() || is_disabled()))
return;
/*
When running a bulk operation, m_status will be DA_OK for the first
@@ -422,7 +377,7 @@ Diagnostics_area::set_eof_status(THD *thd)
In production, refuse to overwrite an error or a custom response
with an EOF packet.
*/
- if (is_error() || is_disabled())
+ if (unlikely(is_error() || is_disabled()))
return;
/*
@@ -452,6 +407,7 @@ Diagnostics_area::set_error_status(uint sql_errno)
set_error_status(sql_errno,
ER(sql_errno),
mysql_errno_to_sqlstate(sql_errno),
+ Sql_user_condition_identity(),
NULL);
}
@@ -465,6 +421,7 @@ Diagnostics_area::set_error_status(uint sql_errno)
@param sql_errno SQL-condition error number
@param message SQL-condition message
@param sqlstate SQL-condition state
+ @param ucid User defined condition identity
@param error_condition SQL-condition object representing the error state
@note Note, that error_condition may be NULL. It happens if a) OOM error is
@@ -475,6 +432,7 @@ void
Diagnostics_area::set_error_status(uint sql_errno,
const char *message,
const char *sqlstate,
+ const Sql_user_condition_identity &ucid,
const Sql_condition *error_condition)
{
DBUG_ENTER("set_error_status");
@@ -501,9 +459,8 @@ Diagnostics_area::set_error_status(uint sql_errno,
return;
#endif
- m_sql_errno= sql_errno;
- memcpy(m_sqlstate, sqlstate, SQLSTATE_LENGTH);
- m_sqlstate[SQLSTATE_LENGTH]= '\0';
+ Sql_state_errno::set(sql_errno, sqlstate);
+ Sql_user_condition_identity::set(ucid);
strmake_buf(m_message, message);
get_warning_info()->set_error_condition(error_condition);
@@ -549,7 +506,7 @@ void Warning_info::init()
{
/* Initialize sub structures */
DBUG_ASSERT(initialized == 0);
- init_sql_alloc(&m_warn_root, WARN_ALLOC_BLOCK_SIZE,
+ init_sql_alloc(&m_warn_root, "Warning_info", WARN_ALLOC_BLOCK_SIZE,
WARN_ALLOC_PREALLOC_SIZE, MYF(MY_THREAD_SPECIFIC));
initialized= 1;
}
@@ -566,8 +523,7 @@ Warning_info::~Warning_info()
}
-bool Warning_info::has_sql_condition(const char *message_str,
- ulong message_length) const
+bool Warning_info::has_sql_condition(const char *message_str, size_t message_length) const
{
Diagnostics_area::Sql_condition_iterator it(m_warn_list);
const Sql_condition *err;
@@ -695,8 +651,7 @@ void Warning_info::reserve_space(THD *thd, uint count)
}
Sql_condition *Warning_info::push_warning(THD *thd,
- uint sql_errno, const char* sqlstate,
- Sql_condition::enum_warning_level level,
+ const Sql_condition_identity *value,
const char *msg)
{
Sql_condition *cond= NULL;
@@ -706,14 +661,11 @@ Sql_condition *Warning_info::push_warning(THD *thd,
if (m_allow_unlimited_warnings ||
m_warn_list.elements() < thd->variables.max_error_count)
{
- cond= new (& m_warn_root) Sql_condition(& m_warn_root);
+ cond= new (& m_warn_root) Sql_condition(& m_warn_root, *value, msg);
if (cond)
- {
- cond->set(sql_errno, sqlstate, level, msg);
m_warn_list.push_back(cond);
- }
}
- m_warn_count[(uint) level]++;
+ m_warn_count[(uint) value->get_level()]++;
}
m_current_statement_warn_count++;
@@ -721,13 +673,11 @@ Sql_condition *Warning_info::push_warning(THD *thd,
}
-Sql_condition *Warning_info::push_warning(THD *thd, const Sql_condition *sql_condition)
+Sql_condition *Warning_info::push_warning(THD *thd,
+ const Sql_condition *sql_condition)
{
- Sql_condition *new_condition= push_warning(thd,
- sql_condition->get_sql_errno(),
- sql_condition->get_sqlstate(),
- sql_condition->get_level(),
- sql_condition->get_message_text());
+ Sql_condition *new_condition= push_warning(thd, sql_condition,
+ sql_condition->get_message_text());
if (new_condition)
new_condition->copy_opt_attributes(sql_condition);
@@ -818,12 +768,12 @@ void push_warning_printf(THD *thd, Sql_condition::enum_warning_level level,
TRUE Error sending data to client
*/
-const LEX_STRING warning_level_names[]=
+const LEX_CSTRING warning_level_names[]=
{
- { C_STRING_WITH_LEN("Note") },
- { C_STRING_WITH_LEN("Warning") },
- { C_STRING_WITH_LEN("Error") },
- { C_STRING_WITH_LEN("?") }
+ { STRING_WITH_LEN("Note") },
+ { STRING_WITH_LEN("Warning") },
+ { STRING_WITH_LEN("Error") },
+ { STRING_WITH_LEN("?") }
};
bool mysqld_show_warnings(THD *thd, ulong levels_to_show)
@@ -967,11 +917,11 @@ char *err_conv(char *buff, uint to_length, const char *from,
length of converted string
*/
-uint32 convert_error_message(char *to, uint32 to_length, CHARSET_INFO *to_cs,
- const char *from, uint32 from_length,
+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;
+ int cnvres;
my_wc_t wc;
const uchar *from_end= (const uchar*) from+from_length;
char *to_start= to;
@@ -979,7 +929,7 @@ uint32 convert_error_message(char *to, uint32 to_length, CHARSET_INFO *to_cs,
my_charset_conv_mb_wc mb_wc= from_cs->cset->mb_wc;
my_charset_conv_wc_mb wc_mb;
uint error_count= 0;
- uint length;
+ size_t length;
DBUG_ASSERT(to_length > 0);
/* Make room for the null terminator. */
@@ -1018,7 +968,7 @@ uint32 convert_error_message(char *to, uint32 to_length, CHARSET_INFO *to_cs,
length= (wc <= 0xFFFF) ? 6/* '\1234' format*/ : 9 /* '\+123456' format*/;
if ((uchar*)(to + length) >= to_end)
break;
- cnvres= my_snprintf(to, 9,
+ cnvres= (int)my_snprintf(to, 9,
(wc <= 0xFFFF) ? "\\%04X" : "\\+%06X", (uint) wc);
to+= cnvres;
}
@@ -1028,7 +978,7 @@ uint32 convert_error_message(char *to, uint32 to_length, CHARSET_INFO *to_cs,
*to= 0;
*errors= error_count;
- return (uint32) (to - to_start);
+ return (size_t) (to - to_start);
}
@@ -1043,7 +993,7 @@ uint32 convert_error_message(char *to, uint32 to_length, CHARSET_INFO *to_cs,
@retval false if it's bad.
*/
-bool is_sqlstate_valid(const LEX_STRING *sqlstate)
+bool is_sqlstate_valid(const LEX_CSTRING *sqlstate)
{
if (sqlstate->length != 5)
return false;
diff --git a/sql/sql_error.h b/sql/sql_error.h
index 239efd06c4b..3b29f7aba06 100644
--- a/sql/sql_error.h
+++ b/sql/sql_error.h
@@ -17,8 +17,7 @@
#ifndef SQL_ERROR_H
#define SQL_ERROR_H
-#include "sql_list.h" /* Sql_alloc, MEM_ROOT */
-#include "m_string.h" /* LEX_STRING */
+#include "sql_list.h" /* Sql_alloc, MEM_ROOT, list */
#include "sql_type_int.h" // Longlong_hybrid
#include "sql_string.h" /* String */
#include "sql_plist.h" /* I_P_List */
@@ -28,15 +27,123 @@
class THD;
class my_decimal;
+class sp_condition_value;
///////////////////////////////////////////////////////////////////////////
-/**
- Representation of a SQL condition.
- A SQL condition can be a completion condition (note, warning),
- or an exception condition (error, not found).
-*/
-class Sql_condition : public Sql_alloc
+class Sql_state
+{
+protected:
+ /**
+ This member is always NUL terminated.
+ */
+ char m_sqlstate[SQLSTATE_LENGTH + 1];
+public:
+ Sql_state()
+ {
+ memset(m_sqlstate, 0, sizeof(m_sqlstate));
+ }
+
+ Sql_state(const char *sqlstate)
+ {
+ set_sqlstate(sqlstate);
+ }
+
+ const char* get_sqlstate() const
+ { return m_sqlstate; }
+
+ void set_sqlstate(const Sql_state *other)
+ {
+ *this= *other;
+ }
+ void set_sqlstate(const char *sqlstate)
+ {
+ memcpy(m_sqlstate, sqlstate, SQLSTATE_LENGTH);
+ m_sqlstate[SQLSTATE_LENGTH]= '\0';
+ }
+ bool eq(const Sql_state *other) const
+ {
+ return strcmp(m_sqlstate, other->m_sqlstate) == 0;
+ }
+
+ bool has_sql_state() const { return m_sqlstate[0] != '\0'; }
+
+ /**
+ Checks if this SQL state defines a WARNING condition.
+ Note: m_sqlstate must contain a valid SQL-state.
+
+ @retval true if this SQL state defines a WARNING condition.
+ @retval false otherwise.
+ */
+ inline bool is_warning() const
+ { return m_sqlstate[0] == '0' && m_sqlstate[1] == '1'; }
+
+
+ /**
+ Checks if this SQL state defines a NOT FOUND condition.
+ Note: m_sqlstate must contain a valid SQL-state.
+
+ @retval true if this SQL state defines a NOT FOUND condition.
+ @retval false otherwise.
+ */
+ inline bool is_not_found() const
+ { return m_sqlstate[0] == '0' && m_sqlstate[1] == '2'; }
+
+
+ /**
+ Checks if this SQL state defines an EXCEPTION condition.
+ Note: m_sqlstate must contain a valid SQL-state.
+
+ @retval true if this SQL state defines an EXCEPTION condition.
+ @retval false otherwise.
+ */
+ inline bool is_exception() const
+ { return m_sqlstate[0] != '0' || m_sqlstate[1] > '2'; }
+
+};
+
+
+class Sql_state_errno: public Sql_state
+{
+protected:
+ /**
+ MySQL extension, MYSQL_ERRNO condition item.
+ SQL error number. One of ER_ codes from share/errmsg.txt.
+ Set by set_error_status.
+ */
+ uint m_sql_errno;
+
+public:
+ Sql_state_errno()
+ :m_sql_errno(0)
+ { }
+ Sql_state_errno(uint sql_errno)
+ :m_sql_errno(sql_errno)
+ { }
+ Sql_state_errno(uint sql_errno, const char *sql_state)
+ :Sql_state(sql_state),
+ m_sql_errno(sql_errno)
+ { }
+ /**
+ Get the SQL_ERRNO of this condition.
+ @return the sql error number condition item.
+ */
+ uint get_sql_errno() const
+ { return m_sql_errno; }
+
+ void set(uint sql_errno, const char *sqlstate)
+ {
+ m_sql_errno= sql_errno;
+ set_sqlstate(sqlstate);
+ }
+ void clear()
+ {
+ m_sql_errno= 0;
+ }
+};
+
+
+class Sql_state_errno_level: public Sql_state_errno
{
public:
/*
@@ -48,6 +155,197 @@ public:
enum enum_warning_level
{ WARN_LEVEL_NOTE, WARN_LEVEL_WARN, WARN_LEVEL_ERROR, WARN_LEVEL_END};
+protected:
+ /** Severity (error, warning, note) of this condition. */
+ enum_warning_level m_level;
+
+ void assign_defaults(const Sql_state_errno *value);
+
+public:
+ /**
+ Get the error level of this condition.
+ @return the error level condition item.
+ */
+ enum_warning_level get_level() const
+ { return m_level; }
+
+ Sql_state_errno_level()
+ :m_level(WARN_LEVEL_ERROR)
+ { }
+
+ Sql_state_errno_level(uint sqlerrno, const char* sqlstate,
+ enum_warning_level level)
+ :Sql_state_errno(sqlerrno, sqlstate),
+ m_level(level)
+ { }
+ Sql_state_errno_level(const Sql_state_errno &state_errno,
+ enum_warning_level level)
+ :Sql_state_errno(state_errno),
+ m_level(level)
+ { }
+ void clear()
+ {
+ m_level= WARN_LEVEL_ERROR;
+ Sql_state_errno::clear();
+ }
+};
+
+
+/*
+ class Sql_user_condition_identity.
+ Instances of this class uniquely idetify user defined conditions (EXCEPTION).
+
+ SET sql_mode=ORACLE;
+ CREATE PROCEDURE p1
+ AS
+ a EXCEPTION;
+ BEGIN
+ RAISE a;
+ EXCEPTION
+ WHEN a THEN NULL;
+ END;
+
+ Currently a user defined condition is identified by a pointer to
+ its parse time sp_condition_value instance. This can change when
+ we add packages. See MDEV-10591.
+*/
+class Sql_user_condition_identity
+{
+protected:
+ const sp_condition_value *m_user_condition_value;
+public:
+ Sql_user_condition_identity()
+ :m_user_condition_value(NULL)
+ { }
+ Sql_user_condition_identity(const sp_condition_value *value)
+ :m_user_condition_value(value)
+ { }
+ const sp_condition_value *get_user_condition_value() const
+ { return m_user_condition_value; }
+
+ void set(const Sql_user_condition_identity &identity)
+ {
+ *this= identity;
+ }
+ void clear()
+ {
+ m_user_condition_value= NULL;
+ }
+};
+
+
+/**
+ class Sql_condition_identity.
+ Instances of this class uniquely identify conditions
+ (including user-defined exceptions for sql_mode=ORACLE)
+ and store everything that is needed for handler search
+ purposes in sp_pcontext::find_handler().
+*/
+class Sql_condition_identity: public Sql_state_errno_level,
+ public Sql_user_condition_identity
+{
+public:
+ Sql_condition_identity()
+ { }
+ Sql_condition_identity(const Sql_state_errno_level &st,
+ const Sql_user_condition_identity &ucid)
+ :Sql_state_errno_level(st),
+ Sql_user_condition_identity(ucid)
+ { }
+ Sql_condition_identity(const Sql_state_errno &st,
+ enum_warning_level level,
+ const Sql_user_condition_identity &ucid)
+ :Sql_state_errno_level(st, level),
+ Sql_user_condition_identity(ucid)
+ { }
+ Sql_condition_identity(uint sqlerrno,
+ const char* sqlstate,
+ enum_warning_level level,
+ const Sql_user_condition_identity &ucid)
+ :Sql_state_errno_level(sqlerrno, sqlstate, level),
+ Sql_user_condition_identity(ucid)
+ { }
+ void clear()
+ {
+ Sql_state_errno_level::clear();
+ Sql_user_condition_identity::clear();
+ }
+};
+
+
+class Sql_condition_items
+{
+protected:
+ /** SQL CLASS_ORIGIN condition item. */
+ String m_class_origin;
+
+ /** SQL SUBCLASS_ORIGIN condition item. */
+ String m_subclass_origin;
+
+ /** SQL CONSTRAINT_CATALOG condition item. */
+ String m_constraint_catalog;
+
+ /** SQL CONSTRAINT_SCHEMA condition item. */
+ String m_constraint_schema;
+
+ /** SQL CONSTRAINT_NAME condition item. */
+ String m_constraint_name;
+
+ /** SQL CATALOG_NAME condition item. */
+ String m_catalog_name;
+
+ /** SQL SCHEMA_NAME condition item. */
+ String m_schema_name;
+
+ /** SQL TABLE_NAME condition item. */
+ String m_table_name;
+
+ /** SQL COLUMN_NAME condition item. */
+ String m_column_name;
+
+ /** SQL CURSOR_NAME condition item. */
+ 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)
+ { }
+
+ void clear()
+ {
+ m_class_origin.length(0);
+ m_subclass_origin.length(0);
+ m_constraint_catalog.length(0);
+ m_constraint_schema.length(0);
+ m_constraint_name.length(0);
+ m_catalog_name.length(0);
+ m_schema_name.length(0);
+ m_table_name.length(0);
+ m_column_name.length(0);
+ m_cursor_name.length(0);
+ }
+};
+
+
+/**
+ Representation of a SQL condition.
+ A SQL condition can be a completion condition (note, warning),
+ or an exception condition (error, not found).
+*/
+class Sql_condition : public Sql_alloc,
+ public Sql_condition_identity,
+ public Sql_condition_items
+{
+public:
+
/**
Convert a bitmask consisting of MYSQL_TIME_{NOTE|WARN}_XXX bits
to WARN_LEVEL_XXX
@@ -70,27 +368,6 @@ public:
*/
int get_message_octet_length() const;
- /**
- Get the SQLSTATE of this condition.
- @return the sql state.
- */
- const char* get_sqlstate() const
- { return m_returned_sqlstate; }
-
- /**
- Get the SQL_ERRNO of this condition.
- @return the sql error number condition item.
- */
- uint get_sql_errno() const
- { return m_sql_errno; }
-
- /**
- Get the error level of this condition.
- @return the error level condition item.
- */
- Sql_condition::enum_warning_level get_level() const
- { return m_level; }
-
private:
/*
The interface of Sql_condition is mostly private, by design,
@@ -117,21 +394,57 @@ private:
This constructor is usefull when allocating arrays.
Note that the init() method should be called to complete the Sql_condition.
*/
- Sql_condition();
+ Sql_condition()
+ :m_mem_root(NULL)
+ { }
/**
Complete the Sql_condition initialisation.
@param mem_root The memory root to use for the condition items
of this condition
*/
- void init(MEM_ROOT *mem_root);
+ void init(MEM_ROOT *mem_root)
+ {
+ DBUG_ASSERT(mem_root != NULL);
+ DBUG_ASSERT(m_mem_root == NULL);
+ m_mem_root= mem_root;
+ }
/**
Constructor.
@param mem_root The memory root to use for the condition items
of this condition
*/
- Sql_condition(MEM_ROOT *mem_root);
+ Sql_condition(MEM_ROOT *mem_root)
+ :m_mem_root(mem_root)
+ {
+ DBUG_ASSERT(mem_root != NULL);
+ }
+
+ Sql_condition(MEM_ROOT *mem_root, const Sql_user_condition_identity &ucid)
+ :Sql_condition_identity(Sql_state_errno_level(), ucid),
+ m_mem_root(mem_root)
+ {
+ DBUG_ASSERT(mem_root != NULL);
+ }
+ /**
+ Constructor for a fixed message text.
+ @param mem_root - memory root
+ @param value - the error number and the sql state for this condition
+ @param level - the error level for this condition
+ @param msg - the message text for this condition
+ */
+ Sql_condition(MEM_ROOT *mem_root,
+ const Sql_condition_identity &value,
+ const char *msg)
+ :Sql_condition_identity(value),
+ m_mem_root(mem_root)
+ {
+ DBUG_ASSERT(mem_root != NULL);
+ DBUG_ASSERT(value.get_sql_errno() != 0);
+ DBUG_ASSERT(msg != NULL);
+ set_builtin_message_text(msg);
+ }
/** Destructor. */
~Sql_condition()
@@ -144,27 +457,12 @@ private:
void copy_opt_attributes(const Sql_condition *cond);
/**
- Set this condition area with a fixed message text.
- @param thd the current thread.
- @param code the error number for this condition.
- @param str the message text for this condition.
- @param level the error level for this condition.
- @param MyFlags additional flags.
- */
- void set(uint sql_errno, const char* sqlstate,
- Sql_condition::enum_warning_level level,
- const char* msg);
-
- /**
Set the condition message test.
@param str Message text, expressed in the character set derived from
the server --language option
*/
void set_builtin_message_text(const char* str);
- /** Set the SQLSTATE of this condition. */
- void set_sqlstate(const char* sqlstate);
-
/** Set the CLASS_ORIGIN of this condition. */
void set_class_origin();
@@ -172,56 +470,27 @@ private:
void set_subclass_origin();
/**
+ Assign the condition items 'MYSQL_ERRNO', 'level' and 'MESSAGE_TEXT'
+ default values of a condition.
+ @param thd - current thread, to access to localized error messages
+ @param from - copy condition items from here (can be NULL)
+ */
+ void assign_defaults(THD *thd, const Sql_state_errno *from);
+
+ /**
Clear this SQL condition.
*/
- void clear();
+ void clear()
+ {
+ Sql_condition_identity::clear();
+ Sql_condition_items::clear();
+ m_message_text.length(0);
+ }
private:
- /** SQL CLASS_ORIGIN condition item. */
- String m_class_origin;
-
- /** SQL SUBCLASS_ORIGIN condition item. */
- String m_subclass_origin;
-
- /** SQL CONSTRAINT_CATALOG condition item. */
- String m_constraint_catalog;
-
- /** SQL CONSTRAINT_SCHEMA condition item. */
- String m_constraint_schema;
-
- /** SQL CONSTRAINT_NAME condition item. */
- String m_constraint_name;
-
- /** SQL CATALOG_NAME condition item. */
- String m_catalog_name;
-
- /** SQL SCHEMA_NAME condition item. */
- String m_schema_name;
-
- /** SQL TABLE_NAME condition item. */
- String m_table_name;
-
- /** SQL COLUMN_NAME condition item. */
- String m_column_name;
-
- /** SQL CURSOR_NAME condition item. */
- String m_cursor_name;
-
/** Message text, expressed in the character set implied by --language. */
String m_message_text;
- /** MySQL extension, MYSQL_ERRNO condition item. */
- uint m_sql_errno;
-
- /**
- SQL RETURNED_SQLSTATE condition item.
- This member is always NUL terminated.
- */
- char m_returned_sqlstate[SQLSTATE_LENGTH+1];
-
- /** Severity (error, warning, note) of this condition. */
- Sql_condition::enum_warning_level m_level;
-
/** Pointers for participating in the list of conditions. */
Sql_condition *next_in_wi;
Sql_condition **prev_in_wi;
@@ -322,7 +591,7 @@ private:
@return true if the Warning_info contains an SQL-condition with the given
message.
*/
- bool has_sql_condition(const char *message_str, ulong message_length) const;
+ bool has_sql_condition(const char *message_str, size_t message_length) const;
/**
Reset the warning information. Clear all warnings,
@@ -468,17 +737,13 @@ private:
counters.
@param thd Thread context.
- @param sql_errno SQL-condition error number.
- @param sqlstate SQL-condition state.
- @param level SQL-condition level.
+ @param identity SQL-condition identity
@param msg SQL-condition message.
@return a pointer to the added SQL-condition.
*/
Sql_condition *push_warning(THD *thd,
- uint sql_errno,
- const char* sqlstate,
- Sql_condition::enum_warning_level level,
+ const Sql_condition_identity *identity,
const char* msg);
/**
@@ -636,7 +901,8 @@ public:
Can not be assigned twice per statement.
*/
-class Diagnostics_area
+class Diagnostics_area: public Sql_state_errno,
+ public Sql_user_condition_identity
{
private:
/** The type of the counted and doubly linked list of conditions. */
@@ -688,8 +954,19 @@ public:
void set_error_status(uint sql_errno,
const char *message,
const char *sqlstate,
+ const Sql_user_condition_identity &ucid,
const Sql_condition *error_condition);
+ void set_error_status(uint sql_errno,
+ const char *message,
+ const char *sqlstate,
+ const Sql_condition *error_condition)
+ {
+ set_error_status(sql_errno, message, sqlstate,
+ Sql_user_condition_identity(),
+ error_condition);
+ }
+
void disable_status();
void reset_diagnostics_area();
@@ -724,10 +1001,13 @@ public:
{ m_skip_flush= TRUE; }
uint sql_errno() const
- { DBUG_ASSERT(m_status == DA_ERROR); return m_sql_errno; }
+ {
+ DBUG_ASSERT(m_status == DA_ERROR);
+ return Sql_state_errno::get_sql_errno();
+ }
const char* get_sqlstate() const
- { DBUG_ASSERT(m_status == DA_ERROR); return m_sqlstate; }
+ { DBUG_ASSERT(m_status == DA_ERROR); return Sql_state::get_sqlstate(); }
ulonglong affected_rows() const
{
@@ -748,6 +1028,18 @@ public:
return m_statement_warn_count;
}
+ /**
+ Get the current errno, state and id of the user defined condition
+ and return them as Sql_condition_identity.
+ */
+ Sql_condition_identity get_error_condition_identity() const
+ {
+ DBUG_ASSERT(m_status == DA_ERROR);
+ return Sql_condition_identity(*this /*Sql_state_errno*/,
+ Sql_condition::WARN_LEVEL_ERROR,
+ *this /*Sql_user_condition_identity*/);
+ }
+
/* Used to count any warnings pushed after calling set_ok_status(). */
void increment_warning()
{
@@ -795,7 +1087,7 @@ public:
ulong current_statement_warn_count() const
{ return get_warning_info()->current_statement_warn_count(); }
- bool has_sql_condition(const char *message_str, ulong message_length) const
+ bool has_sql_condition(const char *message_str, size_t message_length) const
{ return get_warning_info()->has_sql_condition(message_str, message_length); }
void reset_for_next_command()
@@ -844,13 +1136,22 @@ public:
uint sql_errno_arg,
const char* sqlstate,
Sql_condition::enum_warning_level level,
+ const Sql_user_condition_identity &ucid,
const char* msg)
{
- return get_warning_info()->push_warning(thd,
- sql_errno_arg, sqlstate, level,
- msg);
+ Sql_condition_identity tmp(sql_errno_arg, sqlstate, level, ucid);
+ return get_warning_info()->push_warning(thd, &tmp, msg);
}
+ Sql_condition *push_warning(THD *thd,
+ uint sqlerrno,
+ const char* sqlstate,
+ Sql_condition::enum_warning_level level,
+ const char* msg)
+ {
+ return push_warning(thd, sqlerrno, sqlstate, level,
+ Sql_user_condition_identity(), msg);
+ }
void mark_sql_conditions_for_removal()
{ get_warning_info()->mark_sql_conditions_for_removal(); }
@@ -890,14 +1191,6 @@ private:
char m_message[MYSQL_ERRMSG_SIZE];
/**
- SQL error number. One of ER_ codes from share/errmsg.txt.
- Set by set_error_status.
- */
- uint m_sql_errno;
-
- char m_sqlstate[SQLSTATE_LENGTH+1];
-
- /**
The number of rows affected by the last statement. This is
semantically close to thd->m_row_count_func, but has a different
life cycle. thd->m_row_count_func stores the value returned by
@@ -945,14 +1238,14 @@ void push_warning_printf(THD *thd, Sql_condition::enum_warning_level level,
bool mysqld_show_warnings(THD *thd, ulong levels_to_show);
-uint32 convert_error_message(char *to, uint32 to_length,
+size_t convert_error_message(char *to, size_t to_length,
CHARSET_INFO *to_cs,
- const char *from, uint32 from_length,
+ const char *from, size_t from_length,
CHARSET_INFO *from_cs, uint *errors);
-extern const LEX_STRING warning_level_names[];
+extern const LEX_CSTRING warning_level_names[];
-bool is_sqlstate_valid(const LEX_STRING *sqlstate);
+bool is_sqlstate_valid(const LEX_CSTRING *sqlstate);
/**
Checks if the specified SQL-state-string defines COMPLETION condition.
This function assumes that the given string contains a valid SQL-state.
@@ -966,43 +1259,4 @@ inline bool is_sqlstate_completion(const char *s)
{ return s[0] == '0' && s[1] == '0'; }
-/**
- Checks if the specified SQL-state-string defines WARNING condition.
- This function assumes that the given string contains a valid SQL-state.
-
- @param s the condition SQLSTATE.
-
- @retval true if the given string defines WARNING condition.
- @retval false otherwise.
-*/
-inline bool is_sqlstate_warning(const char *s)
-{ return s[0] == '0' && s[1] == '1'; }
-
-
-/**
- Checks if the specified SQL-state-string defines NOT FOUND condition.
- This function assumes that the given string contains a valid SQL-state.
-
- @param s the condition SQLSTATE.
-
- @retval true if the given string defines NOT FOUND condition.
- @retval false otherwise.
-*/
-inline bool is_sqlstate_not_found(const char *s)
-{ return s[0] == '0' && s[1] == '2'; }
-
-
-/**
- Checks if the specified SQL-state-string defines EXCEPTION condition.
- This function assumes that the given string contains a valid SQL-state.
-
- @param s the condition SQLSTATE.
-
- @retval true if the given string defines EXCEPTION condition.
- @retval false otherwise.
-*/
-inline bool is_sqlstate_exception(const char *s)
-{ return s[0] != '0' || s[1] > '2'; }
-
-
#endif // SQL_ERROR_H
diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc
index ec58145bada..95ff94273b4 100644
--- a/sql/sql_explain.cc
+++ b/sql/sql_explain.cc
@@ -18,7 +18,7 @@
#pragma implementation // gcc: Class implementation
#endif
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "sql_select.h"
#include "my_json_writer.h"
@@ -29,6 +29,11 @@ const char * STR_DELETING_ALL_ROWS= "Deleting all rows";
const char * STR_IMPOSSIBLE_WHERE= "Impossible WHERE";
const char * STR_NO_ROWS_AFTER_PRUNING= "No matching rows after partition pruning";
+const char *unit_operation_text[4]=
+{
+ "UNIT RESULT","UNION RESULT","INTERSECT RESULT","EXCEPT RESULT"
+};
+
static void write_item(Json_writer *writer, Item *item);
static void append_item_to_str(String *out, Item *item);
@@ -421,14 +426,34 @@ int print_explain_row(select_result_sink *result,
uint Explain_union::make_union_table_name(char *buf)
{
uint childno= 0;
- uint len= 6, lastop= 0;
- memcpy(buf, STRING_WITH_LEN("<union"));
+ uint len, lastop= 0;
+ LEX_CSTRING type;
+ switch (operation)
+ {
+ case OP_MIX:
+ lex_string_set3(&type, STRING_WITH_LEN("<unit"));
+ break;
+ case OP_UNION:
+ lex_string_set3(&type, STRING_WITH_LEN("<union"));
+ break;
+ case OP_INTERSECT:
+ lex_string_set3(&type, STRING_WITH_LEN("<intersect"));
+ break;
+ case OP_EXCEPT:
+ lex_string_set3(&type, STRING_WITH_LEN("<except"));
+ break;
+ default:
+ DBUG_ASSERT(0);
+ type.str= NULL;
+ type.length= 0;
+ }
+ memcpy(buf, type.str, (len= (uint)type.length));
for (; childno < union_members.elements() && len + lastop + 5 < NAME_LEN;
childno++)
{
len+= lastop;
- lastop= my_snprintf(buf + len, NAME_LEN - len,
+ lastop= (uint)my_snprintf(buf + len, NAME_LEN - len,
"%u,", union_members.at(childno));
}
@@ -465,7 +490,7 @@ int Explain_union::print_explain(Explain_query *query,
if (!using_tmp)
return 0;
- /* Print a line with "UNION RESULT" */
+ /* Print a line with "UNIT RESULT" */
List<Item> item_list;
Item *item_null= new (mem_root) Item_null(thd);
@@ -817,6 +842,28 @@ int Explain_basic_join::print_explain(Explain_query *query,
}
+void Explain_select::add_linkage(Json_writer *writer)
+{
+ const char *operation= NULL;
+ switch (linkage)
+ {
+ case UNION_TYPE:
+ operation= "UNION";
+ break;
+ case INTERSECT_TYPE:
+ operation= "INTERSECT";
+ break;
+ case EXCEPT_TYPE:
+ operation= "EXCEPT";
+ break;
+ default:
+ // It is the first or the only SELECT => no operation
+ break;
+ }
+ if (operation)
+ writer->add_member("operation").add_str(operation);
+}
+
void Explain_select::print_explain_json(Explain_query *query,
Json_writer *writer, bool is_analyze)
{
@@ -828,6 +875,7 @@ void Explain_select::print_explain_json(Explain_query *query,
{
writer->add_member("query_block").start_object();
writer->add_member("select_id").add_ll(select_id);
+ add_linkage(writer);
writer->add_member("table").start_object();
writer->add_member("message").add_str(message);
@@ -840,6 +888,7 @@ void Explain_select::print_explain_json(Explain_query *query,
{
writer->add_member("query_block").start_object();
writer->add_member("select_id").add_ll(select_id);
+ add_linkage(writer);
if (is_analyze && time_tracker.get_loops())
{
@@ -1107,32 +1156,37 @@ void Explain_table_access::fill_key_len_str(String *key_len_str) const
}
-void Explain_index_use::set(MEM_ROOT *mem_root, KEY *key, uint key_len_arg)
+bool Explain_index_use::set(MEM_ROOT *mem_root, KEY *key, uint key_len_arg)
{
- set_pseudo_key(mem_root, key->name);
+ if (set_pseudo_key(mem_root, key->name.str))
+ return 1;
+
key_len= key_len_arg;
uint len= 0;
for (uint i= 0; i < key->usable_key_parts; i++)
{
- key_parts_list.append_str(mem_root, key->key_part[i].field->field_name);
+ if (!key_parts_list.append_str(mem_root,
+ key->key_part[i].field->field_name.str))
+ return 1;
len += key->key_part[i].store_length;
if (len >= key_len_arg)
break;
}
+ return 0;
}
-void Explain_index_use::set_pseudo_key(MEM_ROOT *root, const char* key_name_arg)
+bool Explain_index_use::set_pseudo_key(MEM_ROOT *root, const char* key_name_arg)
{
if (key_name_arg)
{
- size_t name_len= strlen(key_name_arg);
- if ((key_name= (char*)alloc_root(root, name_len+1)))
- memcpy(key_name, key_name_arg, name_len+1);
+ if (!(key_name= strdup_root(root, key_name_arg)))
+ return 1;
}
else
key_name= NULL;
key_len= ~(uint) 0;
+ return 0;
}
@@ -1702,6 +1756,11 @@ void Explain_table_access::print_explain_json(Explain_query *query,
/* This is a derived table. Print its contents here */
writer->add_member("materialized").start_object();
Explain_node *node= query->get_node(derived_select_number);
+ if (node->get_type() == Explain_node::EXPLAIN_SELECT &&
+ ((Explain_select*)node)->is_lateral)
+ {
+ writer->add_member("lateral").add_ll(1);
+ }
node->print_explain_json(query, writer, is_analyze);
writer->end_object();
}
@@ -1772,9 +1831,9 @@ const char * extra_tag_text[]=
"Using join buffer", // special handling
- "const row not found",
- "unique row not found",
- "Impossible ON condition"
+ "Const row not found",
+ "Unique row not found",
+ "Impossible ON condition",
};
@@ -2413,7 +2472,11 @@ int Explain_range_checked_fer::append_possible_keys_stat(MEM_ROOT *alloc,
for (j= 0; j < table->s->keys; j++)
{
if (possible_keys.is_set(j))
- keys_stat_names[j]= key_set.append_str(alloc, table->key_info[j].name);
+ {
+ if (!(keys_stat_names[j]= key_set.append_str(alloc,
+ table->key_info[j].name.str)))
+ return 1;
+ }
else
keys_stat_names[j]= NULL;
}
diff --git a/sql/sql_explain.h b/sql/sql_explain.h
index 08af84b3562..97fe07572cf 100644
--- a/sql/sql_explain.h
+++ b/sql/sql_explain.h
@@ -210,6 +210,8 @@ public:
#ifndef DBUG_OFF
select_lex(NULL),
#endif
+ linkage(UNSPECIFIED_TYPE),
+ is_lateral(false),
message(NULL),
having(NULL), having_value(Item::COND_UNDEF),
using_temporary(false), using_filesort(false),
@@ -217,11 +219,15 @@ public:
aggr_tree(NULL)
{}
+ void add_linkage(Json_writer *writer);
+
public:
#ifndef DBUG_OFF
SELECT_LEX *select_lex;
#endif
const char *select_type;
+ enum sub_select_type linkage;
+ bool is_lateral;
/*
If message != NULL, this is a degenerate join plan, and all subsequent
@@ -324,7 +330,9 @@ public:
/////////////////////////////////////////////////////////////////////////////
-/*
+extern const char *unit_operation_text[4];
+
+/*
Explain structure for a UNION.
A UNION may or may not have "Using filesort".
@@ -340,6 +348,7 @@ public:
{}
enum explain_node_type get_type() { return EXPLAIN_UNION; }
+ unit_common_op operation;
int get_select_id()
{
@@ -590,8 +599,8 @@ public:
key_name= NULL;
key_len= (uint)-1;
}
- void set(MEM_ROOT *root, KEY *key_name, uint key_len_arg);
- void set_pseudo_key(MEM_ROOT *root, const char *key_name);
+ bool set(MEM_ROOT *root, KEY *key_name, uint key_len_arg);
+ bool set_pseudo_key(MEM_ROOT *root, const char *key_name);
inline const char *get_key_name() const { return key_name; }
inline uint get_key_len() const { return key_len; }
diff --git a/sql/sql_expression_cache.cc b/sql/sql_expression_cache.cc
index 4b6d4b4472e..351ec258ddb 100644
--- a/sql/sql_expression_cache.cc
+++ b/sql/sql_expression_cache.cc
@@ -13,7 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_base.h"
#include "sql_select.h"
#include "sql_expression_cache.h"
@@ -97,6 +97,7 @@ void Expression_cache_tmptable::init()
List_iterator<Item> li(items);
Item_iterator_list it(li);
uint field_counter;
+ LEX_CSTRING cache_table_name= { STRING_WITH_LEN("subquery-cache-table") };
DBUG_ENTER("Expression_cache_tmptable::init");
DBUG_ASSERT(!inited);
inited= TRUE;
@@ -124,7 +125,7 @@ void Expression_cache_tmptable::init()
TMP_TABLE_ALL_COLUMNS) &
~TMP_TABLE_FORCE_MYISAM),
HA_POS_ERROR,
- (char *)"subquery-cache-table",
+ &cache_table_name,
TRUE)))
{
DBUG_PRINT("error", ("create_tmp_table failed, caching switched off"));
@@ -269,10 +270,11 @@ my_bool Expression_cache_tmptable::put_value(Item *value)
*(items.head_ref())= value;
fill_record(table_thd, cache_table, cache_table->field, items, TRUE, TRUE);
- if (table_thd->is_error())
+ if (unlikely(table_thd->is_error()))
goto err;;
- if ((error= cache_table->file->ha_write_tmp_row(cache_table->record[0])))
+ if (unlikely((error=
+ cache_table->file->ha_write_tmp_row(cache_table->record[0]))))
{
/* create_myisam_from_heap will generate error if needed */
if (cache_table->file->is_fatal_error(error, HA_CHECK_DUP))
diff --git a/sql/sql_get_diagnostics.cc b/sql/sql_get_diagnostics.cc
index b164a247c4d..b3ae423b914 100644
--- a/sql/sql_get_diagnostics.cc
+++ b/sql/sql_get_diagnostics.cc
@@ -13,6 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
+#include "mariadb.h"
#include "sql_list.h" // Sql_alloc, List, List_iterator
#include "sql_cmd.h" // Sql_cmd
#include "sql_class.h" // Diagnostics_area
@@ -68,7 +69,7 @@ Sql_cmd_get_diagnostics::execute(THD *thd)
const char *sqlstate= new_stmt_da.get_sqlstate();
/* In case of a fatal error, set it into the original DA.*/
- if (thd->is_fatal_error)
+ if (unlikely(thd->is_fatal_error))
{
save_stmt_da->set_error_status(sql_errno, message, sqlstate, NULL);
DBUG_RETURN(true);
@@ -80,7 +81,7 @@ Sql_cmd_get_diagnostics::execute(THD *thd)
message);
/* Appending might have failed. */
- if (! (rv= thd->is_error()))
+ if (unlikely(!(rv= thd->is_error())))
thd->get_stmt_da()->set_ok_status(0, 0, NULL);
DBUG_RETURN(rv);
@@ -109,6 +110,9 @@ Diagnostics_information_item::set_value(THD *thd, Item **value)
DBUG_ASSERT(srp);
+ /* GET DIAGNOSTICS is not allowed in prepared statements */
+ DBUG_ASSERT(srp->get_item_param() == NULL);
+
/* Set variable/parameter value. */
rv= srp->set_value(thd, thd->spcont, value);
@@ -213,8 +217,7 @@ Condition_information::aggregate(THD *thd, const Diagnostics_area *da)
DBUG_ENTER("Condition_information::aggregate");
/* Prepare the expression for evaluation. */
- if (!m_cond_number_expr->fixed &&
- m_cond_number_expr->fix_fields(thd, &m_cond_number_expr))
+ if (m_cond_number_expr->fix_fields_if_needed(thd, &m_cond_number_expr))
DBUG_RETURN(true);
cond_number= m_cond_number_expr->val_int();
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index a811cee998f..9fdfd612c96 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -52,7 +52,7 @@
cursor points at the first record).
*/
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "sql_handler.h"
#include "sql_base.h" // close_thread_tables
@@ -116,7 +116,7 @@ static char *mysql_ha_hash_get_key(SQL_HANDLER *table, size_t *key_len,
my_bool first __attribute__((unused)))
{
*key_len= table->handler_name.length + 1 ; /* include '\0' in comparisons */
- return table->handler_name.str;
+ return (char*) table->handler_name.str;
}
@@ -139,6 +139,48 @@ static void mysql_ha_hash_free(SQL_HANDLER *table)
delete table;
}
+static void mysql_ha_close_childs(THD *thd, TABLE_LIST *current_table_list,
+ TABLE_LIST **next_global)
+{
+ TABLE_LIST *table_list;
+ DBUG_ENTER("mysql_ha_close_childs");
+ DBUG_PRINT("info",("current_table_list: %p", current_table_list));
+ DBUG_PRINT("info",("next_global: %p", *next_global));
+ for (table_list = *next_global; table_list; table_list = *next_global)
+ {
+ *next_global = table_list->next_global;
+ DBUG_PRINT("info",("table_name: %s.%s", table_list->table->s->db.str,
+ table_list->table->s->table_name.str));
+ DBUG_PRINT("info",("parent_l: %p", table_list->parent_l));
+ if (table_list->parent_l == current_table_list)
+ {
+ DBUG_PRINT("info",("found child"));
+ TABLE *table = table_list->table;
+ if (table)
+ {
+ table->open_by_handler= 0;
+ if (!table->s->tmp_table)
+ {
+ (void) close_thread_table(thd, &table);
+ thd->mdl_context.release_lock(table_list->mdl_request.ticket);
+ }
+ else
+ {
+ thd->mark_tmp_table_as_free_for_reuse(table);
+ }
+ }
+ mysql_ha_close_childs(thd, table_list, next_global);
+ }
+ else
+ {
+ /* the end of child tables */
+ *next_global = table_list;
+ break;
+ }
+ }
+ DBUG_VOID_RETURN;
+}
+
/**
Close a HANDLER table.
@@ -153,14 +195,19 @@ static void mysql_ha_hash_free(SQL_HANDLER *table)
static void mysql_ha_close_table(SQL_HANDLER *handler)
{
+ DBUG_ENTER("mysql_ha_close_table");
THD *thd= handler->thd;
TABLE *table= handler->table;
- DBUG_ENTER("mysql_ha_close_table");
+ TABLE_LIST *current_table_list= NULL, *next_global;
/* check if table was already closed */
if (!table)
DBUG_VOID_RETURN;
+ if ((next_global= table->file->get_next_global_for_child()))
+ current_table_list= next_global->parent_l;
+
+ table->open_by_handler= 0;
if (!table->s->tmp_table)
{
/* Non temporary table. */
@@ -171,15 +218,17 @@ static void mysql_ha_close_table(SQL_HANDLER *handler)
}
table->file->ha_index_or_rnd_end();
- table->open_by_handler= 0;
close_thread_table(thd, &table);
+ if (current_table_list)
+ mysql_ha_close_childs(thd, current_table_list, &next_global);
thd->mdl_context.release_lock(handler->mdl_request.ticket);
}
else
{
/* Must be a temporary table */
table->file->ha_index_or_rnd_end();
- table->open_by_handler= 0;
+ if (current_table_list)
+ mysql_ha_close_childs(thd, current_table_list, &next_global);
thd->mark_tmp_table_as_free_for_reuse(table);
}
my_free(handler->lock);
@@ -219,7 +268,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen)
Query_arena backup_arena;
DBUG_ENTER("mysql_ha_open");
DBUG_PRINT("enter",("'%s'.'%s' as '%s' reopen: %d",
- tables->db, tables->table_name, tables->alias,
+ tables->db.str, tables->table_name.str, tables->alias.str,
reopen != 0));
if (thd->locked_tables_mode)
@@ -251,12 +300,12 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen)
}
else if (! reopen) /* Otherwise we have 'tables' already. */
{
- if (my_hash_search(&thd->handler_tables_hash, (uchar*) tables->alias,
- strlen(tables->alias) + 1))
+ if (my_hash_search(&thd->handler_tables_hash, (uchar*) tables->alias.str,
+ tables->alias.length + 1))
{
- DBUG_PRINT("info",("duplicate '%s'", tables->alias));
+ DBUG_PRINT("info",("duplicate '%s'", tables->alias.str));
DBUG_PRINT("exit",("ERROR"));
- my_error(ER_NONUNIQ_TABLE, MYF(0), tables->alias);
+ my_error(ER_NONUNIQ_TABLE, MYF(0), tables->alias.str);
DBUG_RETURN(TRUE);
}
}
@@ -283,12 +332,12 @@ 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, tables->table_name,
+ tables->mdl_request.init(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 */
- tables->required_type= FRMTYPE_TABLE;
+ tables->required_type= TABLE_TYPE_NORMAL;
/*
We use open_tables() here, rather than, say,
@@ -298,7 +347,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen)
error= (thd->open_temporary_tables(tables) ||
open_tables(thd, &tables, &counter, 0));
- if (error)
+ if (unlikely(error))
goto err;
table= tables->table;
@@ -311,29 +360,39 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen)
goto err;
}
- if (tables->mdl_request.ticket &&
- thd->mdl_context.has_lock(mdl_savepoint, tables->mdl_request.ticket))
+ DBUG_PRINT("info",("clone_tickets start"));
+ for (TABLE_LIST *table_list= tables; table_list;
+ table_list= table_list->next_global)
+ {
+ DBUG_PRINT("info",("table_list %s.%s", table_list->table->s->db.str,
+ table_list->table->s->table_name.str));
+ if (table_list->mdl_request.ticket &&
+ thd->mdl_context.has_lock(mdl_savepoint, table_list->mdl_request.ticket))
{
+ DBUG_PRINT("info",("clone_tickets"));
/* The ticket returned is within a savepoint. Make a copy. */
- error= thd->mdl_context.clone_ticket(&tables->mdl_request);
- tables->table->mdl_ticket= tables->mdl_request.ticket;
- if (error)
+ error= thd->mdl_context.clone_ticket(&table_list->mdl_request);
+ table_list->table->mdl_ticket= table_list->mdl_request.ticket;
+ if (unlikely(error))
goto err;
}
+ }
+ DBUG_PRINT("info",("clone_tickets end"));
if (! reopen)
{
/* copy data to sql_handler */
if (!(sql_handler= new SQL_HANDLER(thd)))
goto err;
- init_alloc_root(&sql_handler->mem_root, 1024, 0, MYF(MY_THREAD_SPECIFIC));
+ init_alloc_root(&sql_handler->mem_root, "sql_handler", 1024, 0,
+ MYF(MY_THREAD_SPECIFIC));
- sql_handler->db.length= strlen(tables->db);
- sql_handler->table_name.length= strlen(tables->table_name);
- sql_handler->handler_name.length= strlen(tables->alias);
+ 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,
- &sql_handler->db.str,
+ &sql_handler->base_data,
(uint) sql_handler->db.length + 1,
&sql_handler->table_name.str,
(uint) sql_handler->table_name.length + 1,
@@ -341,12 +400,12 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen)
(uint) sql_handler->handler_name.length + 1,
NullS)))
goto err;
- sql_handler->base_data= sql_handler->db.str; // Free this
- memcpy(sql_handler->db.str, tables->db, sql_handler->db.length +1);
- memcpy(sql_handler->table_name.str, tables->table_name,
- sql_handler->table_name.length+1);
- memcpy(sql_handler->handler_name.str, tables->alias,
- sql_handler->handler_name.length +1);
+ sql_handler->db.str= sql_handler->base_data;
+ memcpy((char*) sql_handler->db.str, tables->db.str, tables->db.length +1);
+ memcpy((char*) sql_handler->table_name.str, tables->table_name.str,
+ tables->table_name.length+1);
+ memcpy((char*) sql_handler->handler_name.str, tables->alias.str,
+ tables->alias.length +1);
/* add to hash */
if (my_hash_insert(&thd->handler_tables_hash, (uchar*) sql_handler))
@@ -367,8 +426,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen)
thd->set_n_backup_active_arena(&sql_handler->arena, &backup_arena);
error= table->fill_item_list(&sql_handler->fields);
thd->restore_active_arena(&sql_handler->arena, &backup_arena);
-
- if (error)
+ if (unlikely(error))
goto err;
sql_handler->mdl_request.move_from(tables->mdl_request);
@@ -380,26 +438,37 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen)
/* Restore the state. */
thd->set_open_tables(backup_open_tables);
+ DBUG_PRINT("info",("set_lock_duration start"));
if (sql_handler->mdl_request.ticket)
{
thd->mdl_context.set_lock_duration(sql_handler->mdl_request.ticket,
MDL_EXPLICIT);
thd->mdl_context.set_needs_thr_lock_abort(TRUE);
}
+ for (TABLE_LIST *table_list= tables->next_global; table_list;
+ table_list= table_list->next_global)
+ {
+ DBUG_PRINT("info",("table_list %s.%s", table_list->table->s->db.str,
+ table_list->table->s->table_name.str));
+ if (table_list->mdl_request.ticket)
+ {
+ thd->mdl_context.set_lock_duration(table_list->mdl_request.ticket,
+ MDL_EXPLICIT);
+ thd->mdl_context.set_needs_thr_lock_abort(TRUE);
+ }
+ }
+ DBUG_PRINT("info",("set_lock_duration end"));
/*
- Assert that the above check prevents opening of views and merge tables.
- For temporary tables, TABLE::next can be set even if only one table
- was opened for HANDLER as it is used to link them together.
- */
- DBUG_ASSERT(sql_handler->table->next == NULL ||
- sql_handler->table->s->tmp_table);
- /*
If it's a temp table, don't reset table->query_id as the table is
being used by this handler. For non-temp tables we use this flag
in asserts.
*/
- table->open_by_handler= 1;
+ for (TABLE_LIST *table_list= tables; table_list;
+ table_list= table_list->next_global)
+ {
+ table_list->table->open_by_handler= 1;
+ }
if (! reopen)
my_ok(thd);
@@ -450,7 +519,7 @@ bool mysql_ha_close(THD *thd, TABLE_LIST *tables)
SQL_HANDLER *handler;
DBUG_ENTER("mysql_ha_close");
DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
- tables->db, tables->table_name, tables->alias));
+ tables->db.str, tables->table_name.str, tables->alias.str));
if (thd->locked_tables_mode)
{
@@ -459,15 +528,15 @@ bool mysql_ha_close(THD *thd, TABLE_LIST *tables)
}
if ((my_hash_inited(&thd->handler_tables_hash)) &&
(handler= (SQL_HANDLER*) my_hash_search(&thd->handler_tables_hash,
- (uchar*) tables->alias,
- strlen(tables->alias) + 1)))
+ (const uchar*) tables->alias.str,
+ tables->alias.length + 1)))
{
mysql_ha_close_table(handler);
my_hash_delete(&thd->handler_tables_hash, (uchar*) handler);
}
else
{
- my_error(ER_UNKNOWN_TABLE, MYF(0), tables->alias, "HANDLER");
+ my_error(ER_UNKNOWN_TABLE, MYF(0), tables->alias.str, "HANDLER");
DBUG_PRINT("exit",("ERROR"));
DBUG_RETURN(TRUE);
}
@@ -494,13 +563,13 @@ bool mysql_ha_close(THD *thd, TABLE_LIST *tables)
@return handler
*/
-SQL_HANDLER *mysql_ha_find_handler(THD *thd, const char *name)
+static SQL_HANDLER *mysql_ha_find_handler(THD *thd, const LEX_CSTRING *name)
{
SQL_HANDLER *handler;
if ((my_hash_inited(&thd->handler_tables_hash)) &&
(handler= (SQL_HANDLER*) my_hash_search(&thd->handler_tables_hash,
- (uchar*) name,
- strlen(name) + 1)))
+ (const uchar*) name->str,
+ name->length + 1)))
{
DBUG_PRINT("info-in-hash",("'%s'.'%s' as '%s' table: %p",
handler->db.str,
@@ -510,9 +579,8 @@ SQL_HANDLER *mysql_ha_find_handler(THD *thd, const char *name)
{
/* The handler table has been closed. Re-open it. */
TABLE_LIST tmp;
- tmp.init_one_table(handler->db.str, handler->db.length,
- handler->table_name.str, handler->table_name.length,
- handler->handler_name.str, TL_READ);
+ tmp.init_one_table(&handler->db, &handler->table_name,
+ &handler->handler_name, TL_READ);
if (mysql_ha_open(thd, &tmp, handler))
{
@@ -523,7 +591,7 @@ SQL_HANDLER *mysql_ha_find_handler(THD *thd, const char *name)
}
else
{
- my_error(ER_UNKNOWN_TABLE, MYF(0), name, "HANDLER");
+ my_error(ER_UNKNOWN_TABLE, MYF(0), name->str, "HANDLER");
return 0;
}
return handler;
@@ -548,8 +616,8 @@ SQL_HANDLER *mysql_ha_find_handler(THD *thd, const char *name)
static bool
mysql_ha_fix_cond_and_key(SQL_HANDLER *handler,
- enum enum_ha_read_modes mode, char *keyname,
- List<Item> *key_expr,
+ enum enum_ha_read_modes mode, const char *keyname,
+ List<Item> *key_expr, enum ha_rkey_function ha_rkey_mode,
Item *cond, bool in_prepare)
{
THD *thd= handler->thd;
@@ -559,8 +627,7 @@ mysql_ha_fix_cond_and_key(SQL_HANDLER *handler,
/* This can only be true for temp tables */
if (table->query_id != thd->query_id)
cond->cleanup(); // File was reopened
- if ((!cond->fixed &&
- cond->fix_fields(thd, &cond)) || cond->check_cols(1))
+ if (cond->fix_fields_if_needed_for_bool(thd, &cond))
return 1;
}
@@ -570,7 +637,7 @@ mysql_ha_fix_cond_and_key(SQL_HANDLER *handler,
if (handler->keyno < 0 ||
my_strcasecmp(&my_charset_latin1,
keyname,
- table->s->key_info[handler->keyno].name))
+ table->s->key_info[handler->keyno].name.str))
{
if ((handler->keyno= find_type(keyname, &table->s->keynames,
FIND_TYPE_NO_PREFIX) - 1) < 0)
@@ -591,6 +658,18 @@ mysql_ha_fix_cond_and_key(SQL_HANDLER *handler,
Item *item;
key_part_map keypart_map;
uint key_len;
+ const KEY *c_key= table->s->key_info + handler->keyno;
+
+ if ((c_key->flags & HA_SPATIAL) ||
+ c_key->algorithm == HA_KEY_ALG_FULLTEXT ||
+ (ha_rkey_mode != HA_READ_KEY_EXACT &&
+ (table->file->index_flags(handler->keyno, 0, TRUE) &
+ (HA_READ_NEXT | HA_READ_PREV | HA_READ_RANGE)) == 0))
+ {
+ my_error(ER_KEY_DOESNT_SUPPORT, MYF(0),
+ table->file->index_type(handler->keyno), keyinfo->name.str);
+ return 1;
+ }
if (key_expr->elements > keyinfo->user_defined_key_parts)
{
@@ -598,14 +677,23 @@ mysql_ha_fix_cond_and_key(SQL_HANDLER *handler,
keyinfo->user_defined_key_parts);
return 1;
}
+
+ if (key_expr->elements < keyinfo->user_defined_key_parts &&
+ (table->file->index_flags(handler->keyno, 0, TRUE) &
+ HA_ONLY_WHOLE_INDEX))
+ {
+ my_error(ER_KEY_DOESNT_SUPPORT, MYF(0),
+ table->file->index_type(handler->keyno), keyinfo->name.str);
+ return 1;
+ }
+
for (keypart_map= key_len=0 ; (item=it_ke++) ; key_part++)
{
my_bitmap_map *old_map;
/* note that 'item' can be changed by fix_fields() call */
- if ((!item->fixed &&
- item->fix_fields(thd, it_ke.ref())) ||
- (item= *it_ke.ref())->check_cols(1))
+ if (item->fix_fields_if_needed_for_scalar(thd, it_ke.ref()))
return 1;
+ item= *it_ke.ref();
if (item->used_tables() & ~(RAND_TABLE_BIT | PARAM_TABLE_BIT))
{
my_error(ER_WRONG_ARGUMENTS,MYF(0),"HANDLER ... READ");
@@ -670,7 +758,7 @@ mysql_ha_fix_cond_and_key(SQL_HANDLER *handler,
*/
bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
- enum enum_ha_read_modes mode, char *keyname,
+ enum enum_ha_read_modes mode, const char *keyname,
List<Item> *key_expr,
enum ha_rkey_function ha_rkey_mode, Item *cond,
ha_rows select_limit_cnt, ha_rows offset_limit_cnt)
@@ -686,7 +774,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
MDL_deadlock_and_lock_abort_error_handler sql_handler_lock_error;
DBUG_ENTER("mysql_ha_read");
DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
- tables->db, tables->table_name, tables->alias));
+ tables->db.str, tables->table_name.str, tables->alias.str));
if (thd->locked_tables_mode)
{
@@ -695,7 +783,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
}
retry:
- if (!(handler= mysql_ha_find_handler(thd, tables->alias)))
+ if (!(handler= mysql_ha_find_handler(thd, &tables->alias)))
goto err0;
table= handler->table;
@@ -706,8 +794,14 @@ retry:
{
int lock_error;
- if (handler->lock->lock_count > 0)
- handler->lock->locks[0]->type= handler->lock->locks[0]->org_type;
+ THR_LOCK_DATA **pos,**end;
+ for (pos= handler->lock->locks,
+ end= handler->lock->locks + handler->lock->lock_count;
+ pos < end;
+ pos++)
+ {
+ pos[0]->type= pos[0]->org_type;
+ }
/* save open_tables state */
TABLE* backup_open_tables= thd->open_tables;
@@ -762,11 +856,12 @@ retry:
goto retry;
}
- if (lock_error)
+ if (unlikely(lock_error))
goto err0; // mysql_lock_tables() printed error message already
}
- if (mysql_ha_fix_cond_and_key(handler, mode, keyname, key_expr, cond, 0))
+ if (mysql_ha_fix_cond_and_key(handler, mode, keyname, key_expr,
+ ha_rkey_mode, cond, 0))
goto err;
mode= handler->mode;
keyno= handler->keyno;
@@ -804,14 +899,14 @@ retry:
case RFIRST:
if (keyname)
{
- if (!(error= table->file->ha_index_or_rnd_end()) &&
- !(error= table->file->ha_index_init(keyno, 1)))
+ if (likely(!(error= table->file->ha_index_or_rnd_end())) &&
+ likely(!(error= table->file->ha_index_init(keyno, 1))))
error= table->file->ha_index_first(table->record[0]);
}
else
{
- if (!(error= table->file->ha_index_or_rnd_end()) &&
- !(error= table->file->ha_rnd_init(1)))
+ if (likely(!(error= table->file->ha_index_or_rnd_end())) &&
+ likely(!(error= table->file->ha_rnd_init(1))))
error= table->file->ha_rnd_next(table->record[0]);
}
mode= RNEXT;
@@ -830,8 +925,8 @@ retry:
/* else fall through */
case RLAST:
DBUG_ASSERT(keyname != 0);
- if (!(error= table->file->ha_index_or_rnd_end()) &&
- !(error= table->file->ha_index_init(keyno, 1)))
+ if (likely(!(error= table->file->ha_index_or_rnd_end())) &&
+ likely(!(error= table->file->ha_index_init(keyno, 1))))
error= table->file->ha_index_last(table->record[0]);
mode=RPREV;
break;
@@ -845,13 +940,13 @@ retry:
{
DBUG_ASSERT(keyname != 0);
- if (!(key= (uchar*) thd->calloc(ALIGN_SIZE(handler->key_len))))
+ if (unlikely(!(key= (uchar*) thd->calloc(ALIGN_SIZE(handler->key_len)))))
goto err;
- if ((error= table->file->ha_index_or_rnd_end()))
+ if (unlikely((error= table->file->ha_index_or_rnd_end())))
break;
key_copy(key, table->record[0], table->key_info + keyno,
handler->key_len);
- if (!(error= table->file->ha_index_init(keyno, 1)))
+ if (unlikely(!(error= table->file->ha_index_init(keyno, 1))))
error= table->file->ha_index_read_map(table->record[0],
key, handler->keypart_map,
ha_rkey_mode);
@@ -864,17 +959,15 @@ retry:
goto err;
}
- if (error)
+ if (unlikely(error))
{
- if (error == HA_ERR_RECORD_DELETED)
- continue;
if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
{
/* Don't give error in the log file for some expected problems */
if (error != HA_ERR_RECORD_CHANGED && error != HA_ERR_WRONG_COMMAND)
sql_print_error("mysql_ha_read: Got error %d when reading "
"table '%s'",
- error, tables->table_name);
+ error, tables->table_name.str);
table->file->print_error(error,MYF(0));
table->file->ha_index_or_rnd_end();
goto err;
@@ -925,16 +1018,19 @@ err0:
*/
SQL_HANDLER *mysql_ha_read_prepare(THD *thd, TABLE_LIST *tables,
- enum enum_ha_read_modes mode, char *keyname,
- List<Item> *key_expr, Item *cond)
+ enum enum_ha_read_modes mode,
+ const char *keyname,
+ List<Item> *key_expr, enum ha_rkey_function ha_rkey_mode,
+ Item *cond)
{
SQL_HANDLER *handler;
DBUG_ENTER("mysql_ha_read_prepare");
- if (!(handler= mysql_ha_find_handler(thd, tables->alias)))
+ if (!(handler= mysql_ha_find_handler(thd, &tables->alias)))
DBUG_RETURN(0);
tables->table= handler->table; // This is used by fix_fields
handler->table->pos_in_table_list= tables;
- if (mysql_ha_fix_cond_and_key(handler, mode, keyname, key_expr, cond, 1))
+ if (mysql_ha_fix_cond_and_key(handler, mode, keyname, key_expr,
+ ha_rkey_mode, cond, 1))
DBUG_RETURN(0);
DBUG_RETURN(handler);
}
@@ -967,7 +1063,7 @@ static SQL_HANDLER *mysql_ha_find_match(THD *thd, TABLE_LIST *tables)
{
if (tables->is_anonymous_derived_table())
continue;
- if ((! *tables->db ||
+ if ((! tables->db.str[0] ||
! my_strcasecmp(&my_charset_latin1, hash_tables->db.str,
tables->get_db_name())) &&
! my_strcasecmp(&my_charset_latin1, hash_tables->table_name.str,
diff --git a/sql/sql_handler.h b/sql/sql_handler.h
index 9f0ef7bd08d..8d9dc5a9af2 100644
--- a/sql/sql_handler.h
+++ b/sql/sql_handler.h
@@ -31,9 +31,9 @@ public:
TABLE *table;
List<Item> fields; /* Fields, set on open */
THD *thd;
- LEX_STRING handler_name;
- LEX_STRING db;
- LEX_STRING table_name;
+ LEX_CSTRING handler_name;
+ LEX_CSTRING db;
+ LEX_CSTRING table_name;
MEM_ROOT mem_root;
MYSQL_LOCK *lock;
MDL_request mdl_request;
@@ -68,7 +68,7 @@ struct TABLE_LIST;
bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen);
bool mysql_ha_close(THD *thd, TABLE_LIST *tables);
-bool mysql_ha_read(THD *, TABLE_LIST *,enum enum_ha_read_modes,char *,
+bool mysql_ha_read(THD *, TABLE_LIST *,enum enum_ha_read_modes, const char *,
List<Item> *,enum ha_rkey_function,Item *,ha_rows,ha_rows);
void mysql_ha_flush(THD *thd);
void mysql_ha_flush_tables(THD *thd, TABLE_LIST *all_tables);
@@ -78,6 +78,8 @@ void mysql_ha_set_explicit_lock_duration(THD *thd);
void mysql_ha_rm_temporary_tables(THD *thd);
SQL_HANDLER *mysql_ha_read_prepare(THD *thd, TABLE_LIST *tables,
- enum enum_ha_read_modes mode, char *keyname,
- List<Item> *key_expr, Item *cond);
+ enum enum_ha_read_modes mode,
+ const char *keyname,
+ List<Item> *key_expr, enum ha_rkey_function ha_rkey_mode,
+ Item *cond);
#endif
diff --git a/sql/sql_help.cc b/sql/sql_help.cc
index c05c9a7d229..48df993afcb 100644
--- a/sql/sql_help.cc
+++ b/sql/sql_help.cc
@@ -13,7 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#include "sql_help.h"
@@ -92,10 +92,14 @@ 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,
- find_fields->field_name);
+ Item_field *field= (new (thd->mem_root)
+ Item_field(thd, context,
+ "mysql",
+ find_fields->table_name,
+ &field_name));
if (!(find_fields->field= find_field_in_tables(thd, field, tables, NULL,
0, REPORT_ALL_ERRORS, 1,
TRUE)))
@@ -198,7 +202,7 @@ int search_topics(THD *thd, TABLE *topics, struct st_find_field *find_fields,
FALSE))
DBUG_RETURN(0);
- while (!read_record_info.read_record(&read_record_info))
+ while (!read_record_info.read_record())
{
if (!select->cond->val_int()) // Doesn't match like
continue;
@@ -242,7 +246,7 @@ int search_keyword(THD *thd, TABLE *keywords,
FALSE))
DBUG_RETURN(0);
- while (!read_record_info.read_record(&read_record_info) && count<2)
+ while (!read_record_info.read_record() && count<2)
{
if (!select->cond->val_int()) // Dosn't match like
continue;
@@ -376,7 +380,7 @@ int search_categories(THD *thd, TABLE *categories,
if (init_read_record(&read_record_info, thd, categories, select, NULL,
1, 0, FALSE))
DBUG_RETURN(0);
- while (!read_record_info.read_record(&read_record_info))
+ while (!read_record_info.read_record())
{
if (select && !select->cond->val_int())
continue;
@@ -414,7 +418,7 @@ void get_all_items_for_category(THD *thd, TABLE *items, Field *pfname,
FALSE))
DBUG_VOID_RETURN;
- while (!read_record_info.read_record(&read_record_info))
+ while (!read_record_info.read_record())
{
if (!select->cond->val_int())
continue;
@@ -607,15 +611,15 @@ int send_variant_2_list(MEM_ROOT *mem_root, Protocol *protocol,
SQL_SELECT *prepare_simple_select(THD *thd, Item *cond,
TABLE *table, int *error)
{
- if (!cond->fixed)
- cond->fix_fields(thd, &cond); // can never fail
+ cond->fix_fields_if_needed(thd, &cond); // can never fail
/* Assume that no indexes cover all required fields */
table->covering_keys.clear_all();
SQL_SELECT *res= make_select(table, 0, 0, cond, 0, 0, error);
- if (*error || (res && res->check_quick(thd, 0, HA_POS_ERROR)) ||
- (res && res->quick && res->quick->reset()))
+ if (unlikely(*error) ||
+ (likely(res) && unlikely(res->check_quick(thd, 0, HA_POS_ERROR))) ||
+ (likely(res) && res->quick && unlikely(res->quick->reset())))
{
delete res;
res=0;
@@ -641,7 +645,7 @@ SQL_SELECT *prepare_simple_select(THD *thd, Item *cond,
# created SQL_SELECT
*/
-SQL_SELECT *prepare_select_for_name(THD *thd, const char *mask, uint mlen,
+SQL_SELECT *prepare_select_for_name(THD *thd, const char *mask, size_t mlen,
TABLE_LIST *tables, TABLE *table,
Field *pfname, int *error)
{
@@ -650,11 +654,11 @@ SQL_SELECT *prepare_select_for_name(THD *thd, const char *mask, uint mlen,
Item_func_like(thd,
new (mem_root)
Item_field(thd, pfname),
- new (mem_root) Item_string(thd, mask, mlen,
+ new (mem_root) Item_string(thd, mask, (uint)mlen,
pfname->charset()),
new (mem_root) Item_string_ascii(thd, "\\"),
FALSE);
- if (thd->is_fatal_error)
+ if (unlikely(thd->is_fatal_error))
return 0; // OOM
return prepare_simple_select(thd, cond, table, error);
}
@@ -682,23 +686,19 @@ static bool mysqld_help_internal(THD *thd, const char *mask)
List<String> topics_list, categories_list, subcategories_list;
String name, description, example;
int count_topics, count_categories, error;
- uint mlen= strlen(mask);
+ size_t mlen= strlen(mask);
size_t i;
MEM_ROOT *mem_root= thd->mem_root;
+ LEX_CSTRING MYSQL_HELP_TOPIC_NAME= {STRING_WITH_LEN("help_topic") };
+ LEX_CSTRING MYSQL_HELP_CATEGORY_NAME= {STRING_WITH_LEN("help_category") };
+ LEX_CSTRING MYSQL_HELP_RELATION_NAME= {STRING_WITH_LEN("help_relation") };
+ LEX_CSTRING MYSQL_HELP_KEYWORD_NAME= {STRING_WITH_LEN("help_keyword") };
DBUG_ENTER("mysqld_help");
- tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
- C_STRING_WITH_LEN("help_topic"),
- "help_topic", TL_READ);
- tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
- C_STRING_WITH_LEN("help_category"),
- "help_category", TL_READ);
- tables[2].init_one_table(C_STRING_WITH_LEN("mysql"),
- C_STRING_WITH_LEN("help_relation"),
- "help_relation", TL_READ);
- tables[3].init_one_table(C_STRING_WITH_LEN("mysql"),
- C_STRING_WITH_LEN("help_keyword"),
- "help_keyword", TL_READ);
+ tables[0].init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_HELP_TOPIC_NAME, 0, TL_READ);
+ tables[1].init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_HELP_CATEGORY_NAME, 0, TL_READ);
+ tables[2].init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_HELP_RELATION_NAME, 0, TL_READ);
+ tables[3].init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_HELP_KEYWORD_NAME, 0, TL_READ);
tables[0].next_global= tables[0].next_local=
tables[0].next_name_resolution_table= &tables[1];
tables[1].next_global= tables[1].next_local=
diff --git a/sql/sql_hset.h b/sql/sql_hset.h
index 18113ec2e7e..7834349a2f7 100644
--- a/sql/sql_hset.h
+++ b/sql/sql_hset.h
@@ -15,7 +15,6 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
-#include "my_global.h"
#include "hash.h"
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index ec784bc6df4..a24cb61449b 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -1,6 +1,6 @@
/*
Copyright (c) 2000, 2016, Oracle and/or its affiliates.
- Copyright (c) 2010, 2018, MariaDB Corporation
+ Copyright (c) 2010, 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
@@ -56,7 +56,7 @@
*/
-#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
#include "sql_insert.h"
#include "sql_update.h" // compare_record
@@ -168,9 +168,9 @@ static bool check_view_single_update(List<Item> &fields, List<Item> *values,
if (!tbl->single_table_updatable())
{
if (insert)
- my_error(ER_NON_INSERTABLE_TABLE, MYF(0), view->alias, "INSERT");
+ my_error(ER_NON_INSERTABLE_TABLE, MYF(0), view->alias.str, "INSERT");
else
- my_error(ER_NON_UPDATABLE_TABLE, MYF(0), view->alias, "UPDATE");
+ my_error(ER_NON_UPDATABLE_TABLE, MYF(0), view->alias.str, "UPDATE");
return TRUE;
}
*map= tables;
@@ -210,7 +210,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
if (!table_list->single_table_updatable())
{
- my_error(ER_NON_INSERTABLE_TABLE, MYF(0), table_list->alias, "INSERT");
+ my_error(ER_NON_INSERTABLE_TABLE, MYF(0), table_list->alias.str, "INSERT");
DBUG_RETURN(-1);
}
@@ -222,7 +222,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
table_list->view_db.str, table_list->view_name.str);
DBUG_RETURN(-1);
}
- if (values.elements != table->s->fields)
+ if (values.elements != table->s->visible_fields)
{
my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), 1L);
DBUG_RETURN(-1);
@@ -290,7 +290,8 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
if (check_unique && thd->dup_field)
{
- my_error(ER_FIELD_SPECIFIED_TWICE, MYF(0), thd->dup_field->field_name);
+ my_error(ER_FIELD_SPECIFIED_TWICE, MYF(0),
+ thd->dup_field->field_name.str);
DBUG_RETURN(-1);
}
}
@@ -303,7 +304,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
(table_list->view &&
check_view_insertability(thd, table_list)))
{
- my_error(ER_NON_INSERTABLE_TABLE, MYF(0), table_list->alias, "INSERT");
+ my_error(ER_NON_INSERTABLE_TABLE, MYF(0), table_list->alias.str, "INSERT");
DBUG_RETURN(-1);
}
@@ -329,7 +330,8 @@ static bool has_no_default_value(THD *thd, Field *field, TABLE_LIST *table_list)
else
{
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_NO_DEFAULT_FOR_FIELD,
- ER_THD(thd, ER_NO_DEFAULT_FOR_FIELD), field->field_name);
+ ER_THD(thd, ER_NO_DEFAULT_FOR_FIELD),
+ field->field_name.str);
}
return thd->really_abort_on_warning();
}
@@ -674,6 +676,12 @@ static void save_insert_query_plan(THD* thd, TABLE_LIST *table_list)
}
+Field **TABLE::field_to_fill()
+{
+ return triggers && triggers->nullable_fields() ? triggers->nullable_fields() : field;
+}
+
+
/**
INSERT statement implementation
@@ -734,19 +742,13 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
*/
if (table_list->lock_type == TL_WRITE_DELAYED &&
thd->locked_tables_mode &&
- find_locked_table(thd->open_tables, table_list->db,
- table_list->table_name))
+ find_locked_table(thd->open_tables, table_list->db.str,
+ table_list->table_name.str))
{
my_error(ER_DELAYED_INSERT_TABLE_LOCKED, MYF(0),
- table_list->table_name);
+ table_list->table_name.str);
DBUG_RETURN(TRUE);
}
- /*
- mark the table_list as a target for insert, to skip the DT/view prepare phase
- for correct access rights checks
- TODO: remove this hack
- */
- table_list->skip_prepare_derived= TRUE;
if (table_list->lock_type == TL_WRITE_DELAYED)
{
@@ -759,9 +761,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
DBUG_RETURN(TRUE);
}
+ THD_STAGE_INFO(thd, stage_init_update);
lock_type= table_list->lock_type;
-
- THD_STAGE_INFO(thd, stage_init);
thd->lex->used_tables=0;
values= its++;
if (bulk_parameters_set(thd))
@@ -858,7 +859,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
#endif
error=0;
- THD_STAGE_INFO(thd, stage_update);
if (duplic == DUP_REPLACE &&
(!table->triggers || !table->triggers->has_delete_triggers()))
table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
@@ -943,6 +943,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
goto values_loop_end;
}
}
+
+ THD_STAGE_INFO(thd, stage_update);
do
{
DBUG_PRINT("info", ("iteration %llu", iteration));
@@ -963,8 +965,9 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
*/
restore_record(table,s->default_values); // Get empty record
table->reset_default_fields();
- if (fill_record_n_invoke_before_triggers(thd, table, fields, *values, 0,
- TRG_EVENT_INSERT))
+ if (unlikely(fill_record_n_invoke_before_triggers(thd, table, fields,
+ *values, 0,
+ TRG_EVENT_INSERT)))
{
if (values_list.elements != 1 && ! thd->is_error())
{
@@ -986,7 +989,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
No field list, all fields are set explicitly:
INSERT INTO t1 VALUES (values)
*/
- if (thd->lex->used_tables) // Column used in values()
+ if (thd->lex->used_tables || // Column used in values()
+ table->s->visible_fields != table->s->fields)
restore_record(table,s->default_values); // Get empty record
else
{
@@ -1007,9 +1011,11 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
}
}
table->reset_default_fields();
- if (fill_record_n_invoke_before_triggers(thd, table,
- table->field_to_fill(),
- *values, 0, TRG_EVENT_INSERT))
+ if (unlikely(fill_record_n_invoke_before_triggers(thd, table,
+ table->
+ field_to_fill(),
+ *values, 0,
+ TRG_EVENT_INSERT)))
{
if (values_list.elements != 1 && ! thd->is_error())
{
@@ -1022,16 +1028,16 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
}
/*
- with triggers a field can get a value *conditionally*, so we have to repeat
- has_no_default_value() check for every row
+ with triggers a field can get a value *conditionally*, so we have to
+ repeat has_no_default_value() check for every row
*/
if (table->triggers &&
table->triggers->has_triggers(TRG_EVENT_INSERT, TRG_ACTION_BEFORE))
{
for (Field **f=table->field ; *f ; f++)
{
- if (!(*f)->has_explicit_value() &&
- has_no_default_value(thd, *f, table_list))
+ if (unlikely(!(*f)->has_explicit_value() &&
+ has_no_default_value(thd, *f, table_list)))
{
error= 1;
goto values_loop_end;
@@ -1064,7 +1070,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
else
#endif
error=write_record(thd, table ,&info);
- if (error)
+ if (unlikely(error))
break;
thd->get_stmt_da()->inc_current_row_for_warning();
}
@@ -1081,9 +1087,9 @@ values_loop_end:
user
*/
#ifndef EMBEDDED_LIBRARY
- if (lock_type == TL_WRITE_DELAYED)
+ if (unlikely(lock_type == TL_WRITE_DELAYED))
{
- if (!error)
+ if (likely(!error))
{
info.copied=values_list.elements;
end_delayed_insert(thd);
@@ -1097,7 +1103,8 @@ values_loop_end:
auto_inc values from the delayed_insert thread as they share TABLE.
*/
table->file->ha_release_auto_increment();
- if (using_bulk_insert && table->file->ha_end_bulk_insert() && !error)
+ if (using_bulk_insert && unlikely(table->file->ha_end_bulk_insert()) &&
+ !error)
{
table->file->print_error(my_errno,MYF(0));
error=1;
@@ -1111,7 +1118,7 @@ values_loop_end:
transactional_table= table->file->has_transactions();
- if ((changed= (info.copied || info.deleted || info.updated)))
+ if (likely(changed= (info.copied || info.deleted || info.updated)))
{
/*
Invalidate the table in the query cache if something changed.
@@ -1149,8 +1156,10 @@ values_loop_end:
}
else
errcode= query_error_code(thd, thd->killed == NOT_KILLED);
-
- /* bug#22725:
+
+ ScopedStatementReplication scoped_stmt_rpl(
+ table->versioned(VERS_TRX_ID) ? thd : NULL);
+ /* bug#22725:
A query which per-row-loop can not be interrupted with
KILLED, like INSERT, and that does not invoke stored
@@ -1177,13 +1186,13 @@ values_loop_end:
else if (thd->binlog_query(THD::ROW_QUERY_TYPE,
log_query.c_ptr(), log_query.length(),
transactional_table, FALSE, FALSE,
- errcode))
+ errcode) > 0)
error= 1;
}
else if (thd->binlog_query(THD::ROW_QUERY_TYPE,
thd->query(), thd->query_length(),
transactional_table, FALSE, FALSE,
- errcode))
+ errcode) > 0)
error= 1;
}
}
@@ -1214,7 +1223,7 @@ values_loop_end:
(!table->triggers || !table->triggers->has_delete_triggers()))
table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
- if (error)
+ if (unlikely(error))
goto abort;
if (thd->lex->analyze_stmt)
{
@@ -1301,7 +1310,7 @@ static bool check_view_insertability(THD * thd, TABLE_LIST *view)
uint used_fields_buff_size= bitmap_buffer_size(table->s->fields);
uint32 *used_fields_buff= (uint32*)thd->alloc(used_fields_buff_size);
MY_BITMAP used_fields;
- enum_mark_columns save_mark_used_columns= thd->mark_used_columns;
+ enum_column_usage saved_column_usage= thd->column_usage;
DBUG_ENTER("check_key_in_view");
if (!used_fields_buff)
@@ -1317,20 +1326,20 @@ static bool check_view_insertability(THD * thd, TABLE_LIST *view)
we must not set query_id for fields as they're not
really used in this context
*/
- thd->mark_used_columns= MARK_COLUMNS_NONE;
+ thd->column_usage= COLUMNS_WRITE;
/* check simplicity and prepare unique test of view */
for (trans= trans_start; trans != trans_end; trans++)
{
- if (!trans->item->fixed && trans->item->fix_fields(thd, &trans->item))
+ if (trans->item->fix_fields_if_needed(thd, &trans->item))
{
- thd->mark_used_columns= save_mark_used_columns;
+ thd->column_usage= saved_column_usage;
DBUG_RETURN(TRUE);
}
Item_field *field;
/* simple SELECT list entry (field without expression) */
if (!(field= trans->item->field_for_view_update()))
{
- thd->mark_used_columns= save_mark_used_columns;
+ thd->column_usage= saved_column_usage;
DBUG_RETURN(TRUE);
}
if (field->field->unireg_check == Field::NEXT_NUMBER)
@@ -1342,7 +1351,7 @@ static bool check_view_insertability(THD * thd, TABLE_LIST *view)
*/
trans->item= field;
}
- thd->mark_used_columns= save_mark_used_columns;
+ thd->column_usage= saved_column_usage;
/* unique test */
for (trans= trans_start; trans != trans_end; trans++)
{
@@ -1383,7 +1392,7 @@ static bool mysql_prepare_insert_check_table(THD *thd, TABLE_LIST *table_list,
if (!table_list->single_table_updatable())
{
- my_error(ER_NON_INSERTABLE_TABLE, MYF(0), table_list->alias, "INSERT");
+ my_error(ER_NON_INSERTABLE_TABLE, MYF(0), table_list->alias.str, "INSERT");
DBUG_RETURN(TRUE);
}
/*
@@ -1570,6 +1579,13 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
if (!table)
table= table_list->table;
+ 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);
+ }
+
if (!select_insert)
{
Item *fake_conds= 0;
@@ -1621,6 +1637,35 @@ static int last_uniq_key(TABLE *table,uint keynr)
/*
+ Inserts one historical row to a table.
+
+ Copies content of the row from table->record[1] to table->record[0],
+ sets Sys_end to now() and calls ha_write_row() .
+*/
+
+int vers_insert_history_row(TABLE *table)
+{
+ DBUG_ASSERT(table->versioned(VERS_TIMESTAMP));
+ if (!table->vers_write)
+ return 0;
+ restore_record(table,record[1]);
+
+ // Set Sys_end to now()
+ table->vers_update_end();
+
+ Field *row_start= table->vers_start_field();
+ Field *row_end= table->vers_end_field();
+ if (row_start->cmp(row_start->ptr, row_end->ptr) >= 0)
+ return 0;
+
+ if (table->vfield &&
+ table->update_virtual_fields(table->file, VCOL_UPDATE_FOR_READ))
+ return HA_ERR_GENERIC;
+
+ return table->file->ha_write_row(table->record[0]);
+}
+
+/*
Write a record to table with optional deleting of conflicting records,
invoke proper triggers if needed.
@@ -1652,7 +1697,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
int error, trg_error= 0;
char *key=0;
MY_BITMAP *save_read_set, *save_write_set;
- ulonglong prev_insert_id= table->file->next_insert_id;
+ table->file->store_auto_increment();
ulonglong insert_id_for_cur_row= 0;
ulonglong prev_insert_id_for_cur_row= 0;
DBUG_ENTER("write_record");
@@ -1664,7 +1709,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
if (info->handle_duplicates == DUP_REPLACE ||
info->handle_duplicates == DUP_UPDATE)
{
- while ((error=table->file->ha_write_row(table->record[0])))
+ while (unlikely(error=table->file->ha_write_row(table->record[0])))
{
uint key_nr;
/*
@@ -1697,7 +1742,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
}
goto err;
}
- if ((int) (key_nr = table->file->get_dup_key(error)) < 0)
+ if (unlikely((int) (key_nr = table->file->get_dup_key(error)) < 0))
{
error= HA_ERR_FOUND_DUPP_KEY; /* Database can't find key */
goto err;
@@ -1805,12 +1850,12 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
if (res == VIEW_CHECK_ERROR)
goto before_trg_err;
- table->file->restore_auto_increment(prev_insert_id);
+ table->file->restore_auto_increment();
info->touched++;
if (different_records)
{
- if ((error=table->file->ha_update_row(table->record[1],
- table->record[0])) &&
+ if (unlikely(error=table->file->ha_update_row(table->record[1],
+ table->record[0])) &&
error != HA_ERR_RECORD_IS_THE_SAME)
{
if (info->ignore &&
@@ -1825,7 +1870,26 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
}
if (error != HA_ERR_RECORD_IS_THE_SAME)
+ {
info->updated++;
+ if (table->versioned())
+ {
+ if (table->versioned(VERS_TIMESTAMP))
+ {
+ store_record(table, record[2]);
+ if ((error= vers_insert_history_row(table)))
+ {
+ info->last_errno= error;
+ table->file->print_error(error, MYF(0));
+ trg_error= 1;
+ restore_record(table, record[2]);
+ goto ok_or_after_trg_err;
+ }
+ restore_record(table, record[2]);
+ }
+ info->copied++;
+ }
+ }
else
error= 0;
/*
@@ -1877,19 +1941,37 @@ 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.
- */
- if (last_uniq_key(table,key_nr) &&
- !table->file->referenced_by_foreign_key() &&
+ 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() &&
(!table->triggers || !table->triggers->has_delete_triggers()))
{
- if ((error=table->file->ha_update_row(table->record[1],
- table->record[0])) &&
+ if (table->versioned(VERS_TRX_ID))
+ {
+ bitmap_set_bit(table->write_set, table->vers_start_field()->field_index);
+ table->file->column_bitmaps_signal();
+ table->vers_start_field()->store(0, false);
+ }
+ if (unlikely(error= table->file->ha_update_row(table->record[1],
+ table->record[0])) &&
error != HA_ERR_RECORD_IS_THE_SAME)
goto err;
- if (error != HA_ERR_RECORD_IS_THE_SAME)
+ if (likely(!error))
+ {
info->deleted++;
+ if (table->versioned(VERS_TIMESTAMP))
+ {
+ store_record(table, record[2]);
+ error= vers_insert_history_row(table);
+ restore_record(table, record[2]);
+ if (unlikely(error))
+ goto err;
+ }
+ }
else
- error= 0;
+ error= 0; // error was HA_ERR_RECORD_IS_THE_SAME
/*
Since we pretend that we have done insert we should call
its after triggers.
@@ -1902,9 +1984,24 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
TRG_ACTION_BEFORE, TRUE))
goto before_trg_err;
- if ((error=table->file->ha_delete_row(table->record[1])))
+
+ if (!table->versioned(VERS_TIMESTAMP))
+ error= table->file->ha_delete_row(table->record[1]);
+ else
+ {
+ store_record(table, record[2]);
+ restore_record(table, record[1]);
+ table->vers_update_end();
+ error= table->file->ha_update_row(table->record[1],
+ table->record[0]);
+ restore_record(table, record[2]);
+ }
+ if (unlikely(error))
goto err;
- info->deleted++;
+ if (!table->versioned(VERS_TIMESTAMP))
+ info->deleted++;
+ else
+ info->updated++;
if (!table->file->has_transactions())
thd->transaction.stmt.modified_non_trans_table= TRUE;
if (table->triggers &&
@@ -1938,7 +2035,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
table->write_set != save_write_set)
table->column_bitmaps_set(save_read_set, save_write_set);
}
- else if ((error=table->file->ha_write_row(table->record[0])))
+ else if (unlikely((error=table->file->ha_write_row(table->record[0]))))
{
DEBUG_SYNC(thd, "write_row_noreplace");
if (!info->ignore ||
@@ -1947,7 +2044,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_JUST_WARNING));
- table->file->restore_auto_increment(prev_insert_id);
+ table->file->restore_auto_increment();
goto ok_or_after_trg_err;
}
@@ -1970,7 +2067,7 @@ err:
table->file->print_error(error,MYF(0));
before_trg_err:
- table->file->restore_auto_increment(prev_insert_id);
+ table->file->restore_auto_increment();
if (key)
my_safe_afree(key, table->s->max_unique_length);
table->column_bitmaps_set(save_read_set, save_write_set);
@@ -1991,7 +2088,9 @@ int check_that_all_fields_are_given_values(THD *thd, TABLE *entry, TABLE_LIST *t
for (Field **field=entry->field ; *field ; field++)
{
if (!bitmap_is_set(write_set, (*field)->field_index) &&
- has_no_default_value(thd, *field, table_list))
+ !(*field)->vers_sys_field() &&
+ has_no_default_value(thd, *field, table_list) &&
+ ((*field)->real_type() != MYSQL_TYPE_ENUM))
err=1;
}
return thd->abort_on_warning ? err : 0;
@@ -2012,7 +2111,7 @@ public:
ulong start_time_sec_part;
sql_mode_t sql_mode;
bool auto_increment_field_not_null;
- bool query_start_used, ignore, log_query, query_start_sec_part_used;
+ bool ignore, log_query, query_start_sec_part_used;
bool stmt_depends_on_first_successful_insert_id_in_prev_stmt;
ulonglong first_successful_insert_id_in_prev_stmt;
ulonglong forced_insert_id;
@@ -2188,8 +2287,8 @@ Delayed_insert *find_handler(THD *thd, TABLE_LIST *table_list)
Delayed_insert *di;
while ((di= it++))
{
- if (!strcmp(table_list->db, di->table_list.db) &&
- !strcmp(table_list->table_name, di->table_list.table_name))
+ if (!cmp(&table_list->db, &di->table_list.db) &&
+ !cmp(&table_list->table_name, &di->table_list.table_name))
{
di->lock();
break;
@@ -2253,7 +2352,7 @@ bool delayed_get_table(THD *thd, MDL_request *grl_protection_request,
DBUG_ENTER("delayed_get_table");
/* Must be set in the parser */
- DBUG_ASSERT(table_list->db);
+ DBUG_ASSERT(table_list->db.str);
/* Find the thread which handles this table. */
if (!(di= find_handler(thd, table_list)))
@@ -2280,11 +2379,12 @@ 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, (uint) strlen(table_list->db));
- di->thd.set_query(my_strdup(table_list->table_name,
- MYF(MY_WME | ME_FATALERROR)),
- 0, system_charset_info);
- if (di->thd.db == NULL || di->thd.query() == NULL)
+ di->thd.set_db(&table_list->db);
+ di->thd.set_query(my_strndup(table_list->table_name.str,
+ table_list->table_name.length,
+ MYF(MY_WME | ME_FATALERROR)),
+ table_list->table_name.length, system_charset_info);
+ if (di->thd.db.str == NULL || di->thd.query() == NULL)
{
/* The error is reported */
delete di;
@@ -2292,7 +2392,8 @@ bool delayed_get_table(THD *thd, MDL_request *grl_protection_request,
}
di->table_list= *table_list; // Needed to open table
/* Replace volatile strings with local copies */
- di->table_list.alias= di->table_list.table_name= di->thd.query();
+ di->table_list.alias.str= di->table_list.table_name.str= di->thd.query();
+ di->table_list.alias.length= di->table_list.table_name.length= di->thd.query_length();
di->table_list.db= di->thd.db;
/* We need the tickets so that they can be cloned in handle_delayed_insert */
di->grl_protection.init(MDL_key::GLOBAL, "", "",
@@ -2527,12 +2628,13 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
share->default_fields)
{
bool error_reported= FALSE;
- if (!(copy->def_vcol_set= (MY_BITMAP*) alloc_root(client_thd->mem_root,
- sizeof(MY_BITMAP))))
+ if (unlikely(!(copy->def_vcol_set=
+ (MY_BITMAP*) alloc_root(client_thd->mem_root,
+ sizeof(MY_BITMAP)))))
goto error;
-
- if (parse_vcol_defs(client_thd, client_thd->mem_root, copy, &error_reported,
- VCOL_INIT_DEPENDENCY_FAILURE_IS_WARNING))
+ if (unlikely(parse_vcol_defs(client_thd, client_thd->mem_root, copy,
+ &error_reported,
+ VCOL_INIT_DEPENDENCY_FAILURE_IS_WARNING)))
goto error;
}
@@ -2663,7 +2765,6 @@ int write_delayed(THD *thd, TABLE *table, enum_duplicates duplic,
row->thread_id= thd->thread_id;
row->start_time= thd->start_time;
- row->query_start_used= thd->query_start_used;
row->start_time_sec_part= thd->start_time_sec_part;
row->query_start_sec_part_used= thd->query_start_sec_part_used;
/*
@@ -2753,9 +2854,9 @@ void kill_delayed_threads(void)
Delayed_insert *di;
while ((di= it++))
{
- mysql_mutex_lock(&di->thd.LOCK_thd_data);
+ mysql_mutex_lock(&di->thd.LOCK_thd_kill);
if (di->thd.killed < KILL_CONNECTION)
- di->thd.set_killed(KILL_CONNECTION);
+ di->thd.set_killed_no_mutex(KILL_CONNECTION);
if (di->thd.mysys_var)
{
mysql_mutex_lock(&di->thd.mysys_var->mutex);
@@ -2773,7 +2874,7 @@ void kill_delayed_threads(void)
}
mysql_mutex_unlock(&di->thd.mysys_var->mutex);
}
- mysql_mutex_unlock(&di->thd.LOCK_thd_data);
+ mysql_mutex_unlock(&di->thd.LOCK_thd_kill);
}
mysql_mutex_unlock(&LOCK_delayed_insert); // For unlink from list
DBUG_VOID_RETURN;
@@ -2811,7 +2912,7 @@ handle_table(THD *thd, Query_tables_list *prelocking_ctx,
if (!(table_list->table->file->ha_table_flags() & HA_CAN_INSERT_DELAYED))
{
- my_error(ER_DELAYED_NOT_SUPPORTED, MYF(0), table_list->table_name);
+ my_error(ER_DELAYED_NOT_SUPPORTED, MYF(0), table_list->table_name.str);
return TRUE;
}
return FALSE;
@@ -2896,7 +2997,7 @@ pthread_handler_t handle_delayed_insert(void *arg)
pthread_detach_this_thread();
/* Add thread to THD list so that's it's visible in 'show processlist' */
- thd->set_current_time();
+ thd->set_start_time();
add_to_active_threads(thd);
if (abort_loop)
thd->set_killed(KILL_CONNECTION);
@@ -3141,9 +3242,9 @@ pthread_handler_t handle_delayed_insert(void *arg)
this.
*/
mysql_mutex_lock(&thd->LOCK_thd_data);
- thd->set_killed(KILL_CONNECTION_HARD); // If error
thd->mdl_context.set_needs_thr_lock_abort(0);
mysql_mutex_unlock(&thd->LOCK_thd_data);
+ thd->set_killed(KILL_CONNECTION_HARD); // If error
close_thread_tables(thd); // Free the table
thd->mdl_context.release_transactional_locks();
@@ -3268,7 +3369,6 @@ bool Delayed_insert::handle_inserts(void)
set_delayed_insert_blobs(table);
thd.start_time=row->start_time;
- thd.query_start_used=row->query_start_used;
thd.start_time_sec_part=row->start_time_sec_part;
thd.query_start_sec_part_used=row->query_start_sec_part_used;
/*
@@ -3338,7 +3438,7 @@ bool Delayed_insert::handle_inserts(void)
thd.clear_error(); // reset error for binlog
tmp_error= 0;
- if (table->vfield)
+ if (unlikely(table->vfield))
{
/*
Virtual fields where not calculated by caller as the temporary
@@ -3349,7 +3449,7 @@ bool Delayed_insert::handle_inserts(void)
VCOL_UPDATE_FOR_WRITE);
}
- if (tmp_error || write_record(&thd, table, &info))
+ if (unlikely(tmp_error) || unlikely(write_record(&thd, table, &info)))
{
info.error_count++; // Ignore errors
thread_safe_increment(delayed_insert_errors,&LOCK_delayed_status);
@@ -3396,7 +3496,7 @@ bool Delayed_insert::handle_inserts(void)
mysql_cond_broadcast(&cond_client); // If waiting clients
THD_STAGE_INFO(&thd, stage_reschedule);
mysql_mutex_unlock(&mutex);
- if ((error=table->file->extra(HA_EXTRA_NO_CACHE)))
+ if (unlikely((error=table->file->extra(HA_EXTRA_NO_CACHE))))
{
/* This should never happen */
table->file->print_error(error,MYF(0));
@@ -3427,7 +3527,7 @@ bool Delayed_insert::handle_inserts(void)
table->file->ha_rnd_end();
if (WSREP((&thd)))
- thd_proc_info(&thd, "insert done");
+ thd_proc_info(&thd, "Insert done");
else
thd_proc_info(&thd, 0);
mysql_mutex_unlock(&mutex);
@@ -3450,7 +3550,7 @@ bool Delayed_insert::handle_inserts(void)
thd.binlog_flush_pending_rows_event(TRUE, has_trans))
goto err;
- if ((error=table->file->extra(HA_EXTRA_NO_CACHE)))
+ if (unlikely((error=table->file->extra(HA_EXTRA_NO_CACHE))))
{ // This shouldn't happen
table->file->print_error(error,MYF(0));
sql_print_error("%s", thd.get_stmt_da()->message());
@@ -3764,7 +3864,7 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
0 OK
*/
-int select_insert::prepare2(void)
+int select_insert::prepare2(JOIN *)
{
DBUG_ENTER("select_insert::prepare2");
if (thd->lex->current_select->options & OPTION_BUFFER_RESULT &&
@@ -3808,19 +3908,22 @@ int select_insert::send_data(List<Item> &values)
unit->offset_limit_cnt--;
DBUG_RETURN(0);
}
- if (thd->killed == ABORT_QUERY)
+ 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 && table->update_default_fields(info.ignore))
+ if (table->default_field &&
+ unlikely(table->update_default_fields(info.ignore)))
DBUG_RETURN(1);
thd->count_cuted_fields= CHECK_FIELD_ERROR_FOR_NULL;
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
{
table->auto_increment_field_not_null= FALSE;
DBUG_RETURN(1);
}
+
+ table->vers_write= table->versioned();
if (table_list) // Not CREATE ... SELECT
{
switch (table_list->view_check_option(thd, info.ignore)) {
@@ -3832,9 +3935,10 @@ int select_insert::send_data(List<Item> &values)
}
error= write_record(thd, table, &info);
+ table->vers_write= table->versioned();
table->auto_increment_field_not_null= FALSE;
- if (!error)
+ if (likely(!error))
{
if (table->triggers || info.handle_duplicates == DUP_UPDATE)
{
@@ -3870,12 +3974,16 @@ int select_insert::send_data(List<Item> &values)
void select_insert::store_values(List<Item> &values)
{
+ DBUG_ENTER("select_insert::store_values");
+
if (fields->elements)
fill_record_n_invoke_before_triggers(thd, table, *fields, values, 1,
TRG_EVENT_INSERT);
else
fill_record_n_invoke_before_triggers(thd, table, table->field_to_fill(),
values, 1, TRG_EVENT_INSERT);
+
+ DBUG_VOID_RETURN;
}
bool select_insert::prepare_eof()
@@ -3883,18 +3991,19 @@ bool select_insert::prepare_eof()
int error;
bool const trans_table= table->file->has_transactions();
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'",
trans_table, table->file->table_type()));
- error = IF_WSREP((thd->wsrep_conflict_state == MUST_ABORT ||
- thd->wsrep_conflict_state == CERT_FAILURE) ? -1 :, )
- (thd->locked_tables_mode <= LTM_LOCK_TABLES ?
- table->file->ha_end_bulk_insert() : 0);
+ error= (IF_WSREP((thd->wsrep_conflict_state == MUST_ABORT ||
+ thd->wsrep_conflict_state == CERT_FAILURE) ? -1 :, )
+ (thd->locked_tables_mode <= LTM_LOCK_TABLES ?
+ table->file->ha_end_bulk_insert() : 0));
- if (!error && thd->is_error())
+ if (likely(!error) && unlikely(thd->is_error()))
error= thd->get_stmt_da()->sql_errno();
if (info.ignore || info.handle_duplicates != DUP_ERROR)
@@ -3903,7 +4012,7 @@ bool select_insert::prepare_eof()
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
- if ((changed= (info.copied || info.deleted || info.updated)))
+ if (likely((changed= (info.copied || info.deleted || info.updated))))
{
/*
We must invalidate the table in the query cache before binlog writing
@@ -3927,24 +4036,28 @@ bool select_insert::prepare_eof()
ha_autocommit_or_rollback() is issued below.
*/
if ((WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open()) &&
- (!error || thd->transaction.stmt.modified_non_trans_table))
+ (likely(!error) || thd->transaction.stmt.modified_non_trans_table))
{
int errcode= 0;
- if (!error)
+ int res;
+ if (likely(!error))
thd->clear_error();
else
errcode= query_error_code(thd, killed_status == NOT_KILLED);
- if (thd->binlog_query(THD::ROW_QUERY_TYPE,
- thd->query(), thd->query_length(),
- trans_table, FALSE, FALSE, errcode))
+ res= thd->binlog_query(THD::ROW_QUERY_TYPE,
+ thd->query(), thd->query_length(),
+ trans_table, FALSE, FALSE, errcode);
+ if (res > 0)
{
table->file->ha_release_auto_increment();
DBUG_RETURN(true);
}
+ binary_logged= res == 0 || !table->s->tmp_table;
}
+ table->s->table_creation_was_logged|= binary_logged;
table->file->ha_release_auto_increment();
- if (error)
+ if (unlikely(error))
{
table->file->print_error(error,MYF(0));
DBUG_RETURN(true);
@@ -3992,8 +4105,9 @@ bool select_insert::send_eof()
DBUG_RETURN(res);
}
-void select_insert::abort_result_set() {
-
+void select_insert::abort_result_set()
+{
+ bool binary_logged= 0;
DBUG_ENTER("select_insert::abort_result_set");
/*
If the creation of the table failed (due to a syntax error, for
@@ -4045,16 +4159,20 @@ void select_insert::abort_result_set() {
if(WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open())
{
int errcode= query_error_code(thd, thd->killed == NOT_KILLED);
+ int res;
/* error of writing binary log is ignored */
- (void) thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query(),
- thd->query_length(),
- transactional_table, FALSE, FALSE, errcode);
+ res= thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query(),
+ thd->query_length(),
+ transactional_table, FALSE, FALSE, errcode);
+ binary_logged= res == 0 || !table->s->tmp_table;
}
if (changed)
query_cache_invalidate3(thd, table, 1);
}
DBUG_ASSERT(transactional_table || !changed ||
thd->transaction.stmt.modified_non_trans_table);
+
+ table->s->table_creation_was_logged|= binary_logged;
table->file->ha_release_auto_increment();
}
@@ -4113,10 +4231,7 @@ Field *Item::create_field_for_create_select(TABLE *table)
@retval 0 Error
*/
-static TABLE *create_table_from_items(THD *thd,
- Table_specification_st *create_info,
- TABLE_LIST *create_table,
- Alter_info *alter_info,
+TABLE *select_create::create_table_from_items(THD *thd,
List<Item> *items,
MYSQL_LOCK **lock,
TABLEOP_HOOKS *hooks)
@@ -4128,7 +4243,8 @@ static TABLE *create_table_from_items(THD *thd,
/* Add selected items to field list */
List_iterator_fast<Item> it(*items);
Item *item;
- DBUG_ENTER("create_table_from_items");
+ bool save_table_creation_was_logged;
+ DBUG_ENTER("select_create::create_table_from_items");
tmp_table.s= &share;
init_tmp_table_share(thd, &share, "", 0, "", "");
@@ -4141,6 +4257,9 @@ static TABLE *create_table_from_items(THD *thd,
if (!opt_explicit_defaults_for_timestamp)
promote_first_timestamp_column(&alter_info->create_list);
+ if (create_info->vers_fix_system_fields(thd, alter_info, *create_table))
+ DBUG_RETURN(NULL);
+
while ((item=it++))
{
Field *tmp_field= item->create_field_for_create_select(&tmp_table);
@@ -4177,6 +4296,12 @@ static TABLE *create_table_from_items(THD *thd,
alter_info->create_list.push_back(cr_field, thd->mem_root);
}
+ if (create_info->vers_check_system_fields(thd, alter_info,
+ create_table->table_name,
+ create_table->db,
+ select_field_count))
+ DBUG_RETURN(NULL);
+
DEBUG_SYNC(thd,"create_table_select_before_create");
/* Check if LOCK TABLES + CREATE OR REPLACE of existing normal table*/
@@ -4206,10 +4331,10 @@ static TABLE *create_table_from_items(THD *thd,
open_table().
*/
- if (!mysql_create_table_no_lock(thd, create_table->db,
- create_table->table_name,
+ if (!mysql_create_table_no_lock(thd, &create_table->db,
+ &create_table->table_name,
create_info, alter_info, NULL,
- select_field_count))
+ select_field_count, create_table))
{
DEBUG_SYNC(thd,"create_table_select_before_open");
@@ -4233,8 +4358,8 @@ static TABLE *create_table_from_items(THD *thd,
*/
if (open_table(thd, create_table, &ot_ctx))
{
- quick_rm_table(thd, create_info->db_type, create_table->db,
- table_case_name(create_info, create_table->table_name),
+ quick_rm_table(thd, create_info->db_type, &create_table->db,
+ table_case_name(create_info, &create_table->table_name),
0);
}
/* Restore */
@@ -4261,9 +4386,9 @@ static TABLE *create_table_from_items(THD *thd,
else
create_table->table= 0; // Create failed
- if (!(table= create_table->table))
+ if (unlikely(!(table= create_table->table)))
{
- if (!thd->is_error()) // CREATE ... IF NOT EXISTS
+ if (likely(!thd->is_error())) // CREATE ... IF NOT EXISTS
my_ok(thd); // succeed, but did nothing
DBUG_RETURN(NULL);
}
@@ -4272,36 +4397,49 @@ static TABLE *create_table_from_items(THD *thd,
table->reginfo.lock_type=TL_WRITE;
hooks->prelock(&table, 1); // Call prelock hooks
+
+ /*
+ Ensure that decide_logging_format(), called by mysql_lock_tables(), works
+ with temporary tables that will be logged later if needed.
+ */
+ save_table_creation_was_logged= table->s->table_creation_was_logged;
+ table->s->table_creation_was_logged= 1;
+
/*
mysql_lock_tables() below should never fail with request to reopen table
since it won't wait for the table lock (we have exclusive metadata lock on
the table) and thus can't get aborted.
*/
- if (! ((*lock)= mysql_lock_tables(thd, &table, 1, 0)) ||
- hooks->postlock(&table, 1))
+ if (unlikely(!((*lock)= mysql_lock_tables(thd, &table, 1, 0)) ||
+ hooks->postlock(&table, 1)))
{
/* purecov: begin tested */
/*
This can happen in innodb when you get a deadlock when using same table
in insert and select or when you run out of memory.
+ It can also happen if there was a conflict in
+ THD::decide_logging_format()
*/
- my_error(ER_CANT_LOCK, MYF(0), my_errno);
+ if (!thd->is_error())
+ my_error(ER_CANT_LOCK, MYF(0), my_errno);
if (*lock)
{
mysql_unlock_tables(thd, *lock);
*lock= 0;
}
- drop_open_table(thd, table, create_table->db, create_table->table_name);
+ drop_open_table(thd, table, &create_table->db, &create_table->table_name);
DBUG_RETURN(0);
/* purecov: end */
}
+ table->s->table_creation_was_logged= save_table_creation_was_logged;
DBUG_RETURN(table);
}
int
-select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
+select_create::prepare(List<Item> &_values, SELECT_LEX_UNIT *u)
{
+ List<Item> values(_values, thd->mem_root);
MYSQL_LOCK *extra_lock= NULL;
DBUG_ENTER("select_create::prepare");
@@ -4347,14 +4485,15 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
create_table->next_global= save_next_global;
- if (error)
+ if (unlikely(error))
return error;
TABLE const *const table = *tables;
if (thd->is_current_stmt_binlog_format_row() &&
!table->s->tmp_table)
{
- if (int error= ptr->binlog_show_create_table(tables, count))
+ int error;
+ if (unlikely((error= ptr->binlog_show_create_table(tables, count))))
return error;
}
return 0;
@@ -4383,10 +4522,7 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
DEBUG_SYNC(thd,"create_table_select_before_check_if_exists");
- if (!(table= create_table_from_items(thd, create_info,
- create_table,
- alter_info, &values,
- &extra_lock, hook_ptr)))
+ if (!(table= create_table_from_items(thd, &values, &extra_lock, hook_ptr)))
/* abort() deletes table */
DBUG_RETURN(-1);
@@ -4421,11 +4557,16 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
}
/* First field to copy */
- field= table->field+table->s->fields - values.elements;
+ field= table->field+table->s->fields;
/* Mark all fields that are given values */
- for (Field **f= field ; *f ; f++)
- bitmap_set_bit(table->write_set, (*f)->field_index);
+ for (uint n= values.elements; n; )
+ {
+ if ((*--field)->invisible >= INVISIBLE_SYSTEM)
+ continue;
+ n--;
+ bitmap_set_bit(table->write_set, (*field)->field_index);
+ }
table->next_number_field=table->found_next_number_field;
@@ -4496,7 +4637,7 @@ select_create::binlog_show_create_table(TABLE **tables, uint count)
/* is_trans */ TRUE,
/* direct */ FALSE,
/* suppress_use */ FALSE,
- errcode);
+ errcode) > 0;
}
ha_fake_trx_id(thd);
@@ -4565,8 +4706,8 @@ bool select_create::send_eof()
*/
wsrep_key_arr_t key_arr= {0, 0};
wsrep_prepare_keys_for_isolation(thd,
- create_table->db,
- create_table->table_name,
+ create_table->db.str,
+ create_table->table_name.str,
table_list,
&key_arr);
int rcode = wsrep->append_key(
@@ -4609,8 +4750,6 @@ bool select_create::send_eof()
}
#endif /* WITH_WSREP */
}
- else if (!thd->is_current_stmt_binlog_format_row())
- table->s->table_creation_was_logged= 1;
/*
exit_done must only be set after last potential call to
@@ -4696,7 +4835,8 @@ void select_create::abort_result_set()
if (table)
{
bool tmp_table= table->s->tmp_table;
-
+ bool table_creation_was_logged= (!tmp_table ||
+ table->s->table_creation_was_logged);
if (tmp_table)
{
DBUG_ASSERT(saved_tmp_table_share);
@@ -4718,16 +4858,16 @@ void select_create::abort_result_set()
m_plock= NULL;
}
- drop_open_table(thd, table, create_table->db, create_table->table_name);
+ drop_open_table(thd, table, &create_table->db, &create_table->table_name);
table=0; // Safety
if (thd->log_current_statement && mysql_bin_log.is_open())
{
/* Remove logging of drop, create + insert rows */
binlog_reset_cache(thd);
/* Original table was deleted. We have to log it */
- log_drop_table(thd, create_table->db, create_table->db_length,
- create_table->table_name, create_table->table_name_length,
- tmp_table);
+ if (table_creation_was_logged)
+ log_drop_table(thd, &create_table->db, &create_table->table_name,
+ tmp_table);
}
}
DBUG_VOID_RETURN;
diff --git a/sql/sql_insert.h b/sql/sql_insert.h
index 3c0d0164da4..a37ed1f31e5 100644
--- a/sql/sql_insert.h
+++ b/sql/sql_insert.h
@@ -37,6 +37,7 @@ void upgrade_lock_type_for_insert(THD *thd, thr_lock_type *lock_type,
bool is_multi_insert);
int check_that_all_fields_are_given_values(THD *thd, TABLE *entry,
TABLE_LIST *table_list);
+int vers_insert_history_row(TABLE *table);
int write_record(THD *thd, TABLE *table, COPY_INFO *info);
void kill_delayed_threads(void);
diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc
index 77017812074..3a509b3d750 100644
--- a/sql/sql_join_cache.cc
+++ b/sql/sql_join_cache.cc
@@ -27,6 +27,7 @@
#pragma implementation // gcc: Class implementation
#endif
+#include "mariadb.h"
#include "key.h"
#include "sql_base.h"
#include "sql_select.h"
@@ -764,7 +765,7 @@ uint JOIN_CACHE::get_record_max_affix_length()
The minimal possible size of the join buffer of this cache
*/
-ulong JOIN_CACHE::get_min_join_buffer_size()
+size_t JOIN_CACHE::get_min_join_buffer_size()
{
if (!min_buff_size)
{
@@ -823,7 +824,7 @@ ulong JOIN_CACHE::get_min_join_buffer_size()
The maximum possible size of the join buffer of this cache
*/
-ulong JOIN_CACHE::get_max_join_buffer_size(bool optimize_buff_size)
+size_t JOIN_CACHE::get_max_join_buffer_size(bool optimize_buff_size)
{
if (!max_buff_size)
{
@@ -932,9 +933,9 @@ int JOIN_CACHE::alloc_buffer()
if (for_explain_only)
return 0;
- for (ulong buff_size_decr= (buff_size-min_buff_size)/4 + 1; ; )
+ for (size_t buff_size_decr= (buff_size-min_buff_size)/4 + 1; ; )
{
- ulong next_buff_size;
+ size_t next_buff_size;
if ((buff= (uchar*) my_malloc(buff_size, MYF(MY_THREAD_SPECIFIC))))
break;
@@ -1700,7 +1701,7 @@ enum JOIN_CACHE::Match_flag JOIN_CACHE::get_match_flag_by_pos(uchar *rec_ptr)
the number of bytes in the increment
*/
-uint JOIN_CACHE::aux_buffer_incr(ulong recno)
+uint JOIN_CACHE::aux_buffer_incr(size_t recno)
{
return join_tab_scan->aux_buffer_incr(recno);
}
@@ -2140,7 +2141,7 @@ enum_nested_loop_state JOIN_CACHE::join_records(bool skip_last)
DBUG_ASSERT(!is_key_access());
/*
Restore the last record from the join buffer to generate
- all extentions for it.
+ all extensions for it.
*/
get_record();
}
@@ -2248,7 +2249,7 @@ enum_nested_loop_state JOIN_CACHE::join_matching_records(bool skip_last)
goto finish2;
/* Prepare to retrieve all records of the joined table */
- if ((error= join_tab_scan->open()))
+ if (unlikely((error= join_tab_scan->open())))
{
/*
TODO: if we get here, we will assert in net_send_statement(). Add test
@@ -2259,10 +2260,9 @@ enum_nested_loop_state JOIN_CACHE::join_matching_records(bool skip_last)
while (!(error= join_tab_scan->next()))
{
- if (join->thd->check_killed())
+ if (unlikely(join->thd->check_killed()))
{
/* The user has aborted the execution of the query */
- join->thd->send_kill_message();
rc= NESTED_LOOP_KILLED;
goto finish;
}
@@ -2411,7 +2411,7 @@ enum_nested_loop_state JOIN_CACHE::generate_full_extensions(uchar *rec_ptr)
DBUG_RETURN(rc);
}
}
- else if (join->thd->is_error())
+ else if (unlikely(join->thd->is_error()))
rc= NESTED_LOOP_ERROR;
DBUG_RETURN(rc);
}
@@ -2533,10 +2533,9 @@ enum_nested_loop_state JOIN_CACHE::join_null_complements(bool skip_last)
for ( ; cnt; cnt--)
{
- if (join->thd->check_killed())
+ if (unlikely(join->thd->check_killed()))
{
/* The user has aborted the execution of the query */
- join->thd->send_kill_message();
rc= NESTED_LOOP_KILLED;
goto finish;
}
@@ -2571,10 +2570,11 @@ finish:
BNLH, BKA or BKAH) to the data structure
RETURN VALUE
- none
+ 0 ok
+ 1 error
*/
-void JOIN_CACHE::save_explain_data(EXPLAIN_BKA_TYPE *explain)
+bool JOIN_CACHE::save_explain_data(EXPLAIN_BKA_TYPE *explain)
{
explain->incremental= MY_TEST(prev_cache);
@@ -2596,6 +2596,7 @@ void JOIN_CACHE::save_explain_data(EXPLAIN_BKA_TYPE *explain)
default:
DBUG_ASSERT(0);
}
+ return 0;
}
/**
@@ -2608,7 +2609,7 @@ THD *JOIN_CACHE::thd()
}
-static void add_mrr_explain_info(String *str, uint mrr_mode, handler *file)
+static bool add_mrr_explain_info(String *str, uint mrr_mode, handler *file)
{
char mrr_str_buf[128]={0};
int len;
@@ -2617,22 +2618,30 @@ static void add_mrr_explain_info(String *str, uint mrr_mode, handler *file)
if (len > 0)
{
if (str->length())
- str->append(STRING_WITH_LEN("; "));
- str->append(mrr_str_buf, len);
+ {
+ if (str->append(STRING_WITH_LEN("; ")))
+ return 1;
+ }
+ if (str->append(mrr_str_buf, len))
+ return 1;
}
+ return 0;
}
-void JOIN_CACHE_BKA::save_explain_data(EXPLAIN_BKA_TYPE *explain)
+
+bool JOIN_CACHE_BKA::save_explain_data(EXPLAIN_BKA_TYPE *explain)
{
- JOIN_CACHE::save_explain_data(explain);
- add_mrr_explain_info(&explain->mrr_type, mrr_mode, join_tab->table->file);
+ if (JOIN_CACHE::save_explain_data(explain))
+ return 1;
+ return add_mrr_explain_info(&explain->mrr_type, mrr_mode, join_tab->table->file);
}
-void JOIN_CACHE_BKAH::save_explain_data(EXPLAIN_BKA_TYPE *explain)
+bool JOIN_CACHE_BKAH::save_explain_data(EXPLAIN_BKA_TYPE *explain)
{
- JOIN_CACHE::save_explain_data(explain);
- add_mrr_explain_info(&explain->mrr_type, mrr_mode, join_tab->table->file);
+ if (JOIN_CACHE::save_explain_data(explain))
+ return 1;
+ return add_mrr_explain_info(&explain->mrr_type, mrr_mode, join_tab->table->file);
}
@@ -2749,22 +2758,22 @@ int JOIN_CACHE_HASHED::init_hash_table()
size_of_key_ofs + // reference to the next key
(use_emb_key ? get_size_of_rec_offset() : key_length);
- ulong space_per_rec= avg_record_length +
+ size_t space_per_rec= avg_record_length +
avg_aux_buffer_incr +
key_entry_length+size_of_key_ofs;
- uint n= buff_size / space_per_rec;
+ size_t n= buff_size / space_per_rec;
/*
TODO: Make a better estimate for this upper bound of
the number of records in in the join buffer.
*/
- uint max_n= buff_size / (pack_length-length+
+ size_t max_n= buff_size / (pack_length-length+
key_entry_length+size_of_key_ofs);
hash_entries= (uint) (n / 0.7);
set_if_bigger(hash_entries, 1);
- if (offset_size(max_n*key_entry_length) <=
+ if (offset_size((uint)(max_n*key_entry_length)) <=
size_of_key_ofs)
break;
}
@@ -3373,7 +3382,7 @@ int JOIN_TAB_SCAN::next()
if (is_first_record)
is_first_record= FALSE;
else
- err= info->read_record(info);
+ err= info->read_record();
if (!err)
{
@@ -3382,13 +3391,13 @@ int JOIN_TAB_SCAN::next()
while (!err && select && (skip_rc= select->skip_record(thd)) <= 0)
{
- if (thd->check_killed() || skip_rc < 0)
+ if (unlikely(thd->check_killed()) || skip_rc < 0)
return 1;
/*
Move to the next record if the last retrieved record does not
meet the condition pushed to the table join_tab.
*/
- err= info->read_record(info);
+ err= info->read_record();
if (!err)
{
join_tab->tracker->r_rows++;
@@ -3491,7 +3500,7 @@ bool JOIN_CACHE_BNL::prepare_look_for_matches(bool skip_last)
if (!records)
return TRUE;
reset(FALSE);
- rem_records= records - MY_TEST(skip_last);
+ rem_records= (uint)records - MY_TEST(skip_last);
return rem_records == 0;
}
@@ -3819,7 +3828,7 @@ int JOIN_CACHE_BNLH::init(bool for_explain)
the increment of the size of the MRR buffer for the next record
*/
-uint JOIN_TAB_SCAN_MRR::aux_buffer_incr(ulong recno)
+uint JOIN_TAB_SCAN_MRR::aux_buffer_incr(size_t recno)
{
uint incr= 0;
TABLE_REF *ref= &join_tab->ref;
diff --git a/sql/sql_join_cache.h b/sql/sql_join_cache.h
index 1cbc6acfd79..7b8b942180f 100644
--- a/sql/sql_join_cache.h
+++ b/sql/sql_join_cache.h
@@ -107,7 +107,7 @@ protected:
/* 3 functions below actually do not use the hidden parameter 'this' */
/* Calculate the number of bytes used to store an offset value */
- uint offset_size(uint len)
+ uint offset_size(size_t len)
{ return (len < 256 ? 1 : len < 256*256 ? 2 : 4); }
/* Get the offset value that takes ofs_sz bytes at the position ptr */
@@ -420,7 +420,7 @@ protected:
incremented when a new record is added to the join buffer.
If no auxiliary buffer is needed the function should return 0.
*/
- virtual uint aux_buffer_incr(ulong recno);
+ virtual uint aux_buffer_incr(size_t recno);
/* Shall calculate how much space is remaining in the join buffer */
virtual size_t rem_space()
@@ -606,9 +606,9 @@ public:
void set_join_buffer_size(size_t sz) { buff_size= sz; }
/* Get the minimum possible size of the cache join buffer */
- virtual ulong get_min_join_buffer_size();
+ virtual size_t get_min_join_buffer_size();
/* Get the maximum possible size of the cache join buffer */
- virtual ulong get_max_join_buffer_size(bool optimize_buff_size);
+ virtual size_t get_max_join_buffer_size(bool optimize_buff_size);
/* Shrink the size if the cache join buffer in a given ratio */
bool shrink_join_buffer_in_ratio(ulonglong n, ulonglong d);
@@ -662,7 +662,7 @@ public:
enum_nested_loop_state join_records(bool skip_last);
/* Add a comment on the join algorithm employed by the join cache */
- virtual void save_explain_data(EXPLAIN_BKA_TYPE *explain);
+ virtual bool save_explain_data(EXPLAIN_BKA_TYPE *explain);
THD *thd();
@@ -1069,7 +1069,7 @@ public:
Shall calculate the increment of the auxiliary buffer for a record
write if such a buffer is used by the table scan object
*/
- virtual uint aux_buffer_incr(ulong recno) { return 0; }
+ virtual uint aux_buffer_incr(size_t recno) { return 0; }
/* Initiate the process of iteration over the joined table */
virtual int open();
@@ -1244,7 +1244,7 @@ public:
JOIN_TAB_SCAN_MRR(JOIN *j, JOIN_TAB *tab, uint flags, RANGE_SEQ_IF rs_funcs)
:JOIN_TAB_SCAN(j, tab), range_seq_funcs(rs_funcs), mrr_mode(flags) {}
- uint aux_buffer_incr(ulong recno);
+ uint aux_buffer_incr(size_t recno);
int open();
@@ -1325,6 +1325,10 @@ public:
JOIN_CACHE_BKA(JOIN *j, JOIN_TAB *tab, uint flags, JOIN_CACHE *prev)
:JOIN_CACHE(j, tab, prev), mrr_mode(flags) {}
+ JOIN_CACHE_BKA(JOIN_CACHE_BKA *bka)
+ :JOIN_CACHE(bka->join, bka->join_tab, bka->prev_cache),
+ mrr_mode(bka->mrr_mode) {}
+
uchar **get_curr_association_ptr() { return &curr_association; }
/* Initialize the BKA cache */
@@ -1340,7 +1344,7 @@ public:
/* Check index condition of the joined table for a record from BKA cache */
bool skip_index_tuple(range_id_t range_info);
- void save_explain_data(EXPLAIN_BKA_TYPE *explain);
+ bool save_explain_data(EXPLAIN_BKA_TYPE *explain);
};
@@ -1421,6 +1425,10 @@ public:
JOIN_CACHE_BKAH(JOIN *j, JOIN_TAB *tab, uint flags, JOIN_CACHE *prev)
:JOIN_CACHE_BNLH(j, tab, prev), mrr_mode(flags) {}
+ JOIN_CACHE_BKAH(JOIN_CACHE_BKAH *bkah)
+ :JOIN_CACHE_BNLH(bkah->join, bkah->join_tab, bkah->prev_cache),
+ mrr_mode(bkah->mrr_mode) {}
+
uchar **get_curr_association_ptr() { return &curr_matching_chain; }
/* Initialize the BKAH cache */
@@ -1431,5 +1439,5 @@ public:
/* Check index condition of the joined table for a record from BKAH cache */
bool skip_index_tuple(range_id_t range_info);
- void save_explain_data(EXPLAIN_BKA_TYPE *explain);
+ bool save_explain_data(EXPLAIN_BKA_TYPE *explain);
};
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index e1e307330e3..4f64dbfbbf9 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -18,7 +18,7 @@
/* A lexical scanner on a temporary buffer with a yacc interface */
#define MYSQL_LEX 1
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "sql_class.h" // sql_lex.h: SQLCOM_END
#include "sql_lex.h"
@@ -30,21 +30,25 @@
#include "sp.h"
#include "sql_select.h"
#include "sql_cte.h"
+#include "sql_signal.h"
+#include "sql_partition.h"
-static int lex_one_token(YYSTYPE *yylval, THD *thd);
-/*
- We are using pointer to this variable for distinguishing between assignment
- to NEW row field (when parsing trigger definition) and structured variable.
-*/
+void LEX::parse_error(uint err_number)
+{
+ thd->parse_error(err_number);
+}
-sys_var *trg_new_row_fake_var= (sys_var*) 0x01;
/**
LEX_STRING constant for null-string to be used in parser and other places.
*/
-const LEX_STRING null_lex_str= {NULL, 0};
-const LEX_STRING empty_lex_str= {(char *) "", 0};
+const LEX_STRING empty_lex_str= {(char *) "", 0};
+const LEX_CSTRING null_clex_str= {NULL, 0};
+const LEX_CSTRING empty_clex_str= {"", 0};
+const LEX_CSTRING star_clex_str= {"*", 1};
+const LEX_CSTRING param_clex_str= {"?", 1};
+
/**
@note The order of the elements of this array must correspond to
the order of elements in enum_binlog_stmt_unsafe.
@@ -119,7 +123,7 @@ const char * index_hint_type_name[] =
inline int lex_casecmp(const char *s, const char *t, uint len)
{
while (len-- != 0 &&
- to_upper_lex[(uchar) *s++] == to_upper_lex[(uchar) *t++]) ;
+ to_upper_lex[(uchar) *s++] == to_upper_lex[(uchar) *t++]) ;
return (int) len+1;
}
@@ -140,7 +144,7 @@ void lex_init(void)
void lex_free(void)
-{ // Call this when daemon ends
+{ // Call this when daemon ends
DBUG_ENTER("lex_free");
DBUG_VOID_RETURN;
}
@@ -183,13 +187,14 @@ init_lex_with_single_table(THD *thd, TABLE *table, LEX *lex)
thd->lex= lex;
lex_start(thd);
context->init();
- if ((!(table_ident= new Table_ident(thd,
- table->s->table_name,
- table->s->db, TRUE))) ||
- (!(table_list= select_lex->add_table_to_list(thd,
- table_ident,
- NULL,
- 0))))
+ if (unlikely((!(table_ident= new Table_ident(thd,
+ &table->s->db,
+ &table->s->table_name,
+ TRUE)))) ||
+ (unlikely(!(table_list= select_lex->add_table_to_list(thd,
+ table_ident,
+ NULL,
+ 0)))))
return TRUE;
context->resolve_in_table_list_only(table_list);
lex->use_only_table_context= TRUE;
@@ -198,6 +203,7 @@ init_lex_with_single_table(THD *thd, TABLE *table, LEX *lex)
table->map= 1; //To ensure correct calculation of const item
table_list->table= table;
table_list->cacheable_table= false;
+ lex->create_last_non_select_table= table_list;
return FALSE;
}
@@ -231,6 +237,7 @@ void
st_parsing_options::reset()
{
allows_variable= TRUE;
+ lookup_keywords_after_qualifier= false;
}
@@ -244,8 +251,8 @@ st_parsing_options::reset()
*/
bool Lex_input_stream::init(THD *thd,
- char* buff,
- unsigned int length)
+ char* buff,
+ size_t length)
{
DBUG_EXECUTE_IF("bug42064_simulate_oom",
DBUG_SET("+d,simulate_out_of_memory"););
@@ -256,12 +263,12 @@ bool Lex_input_stream::init(THD *thd,
DBUG_SET("-d,bug42064_simulate_oom"););
if (m_cpp_buf == NULL)
- return TRUE;
+ return true;
m_thd= thd;
reset(buff, length);
- return FALSE;
+ return false;
}
@@ -274,10 +281,9 @@ bool Lex_input_stream::init(THD *thd,
*/
void
-Lex_input_stream::reset(char *buffer, unsigned int length)
+Lex_input_stream::reset(char *buffer, size_t length)
{
yylineno= 1;
- yylval= NULL;
lookahead_token= -1;
lookahead_yylval= NULL;
m_ptr= buffer;
@@ -321,7 +327,7 @@ void Lex_input_stream::body_utf8_start(THD *thd, const char *begin_ptr)
DBUG_ASSERT(begin_ptr);
DBUG_ASSERT(m_cpp_buf <= begin_ptr && begin_ptr <= m_cpp_buf + m_buf_length);
- uint body_utf8_length= get_body_utf8_maximum_length(thd);
+ size_t body_utf8_length= get_body_utf8_maximum_length(thd);
m_body_utf8= (char *) thd->alloc(body_utf8_length + 1);
m_body_utf8_ptr= m_body_utf8;
@@ -331,7 +337,7 @@ void Lex_input_stream::body_utf8_start(THD *thd, const char *begin_ptr)
}
-uint Lex_input_stream::get_body_utf8_maximum_length(THD *thd)
+size_t Lex_input_stream::get_body_utf8_maximum_length(THD *thd)
{
/*
String literals can grow during escaping:
@@ -413,31 +419,18 @@ void Lex_input_stream::body_utf8_append(const char *ptr)
operation.
*/
-void Lex_input_stream::body_utf8_append_ident(THD *thd,
- const LEX_STRING *txt,
- const char *end_ptr)
+void
+Lex_input_stream::body_utf8_append_ident(THD *thd,
+ const Lex_string_with_metadata_st *txt,
+ const char *end_ptr)
{
if (!m_cpp_utf8_processed_ptr)
return;
- LEX_STRING utf_txt;
- CHARSET_INFO *txt_cs= thd->charset();
-
- if (!my_charset_same(txt_cs, &my_charset_utf8_general_ci))
- {
- thd->convert_string(&utf_txt,
- &my_charset_utf8_general_ci,
- txt->str, (uint) txt->length,
- txt_cs);
- }
- else
- {
- utf_txt.str= txt->str;
- utf_txt.length= txt->length;
- }
+ LEX_CSTRING utf_txt;
+ thd->make_text_string_sys(&utf_txt, txt); // QQ: check return value?
/* NOTE: utf_txt.length is in bytes, not in symbols. */
-
memcpy(m_body_utf8_ptr, utf_txt.str, utf_txt.length);
m_body_utf8_ptr += utf_txt.length;
*m_body_utf8_ptr= 0;
@@ -595,7 +588,7 @@ Lex_input_stream::get_escape_func(THD *thd, my_wc_t sep) const
@param sep - the string delimiter (single or double quote)
*/
void Lex_input_stream::body_utf8_append_escape(THD *thd,
- const LEX_STRING *txt,
+ const LEX_CSTRING *txt,
CHARSET_INFO *cs,
const char *end_ptr,
my_wc_t sep)
@@ -644,119 +637,127 @@ void Lex_input_stream::reduce_digest_token(uint token_left, uint token_right)
}
}
+void lex_start(THD *thd)
+{
+ DBUG_ENTER("lex_start");
+ thd->lex->start(thd);
+ DBUG_VOID_RETURN;
+}
+
+
/*
This is called before every query that is to be parsed.
Because of this, it's critical to not do too much things here.
(We already do too much here)
*/
-void lex_start(THD *thd)
+void LEX::start(THD *thd_arg)
{
- LEX *lex= thd->lex;
- DBUG_ENTER("lex_start");
- DBUG_PRINT("info", ("Lex %p", thd->lex));
+ DBUG_ENTER("LEX::start");
+ DBUG_PRINT("info", ("This: %p thd_arg->lex: %p", this, thd_arg->lex));
- lex->thd= lex->unit.thd= thd;
+ thd= unit.thd= thd_arg;
+ stmt_lex= this; // default, should be rewritten for VIEWs And CTEs
- lex->stmt_lex= lex; // default, should be rewritten for VIEWs And CTEs
- DBUG_ASSERT(!lex->explain);
+ DBUG_ASSERT(!explain);
- lex->context_stack.empty();
- lex->unit.init_query();
- lex->unit.init_select();
+ context_stack.empty();
+ unit.init_query();
+ current_select_number= 1;
+ select_lex.linkage= UNSPECIFIED_TYPE;
/* 'parent_lex' is used in init_query() so it must be before it. */
- lex->select_lex.parent_lex= lex;
- lex->select_lex.init_query();
- lex->current_select_number= 1;
- lex->curr_with_clause= 0;
- lex->with_clauses_list= 0;
- lex->with_clauses_list_last_next= &lex->with_clauses_list;
- lex->clone_spec_offset= 0;
- lex->value_list.empty();
- lex->update_list.empty();
- lex->set_var_list.empty();
- lex->param_list.empty();
- lex->view_list.empty();
- lex->with_column_list.empty();
- lex->with_persistent_for_clause= FALSE;
- lex->column_list= NULL;
- lex->index_list= NULL;
- lex->prepared_stmt_params.empty();
- lex->auxiliary_table_list.empty();
- lex->unit.next= lex->unit.master=
- lex->unit.link_next= lex->unit.return_to= 0;
- lex->unit.prev= lex->unit.link_prev= 0;
- lex->unit.slave= lex->current_select=
- lex->all_selects_list= &lex->select_lex;
- lex->select_lex.master= &lex->unit;
- lex->select_lex.prev= &lex->unit.slave;
- lex->select_lex.link_next= lex->select_lex.slave= lex->select_lex.next= 0;
- lex->select_lex.link_prev= (st_select_lex_node**)&(lex->all_selects_list);
- lex->select_lex.options= 0;
- lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE_UNSPECIFIED;
- lex->select_lex.init_order();
- lex->select_lex.group_list.empty();
- if (lex->select_lex.group_list_ptrs)
- lex->select_lex.group_list_ptrs->clear();
- lex->describe= 0;
- lex->analyze_stmt= 0;
- lex->explain_json= false;
- lex->subqueries= FALSE;
- lex->context_analysis_only= 0;
- lex->derived_tables= 0;
- lex->safe_to_cache_query= 1;
- lex->parsing_options.reset();
- lex->empty_field_list_on_rset= 0;
- lex->select_lex.select_number= 1;
- lex->part_info= 0;
- lex->select_lex.in_sum_expr=0;
- lex->select_lex.ftfunc_list_alloc.empty();
- lex->select_lex.ftfunc_list= &lex->select_lex.ftfunc_list_alloc;
- lex->select_lex.group_list.empty();
- lex->select_lex.order_list.empty();
- lex->select_lex.gorder_list.empty();
- lex->m_sql_cmd= NULL;
- lex->duplicates= DUP_ERROR;
- lex->ignore= 0;
- lex->spname= NULL;
- lex->spcont= NULL;
- lex->proc_list.first= 0;
- lex->escape_used= FALSE;
- lex->query_tables= 0;
- lex->reset_query_tables_list(FALSE);
- lex->expr_allows_subselect= TRUE;
- lex->use_only_table_context= FALSE;
- lex->parse_vcol_expr= FALSE;
- lex->check_exists= FALSE;
- lex->create_info.lex_start();
- lex->verbose= 0;
-
- lex->name= null_lex_str;
- lex->event_parse_data= NULL;
- lex->profile_options= PROFILE_NONE;
- lex->nest_level=0 ;
- lex->select_lex.nest_level_base= &lex->unit;
- lex->allow_sum_func= 0;
- lex->in_sum_func= NULL;
-
- lex->used_tables= 0;
- lex->only_view= FALSE;
- lex->reset_slave_info.all= false;
- lex->limit_rows_examined= 0;
- lex->limit_rows_examined_cnt= ULONGLONG_MAX;
- lex->var_list.empty();
- lex->stmt_var_list.empty();
- lex->proc_list.elements=0;
-
- lex->save_group_list.empty();
- lex->save_order_list.empty();
- lex->win_ref= NULL;
- lex->win_frame= NULL;
- lex->frame_top_bound= NULL;
- lex->frame_bottom_bound= NULL;
- lex->win_spec= NULL;
-
- lex->is_lex_started= TRUE;
+ select_lex.parent_lex= this;
+ select_lex.init_query();
+ curr_with_clause= 0;
+ with_clauses_list= 0;
+ with_clauses_list_last_next= &with_clauses_list;
+ clone_spec_offset= 0;
+ create_view= NULL;
+ value_list.empty();
+ update_list.empty();
+ 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;
+ prepared_stmt_params.empty();
+ auxiliary_table_list.empty();
+ unit.next= unit.master= unit.link_next= unit.return_to= 0;
+ unit.prev= unit.link_prev= 0;
+ unit.slave= current_select= all_selects_list= &select_lex;
+ select_lex.master= &unit;
+ select_lex.prev= &unit.slave;
+ select_lex.link_next= select_lex.slave= select_lex.next= 0;
+ select_lex.link_prev= (st_select_lex_node**)&(all_selects_list);
+ select_lex.options= 0;
+ select_lex.sql_cache= SELECT_LEX::SQL_CACHE_UNSPECIFIED;
+ select_lex.init_order();
+ select_lex.group_list.empty();
+ if (select_lex.group_list_ptrs)
+ select_lex.group_list_ptrs->clear();
+ describe= 0;
+ analyze_stmt= 0;
+ explain_json= false;
+ context_analysis_only= 0;
+ derived_tables= 0;
+ safe_to_cache_query= 1;
+ parsing_options.reset();
+ empty_field_list_on_rset= 0;
+ select_lex.select_number= 1;
+ part_info= 0;
+ select_lex.in_sum_expr=0;
+ select_lex.ftfunc_list_alloc.empty();
+ select_lex.ftfunc_list= &select_lex.ftfunc_list_alloc;
+ select_lex.group_list.empty();
+ select_lex.order_list.empty();
+ select_lex.gorder_list.empty();
+ m_sql_cmd= NULL;
+ duplicates= DUP_ERROR;
+ ignore= 0;
+ spname= NULL;
+ spcont= NULL;
+ proc_list.first= 0;
+ escape_used= FALSE;
+ default_used= FALSE;
+ query_tables= 0;
+ reset_query_tables_list(FALSE);
+ expr_allows_subselect= TRUE;
+ use_only_table_context= FALSE;
+ parse_vcol_expr= FALSE;
+ check_exists= FALSE;
+ create_info.lex_start();
+ verbose= 0;
+
+ name= null_clex_str;
+ event_parse_data= NULL;
+ profile_options= PROFILE_NONE;
+ nest_level=0 ;
+ select_lex.nest_level_base= &unit;
+ allow_sum_func.clear_all();
+ in_sum_func= NULL;
+
+ used_tables= 0;
+ table_type= TABLE_TYPE_UNKNOWN;
+ reset_slave_info.all= false;
+ limit_rows_examined= 0;
+ limit_rows_examined_cnt= ULONGLONG_MAX;
+ var_list.empty();
+ stmt_var_list.empty();
+ proc_list.elements=0;
+
+ save_group_list.empty();
+ save_order_list.empty();
+ win_ref= NULL;
+ win_frame= NULL;
+ frame_top_bound= NULL;
+ frame_bottom_bound= NULL;
+ win_spec= NULL;
+
+ vers_conditions.empty();
+
+ is_lex_started= TRUE;
DBUG_VOID_RETURN;
}
@@ -826,23 +827,53 @@ Yacc_state::~Yacc_state()
}
}
-static int find_keyword(Lex_input_stream *lip, uint len, bool function)
+int Lex_input_stream::find_keyword(Lex_ident_cli_st *kwd,
+ uint len, bool function)
{
- const char *tok= lip->get_tok_start();
+ const char *tok= m_tok_start;
SYMBOL *symbol= get_hash_symbol(tok, len, function);
if (symbol)
{
- lip->yylval->symbol.symbol=symbol;
- lip->yylval->symbol.str= (char*) tok;
- lip->yylval->symbol.length=len;
+ kwd->set_keyword(tok, len);
+ DBUG_ASSERT(tok >= get_buf());
+ DBUG_ASSERT(tok < get_end_of_query());
+
+ if (m_thd->variables.sql_mode & MODE_ORACLE)
+ {
+ switch (symbol->tok) {
+ case BEGIN_MARIADB_SYM: return BEGIN_ORACLE_SYM;
+ case BLOB_MARIADB_SYM: return BLOB_ORACLE_SYM;
+ case BODY_MARIADB_SYM: return BODY_ORACLE_SYM;
+ case CLOB_MARIADB_SYM: return CLOB_ORACLE_SYM;
+ case CONTINUE_MARIADB_SYM: return CONTINUE_ORACLE_SYM;
+ case DECLARE_MARIADB_SYM: return DECLARE_ORACLE_SYM;
+ case DECODE_MARIADB_SYM: return DECODE_ORACLE_SYM;
+ case ELSEIF_MARIADB_SYM: return ELSEIF_ORACLE_SYM;
+ case ELSIF_MARIADB_SYM: return ELSIF_ORACLE_SYM;
+ case EXCEPTION_MARIADB_SYM: return EXCEPTION_ORACLE_SYM;
+ case EXIT_MARIADB_SYM: return EXIT_ORACLE_SYM;
+ case GOTO_MARIADB_SYM: return GOTO_ORACLE_SYM;
+ case NUMBER_MARIADB_SYM: return NUMBER_ORACLE_SYM;
+ case OTHERS_MARIADB_SYM: return OTHERS_ORACLE_SYM;
+ case PACKAGE_MARIADB_SYM: return PACKAGE_ORACLE_SYM;
+ case RAISE_MARIADB_SYM: return RAISE_ORACLE_SYM;
+ case RAW_MARIADB_SYM: return RAW_ORACLE_SYM;
+ case RETURN_MARIADB_SYM: return RETURN_ORACLE_SYM;
+ case ROWTYPE_MARIADB_SYM: return ROWTYPE_ORACLE_SYM;
+ case VARCHAR2_MARIADB_SYM: return VARCHAR2_ORACLE_SYM;
+ }
+ }
if ((symbol->tok == NOT_SYM) &&
- (lip->m_thd->variables.sql_mode & MODE_HIGH_NOT_PRECEDENCE))
+ (m_thd->variables.sql_mode & MODE_HIGH_NOT_PRECEDENCE))
return NOT2_SYM;
- if ((symbol->tok == OR_OR_SYM) &&
- !(lip->m_thd->variables.sql_mode & MODE_PIPES_AS_CONCAT))
- return OR2_SYM;
+ if ((symbol->tok == OR2_SYM) &&
+ (m_thd->variables.sql_mode & MODE_PIPES_AS_CONCAT))
+ {
+ return (m_thd->variables.sql_mode & MODE_ORACLE) ?
+ ORACLE_CONCAT_SYM : MYSQL_CONCAT_SYM;
+ }
return symbol->tok;
}
@@ -878,60 +909,77 @@ bool is_keyword(const char *name, uint len)
@retval 1 name isn't a function
*/
-bool is_lex_native_function(const LEX_STRING *name)
+bool is_lex_native_function(const LEX_CSTRING *name)
{
DBUG_ASSERT(name != NULL);
return (get_hash_symbol(name->str, (uint) name->length, 1) != 0);
}
-/* make a copy of token before ptr and set yytoklen */
-static LEX_STRING get_token(Lex_input_stream *lip, uint skip, uint length)
+bool is_native_function(THD *thd, const LEX_CSTRING *name)
{
- LEX_STRING tmp;
- lip->yyUnget(); // ptr points now after last token char
- tmp.length= length;
- tmp.str= lip->m_thd->strmake(lip->get_tok_start() + skip, tmp.length);
+ if (find_native_function_builder(thd, name))
+ return true;
- lip->m_cpp_text_start= lip->get_cpp_tok_start() + skip;
- lip->m_cpp_text_end= lip->m_cpp_text_start + tmp.length;
+ if (is_lex_native_function(name))
+ return true;
- return tmp;
+ return false;
}
-/*
- todo:
- There are no dangerous charsets in mysql for function
- get_quoted_token yet. But it should be fixed in the
- future to operate multichar strings (like ucs2)
-*/
-static LEX_STRING get_quoted_token(Lex_input_stream *lip,
- uint skip,
- uint length, char quote)
+bool is_native_function_with_warn(THD *thd, const LEX_CSTRING *name)
{
- LEX_STRING tmp;
- const char *from, *end;
- char *to;
- lip->yyUnget(); // ptr points now after last token char
+ if (!is_native_function(thd, name))
+ return false;
+ /*
+ This warning will be printed when
+ [1] A client query is parsed,
+ [2] A stored function is loaded by db_load_routine.
+ Printing the warning for [2] is intentional, to cover the
+ following scenario:
+ - A user define a SF 'foo' using MySQL 5.N
+ - An application uses select foo(), and works.
+ - MySQL 5.{N+1} defines a new native function 'foo', as
+ part of a new feature.
+ - MySQL 5.{N+1} documentation is updated, and should mention
+ that there is a potential incompatible change in case of
+ existing stored function named 'foo'.
+ - The user deploys 5.{N+1}. At this point, 'select foo()'
+ means something different, and the user code is most likely
+ broken (it's only safe if the code is 'select db.foo()').
+ With a warning printed when the SF is loaded (which has to
+ occur before the call), the warning will provide a hint
+ explaining the root cause of a later failure of 'select foo()'.
+ With no warning printed, the user code will fail with no
+ apparent reason.
+ Printing a warning each time db_load_routine is executed for
+ an ambiguous function is annoying, since that can happen a lot,
+ but in practice should not happen unless there *are* name
+ collisions.
+ If a collision exists, it should not be silenced but fixed.
+ */
+ push_warning_printf(thd,
+ Sql_condition::WARN_LEVEL_NOTE,
+ ER_NATIVE_FCT_NAME_COLLISION,
+ ER_THD(thd, ER_NATIVE_FCT_NAME_COLLISION),
+ name->str);
+ return true;
+}
+
+
+/* make a copy of token before ptr and set yytoklen */
+
+LEX_CSTRING Lex_input_stream::get_token(uint skip, uint length)
+{
+ LEX_CSTRING tmp;
+ yyUnget(); // ptr points now after last token char
tmp.length= length;
- tmp.str=(char*) lip->m_thd->alloc(tmp.length+1);
- from= lip->get_tok_start() + skip;
- to= tmp.str;
- end= to+length;
+ tmp.str= m_thd->strmake(m_tok_start + skip, tmp.length);
- lip->m_cpp_text_start= lip->get_cpp_tok_start() + skip;
- lip->m_cpp_text_end= lip->m_cpp_text_start + length;
+ m_cpp_text_start= m_cpp_tok_start + skip;
+ m_cpp_text_end= m_cpp_text_start + tmp.length;
- for ( ; to != end; )
- {
- if ((*to++= *from++) == quote)
- {
- from++; // Skip double quotes
- lip->m_cpp_text_start++;
- }
- }
- *to= 0; // End null for safety
return tmp;
}
@@ -1007,18 +1055,19 @@ Lex_input_stream::unescape(CHARSET_INFO *cs, char *to,
Fix sometimes to do only one scan of the string
*/
-bool Lex_input_stream::get_text(LEX_STRING *dst, uint sep,
+bool Lex_input_stream::get_text(Lex_string_with_metadata_st *dst, uint sep,
int pre_skip, int post_skip)
{
uchar c;
uint found_escape=0;
CHARSET_INFO *cs= m_thd->charset();
+ bool is_8bit= false;
- tok_bitmap= 0;
while (! eof())
{
c= yyGet();
- tok_bitmap|= c;
+ if (c & 0x80)
+ is_8bit= true;
#ifdef USE_MB
{
int l;
@@ -1033,10 +1082,10 @@ bool Lex_input_stream::get_text(LEX_STRING *dst, uint sep,
#endif
if (c == '\\' &&
!(m_thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES))
- { // Escaped character
+ { // Escaped character
found_escape=1;
if (eof())
- return true;
+ return true;
yySkip();
}
else if (c == sep)
@@ -1044,39 +1093,42 @@ bool Lex_input_stream::get_text(LEX_STRING *dst, uint sep,
if (c == yyGet()) // Check if two separators in a row
{
found_escape=1; // duplicate. Remember for delete
- continue;
+ continue;
}
else
yyUnget();
/* Found end. Unescape and return string */
const char *str, *end;
+ char *to;
- str= get_tok_start();
+ str= m_tok_start;
end= get_ptr();
/* Extract the text from the token */
str += pre_skip;
end -= post_skip;
DBUG_ASSERT(end >= str);
- if (!(dst->str= (char*) m_thd->alloc((uint) (end - str) + 1)))
+ if (!(to= (char*) m_thd->alloc((uint) (end - str) + 1)))
{
- dst->str= (char*) ""; // Sql_alloc has set error flag
- dst->length= 0;
- return true;
+ dst->set(&empty_clex_str, 0, '\0');
+ return true; // Sql_alloc has set error flag
}
- m_cpp_text_start= get_cpp_tok_start() + pre_skip;
+ m_cpp_text_start= m_cpp_tok_start + pre_skip;
m_cpp_text_end= get_cpp_ptr() - post_skip;
if (!found_escape)
{
- memcpy(dst->str, str, dst->length= (end - str));
- dst->str[dst->length]= 0;
+ size_t len= (end - str);
+ memcpy(to, str, len);
+ to[len]= '\0';
+ dst->set(to, len, is_8bit, '\0');
}
else
{
- dst->length= unescape(cs, dst->str, str, end, sep);
+ size_t len= unescape(cs, to, str, end, sep);
+ dst->set(to, len, is_8bit, '\0');
}
return false;
}
@@ -1105,11 +1157,11 @@ static const uint unsigned_longlong_len=20;
static inline uint int_token(const char *str,uint length)
{
- if (length < long_len) // quick normal case
+ if (length < long_len) // quick normal case
return NUM;
bool neg=0;
- if (*str == '+') // Remove sign and pre-zeros
+ if (*str == '+') // Remove sign and pre-zeros
{
str++; length--;
}
@@ -1131,9 +1183,9 @@ static inline uint int_token(const char *str,uint length)
{
if (length == long_len)
{
- cmp= signed_long_str+1;
- smaller=NUM; // If <= signed_long_str
- bigger=LONG_NUM; // If >= signed_long_str
+ cmp= signed_long_str + 1;
+ smaller= NUM; // If <= signed_long_str
+ bigger= LONG_NUM; // If >= signed_long_str
}
else if (length < signed_longlong_len)
return LONG_NUM;
@@ -1141,8 +1193,8 @@ static inline uint int_token(const char *str,uint length)
return DECIMAL_NUM;
else
{
- cmp=signed_longlong_str+1;
- smaller=LONG_NUM; // If <= signed_longlong_str
+ cmp= signed_longlong_str + 1;
+ smaller= LONG_NUM; // If <= signed_longlong_str
bigger=DECIMAL_NUM;
}
}
@@ -1188,44 +1240,44 @@ static inline uint int_token(const char *str,uint length)
@retval Whether EOF reached before comment is closed.
*/
-bool consume_comment(Lex_input_stream *lip, int remaining_recursions_permitted)
+bool Lex_input_stream::consume_comment(int remaining_recursions_permitted)
{
// only one level of nested comments are allowed
DBUG_ASSERT(remaining_recursions_permitted == 0 ||
remaining_recursions_permitted == 1);
uchar c;
- while (! lip->eof())
+ while (!eof())
{
- c= lip->yyGet();
+ c= yyGet();
if (remaining_recursions_permitted == 1)
{
- if ((c == '/') && (lip->yyPeek() == '*'))
+ if ((c == '/') && (yyPeek() == '*'))
{
- lip->yyUnput('('); // Replace nested "/*..." with "(*..."
- lip->yySkip(); // and skip "("
+ yyUnput('('); // Replace nested "/*..." with "(*..."
+ yySkip(); // and skip "("
- lip->yySkip(); /* Eat asterisk */
- if (consume_comment(lip, 0))
+ yySkip(); /* Eat asterisk */
+ if (consume_comment(0))
return true;
- lip->yyUnput(')'); // Replace "...*/" with "...*)"
- lip->yySkip(); // and skip ")"
+ yyUnput(')'); // Replace "...*/" with "...*)"
+ yySkip(); // and skip ")"
continue;
}
}
if (c == '*')
{
- if (lip->yyPeek() == '/')
+ if (yyPeek() == '/')
{
- lip->yySkip(); /* Eat slash */
+ yySkip(); // Eat slash
return FALSE;
}
}
if (c == '\n')
- lip->yylineno++;
+ yylineno++;
}
return TRUE;
@@ -1238,31 +1290,42 @@ bool consume_comment(Lex_input_stream *lip, int remaining_recursions_permitted)
@param yylval [out] semantic value of the token being parsed (yylval)
@param thd THD
- - MY_LEX_EOQ Found end of query
- - MY_LEX_OPERATOR_OR_IDENT Last state was an ident, text or number
- (which can't be followed by a signed number)
+ - MY_LEX_EOQ Found end of query
+ - MY_LEX_OPERATOR_OR_IDENT Last state was an ident, text or number
+ (which can't be followed by a signed number)
*/
int MYSQLlex(YYSTYPE *yylval, THD *thd)
{
- Lex_input_stream *lip= & thd->m_parser_state->m_lip;
- int token;
+ return thd->m_parser_state->m_lip.lex_token(yylval, thd);
+}
+
- if (lip->lookahead_token >= 0)
+int ORAlex(YYSTYPE *yylval, THD *thd)
+{
+ return thd->m_parser_state->m_lip.lex_token(yylval, thd);
+}
+
+
+int Lex_input_stream::lex_token(YYSTYPE *yylval, THD *thd)
+{
+ int token;
+
+ if (lookahead_token >= 0)
{
/*
The next token was already parsed in advance,
return it.
*/
- token= lip->lookahead_token;
- lip->lookahead_token= -1;
- *yylval= *(lip->lookahead_yylval);
- lip->lookahead_yylval= NULL;
+ token= lookahead_token;
+ lookahead_token= -1;
+ *yylval= *(lookahead_yylval);
+ lookahead_yylval= NULL;
return token;
}
token= lex_one_token(yylval, thd);
- lip->add_digest_token(token, yylval);
+ add_digest_token(token, yylval);
switch(token) {
case WITH:
@@ -1274,88 +1337,130 @@ int MYSQLlex(YYSTYPE *yylval, THD *thd)
which sql_yacc.yy can process.
*/
token= lex_one_token(yylval, thd);
- lip->add_digest_token(token, yylval);
+ add_digest_token(token, yylval);
switch(token) {
case CUBE_SYM:
return WITH_CUBE_SYM;
case ROLLUP_SYM:
return WITH_ROLLUP_SYM;
+ case SYSTEM:
+ return WITH_SYSTEM_SYM;
default:
/*
Save the token following 'WITH'
*/
- lip->lookahead_yylval= lip->yylval;
- lip->yylval= NULL;
- lip->lookahead_token= token;
+ lookahead_yylval= yylval;
+ lookahead_token= token;
return WITH;
}
break;
+ case FOR_SYM:
+ /*
+ * Additional look-ahead to resolve doubtful cases like:
+ * SELECT ... FOR UPDATE
+ * SELECT ... FOR SYSTEM_TIME ... .
+ */
+ token= lex_one_token(yylval, thd);
+ add_digest_token(token, yylval);
+ switch(token) {
+ case SYSTEM_TIME_SYM:
+ return FOR_SYSTEM_TIME_SYM;
+ default:
+ /*
+ Save the token following 'FOR_SYM'
+ */
+ lookahead_yylval= yylval;
+ lookahead_token= token;
+ return FOR_SYM;
+ }
+ break;
+ case VALUES:
+ if (thd->lex->current_select->parsing_place == IN_UPDATE_ON_DUP_KEY ||
+ thd->lex->current_select->parsing_place == IN_PART_FUNC)
+ return VALUE_SYM;
+ token= lex_one_token(yylval, thd);
+ add_digest_token(token, yylval);
+ switch(token) {
+ case LESS_SYM:
+ return VALUES_LESS_SYM;
+ case IN_SYM:
+ return VALUES_IN_SYM;
+ default:
+ lookahead_yylval= yylval;
+ lookahead_token= token;
+ return VALUES;
+ }
+ break;
default:
break;
}
return token;
}
-static int lex_one_token(YYSTYPE *yylval, THD *thd)
+
+int Lex_input_stream::lex_one_token(YYSTYPE *yylval, THD *thd)
{
uchar UNINIT_VAR(c);
bool comment_closed;
- int tokval, result_state;
+ int tokval;
uint length;
enum my_lex_states state;
- Lex_input_stream *lip= & thd->m_parser_state->m_lip;
LEX *lex= thd->lex;
CHARSET_INFO *const cs= thd->charset();
const uchar *const state_map= cs->state_map;
const uchar *const ident_map= cs->ident_map;
- lip->yylval=yylval; // The global state
-
- lip->start_token();
- state=lip->next_state;
- lip->next_state=MY_LEX_OPERATOR_OR_IDENT;
+ start_token();
+ state= next_state;
+ next_state= MY_LEX_OPERATOR_OR_IDENT;
for (;;)
{
switch (state) {
- case MY_LEX_OPERATOR_OR_IDENT: // Next is operator or keyword
- case MY_LEX_START: // Start of token
+ case MY_LEX_OPERATOR_OR_IDENT: // Next is operator or keyword
+ case MY_LEX_START: // Start of token
// Skip starting whitespace
- while(state_map[c= lip->yyPeek()] == MY_LEX_SKIP)
+ while(state_map[c= yyPeek()] == MY_LEX_SKIP)
{
- if (c == '\n')
- lip->yylineno++;
+ if (c == '\n')
+ yylineno++;
- lip->yySkip();
+ yySkip();
}
/* Start of real token */
- lip->restart_token();
- c= lip->yyGet();
+ restart_token();
+ c= yyGet();
state= (enum my_lex_states) state_map[c];
break;
case MY_LEX_ESCAPE:
- if (!lip->eof() && lip->yyGet() == 'N')
- { // Allow \N as shortcut for NULL
- yylval->lex_str.str=(char*) "\\N";
- yylval->lex_str.length=2;
- return NULL_SYM;
+ if (!eof() && yyGet() == 'N')
+ { // Allow \N as shortcut for NULL
+ yylval->lex_str.str= (char*) "\\N";
+ yylval->lex_str.length= 2;
+ return NULL_SYM;
+ }
+ /* Fall through */
+ case MY_LEX_CHAR: // Unknown or single char token
+ if (c == '%' && (m_thd->variables.sql_mode & MODE_ORACLE))
+ {
+ next_state= MY_LEX_START;
+ return PERCENT_ORACLE_SYM;
}
/* Fall through */
- case MY_LEX_CHAR: // Unknown or single char token
- case MY_LEX_SKIP: // This should not happen
+ case MY_LEX_SKIP: // This should not happen
if (c != ')')
- lip->next_state= MY_LEX_START; // Allow signed numbers
+ next_state= MY_LEX_START; // Allow signed numbers
return((int) c);
case MY_LEX_MINUS_OR_COMMENT:
- if (lip->yyPeek() == '-' &&
- (my_isspace(cs,lip->yyPeekn(1)) ||
- my_iscntrl(cs,lip->yyPeekn(1))))
+ if (yyPeek() == '-' &&
+ (my_isspace(cs,yyPeekn(1)) ||
+ my_iscntrl(cs,yyPeekn(1))))
{
state=MY_LEX_COMMENT;
break;
}
- lip->next_state= MY_LEX_START; // Allow signed numbers
+ next_state= MY_LEX_START; // Allow signed numbers
return((int) c);
case MY_LEX_PLACEHOLDER:
@@ -1365,13 +1470,13 @@ static int lex_one_token(YYSTYPE *yylval, THD *thd)
its value in a query for the binlog, the query must stay
grammatically correct.
*/
- lip->next_state= MY_LEX_START; // Allow signed numbers
- if (lip->stmt_prepare_mode && !ident_map[(uchar) lip->yyPeek()])
+ next_state= MY_LEX_START; // Allow signed numbers
+ if (stmt_prepare_mode && !ident_map[(uchar) yyPeek()])
return(PARAM_MARKER);
return((int) c);
case MY_LEX_COMMA:
- lip->next_state= MY_LEX_START; // Allow signed numbers
+ next_state= MY_LEX_START; // Allow signed numbers
/*
Warning:
This is a work around, to make the "remember_name" rule in
@@ -1381,428 +1486,298 @@ static int lex_one_token(YYSTYPE *yylval, THD *thd)
remember_name (see select_item) *before* actually parsing the
first token of expr2.
*/
- lip->restart_token();
+ restart_token();
return((int) c);
case MY_LEX_IDENT_OR_NCHAR:
{
uint sep;
- if (lip->yyPeek() != '\'')
+ if (yyPeek() != '\'')
{
- state= MY_LEX_IDENT;
- break;
+ state= MY_LEX_IDENT;
+ break;
}
/* Found N'string' */
- lip->yySkip(); // Skip '
- if (lip->get_text(&yylval->lex_str, (sep= lip->yyGetLast()), 2, 1))
+ yySkip(); // Skip '
+ if (get_text(&yylval->lex_string_with_metadata, (sep= yyGetLast()), 2, 1))
{
- state= MY_LEX_CHAR; // Read char by char
- break;
+ state= MY_LEX_CHAR; // Read char by char
+ break;
}
- lip->body_utf8_append(lip->m_cpp_text_start);
- lip->body_utf8_append_escape(thd, &yylval->lex_str,
+ body_utf8_append(m_cpp_text_start);
+ body_utf8_append_escape(thd, &yylval->lex_string_with_metadata,
national_charset_info,
- lip->m_cpp_text_end, sep);
-
- lex->text_string_is_7bit= (lip->tok_bitmap & 0x80) ? 0 : 1;
+ m_cpp_text_end, sep);
return(NCHAR_STRING);
}
case MY_LEX_IDENT_OR_HEX:
- if (lip->yyPeek() == '\'')
- { // Found x'hex-number'
- state= MY_LEX_HEX_NUMBER;
- break;
+ if (yyPeek() == '\'')
+ { // Found x'hex-number'
+ state= MY_LEX_HEX_NUMBER;
+ break;
}
/* fall through */
case MY_LEX_IDENT_OR_BIN:
- if (lip->yyPeek() == '\'')
+ if (yyPeek() == '\'')
{ // Found b'bin-number'
state= MY_LEX_BIN_NUMBER;
break;
}
/* fall through */
case MY_LEX_IDENT:
- const char *start;
-#if defined(USE_MB) && defined(USE_MB_IDENT)
- if (use_mb(cs))
- {
- result_state= IDENT_QUOTED;
- int char_length= my_charlen(cs, lip->get_ptr() - 1,
- lip->get_end_of_query());
- if (char_length <= 0)
- {
- state= MY_LEX_CHAR;
- continue;
- }
- lip->skip_binary(char_length - 1);
-
- while (ident_map[c=lip->yyGet()])
- {
- char_length= my_charlen(cs, lip->get_ptr() - 1,
- lip->get_end_of_query());
- if (char_length <= 0)
- break;
- lip->skip_binary(char_length - 1);
- }
- }
- else
-#endif
- {
- for (result_state= c;
- ident_map[(uchar) (c= lip->yyGet())];
- result_state|= c)
- ;
- /* If there were non-ASCII characters, mark that we must convert */
- result_state= result_state & 0x80 ? IDENT_QUOTED : IDENT;
- }
- length= lip->yyLength();
- start= lip->get_ptr();
- if (lip->ignore_space)
- {
- /*
- If we find a space then this can't be an identifier. We notice this
- below by checking start != lex->ptr.
- */
- for (; state_map[(uchar) c] == MY_LEX_SKIP ; c= lip->yyGet())
- {
- if (c == '\n')
- lip->yylineno++;
- }
- }
- if (start == lip->get_ptr() && c == '.' &&
- ident_map[(uchar) lip->yyPeek()])
- lip->next_state=MY_LEX_IDENT_SEP;
- else
- { // '(' must follow directly if function
- lip->yyUnget();
- if ((tokval = find_keyword(lip, length, c == '(')))
- {
- lip->next_state= MY_LEX_START; // Allow signed numbers
- return(tokval); // Was keyword
- }
- lip->yySkip(); // next state does a unget
- }
- yylval->lex_str=get_token(lip, 0, length);
-
- /*
- Note: "SELECT _bla AS 'alias'"
- _bla should be considered as a IDENT if charset haven't been found.
- So we don't use MYF(MY_WME) with get_charset_by_csname to avoid
- producing an error.
- */
-
- if (yylval->lex_str.str[0] == '_')
- {
- CHARSET_INFO *cs= get_charset_by_csname(yylval->lex_str.str + 1,
- MY_CS_PRIMARY, MYF(0));
- if (cs)
- {
- yylval->charset= cs;
- lip->m_underscore_cs= cs;
-
- lip->body_utf8_append(lip->m_cpp_text_start,
- lip->get_cpp_tok_start() + length);
- return(UNDERSCORE_CHARSET);
- }
- }
-
- lip->body_utf8_append(lip->m_cpp_text_start);
-
- lip->body_utf8_append_ident(thd, &yylval->lex_str, lip->m_cpp_text_end);
-
- return(result_state); // IDENT or IDENT_QUOTED
+ {
+ tokval= scan_ident_middle(thd, &yylval->ident_cli,
+ &yylval->charset, &state);
+ if (!tokval)
+ continue;
+ if (tokval == UNDERSCORE_CHARSET)
+ m_underscore_cs= yylval->charset;
+ return tokval;
+ }
case MY_LEX_IDENT_SEP: // Found ident and now '.'
- yylval->lex_str.str= (char*) lip->get_ptr();
+ yylval->lex_str.str= (char*) get_ptr();
yylval->lex_str.length= 1;
- c= lip->yyGet(); // should be '.'
- lip->next_state= MY_LEX_IDENT_START; // Next is ident (not keyword)
- if (!ident_map[(uchar) lip->yyPeek()]) // Probably ` or "
- lip->next_state= MY_LEX_START;
+ c= yyGet(); // should be '.'
+ if (lex->parsing_options.lookup_keywords_after_qualifier)
+ next_state= MY_LEX_IDENT_OR_KEYWORD;
+ else
+ next_state= MY_LEX_IDENT_START; // Next is ident (not keyword)
+ if (!ident_map[(uchar) yyPeek()]) // Probably ` or "
+ next_state= MY_LEX_START;
return((int) c);
- case MY_LEX_NUMBER_IDENT: // number or ident which num-start
- if (lip->yyGetLast() == '0')
+ case MY_LEX_NUMBER_IDENT: // number or ident which num-start
+ if (yyGetLast() == '0')
{
- c= lip->yyGet();
+ c= yyGet();
if (c == 'x')
{
- while (my_isxdigit(cs,(c = lip->yyGet()))) ;
- if ((lip->yyLength() >= 3) && !ident_map[c])
+ while (my_isxdigit(cs, (c = yyGet()))) ;
+ if ((yyLength() >= 3) && !ident_map[c])
{
/* skip '0x' */
- yylval->lex_str=get_token(lip, 2, lip->yyLength()-2);
+ yylval->lex_str= get_token(2, yyLength() - 2);
return (HEX_NUM);
}
- lip->yyUnget();
+ yyUnget();
state= MY_LEX_IDENT_START;
break;
}
else if (c == 'b')
{
- while ((c= lip->yyGet()) == '0' || c == '1')
+ while ((c= yyGet()) == '0' || c == '1')
;
- if ((lip->yyLength() >= 3) && !ident_map[c])
+ if ((yyLength() >= 3) && !ident_map[c])
{
/* Skip '0b' */
- yylval->lex_str= get_token(lip, 2, lip->yyLength()-2);
+ yylval->lex_str= get_token(2, yyLength() - 2);
return (BIN_NUM);
}
- lip->yyUnget();
+ yyUnget();
state= MY_LEX_IDENT_START;
break;
}
- lip->yyUnget();
+ yyUnget();
}
- while (my_isdigit(cs, (c = lip->yyGet()))) ;
+ while (my_isdigit(cs, (c= yyGet()))) ;
if (!ident_map[c])
- { // Can't be identifier
- state=MY_LEX_INT_OR_REAL;
- break;
+ { // Can't be identifier
+ state=MY_LEX_INT_OR_REAL;
+ break;
}
if (c == 'e' || c == 'E')
{
- // The following test is written this way to allow numbers of type 1e1
- if (my_isdigit(cs,lip->yyPeek()) ||
- (c=(lip->yyGet())) == '+' || c == '-')
- { // Allow 1E+10
- if (my_isdigit(cs,lip->yyPeek())) // Number must have digit after sign
- {
- lip->yySkip();
- while (my_isdigit(cs,lip->yyGet())) ;
- yylval->lex_str=get_token(lip, 0, lip->yyLength());
- return(FLOAT_NUM);
- }
- }
- lip->yyUnget();
- }
- // fall through
- case MY_LEX_IDENT_START: // We come here after '.'
- result_state= IDENT;
-#if defined(USE_MB) && defined(USE_MB_IDENT)
- if (use_mb(cs))
- {
- result_state= IDENT_QUOTED;
- while (ident_map[c=lip->yyGet()])
- {
- int char_length= my_charlen(cs, lip->get_ptr() - 1,
- lip->get_end_of_query());
- if (char_length <= 0)
- break;
- lip->skip_binary(char_length - 1);
+ // The following test is written this way to allow numbers of type 1e1
+ if (my_isdigit(cs, yyPeek()) ||
+ (c=(yyGet())) == '+' || c == '-')
+ { // Allow 1E+10
+ if (my_isdigit(cs, yyPeek())) // Number must have digit after sign
+ {
+ yySkip();
+ while (my_isdigit(cs, yyGet())) ;
+ yylval->lex_str= get_token(0, yyLength());
+ return(FLOAT_NUM);
+ }
}
+ /*
+ We've found:
+ - A sequence of digits
+ - Followed by 'e' or 'E'
+ - Followed by some byte XX which is not a known mantissa start,
+ and it's known to be a valid identifier part.
+ XX can be either a 8bit identifier character, or a multi-byte head.
+ */
+ yyUnget();
+ return scan_ident_start(thd, &yylval->ident_cli);
}
- else
-#endif
- {
- for (result_state=0; ident_map[c= lip->yyGet()]; result_state|= c)
- ;
- /* If there were non-ASCII characters, mark that we must convert */
- result_state= result_state & 0x80 ? IDENT_QUOTED : IDENT;
- }
- if (c == '.' && ident_map[(uchar) lip->yyPeek()])
- lip->next_state=MY_LEX_IDENT_SEP;// Next is '.'
-
- yylval->lex_str= get_token(lip, 0, lip->yyLength());
-
- lip->body_utf8_append(lip->m_cpp_text_start);
+ /*
+ We've found:
+ - A sequence of digits
+ - Followed by some character XX, which is neither 'e' nor 'E',
+ and it's known to be a valid identifier part.
+ XX can be a 8bit identifier character, or a multi-byte head.
+ */
+ yyUnget();
+ return scan_ident_start(thd, &yylval->ident_cli);
- lip->body_utf8_append_ident(thd, &yylval->lex_str, lip->m_cpp_text_end);
+ case MY_LEX_IDENT_START: // We come here after '.'
+ return scan_ident_start(thd, &yylval->ident_cli);
- return(result_state);
+ case MY_LEX_USER_VARIABLE_DELIMITER: // Found quote char
+ return scan_ident_delimited(thd, &yylval->ident_cli);
- case MY_LEX_USER_VARIABLE_DELIMITER: // Found quote char
- {
- uint double_quotes= 0;
- char quote_char= c; // Used char
- while ((c=lip->yyGet()))
+ case MY_LEX_INT_OR_REAL: // Complete int or incomplete real
+ if (c != '.' || yyPeek() == '.')
{
- int var_length= my_charlen(cs, lip->get_ptr() - 1,
- lip->get_end_of_query());
- if (var_length == 1)
- {
- if (c == quote_char)
- {
- if (lip->yyPeek() != quote_char)
- break;
- c=lip->yyGet();
- double_quotes++;
- continue;
- }
- }
-#ifdef USE_MB
- else if (var_length > 1)
- {
- lip->skip_binary(var_length - 1);
- }
-#endif
- }
- if (double_quotes)
- yylval->lex_str=get_quoted_token(lip, 1,
- lip->yyLength() - double_quotes -1,
- quote_char);
- else
- yylval->lex_str=get_token(lip, 1, lip->yyLength() -1);
- if (c == quote_char)
- lip->yySkip(); // Skip end `
- lip->next_state= MY_LEX_START;
-
- lip->body_utf8_append(lip->m_cpp_text_start);
-
- lip->body_utf8_append_ident(thd, &yylval->lex_str, lip->m_cpp_text_end);
-
- return(IDENT_QUOTED);
- }
- case MY_LEX_INT_OR_REAL: // Complete int or incomplete real
- if (c != '.')
- { // Found complete integer number.
- yylval->lex_str=get_token(lip, 0, lip->yyLength());
- return int_token(yylval->lex_str.str, (uint) yylval->lex_str.length);
+ /*
+ Found a complete integer number:
+ - the number is either not followed by a dot at all, or
+ - the number is followed by a double dot as in: FOR i IN 1..10
+ */
+ yylval->lex_str= get_token(0, yyLength());
+ return int_token(yylval->lex_str.str, (uint) yylval->lex_str.length);
}
// fall through
- case MY_LEX_REAL: // Incomplete real number
- while (my_isdigit(cs,c = lip->yyGet())) ;
+ case MY_LEX_REAL: // Incomplete real number
+ while (my_isdigit(cs, c= yyGet())) ;
if (c == 'e' || c == 'E')
{
- c = lip->yyGet();
- if (c == '-' || c == '+')
- c = lip->yyGet(); // Skip sign
- if (!my_isdigit(cs,c))
- { // No digit after sign
- state= MY_LEX_CHAR;
- break;
- }
- while (my_isdigit(cs,lip->yyGet())) ;
- yylval->lex_str=get_token(lip, 0, lip->yyLength());
- return(FLOAT_NUM);
+ c= yyGet();
+ if (c == '-' || c == '+')
+ c= yyGet(); // Skip sign
+ if (!my_isdigit(cs, c))
+ { // No digit after sign
+ state= MY_LEX_CHAR;
+ break;
+ }
+ while (my_isdigit(cs, yyGet())) ;
+ yylval->lex_str= get_token(0, yyLength());
+ return(FLOAT_NUM);
}
- yylval->lex_str=get_token(lip, 0, lip->yyLength());
+ yylval->lex_str= get_token(0, yyLength());
return(DECIMAL_NUM);
- case MY_LEX_HEX_NUMBER: // Found x'hexstring'
- lip->yySkip(); // Accept opening '
- while (my_isxdigit(cs, (c= lip->yyGet()))) ;
+ case MY_LEX_HEX_NUMBER: // Found x'hexstring'
+ yySkip(); // Accept opening '
+ while (my_isxdigit(cs, (c= yyGet()))) ;
if (c != '\'')
return(ABORT_SYM); // Illegal hex constant
- lip->yySkip(); // Accept closing '
- length= lip->yyLength(); // Length of hexnum+3
+ yySkip(); // Accept closing '
+ length= yyLength(); // Length of hexnum+3
if ((length % 2) == 0)
return(ABORT_SYM); // odd number of hex digits
- yylval->lex_str=get_token(lip,
- 2, // skip x'
- length-3); // don't count x' and last '
+ yylval->lex_str= get_token(2, // skip x'
+ length - 3); // don't count x' and last '
return HEX_STRING;
case MY_LEX_BIN_NUMBER: // Found b'bin-string'
- lip->yySkip(); // Accept opening '
- while ((c= lip->yyGet()) == '0' || c == '1')
+ yySkip(); // Accept opening '
+ while ((c= yyGet()) == '0' || c == '1')
;
if (c != '\'')
return(ABORT_SYM); // Illegal hex constant
- lip->yySkip(); // Accept closing '
- length= lip->yyLength(); // Length of bin-num + 3
- yylval->lex_str= get_token(lip,
- 2, // skip b'
- length-3); // don't count b' and last '
+ yySkip(); // Accept closing '
+ length= yyLength(); // Length of bin-num + 3
+ yylval->lex_str= get_token(2, // skip b'
+ length - 3); // don't count b' and last '
return (BIN_NUM);
- case MY_LEX_CMP_OP: // Incomplete comparison operator
- lip->next_state= MY_LEX_START; // Allow signed numbers
- if (state_map[(uchar) lip->yyPeek()] == MY_LEX_CMP_OP ||
- state_map[(uchar) lip->yyPeek()] == MY_LEX_LONG_CMP_OP)
+ case MY_LEX_CMP_OP: // Incomplete comparison operator
+ next_state= MY_LEX_START; // Allow signed numbers
+ if (state_map[(uchar) yyPeek()] == MY_LEX_CMP_OP ||
+ state_map[(uchar) yyPeek()] == MY_LEX_LONG_CMP_OP)
{
- lip->yySkip();
- if ((tokval= find_keyword(lip, 2, 0)))
+ yySkip();
+ if ((tokval= find_keyword(&yylval->kwd, 2, 0)))
return(tokval);
- lip->yyUnget();
+ yyUnget();
}
return(c);
- case MY_LEX_LONG_CMP_OP: // Incomplete comparison operator
- lip->next_state= MY_LEX_START;
- if (state_map[(uchar) lip->yyPeek()] == MY_LEX_CMP_OP ||
- state_map[(uchar) lip->yyPeek()] == MY_LEX_LONG_CMP_OP)
+ case MY_LEX_LONG_CMP_OP: // Incomplete comparison operator
+ next_state= MY_LEX_START;
+ if (state_map[(uchar) yyPeek()] == MY_LEX_CMP_OP ||
+ state_map[(uchar) yyPeek()] == MY_LEX_LONG_CMP_OP)
{
- lip->yySkip();
- if (state_map[(uchar) lip->yyPeek()] == MY_LEX_CMP_OP)
+ yySkip();
+ if (state_map[(uchar) yyPeek()] == MY_LEX_CMP_OP)
{
- lip->yySkip();
- if ((tokval= find_keyword(lip, 3, 0)))
+ yySkip();
+ if ((tokval= find_keyword(&yylval->kwd, 3, 0)))
return(tokval);
- lip->yyUnget();
+ yyUnget();
}
- if ((tokval= find_keyword(lip, 2, 0)))
+ if ((tokval= find_keyword(&yylval->kwd, 2, 0)))
return(tokval);
- lip->yyUnget();
+ yyUnget();
}
return(c);
case MY_LEX_BOOL:
- if (c != lip->yyPeek())
+ if (c != yyPeek())
{
- state=MY_LEX_CHAR;
- break;
+ state= MY_LEX_CHAR;
+ break;
}
- lip->yySkip();
- tokval = find_keyword(lip,2,0); // Is a bool operator
- lip->next_state= MY_LEX_START; // Allow signed numbers
+ yySkip();
+ tokval= find_keyword(&yylval->kwd, 2, 0); // Is a bool operator
+ next_state= MY_LEX_START; // Allow signed numbers
return(tokval);
case MY_LEX_STRING_OR_DELIMITER:
if (thd->variables.sql_mode & MODE_ANSI_QUOTES)
{
- state= MY_LEX_USER_VARIABLE_DELIMITER;
- break;
+ state= MY_LEX_USER_VARIABLE_DELIMITER;
+ break;
}
/* " used for strings */
/* fall through */
- case MY_LEX_STRING: // Incomplete text string
+ case MY_LEX_STRING: // Incomplete text string
{
uint sep;
- if (lip->get_text(&yylval->lex_str, (sep= lip->yyGetLast()), 1, 1))
+ if (get_text(&yylval->lex_string_with_metadata, (sep= yyGetLast()), 1, 1))
{
- state= MY_LEX_CHAR; // Read char by char
- break;
+ state= MY_LEX_CHAR; // Read char by char
+ break;
}
- CHARSET_INFO *strcs= lip->m_underscore_cs ? lip->m_underscore_cs : cs;
- lip->body_utf8_append(lip->m_cpp_text_start);
-
- lip->body_utf8_append_escape(thd, &yylval->lex_str, strcs,
- lip->m_cpp_text_end, sep);
- lip->m_underscore_cs= NULL;
+ CHARSET_INFO *strcs= m_underscore_cs ? m_underscore_cs : cs;
+ body_utf8_append(m_cpp_text_start);
- lex->text_string_is_7bit= (lip->tok_bitmap & 0x80) ? 0 : 1;
+ body_utf8_append_escape(thd, &yylval->lex_string_with_metadata,
+ strcs, m_cpp_text_end, sep);
+ m_underscore_cs= NULL;
return(TEXT_STRING);
}
- case MY_LEX_COMMENT: // Comment
+ case MY_LEX_COMMENT: // Comment
lex->select_lex.options|= OPTION_FOUND_COMMENT;
- while ((c = lip->yyGet()) != '\n' && c) ;
- lip->yyUnget(); // Safety against eof
- state = MY_LEX_START; // Try again
+ while ((c= yyGet()) != '\n' && c) ;
+ yyUnget(); // Safety against eof
+ state= MY_LEX_START; // Try again
break;
- case MY_LEX_LONG_COMMENT: /* Long C comment? */
- if (lip->yyPeek() != '*')
+ case MY_LEX_LONG_COMMENT: // Long C comment?
+ if (yyPeek() != '*')
{
- state=MY_LEX_CHAR; // Probable division
- break;
+ state= MY_LEX_CHAR; // Probable division
+ break;
}
lex->select_lex.options|= OPTION_FOUND_COMMENT;
/* Reject '/' '*', since we might need to turn off the echo */
- lip->yyUnget();
+ yyUnget();
- lip->save_in_comment_state();
+ save_in_comment_state();
- if (lip->yyPeekn(2) == '!' ||
- (lip->yyPeekn(2) == 'M' && lip->yyPeekn(3) == '!'))
+ if (yyPeekn(2) == '!' ||
+ (yyPeekn(2) == 'M' && yyPeekn(3) == '!'))
{
- bool maria_comment_syntax= lip->yyPeekn(2) == 'M';
- lip->in_comment= DISCARD_COMMENT;
+ bool maria_comment_syntax= yyPeekn(2) == 'M';
+ in_comment= DISCARD_COMMENT;
/* Accept '/' '*' '!', but do not keep this marker. */
- lip->set_echo(FALSE);
- lip->yySkipn(maria_comment_syntax ? 4 : 3);
+ set_echo(FALSE);
+ yySkipn(maria_comment_syntax ? 4 : 3);
/*
The special comment format is very strict:
@@ -1813,24 +1788,24 @@ static int lex_one_token(YYSTYPE *yylval, THD *thd)
50114 -> 5.1.14
100000 -> 10.0.0
*/
- if ( my_isdigit(cs, lip->yyPeekn(0))
- && my_isdigit(cs, lip->yyPeekn(1))
- && my_isdigit(cs, lip->yyPeekn(2))
- && my_isdigit(cs, lip->yyPeekn(3))
- && my_isdigit(cs, lip->yyPeekn(4))
+ if ( my_isdigit(cs, yyPeekn(0))
+ && my_isdigit(cs, yyPeekn(1))
+ && my_isdigit(cs, yyPeekn(2))
+ && my_isdigit(cs, yyPeekn(3))
+ && my_isdigit(cs, yyPeekn(4))
)
{
ulong version;
uint length= 5;
- char *end_ptr= (char*) lip->get_ptr()+length;
+ char *end_ptr= (char*) get_ptr() + length;
int error;
- if (my_isdigit(cs, lip->yyPeekn(5)))
+ if (my_isdigit(cs, yyPeekn(5)))
{
end_ptr++; // 6 digit number
length++;
}
- version= (ulong) my_strtoll10(lip->get_ptr(), &end_ptr, &error);
+ version= (ulong) my_strtoll10(get_ptr(), &end_ptr, &error);
/*
MySQL-5.7 has new features and might have new SQL syntax that
@@ -1842,31 +1817,31 @@ static int lex_one_token(YYSTYPE *yylval, THD *thd)
(version < 50700 || version > 99999 || maria_comment_syntax))
{
/* Accept 'M' 'm' 'm' 'd' 'd' */
- lip->yySkipn(length);
+ yySkipn(length);
/* Expand the content of the special comment as real code */
- lip->set_echo(TRUE);
+ set_echo(TRUE);
state=MY_LEX_START;
break; /* Do not treat contents as a comment. */
}
else
{
#ifdef WITH_WSREP
- if (WSREP(thd) && version == 99997 && thd->wsrep_exec_mode == LOCAL_STATE)
- {
- WSREP_DEBUG("consistency check: %s", thd->query());
- thd->wsrep_consistency_check= CONSISTENCY_CHECK_DECLARED;
- lip->yySkipn(5);
- lip->set_echo(TRUE);
- state=MY_LEX_START;
- break; /* Do not treat contents as a comment. */
- }
+ if (WSREP(thd) && version == 99997 && thd->wsrep_exec_mode == LOCAL_STATE)
+ {
+ WSREP_DEBUG("consistency check: %s", thd->query());
+ thd->wsrep_consistency_check= CONSISTENCY_CHECK_DECLARED;
+ yySkipn(5);
+ set_echo(TRUE);
+ state= MY_LEX_START;
+ break; /* Do not treat contents as a comment. */
+ }
#endif /* WITH_WSREP */
/*
Patch and skip the conditional comment to avoid it
being propagated infinitely (eg. to a slave).
*/
- char *pcom= lip->yyUnput(' ');
- comment_closed= ! consume_comment(lip, 1);
+ char *pcom= yyUnput(' ');
+ comment_closed= ! consume_comment(1);
if (! comment_closed)
{
*pcom= '!';
@@ -1878,16 +1853,16 @@ static int lex_one_token(YYSTYPE *yylval, THD *thd)
{
/* Not a version comment. */
state=MY_LEX_START;
- lip->set_echo(TRUE);
+ set_echo(TRUE);
break;
}
}
else
{
- lip->in_comment= PRESERVE_COMMENT;
- lip->yySkip(); // Accept /
- lip->yySkip(); // Accept *
- comment_closed= ! consume_comment(lip, 0);
+ in_comment= PRESERVE_COMMENT;
+ yySkip(); // Accept /
+ yySkip(); // Accept *
+ comment_closed= ! consume_comment(0);
/* regular comments can have zero comments inside. */
}
/*
@@ -1901,139 +1876,382 @@ static int lex_one_token(YYSTYPE *yylval, THD *thd)
/#!VERSI oned containing /# regular #/ is allowed #/
- Inside one versioned comment, another versioned comment
- is treated as a regular discardable comment. It gets
- no special parsing.
+ Inside one versioned comment, another versioned comment
+ is treated as a regular discardable comment. It gets
+ no special parsing.
*/
/* Unbalanced comments with a missing '*' '/' are a syntax error */
if (! comment_closed)
return (ABORT_SYM);
state = MY_LEX_START; // Try again
- lip->restore_in_comment_state();
+ restore_in_comment_state();
break;
case MY_LEX_END_LONG_COMMENT:
- if ((lip->in_comment != NO_COMMENT) && lip->yyPeek() == '/')
+ if ((in_comment != NO_COMMENT) && yyPeek() == '/')
{
/* Reject '*' '/' */
- lip->yyUnget();
+ yyUnget();
/* Accept '*' '/', with the proper echo */
- lip->set_echo(lip->in_comment == PRESERVE_COMMENT);
- lip->yySkipn(2);
+ set_echo(in_comment == PRESERVE_COMMENT);
+ yySkipn(2);
/* And start recording the tokens again */
- lip->set_echo(TRUE);
- lip->in_comment=NO_COMMENT;
+ set_echo(TRUE);
+ in_comment= NO_COMMENT;
state=MY_LEX_START;
}
else
- state=MY_LEX_CHAR; // Return '*'
+ state= MY_LEX_CHAR; // Return '*'
break;
- case MY_LEX_SET_VAR: // Check if ':='
- if (lip->yyPeek() != '=')
+ case MY_LEX_SET_VAR: // Check if ':='
+ if (yyPeek() != '=')
{
- state=MY_LEX_CHAR; // Return ':'
- break;
+ next_state= MY_LEX_START;
+ if (m_thd->variables.sql_mode & MODE_ORACLE)
+ {
+ yylval->kwd.set_keyword(m_tok_start, 1);
+ return COLON_ORACLE_SYM;
+ }
+ return (int) ':';
}
- lip->yySkip();
+ yySkip();
return (SET_VAR);
- case MY_LEX_SEMICOLON: // optional line terminator
+ case MY_LEX_SEMICOLON: // optional line terminator
state= MY_LEX_CHAR; // Return ';'
break;
case MY_LEX_EOL:
- if (lip->eof())
+ if (eof())
{
- lip->yyUnget(); // Reject the last '\0'
- lip->set_echo(FALSE);
- lip->yySkip();
- lip->set_echo(TRUE);
+ yyUnget(); // Reject the last '\0'
+ set_echo(FALSE);
+ yySkip();
+ set_echo(TRUE);
/* Unbalanced comments with a missing '*' '/' are a syntax error */
- if (lip->in_comment != NO_COMMENT)
+ if (in_comment != NO_COMMENT)
return (ABORT_SYM);
- lip->next_state=MY_LEX_END; // Mark for next loop
+ next_state= MY_LEX_END; // Mark for next loop
return(END_OF_INPUT);
}
state=MY_LEX_CHAR;
break;
case MY_LEX_END:
- lip->next_state=MY_LEX_END;
- return(0); // We found end of input last time
+ next_state= MY_LEX_END;
+ return(0); // We found end of input last time
/* Actually real shouldn't start with . but allow them anyhow */
case MY_LEX_REAL_OR_POINT:
- if (my_isdigit(cs,lip->yyPeek()))
- state = MY_LEX_REAL; // Real
+ if (my_isdigit(cs, (c= yyPeek())))
+ state = MY_LEX_REAL; // Real
+ else if (c == '.')
+ {
+ yySkip();
+ return DOT_DOT_SYM;
+ }
else
{
- state= MY_LEX_IDENT_SEP; // return '.'
- lip->yyUnget(); // Put back '.'
+ state= MY_LEX_IDENT_SEP; // return '.'
+ yyUnget(); // Put back '.'
}
break;
- case MY_LEX_USER_END: // end '@' of user@hostname
- switch (state_map[(uchar) lip->yyPeek()]) {
+ case MY_LEX_USER_END: // end '@' of user@hostname
+ switch (state_map[(uchar) yyPeek()]) {
case MY_LEX_STRING:
case MY_LEX_USER_VARIABLE_DELIMITER:
case MY_LEX_STRING_OR_DELIMITER:
- break;
+ break;
case MY_LEX_USER_END:
- lip->next_state=MY_LEX_SYSTEM_VAR;
- break;
+ next_state= MY_LEX_SYSTEM_VAR;
+ break;
default:
- lip->next_state=MY_LEX_HOSTNAME;
- break;
+ next_state= MY_LEX_HOSTNAME;
+ break;
}
- yylval->lex_str.str=(char*) lip->get_ptr();
- yylval->lex_str.length=1;
+ yylval->lex_str.str= (char*) get_ptr();
+ yylval->lex_str.length= 1;
return((int) '@');
- case MY_LEX_HOSTNAME: // end '@' of user@hostname
- for (c=lip->yyGet() ;
- my_isalnum(cs,c) || c == '.' || c == '_' || c == '$';
- c= lip->yyGet()) ;
- yylval->lex_str=get_token(lip, 0, lip->yyLength());
+ case MY_LEX_HOSTNAME: // end '@' of user@hostname
+ for (c= yyGet() ;
+ my_isalnum(cs, c) || c == '.' || c == '_' || c == '$';
+ c= yyGet()) ;
+ yylval->lex_str= get_token(0, yyLength());
return(LEX_HOSTNAME);
case MY_LEX_SYSTEM_VAR:
- yylval->lex_str.str=(char*) lip->get_ptr();
- yylval->lex_str.length=1;
- lip->yySkip(); // Skip '@'
- lip->next_state= (state_map[(uchar) lip->yyPeek()] ==
- MY_LEX_USER_VARIABLE_DELIMITER ?
- MY_LEX_OPERATOR_OR_IDENT :
- MY_LEX_IDENT_OR_KEYWORD);
+ yylval->lex_str.str= (char*) get_ptr();
+ yylval->lex_str.length= 1;
+ yySkip(); // Skip '@'
+ next_state= (state_map[(uchar) yyPeek()] ==
+ MY_LEX_USER_VARIABLE_DELIMITER ?
+ MY_LEX_OPERATOR_OR_IDENT :
+ MY_LEX_IDENT_OR_KEYWORD);
return((int) '@');
case MY_LEX_IDENT_OR_KEYWORD:
/*
- We come here when we have found two '@' in a row.
- We should now be able to handle:
- [(global | local | session) .]variable_name
+ We come here when we have found two '@' in a row.
+ We should now be able to handle:
+ [(global | local | session) .]variable_name
*/
+ return scan_ident_sysvar(thd, &yylval->ident_cli);
+ }
+ }
+}
- for (result_state= 0; ident_map[c= lip->yyGet()]; result_state|= c)
- ;
- /* If there were non-ASCII characters, mark that we must convert */
- result_state= result_state & 0x80 ? IDENT_QUOTED : IDENT;
-
- if (c == '.')
- lip->next_state=MY_LEX_IDENT_SEP;
- length= lip->yyLength();
- if (length == 0)
- return(ABORT_SYM); // Names must be nonempty.
- if ((tokval= find_keyword(lip, length,0)))
- {
- lip->yyUnget(); // Put back 'c'
- return(tokval); // Was keyword
- }
- yylval->lex_str=get_token(lip, 0, length);
- lip->body_utf8_append(lip->m_cpp_text_start);
+bool Lex_input_stream::get_7bit_or_8bit_ident(THD *thd, uchar *last_char)
+{
+ uchar c;
+ CHARSET_INFO *const cs= thd->charset();
+ const uchar *const ident_map= cs->ident_map;
+ bool is_8bit= false;
+ for ( ; ident_map[c= yyGet()]; )
+ {
+ if (c & 0x80)
+ is_8bit= true; // will convert
+ }
+ *last_char= c;
+ return is_8bit;
+}
- lip->body_utf8_append_ident(thd, &yylval->lex_str, lip->m_cpp_text_end);
- return(result_state);
+int Lex_input_stream::scan_ident_sysvar(THD *thd, Lex_ident_cli_st *str)
+{
+ uchar last_char;
+ uint length;
+ int tokval;
+ bool is_8bit;
+ DBUG_ASSERT(m_tok_start == m_ptr);
+
+ is_8bit= get_7bit_or_8bit_ident(thd, &last_char);
+
+ if (last_char == '.')
+ next_state= MY_LEX_IDENT_SEP;
+ if (!(length= yyLength()))
+ return ABORT_SYM; // Names must be nonempty.
+ if ((tokval= find_keyword(str, length, 0)))
+ {
+ yyUnget(); // Put back 'c'
+ return tokval; // Was keyword
+ }
+
+ yyUnget(); // ptr points now after last token char
+ str->set_ident(m_tok_start, length, is_8bit);
+
+ m_cpp_text_start= m_cpp_tok_start;
+ m_cpp_text_end= m_cpp_text_start + length;
+ body_utf8_append(m_cpp_text_start);
+ body_utf8_append_ident(thd, str, m_cpp_text_end);
+
+ return is_8bit ? IDENT_QUOTED : IDENT;
+}
+
+
+/*
+ We can come here if different parsing stages:
+ - In an identifier chain:
+ SELECT t1.cccc FROM t1;
+ (when the "cccc" part starts)
+ In this case both m_tok_start and m_ptr point to "cccc".
+ - When a sequence of digits has changed to something else,
+ therefore the token becomes an identifier rather than a number:
+ SELECT 12345_6 FROM t1;
+ In this case m_tok_start points to the entire "12345_678",
+ while m_ptr points to "678".
+*/
+int Lex_input_stream::scan_ident_start(THD *thd, Lex_ident_cli_st *str)
+{
+ uchar c;
+ bool is_8bit;
+ CHARSET_INFO *const cs= thd->charset();
+ const uchar *const ident_map= cs->ident_map;
+ DBUG_ASSERT(m_tok_start <= m_ptr);
+
+ if (use_mb(cs))
+ {
+ is_8bit= true;
+ while (ident_map[c= yyGet()])
+ {
+ int char_length= my_charlen(cs, get_ptr() - 1, get_end_of_query());
+ if (char_length <= 0)
+ break;
+ skip_binary(char_length - 1);
}
}
+ else
+ {
+ is_8bit= get_7bit_or_8bit_ident(thd, &c);
+ }
+ if (c == '.' && ident_map[(uchar) yyPeek()])
+ next_state= MY_LEX_IDENT_SEP;// Next is '.'
+
+ uint length= yyLength();
+ yyUnget(); // ptr points now after last token char
+ str->set_ident(m_tok_start, length, is_8bit);
+ m_cpp_text_start= m_cpp_tok_start;
+ m_cpp_text_end= m_cpp_text_start + length;
+ body_utf8_append(m_cpp_text_start);
+ body_utf8_append_ident(thd, str, m_cpp_text_end);
+ return is_8bit ? IDENT_QUOTED : IDENT;
}
-void trim_whitespace(CHARSET_INFO *cs, LEX_STRING *str, uint *prefix_length)
+int Lex_input_stream::scan_ident_middle(THD *thd, Lex_ident_cli_st *str,
+ CHARSET_INFO **introducer,
+ my_lex_states *st)
+{
+ CHARSET_INFO *const cs= thd->charset();
+ const uchar *const ident_map= cs->ident_map;
+ const uchar *const state_map= cs->state_map;
+ const char *start;
+ uint length;
+ uchar c;
+ bool is_8bit;
+ bool resolve_introducer= true;
+ DBUG_ASSERT(m_ptr == m_tok_start + 1); // m_ptr points to the second byte
+
+ if (use_mb(cs))
+ {
+ is_8bit= true;
+ int char_length= my_charlen(cs, get_ptr() - 1, get_end_of_query());
+ if (char_length <= 0)
+ {
+ *st= MY_LEX_CHAR;
+ return 0;
+ }
+ skip_binary(char_length - 1);
+
+ while (ident_map[c= yyGet()])
+ {
+ char_length= my_charlen(cs, get_ptr() - 1, get_end_of_query());
+ if (char_length <= 0)
+ break;
+ if (char_length > 1 || (c & 0x80))
+ resolve_introducer= false;
+ skip_binary(char_length - 1);
+ }
+ }
+ else
+ {
+ is_8bit= get_7bit_or_8bit_ident(thd, &c) || (m_tok_start[0] & 0x80);
+ resolve_introducer= !is_8bit;
+ }
+ length= yyLength();
+ start= get_ptr();
+ if (ignore_space)
+ {
+ /*
+ If we find a space then this can't be an identifier. We notice this
+ below by checking start != lex->ptr.
+ */
+ for (; state_map[(uchar) c] == MY_LEX_SKIP ; c= yyGet())
+ {
+ if (c == '\n')
+ yylineno++;
+ }
+ }
+ if (start == get_ptr() && c == '.' && ident_map[(uchar) yyPeek()])
+ next_state= MY_LEX_IDENT_SEP;
+ else
+ { // '(' must follow directly if function
+ int tokval;
+ yyUnget();
+ if ((tokval= find_keyword(str, length, c == '(')))
+ {
+ next_state= MY_LEX_START; // Allow signed numbers
+ return(tokval); // Was keyword
+ }
+ yySkip(); // next state does a unget
+ }
+
+ /*
+ Note: "SELECT _bla AS 'alias'"
+ _bla should be considered as a IDENT if charset haven't been found.
+ So we don't use MYF(MY_WME) with get_charset_by_csname to avoid
+ producing an error.
+ */
+ DBUG_ASSERT(length > 0);
+ if (resolve_introducer && m_tok_start[0] == '_')
+ {
+
+ yyUnget(); // ptr points now after last token char
+ str->set_ident(m_tok_start, length, false);
+
+ m_cpp_text_start= m_cpp_tok_start;
+ m_cpp_text_end= m_cpp_text_start + length;
+ body_utf8_append(m_cpp_text_start, m_cpp_tok_start + length);
+ ErrConvString csname(str->str + 1, str->length - 1, &my_charset_bin);
+ CHARSET_INFO *cs= get_charset_by_csname(csname.ptr(),
+ MY_CS_PRIMARY, MYF(0));
+ if (cs)
+ {
+ *introducer= cs;
+ return UNDERSCORE_CHARSET;
+ }
+ return IDENT;
+ }
+
+ yyUnget(); // ptr points now after last token char
+ str->set_ident(m_tok_start, length, is_8bit);
+ m_cpp_text_start= m_cpp_tok_start;
+ m_cpp_text_end= m_cpp_text_start + length;
+ body_utf8_append(m_cpp_text_start);
+ body_utf8_append_ident(thd, str, m_cpp_text_end);
+ return is_8bit ? IDENT_QUOTED : IDENT;
+}
+
+
+int Lex_input_stream::scan_ident_delimited(THD *thd,
+ Lex_ident_cli_st *str)
+{
+ CHARSET_INFO *const cs= thd->charset();
+ uint double_quotes= 0;
+ uchar c, quote_char= m_tok_start[0];
+ DBUG_ASSERT(m_ptr == m_tok_start + 1);
+
+ for ( ; ; )
+ {
+ if (!(c= yyGet()))
+ {
+ /*
+ End-of-query or straight 0x00 inside a delimited identifier.
+ Return the quote character, to have the parser fail on syntax error.
+ */
+ m_ptr= (char *) m_tok_start + 1;
+ return quote_char;
+ }
+ int var_length= my_charlen(cs, get_ptr() - 1, get_end_of_query());
+ if (var_length == 1)
+ {
+ if (c == quote_char)
+ {
+ if (yyPeek() != quote_char)
+ break;
+ c= yyGet();
+ double_quotes++;
+ continue;
+ }
+ }
+ else if (var_length > 1)
+ {
+ skip_binary(var_length - 1);
+ }
+ }
+
+ str->set_ident_quoted(m_tok_start + 1, yyLength() - 1, true, quote_char);
+ yyUnget(); // ptr points now after last token char
+
+ m_cpp_text_start= m_cpp_tok_start + 1;
+ m_cpp_text_end= m_cpp_text_start + str->length;
+
+ if (c == quote_char)
+ yySkip(); // Skip end `
+ next_state= MY_LEX_START;
+ body_utf8_append(m_cpp_text_start);
+ // QQQ: shouldn't it add unescaped version ????
+ body_utf8_append_ident(thd, str, m_cpp_text_end);
+ return IDENT_QUOTED;
+}
+
+
+void trim_whitespace(CHARSET_INFO *cs, LEX_CSTRING *str, size_t * prefix_length)
{
/*
TODO:
@@ -2041,14 +2259,15 @@ void trim_whitespace(CHARSET_INFO *cs, LEX_STRING *str, uint *prefix_length)
that can be considered white-space.
*/
- *prefix_length= 0;
+ size_t plen= 0;
while ((str->length > 0) && (my_isspace(cs, str->str[0])))
{
- (*prefix_length)++;
+ plen++;
str->length --;
str->str ++;
}
-
+ if (prefix_length)
+ *prefix_length= plen;
/*
FIXME:
Also, parsing backward is not safe with multi bytes characters
@@ -2064,7 +2283,7 @@ void trim_whitespace(CHARSET_INFO *cs, LEX_STRING *str, uint *prefix_length)
st_select_lex structures initialisations
*/
-void st_select_lex_node::init_query()
+void st_select_lex_node::init_query_common()
{
options= 0;
sql_cache= SQL_CACHE_UNSPECIFIED;
@@ -2073,18 +2292,14 @@ void st_select_lex_node::init_query()
uncacheable= 0;
}
-void st_select_lex_node::init_select()
-{
-}
-
void st_select_lex_unit::init_query()
{
- st_select_lex_node::init_query();
+ init_query_common();
linkage= GLOBAL_OPTIONS_TYPE;
select_limit_cnt= HA_POS_ERROR;
offset_limit_cnt= 0;
union_distinct= 0;
- prepared= optimized= executed= 0;
+ prepared= optimized= optimized_2= executed= 0;
optimize_started= 0;
item= 0;
union_result= 0;
@@ -2100,11 +2315,13 @@ void st_select_lex_unit::init_query()
with_clause= 0;
with_element= 0;
columns_are_renamed= false;
+ intersect_mark= NULL;
+ with_wrapped_tvc= false;
}
void st_select_lex::init_query()
{
- st_select_lex_node::init_query();
+ init_query_common();
table_list.empty();
top_join_list.empty();
join_list= &top_join_list;
@@ -2156,19 +2373,23 @@ void st_select_lex::init_query()
select_list_tables= 0;
m_non_agg_field_used= false;
m_agg_func_used= false;
+ m_custom_agg_func_used= false;
window_specs.empty();
window_funcs.empty();
+ tvc= 0;
+ in_tvc= false;
+ versioned_tables= 0;
}
void st_select_lex::init_select()
{
- st_select_lex_node::init_select();
sj_nests.empty();
sj_subselects.empty();
group_list.empty();
if (group_list_ptrs)
group_list_ptrs->clear();
- type= db= 0;
+ type= 0;
+ db= null_clex_str;
having= 0;
table_join_options= 0;
in_sum_expr= with_wild= 0;
@@ -2177,7 +2398,6 @@ void st_select_lex::init_select()
ftfunc_list_alloc.empty();
inner_sum_func_list= 0;
ftfunc_list= &ftfunc_list_alloc;
- linkage= UNSPECIFIED_TYPE;
order_list.elements= 0;
order_list.first= 0;
order_list.next= &order_list.first;
@@ -2194,10 +2414,16 @@ void st_select_lex::init_select()
merged_into= 0;
m_non_agg_field_used= false;
m_agg_func_used= false;
- name_visibility_map= 0;
+ m_custom_agg_func_used= false;
+ name_visibility_map.clear_all();
with_dep= 0;
join= 0;
lock_type= TL_READ_DEFAULT;
+ tvc= 0;
+ in_funcs.empty();
+ curr_tvc_name= 0;
+ in_tvc= false;
+ versioned_tables= 0;
}
/*
@@ -2249,7 +2475,7 @@ void st_select_lex_node::add_slave(st_select_lex_node *slave_arg)
ref - references on reference on this node
*/
void st_select_lex_node::include_standalone(st_select_lex_node *upper,
- st_select_lex_node **ref)
+ st_select_lex_node **ref)
{
next= 0;
prev= ref;
@@ -2314,7 +2540,7 @@ void st_select_lex_node::fast_exclude()
*/
st_select_lex_node *st_select_lex_node:: insert_chain_before(
- st_select_lex_node **ptr_pos_to_insert,
+ st_select_lex_node **ptr_pos_to_insert,
st_select_lex_node *end_chain_node)
{
end_chain_node->link_next= *ptr_pos_to_insert;
@@ -2509,26 +2735,6 @@ bool st_select_lex::mark_as_dependent(THD *thd, st_select_lex *last,
return FALSE;
}
-bool st_select_lex_node::inc_in_sum_expr() { return 1; }
-uint st_select_lex_node::get_in_sum_expr() { return 0; }
-TABLE_LIST* st_select_lex_node::get_table_list() { return 0; }
-List<Item>* st_select_lex_node::get_item_list() { return 0; }
-TABLE_LIST *st_select_lex_node::add_table_to_list(THD *thd, Table_ident *table,
- LEX_STRING *alias,
- ulong table_join_options,
- thr_lock_type flags,
- enum_mdl_type mdl_type,
- List<Index_hint> *hints,
- List<String> *partition_names,
- LEX_STRING *option)
-{
- return 0;
-}
-ulong st_select_lex_node::get_table_join_options()
-{
- return 0;
-}
-
/*
prohibit using LIMIT clause
*/
@@ -2558,15 +2764,9 @@ ha_rows st_select_lex::get_offset()
if (offset_limit)
{
// see comment for st_select_lex::get_limit()
- bool fix_fields_successful= true;
- if (!offset_limit->fixed)
- {
- fix_fields_successful= !offset_limit->fix_fields(master_unit()->thd,
- NULL);
-
- DBUG_ASSERT(fix_fields_successful);
- }
- val= fix_fields_successful ? offset_limit->val_uint() : HA_POS_ERROR;
+ bool err= offset_limit->fix_fields_if_needed(master_unit()->thd, NULL);
+ DBUG_ASSERT(!err);
+ val= err ? HA_POS_ERROR : offset_limit->val_uint();
}
return (ha_rows)val;
@@ -2605,15 +2805,9 @@ ha_rows st_select_lex::get_limit()
fix_fields() implementation. Also added runtime check against a result
of fix_fields() in order to handle error condition in non-debug build.
*/
- bool fix_fields_successful= true;
- if (!select_limit->fixed)
- {
- fix_fields_successful= !select_limit->fix_fields(master_unit()->thd,
- NULL);
-
- DBUG_ASSERT(fix_fields_successful);
- }
- val= fix_fields_successful ? select_limit->val_uint() : HA_POS_ERROR;
+ bool err= select_limit->fix_fields_if_needed(master_unit()->thd, NULL);
+ DBUG_ASSERT(!err);
+ val= err ? HA_POS_ERROR : select_limit->val_uint();
}
return (ha_rows)val;
@@ -2723,7 +2917,7 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num)
return false;
}
Item **array= static_cast<Item**>(arena->alloc(sizeof(Item*) * n_elems));
- if (array != NULL)
+ if (likely(array != NULL))
ref_pointer_array= Ref_ptr_array(array, n_elems);
return array == NULL;
@@ -2739,10 +2933,24 @@ void st_select_lex_unit::print(String *str, enum_query_type query_type)
{
if (sl != first_select())
{
- str->append(STRING_WITH_LEN(" union "));
- if (union_all)
- str->append(STRING_WITH_LEN("all "));
- else if (union_distinct == sl)
+ switch (sl->linkage)
+ {
+ default:
+ DBUG_ASSERT(0);
+ /* 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 "));
+ break;
+ case EXCEPT_TYPE:
+ str->append(STRING_WITH_LEN(" except "));
+ break;
+ }
+ if (sl == union_distinct)
union_all= TRUE;
}
if (sl->braces)
@@ -2854,9 +3062,28 @@ void LEX::cleanup_lex_after_parse_error(THD *thd)
*/
if (thd->lex->sphead)
{
+ sp_package *pkg;
thd->lex->sphead->restore_thd_mem_root(thd);
- sp_head::destroy(thd->lex->sphead);
- thd->lex->sphead= NULL;
+ if ((pkg= thd->lex->sphead->m_parent))
+ {
+ /*
+ If a syntax error happened inside a package routine definition,
+ then thd->lex points to the routine sublex. We need to restore to
+ the top level LEX.
+ */
+ DBUG_ASSERT(pkg->m_top_level_lex);
+ DBUG_ASSERT(pkg == pkg->m_top_level_lex->sphead);
+ pkg->restore_thd_mem_root(thd);
+ LEX *top= pkg->m_top_level_lex;
+ sp_package::destroy(pkg);
+ thd->lex= top;
+ thd->lex->sphead= NULL;
+ }
+ else
+ {
+ sp_head::destroy(thd->lex->sphead);
+ thd->lex->sphead= NULL;
+ }
}
}
@@ -2944,10 +3171,9 @@ void Query_tables_list::destroy_query_tables_list()
*/
LEX::LEX()
- : explain(NULL),
- result(0), arena_for_set_stmt(0), mem_root_for_set_stmt(0),
+ : explain(NULL), result(0), part_info(NULL), arena_for_set_stmt(0), mem_root_for_set_stmt(0),
option_type(OPT_DEFAULT), context_analysis_only(0), sphead(0),
- is_lex_started(0), limit_rows_examined_cnt(ULONGLONG_MAX)
+ default_used(0), is_lex_started(0), limit_rows_examined_cnt(ULONGLONG_MAX)
{
init_dynamic_array2(&plugins, sizeof(plugin_ref), plugins_static_buffer,
@@ -3007,11 +3233,11 @@ bool LEX::can_be_merged()
}
return (selects_allow_merge &&
- select_lex.group_list.elements == 0 &&
- select_lex.having == 0 &&
+ select_lex.group_list.elements == 0 &&
+ select_lex.having == 0 &&
select_lex.with_sum_func == 0 &&
- select_lex.table_list.elements >= 1 &&
- !(select_lex.options & SELECT_DISTINCT) &&
+ select_lex.table_list.elements >= 1 &&
+ !(select_lex.options & SELECT_DISTINCT) &&
select_lex.select_limit == 0);
}
@@ -3184,8 +3410,7 @@ uint8 LEX::get_effective_with_check(TABLE_LIST *view)
case of success
*/
-bool
-LEX::copy_db_to(char **p_db, size_t *p_db_length) const
+bool LEX::copy_db_to(LEX_CSTRING *to)
{
if (sphead && sphead->m_name.str)
{
@@ -3194,12 +3419,10 @@ LEX::copy_db_to(char **p_db, size_t *p_db_length) const
It is safe to assign the string by-pointer, both sphead and
its statements reside in the same memory root.
*/
- *p_db= sphead->m_db.str;
- if (p_db_length)
- *p_db_length= sphead->m_db.length;
+ *to= sphead->m_db;
return FALSE;
}
- return thd->copy_db_to(p_db, p_db_length);
+ return thd->copy_db_to(to);
}
/**
@@ -3232,6 +3455,19 @@ bool st_select_lex_unit::union_needs_tmp_table()
{
if (with_element && with_element->is_recursive)
return true;
+ if (!with_wrapped_tvc)
+ {
+ for (st_select_lex *sl= first_select(); sl; sl=sl->next_select())
+ {
+ if (sl->tvc && sl->tvc->to_be_wrapped_as_with_tail())
+ {
+ with_wrapped_tvc= true;
+ break;
+ }
+ }
+ }
+ if (with_wrapped_tvc)
+ return true;
return union_distinct != NULL ||
global_parameters()->order_list.elements != 0 ||
thd->lex->sql_command == SQLCOM_INSERT_SELECT ||
@@ -3294,12 +3530,8 @@ void LEX::set_trg_event_type_for_tables()
On a LOCK TABLE, all triggers must be pre-loaded for this TABLE_LIST
when opening an associated TABLE.
*/
- new_trg_event_map= static_cast<uint8>
- (1 << static_cast<int>(TRG_EVENT_INSERT)) |
- static_cast<uint8>
- (1 << static_cast<int>(TRG_EVENT_UPDATE)) |
- static_cast<uint8>
- (1 << static_cast<int>(TRG_EVENT_DELETE));
+ new_trg_event_map= trg2bit(TRG_EVENT_INSERT) | trg2bit(TRG_EVENT_UPDATE) |
+ trg2bit(TRG_EVENT_DELETE);
break;
/*
Basic INSERT. If there is an additional ON DUPLIATE KEY UPDATE
@@ -3329,20 +3561,18 @@ void LEX::set_trg_event_type_for_tables()
REPLACE SELECT is handled later in this method.
*/
case SQLCOM_CREATE_TABLE:
- new_trg_event_map|= static_cast<uint8>
- (1 << static_cast<int>(TRG_EVENT_INSERT));
+ case SQLCOM_CREATE_SEQUENCE:
+ new_trg_event_map|= trg2bit(TRG_EVENT_INSERT);
break;
/* Basic update and multi-update */
case SQLCOM_UPDATE: /* fall through */
case SQLCOM_UPDATE_MULTI:
- new_trg_event_map|= static_cast<uint8>
- (1 << static_cast<int>(TRG_EVENT_UPDATE));
+ new_trg_event_map|= trg2bit(TRG_EVENT_UPDATE);
break;
/* Basic delete and multi-delete */
case SQLCOM_DELETE: /* fall through */
case SQLCOM_DELETE_MULTI:
- new_trg_event_map|= static_cast<uint8>
- (1 << static_cast<int>(TRG_EVENT_DELETE));
+ new_trg_event_map|= trg2bit(TRG_EVENT_DELETE);
break;
default:
break;
@@ -3350,12 +3580,10 @@ void LEX::set_trg_event_type_for_tables()
switch (duplicates) {
case DUP_UPDATE:
- new_trg_event_map|= static_cast<uint8>
- (1 << static_cast<int>(TRG_EVENT_UPDATE));
+ new_trg_event_map|= trg2bit(TRG_EVENT_UPDATE);
break;
case DUP_REPLACE:
- new_trg_event_map|= static_cast<uint8>
- (1 << static_cast<int>(TRG_EVENT_DELETE));
+ new_trg_event_map|= trg2bit(TRG_EVENT_DELETE);
break;
case DUP_ERROR:
default:
@@ -3394,14 +3622,14 @@ void LEX::set_trg_event_type_for_tables()
SYNOPSIS
unlink_first_table()
- link_to_local Set to 1 if caller should link this table to local list
+ link_to_local Set to 1 if caller should link this table to local list
NOTES
We assume that first tables in both lists is the same table or the local
list is empty.
RETURN
- 0 If 'query_tables' == 0
+ 0 If 'query_tables' == 0
unlinked table
In this case link_to_local is set.
@@ -3428,7 +3656,7 @@ TABLE_LIST *LEX::unlink_first_table(bool *link_to_local)
select_lex.context.table_list=
select_lex.context.first_name_resolution_table= first->next_local;
select_lex.table_list.first= first->next_local;
- select_lex.table_list.elements--; //safety
+ select_lex.table_list.elements--; //safety
first->next_local= 0;
/*
Ensure that the global list has the same first table as the local
@@ -3490,14 +3718,14 @@ void LEX::first_lists_tables_same()
SYNOPSIS
link_first_table_back()
- link_to_local do we need link this table to local
+ link_to_local do we need link this table to local
RETURN
global list
*/
void LEX::link_first_table_back(TABLE_LIST *first,
- bool link_to_local)
+ bool link_to_local)
{
if (first)
{
@@ -3512,7 +3740,7 @@ void LEX::link_first_table_back(TABLE_LIST *first,
first->next_local= select_lex.table_list.first;
select_lex.context.table_list= first;
select_lex.table_list.first= first;
- select_lex.table_list.elements++; //safety
+ select_lex.table_list.elements++; //safety
}
}
}
@@ -3675,6 +3903,7 @@ void st_select_lex::fix_prepare_information(THD *thd, Item **conds,
if (!thd->stmt_arena->is_conventional() &&
!(changed_elements & TOUCHED_SEL_COND))
{
+ Query_arena_stmt on_stmt_arena(thd);
changed_elements|= TOUCHED_SEL_COND;
if (group_list.first)
{
@@ -3768,7 +3997,7 @@ void st_select_lex::alloc_index_hints (THD *thd)
RETURN VALUE
0 on success, non-zero otherwise
*/
-bool st_select_lex::add_index_hint (THD *thd, char *str, uint length)
+bool st_select_lex::add_index_hint (THD *thd, const char *str, size_t length)
{
return index_hints->push_front(new (thd->mem_root)
Index_hint(current_index_hint_type,
@@ -3807,15 +4036,15 @@ bool st_select_lex::optimize_unflattened_subqueries(bool const_only)
{
if (!subquery_predicate->fixed)
{
- /*
- This subquery was excluded as part of some expression so it is
- invisible from all prepared expression.
+ /*
+ This subquery was excluded as part of some expression so it is
+ invisible from all prepared expression.
*/
- next_unit= un->next_unit();
- un->exclude_level();
- if (next_unit)
- continue;
- break;
+ next_unit= un->next_unit();
+ un->exclude_level();
+ if (next_unit)
+ continue;
+ break;
}
if (subquery_predicate->substype() == Item_subselect::IN_SUBS)
{
@@ -4127,6 +4356,22 @@ bool SELECT_LEX::merge_subquery(THD *thd, TABLE_LIST *derived,
if (in_subq->emb_on_expr_nest == NO_JOIN_NEST)
in_subq->emb_on_expr_nest= derived;
}
+
+ uint cnt= sizeof(expr_cache_may_be_used)/sizeof(bool);
+ for (uint i= 0; i < cnt; i++)
+ {
+ if (subq_select->expr_cache_may_be_used[i])
+ expr_cache_may_be_used[i]= true;
+ }
+
+ List_iterator_fast<Item_func_in> it(subq_select->in_funcs);
+ Item_func_in *in_func;
+ while ((in_func= it++))
+ {
+ in_funcs.push_back(in_func, thd->mem_root);
+ if (in_func->emb_on_expr_nest == NO_JOIN_NEST)
+ in_func->emb_on_expr_nest= derived;
+ }
}
/* Walk through child's tables and adjust table map, tablenr,
@@ -4182,7 +4427,7 @@ void SELECT_LEX::update_used_tables()
for (embedding= tl->embedding; embedding; embedding=embedding->embedding)
{
if (embedding->is_view_or_derived())
- {
+ {
DBUG_ASSERT(embedding->is_merged_derived());
TABLE *tab= tl->table;
tab->covering_keys= tab->s->keys_for_keyread;
@@ -4214,7 +4459,7 @@ void SELECT_LEX::update_used_tables()
bool maybe_null;
if ((maybe_null= MY_TEST(embedding->outer_join)))
{
- tl->table->maybe_null= maybe_null;
+ tl->table->maybe_null= maybe_null;
break;
}
}
@@ -4278,7 +4523,8 @@ void SELECT_LEX::update_used_tables()
}
for (ORDER *order= group_list.first; order; order= order->next)
(*order->item)->update_used_tables();
- if (!master_unit()->is_union() || master_unit()->global_parameters() != this)
+ if (!master_unit()->is_unit_op() ||
+ master_unit()->global_parameters() != this)
{
for (ORDER *order= order_list.first; order; order= order->next)
(*order->item)->update_used_tables();
@@ -4336,7 +4582,7 @@ void st_select_lex::update_correlated_cache()
is_correlated|= MY_TEST((*order->item)->used_tables() &
OUTER_REF_TABLE_BIT);
- if (!master_unit()->is_union())
+ if (!master_unit()->is_unit_op())
{
for (ORDER *order= order_list.first; order; order= order->next)
is_correlated|= MY_TEST((*order->item)->used_tables() &
@@ -4408,7 +4654,12 @@ void st_select_lex::set_explain_type(bool on_the_fly)
{
/* If we're a direct child of a UNION, we're the first sibling there */
if (linkage == DERIVED_TABLE_TYPE)
- type= "DERIVED";
+ {
+ if (is_uncacheable & UNCACHEABLE_DEPENDENT)
+ type= "LATERAL DERIVED";
+ else
+ type= "DERIVED";
+ }
else if (using_materialization)
type= "MATERIALIZED";
else
@@ -4424,43 +4675,54 @@ void st_select_lex::set_explain_type(bool on_the_fly)
}
else
{
- /* This a non-first sibling in UNION */
- if (is_uncacheable & UNCACHEABLE_DEPENDENT)
- type= "DEPENDENT UNION";
- else if (using_materialization)
- type= "MATERIALIZED UNION";
- else
+ switch (linkage)
{
- type= is_uncacheable ? "UNCACHEABLE UNION": "UNION";
- if (this == master_unit()->fake_select_lex)
- type= "UNION RESULT";
- /*
- join below may be =NULL when this functions is called at an early
- stage. It will be later called again and we will set the correct
- value.
- */
- if (join)
+ case INTERSECT_TYPE:
+ type= "INTERSECT";
+ break;
+ case EXCEPT_TYPE:
+ type= "EXCEPT";
+ break;
+ default:
+ /* This a non-first sibling in UNION */
+ if (is_uncacheable & UNCACHEABLE_DEPENDENT)
+ type= "DEPENDENT UNION";
+ else if (using_materialization)
+ type= "MATERIALIZED UNION";
+ else
{
- bool uses_cte= false;
- for (JOIN_TAB *tab= first_linear_tab(join, WITHOUT_BUSH_ROOTS,
- WITH_CONST_TABLES);
- tab;
- tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS))
+ type= is_uncacheable ? "UNCACHEABLE UNION": "UNION";
+ if (this == master_unit()->fake_select_lex)
+ type= unit_operation_text[master_unit()->common_op()];
+ /*
+ join below may be =NULL when this functions is called at an early
+ stage. It will be later called again and we will set the correct
+ value.
+ */
+ if (join)
{
- /*
- pos_in_table_list=NULL for e.g. post-join aggregation JOIN_TABs.
- */
- if (tab->table && tab->table->pos_in_table_list &&
- tab->table->pos_in_table_list->with &&
- tab->table->pos_in_table_list->with->is_recursive)
+ bool uses_cte= false;
+ for (JOIN_TAB *tab= first_linear_tab(join, WITHOUT_BUSH_ROOTS,
+ WITH_CONST_TABLES);
+ tab;
+ tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS))
{
- uses_cte= true;
- break;
+ /*
+ pos_in_table_list=NULL for e.g. post-join aggregation JOIN_TABs.
+ */
+ if (tab->table && tab->table->pos_in_table_list &&
+ tab->table->pos_in_table_list->with &&
+ tab->table->pos_in_table_list->with->is_recursive)
+ {
+ uses_cte= true;
+ break;
+ }
}
+ if (uses_cte)
+ type= "RECURSIVE UNION";
}
- if (uses_cte)
- type= "RECURSIVE UNION";
}
+ break;
}
}
}
@@ -4499,11 +4761,23 @@ void SELECT_LEX::increase_derived_records(ha_rows records)
return;
}
- select_union *result= (select_union*)unit->result;
- if (HA_ROWS_MAX - records > result->records)
- result->records+= records;
- else
- result->records= HA_ROWS_MAX;
+ select_result *result= unit->result;
+ switch (linkage)
+ {
+ case INTERSECT_TYPE:
+ // result of intersect can't be more then one of components
+ set_if_smaller(result->est_records, records);
+ case EXCEPT_TYPE:
+ // in worse case none of record will be removed
+ break;
+ default:
+ // usual UNION
+ if (HA_ROWS_MAX - records > result->est_records)
+ result->est_records+= records;
+ else
+ result->est_records= HA_ROWS_MAX;
+ break;
+ }
}
@@ -4528,7 +4802,8 @@ void SELECT_LEX::mark_const_derived(bool empty)
{
if (!empty)
increase_derived_records(1);
- if (!master_unit()->is_union() && !derived->is_merged_derived())
+ if (!master_unit()->is_unit_op() && !derived->is_merged_derived() &&
+ !(join && join->with_two_phase_optimization))
derived->fill_me= TRUE;
}
}
@@ -4699,14 +4974,14 @@ bool LEX::set_arena_for_set_stmt(Query_arena *backup)
if (!mem_root_for_set_stmt)
{
mem_root_for_set_stmt= new MEM_ROOT();
- if (!(mem_root_for_set_stmt))
+ if (unlikely(!(mem_root_for_set_stmt)))
DBUG_RETURN(1);
- init_sql_alloc(mem_root_for_set_stmt, ALLOC_ROOT_SET, ALLOC_ROOT_SET,
- MYF(MY_THREAD_SPECIFIC));
+ init_sql_alloc(mem_root_for_set_stmt, "set_stmt",
+ ALLOC_ROOT_SET, ALLOC_ROOT_SET, MYF(MY_THREAD_SPECIFIC));
}
- if (!(arena_for_set_stmt= new(mem_root_for_set_stmt)
- Query_arena_memroot(mem_root_for_set_stmt,
- Query_arena::STMT_INITIALIZED)))
+ if (unlikely(!(arena_for_set_stmt= new(mem_root_for_set_stmt)
+ Query_arena_memroot(mem_root_for_set_stmt,
+ Query_arena::STMT_INITIALIZED))))
DBUG_RETURN(1);
DBUG_PRINT("info", ("mem_root: %p arena: %p",
mem_root_for_set_stmt,
@@ -4757,6 +5032,42 @@ void LEX::restore_set_statement_var()
DBUG_VOID_RETURN;
}
+unit_common_op st_select_lex_unit::common_op()
+{
+ SELECT_LEX *first= first_select();
+ bool first_op= TRUE;
+ unit_common_op operation= OP_MIX; // if no op
+ for (SELECT_LEX *sl= first; sl; sl= sl->next_select())
+ {
+ if (sl != first)
+ {
+ unit_common_op op;
+ switch (sl->linkage)
+ {
+ case INTERSECT_TYPE:
+ op= OP_INTERSECT;
+ break;
+ case EXCEPT_TYPE:
+ op= OP_EXCEPT;
+ break;
+ default:
+ op= OP_UNION;
+ break;
+ }
+ if (first_op)
+ {
+ operation= op;
+ first_op= FALSE;
+ }
+ else
+ {
+ if (operation != op)
+ operation= OP_MIX;
+ }
+ }
+ }
+ return operation;
+}
/*
Save explain structures of a UNION. The only variable member is whether the
union has "Using filesort".
@@ -4783,6 +5094,8 @@ int st_select_lex_unit::save_union_explain(Explain_query *output)
Explain_union *eu=
new (output->mem_root) Explain_union(output->mem_root,
thd->lex->analyze_stmt);
+ if (unlikely(!eu))
+ return 0;
if (with_element && with_element->is_recursive)
eu->is_recursive_cte= true;
@@ -4793,11 +5106,10 @@ int st_select_lex_unit::save_union_explain(Explain_query *output)
Note: Non-merged semi-joins cannot be made out of UNIONs currently, so we
dont ever set EXPLAIN_NODE_NON_MERGED_SJ.
*/
-
for (SELECT_LEX *sl= first; sl; sl= sl->next_select())
eu->add_select(sl->select_number);
- eu->fake_select_type= "UNION RESULT";
+ eu->fake_select_type= unit_operation_text[eu->operation= common_op()];
eu->using_filesort= MY_TEST(global_parameters()->order_list.first);
eu->using_tmp= union_needs_tmp_table();
@@ -4848,8 +5160,2181 @@ int st_select_lex_unit::save_union_explain_part2(Explain_query *output)
bool LEX::is_partition_management() const
{
return (sql_command == SQLCOM_ALTER_TABLE &&
- (alter_info.flags == Alter_info::ALTER_ADD_PARTITION ||
- alter_info.flags == Alter_info::ALTER_REORGANIZE_PARTITION));
+ (alter_info.partition_flags == ALTER_PARTITION_ADD ||
+ alter_info.partition_flags == ALTER_PARTITION_REORGANIZE));
+}
+
+
+/**
+ Exclude last added SELECT_LEX (current) in the UNIT and return pointer in it
+ (previous become currect)
+
+ @return detached SELECT_LEX or NULL in case of error
+*/
+
+SELECT_LEX *LEX::exclude_last_select()
+{
+ DBUG_ENTER("SELECT_LEX::exclude_last_select");
+ SELECT_LEX *exclude= current_select;
+ SELECT_LEX_UNIT *unit= exclude->master_unit();
+ SELECT_LEX *sl;
+ DBUG_ASSERT(unit->first_select() != exclude);
+ /* we should go through the list to correctly set current_select */
+ for(sl= unit->first_select();
+ sl->next_select() && sl->next_select() != exclude;
+ sl= sl->next_select());
+ DBUG_PRINT("info", ("excl: %p unit: %p prev: %p", exclude, unit, sl));
+ if (!sl)
+ DBUG_RETURN(NULL);
+ DBUG_ASSERT(exclude->next_select() == NULL);
+ exclude->exclude_from_tree();
+ current_select= sl;
+ DBUG_RETURN(exclude);
+}
+
+
+/**
+ Put given (new) SELECT_LEX level below after currect (last) SELECT
+
+ LAST SELECT -> DUMMY SELECT
+ |
+ V
+ NEW UNIT
+ |
+ V
+ NEW SELECT
+
+ SELECT (*LAST*) ... FROM (SELECT (*NEW*) ... )
+
+ @param nselect Select to put one level below
+
+ @retval TRUE Error
+ @retval FALSE OK
+*/
+
+bool LEX::add_unit_in_brackets(SELECT_LEX *nselect)
+{
+ DBUG_ENTER("LEX::add_unit_in_brackets");
+ bool distinct= nselect->master_unit()->union_distinct == nselect;
+ bool rc= add_select_to_union_list(distinct, nselect->linkage, 0);
+ if (rc)
+ DBUG_RETURN(TRUE);
+ SELECT_LEX* dummy_select= current_select;
+ dummy_select->automatic_brackets= TRUE;
+ dummy_select->linkage= nselect->linkage;
+
+ /* stuff dummy SELECT * FROM (...) */
+ Name_resolution_context *context= &dummy_select->context;
+ context->init();
+
+ /* add SELECT list*/
+ Item *item= new (thd->mem_root)
+ Item_field(thd, context, NULL, NULL, &star_clex_str);
+ if (unlikely(item == NULL))
+ DBUG_RETURN(TRUE);
+ if (unlikely(add_item_to_list(thd, item)))
+ DBUG_RETURN(TRUE);
+ (dummy_select->with_wild)++;
+
+ rc= mysql_new_select(this, 1, nselect);
+ nselect->linkage= DERIVED_TABLE_TYPE;
+ DBUG_ASSERT(nselect->outer_select() == dummy_select);
+
+ current_select= dummy_select;
+ current_select->nest_level--;
+
+ SELECT_LEX_UNIT *unit= nselect->master_unit();
+ Table_ident *ti= new (thd->mem_root) Table_ident(unit);
+ if (unlikely(ti == NULL))
+ DBUG_RETURN(TRUE);
+ char buff[10];
+ LEX_CSTRING alias;
+ alias.length= my_snprintf(buff, sizeof(buff),
+ "__%u", dummy_select->select_number);
+ alias.str= thd->strmake(buff, alias.length);
+ if (unlikely(!alias.str))
+ DBUG_RETURN(TRUE);
+
+ TABLE_LIST *table_list;
+ if (unlikely(!(table_list=
+ dummy_select->add_table_to_list(thd, ti, &alias,
+ 0, TL_READ,
+ MDL_SHARED_READ))))
+ DBUG_RETURN(TRUE);
+ context->resolve_in_table_list_only(table_list);
+ dummy_select->add_joined_table(table_list);
+
+ derived_tables|= DERIVED_SUBQUERY;
+
+ current_select= nselect;
+ current_select->nest_level++;
+ DBUG_RETURN(rc);
+}
+
+
+/**
+ Checks if we need finish "automatic brackets" mode
+
+ INTERSECT has higher priority then UNION and EXCEPT, so when it is need we
+ automatically create lower layer for INTERSECT (automatic brackets) and
+ here we check if we should return back one level up during parsing procedure.
+*/
+
+void LEX::check_automatic_up(enum sub_select_type type)
+{
+ if (type != INTERSECT_TYPE &&
+ current_select->linkage == INTERSECT_TYPE &&
+ current_select->outer_select() &&
+ current_select->outer_select()->automatic_brackets)
+ {
+ nest_level--;
+ current_select= current_select->outer_select();
+ }
+}
+
+
+sp_variable *LEX::sp_param_init(LEX_CSTRING *name)
+{
+ if (spcont->find_variable(name, true))
+ {
+ my_error(ER_SP_DUP_PARAM, MYF(0), name->str);
+ return NULL;
+ }
+ sp_variable *spvar= spcont->add_variable(thd, name);
+ init_last_field(&spvar->field_def, name,
+ thd->variables.collation_database);
+ return spvar;
+}
+
+
+bool LEX::sp_param_fill_definition(sp_variable *spvar)
+{
+ return sphead->fill_spvar_definition(thd, last_field, &spvar->name);
+}
+
+
+void LEX::set_stmt_init()
+{
+ sql_command= SQLCOM_SET_OPTION;
+ mysql_init_select(this);
+ option_type= OPT_SESSION;
+ autocommit= 0;
+};
+
+
+/**
+ Find a local or a package body variable by name.
+ @param IN name - the variable name
+ @param OUT ctx - NULL, if the variable was not found,
+ or LEX::spcont (if a local variable was found)
+ or the package top level context
+ (if a package variable was found)
+ @param OUT handler - NULL, if the variable was not found,
+ or a pointer to rcontext handler
+ @retval - the variable (if found), or NULL otherwise.
+*/
+sp_variable *
+LEX::find_variable(const LEX_CSTRING *name,
+ sp_pcontext **ctx,
+ const Sp_rcontext_handler **rh) const
+{
+ sp_variable *spv;
+ if (spcont && (spv= spcont->find_variable(name, false)))
+ {
+ *ctx= spcont;
+ *rh= &sp_rcontext_handler_local;
+ return spv;
+ }
+ sp_package *pkg= sphead ? sphead->m_parent : NULL;
+ if (pkg && (spv= pkg->find_package_variable(name)))
+ {
+ *ctx= pkg->get_parse_context()->child_context(0);
+ *rh= &sp_rcontext_handler_package_body;
+ return spv;
+ }
+ *ctx= NULL;
+ *rh= NULL;
+ return NULL;
+}
+
+
+static bool is_new(const char *str)
+{
+ return (str[0] == 'n' || str[0] == 'N') &&
+ (str[1] == 'e' || str[1] == 'E') &&
+ (str[2] == 'w' || str[2] == 'W');
+}
+
+static bool is_old(const char *str)
+{
+ return (str[0] == 'o' || str[0] == 'O') &&
+ (str[1] == 'l' || str[1] == 'L') &&
+ (str[2] == 'd' || str[2] == 'D');
+}
+
+
+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 &&
+ name->length == 3 && (is_new(name->str) || is_old(name->str));
+}
+
+
+void LEX::sp_variable_declarations_init(THD *thd, int nvars)
+{
+ sp_variable *spvar= spcont->get_last_context_variable();
+
+ sphead->reset_lex(thd);
+ spcont->declare_var_boundary(nvars);
+ thd->lex->init_last_field(&spvar->field_def, &spvar->name,
+ thd->variables.collation_database);
+}
+
+
+bool LEX::sp_variable_declarations_set_default(THD *thd, int nvars,
+ Item *dflt_value_item)
+{
+ if (!dflt_value_item &&
+ unlikely(!(dflt_value_item= new (thd->mem_root) Item_null(thd))))
+ return true;
+
+ for (uint i= 0 ; i < (uint) nvars ; i++)
+ {
+ sp_variable *spvar= spcont->get_last_context_variable((uint) nvars - 1 - i);
+ 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(sphead->instructions(),
+ spcont, &sp_rcontext_handler_local,
+ spvar->offset, dflt_value_item,
+ this, last);
+ if (unlikely(is == NULL || sphead->add_instr(is)))
+ return true;
+ }
+ return false;
+}
+
+
+bool
+LEX::sp_variable_declarations_copy_type_finalize(THD *thd, int nvars,
+ const Column_definition &ref,
+ Row_definition_list *fields,
+ Item *default_value)
+{
+ for (uint i= 0 ; i < (uint) nvars; i++)
+ {
+ sp_variable *spvar= spcont->get_last_context_variable((uint) nvars - 1 - i);
+ spvar->field_def.set_type(ref);
+ if (fields)
+ {
+ DBUG_ASSERT(ref.type_handler() == &type_handler_row);
+ spvar->field_def.set_row_field_definitions(fields);
+ }
+ spvar->field_def.field_name= spvar->name;
+ }
+ if (unlikely(sp_variable_declarations_set_default(thd, nvars,
+ default_value)))
+ return true;
+ spcont->declare_var_boundary(0);
+ return sphead->restore_lex(thd);
+}
+
+
+bool LEX::sp_variable_declarations_finalize(THD *thd, int nvars,
+ const Column_definition *cdef,
+ Item *dflt_value_item)
+{
+ DBUG_ASSERT(cdef);
+ Column_definition tmp(*cdef);
+ if (sphead->fill_spvar_definition(thd, &tmp))
+ return true;
+ return sp_variable_declarations_copy_type_finalize(thd, nvars, tmp, NULL,
+ dflt_value_item);
+}
+
+
+bool LEX::sp_variable_declarations_row_finalize(THD *thd, int nvars,
+ Row_definition_list *row,
+ Item *dflt_value_item)
+{
+ DBUG_ASSERT(row);
+ /*
+ Prepare all row fields.
+ Note, we do it only one time outside of the below loop.
+ The converted list in "row" is further reused by all variable
+ declarations processed by the current call.
+ Example:
+ DECLARE
+ a, b, c ROW(x VARCHAR(10) CHARACTER SET utf8);
+ BEGIN
+ ...
+ END;
+ */
+ if (sphead->row_fill_field_definitions(thd, row))
+ return true;
+
+ for (uint i= 0 ; i < (uint) nvars ; i++)
+ {
+ sp_variable *spvar= spcont->get_last_context_variable((uint) nvars - 1 - i);
+ spvar->field_def.set_row_field_definitions(row);
+ if (sphead->fill_spvar_definition(thd, &spvar->field_def, &spvar->name))
+ return true;
+ }
+
+ if (sp_variable_declarations_set_default(thd, nvars, dflt_value_item))
+ return true;
+ spcont->declare_var_boundary(0);
+ return sphead->restore_lex(thd);
+}
+
+
+/**
+ Finalize a %ROWTYPE declaration, e.g.:
+ DECLARE a,b,c,d t1%ROWTYPE := ROW(1,2,3);
+
+ @param thd - the current thd
+ @param nvars - the number of variables in the declaration
+ @param ref - the table or cursor name (see comments below)
+ @param def - the default value, e.g., ROW(1,2,3), or NULL (no default).
+*/
+bool
+LEX::sp_variable_declarations_rowtype_finalize(THD *thd, int nvars,
+ Qualified_column_ident *ref,
+ Item *def)
+{
+ uint coffp;
+ const sp_pcursor *pcursor= ref->table.str && ref->db.str ? NULL :
+ spcont->find_cursor(&ref->m_column, &coffp,
+ false);
+ if (pcursor)
+ return sp_variable_declarations_cursor_rowtype_finalize(thd, nvars,
+ coffp, def);
+ /*
+ When parsing a qualified identifier chain, the parser does not know yet
+ if it's going to be a qualified column name (for %TYPE),
+ or a qualified table name (for %ROWTYPE). So it collects the chain
+ into Qualified_column_ident.
+ Now we know that it was actually a qualified table name (%ROWTYPE).
+ Create a new Table_ident from Qualified_column_ident,
+ shifting fields as follows:
+ - ref->m_column becomes table_ref->table
+ - ref->table becomes table_ref->db
+ */
+ return sp_variable_declarations_table_rowtype_finalize(thd, nvars,
+ ref->table,
+ ref->m_column,
+ def);
+}
+
+
+bool
+LEX::sp_variable_declarations_table_rowtype_finalize(THD *thd, int nvars,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &table,
+ Item *def)
+{
+ Table_ident *table_ref;
+ if (unlikely(!(table_ref=
+ new (thd->mem_root) Table_ident(thd, &db, &table, false))))
+ return true;
+ // Loop through all variables in the same declaration
+ for (uint i= 0 ; i < (uint) nvars; i++)
+ {
+ sp_variable *spvar= spcont->get_last_context_variable((uint) nvars - 1 - i);
+ spvar->field_def.set_table_rowtype_ref(table_ref);
+ sphead->fill_spvar_definition(thd, &spvar->field_def, &spvar->name);
+ }
+ if (sp_variable_declarations_set_default(thd, nvars, def))
+ return true;
+ // Make sure sp_rcontext is created using the invoker security context:
+ sphead->m_flags|= sp_head::HAS_COLUMN_TYPE_REFS;
+ spcont->declare_var_boundary(0);
+ return sphead->restore_lex(thd);
+}
+
+
+bool
+LEX::sp_variable_declarations_cursor_rowtype_finalize(THD *thd, int nvars,
+ uint offset,
+ Item *def)
+{
+ const sp_pcursor *pcursor= spcont->find_cursor(offset);
+
+ // Loop through all variables in the same declaration
+ for (uint i= 0 ; i < (uint) nvars; i++)
+ {
+ sp_variable *spvar= spcont->get_last_context_variable((uint) nvars - 1 - i);
+
+ spvar->field_def.set_cursor_rowtype_ref(offset);
+ sp_instr_cursor_copy_struct *instr=
+ new (thd->mem_root) sp_instr_cursor_copy_struct(sphead->instructions(),
+ spcont, offset,
+ pcursor->lex(),
+ spvar->offset);
+ if (instr == NULL || sphead->add_instr(instr))
+ return true;
+
+ sphead->fill_spvar_definition(thd, &spvar->field_def, &spvar->name);
+ }
+ if (unlikely(sp_variable_declarations_set_default(thd, nvars, def)))
+ return true;
+ // Make sure sp_rcontext is created using the invoker security context:
+ sphead->m_flags|= sp_head::HAS_COLUMN_TYPE_REFS;
+ spcont->declare_var_boundary(0);
+ return sphead->restore_lex(thd);
+}
+
+
+/*
+ Add declarations for table column and SP variable anchor types:
+ - DECLARE spvar1 TYPE OF db1.table1.column1;
+ - DECLARE spvar1 TYPE OF table1.column1;
+ - DECLARE spvar1 TYPE OF spvar0;
+*/
+bool
+LEX::sp_variable_declarations_with_ref_finalize(THD *thd, int nvars,
+ Qualified_column_ident *ref,
+ Item *def)
+{
+ return ref->db.length == 0 && ref->table.length == 0 ?
+ sp_variable_declarations_vartype_finalize(thd, nvars, ref->m_column, def) :
+ sp_variable_declarations_column_type_finalize(thd, nvars, ref, def);
+}
+
+
+bool
+LEX::sp_variable_declarations_column_type_finalize(THD *thd, int nvars,
+ Qualified_column_ident *ref,
+ Item *def)
+{
+ for (uint i= 0 ; i < (uint) nvars; i++)
+ {
+ sp_variable *spvar= spcont->get_last_context_variable((uint) nvars - 1 - i);
+ spvar->field_def.set_column_type_ref(ref);
+ spvar->field_def.field_name= spvar->name;
+ }
+ sphead->m_flags|= sp_head::HAS_COLUMN_TYPE_REFS;
+ if (sp_variable_declarations_set_default(thd, nvars, def))
+ return true;
+ spcont->declare_var_boundary(0);
+ return sphead->restore_lex(thd);
+}
+
+
+bool
+LEX::sp_variable_declarations_vartype_finalize(THD *thd, int nvars,
+ const LEX_CSTRING &ref,
+ Item *default_value)
+{
+ sp_variable *t;
+ if (!spcont || !(t= spcont->find_variable(&ref, false)))
+ {
+ my_error(ER_SP_UNDECLARED_VAR, MYF(0), ref.str);
+ return true;
+ }
+
+ if (t->field_def.is_cursor_rowtype_ref())
+ {
+ uint offset= t->field_def.cursor_rowtype_offset();
+ return sp_variable_declarations_cursor_rowtype_finalize(thd, nvars,
+ offset,
+ default_value);
+ }
+
+ if (t->field_def.is_column_type_ref())
+ {
+ Qualified_column_ident *tmp= t->field_def.column_type_ref();
+ return sp_variable_declarations_column_type_finalize(thd, nvars, tmp,
+ default_value);
+ }
+
+ if (t->field_def.is_table_rowtype_ref())
+ {
+ const Table_ident *tmp= t->field_def.table_rowtype_ref();
+ return sp_variable_declarations_table_rowtype_finalize(thd, nvars,
+ tmp->db,
+ tmp->table,
+ default_value);
+ }
+
+ // A reference to a scalar or a row variable with an explicit data type
+ return sp_variable_declarations_copy_type_finalize(thd, nvars,
+ t->field_def,
+ t->field_def.
+ row_field_definitions(),
+ default_value);
+}
+
+
+/**********************************************************************
+ The FOR LOOP statement
+
+ This syntax:
+ FOR i IN lower_bound .. upper_bound
+ LOOP
+ statements;
+ END LOOP;
+
+ is translated into:
+
+ DECLARE
+ i INT := lower_bound;
+ j INT := upper_bound;
+ BEGIN
+ WHILE i <= j
+ LOOP
+ statements;
+ i:= i + 1;
+ END LOOP;
+ END;
+*/
+
+
+sp_variable *LEX::sp_add_for_loop_variable(THD *thd, const LEX_CSTRING *name,
+ Item *value)
+{
+ 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);
+ 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(sphead->instructions(),
+ spcont, &sp_rcontext_handler_local,
+ spvar->offset, value,
+ this, true);
+ if (unlikely(is == NULL || sphead->add_instr(is)))
+ return NULL;
+ spcont->declare_var_boundary(0);
+ return spvar;
+}
+
+
+bool LEX::sp_for_loop_implicit_cursor_statement(THD *thd,
+ Lex_for_loop_bounds_st *bounds,
+ sp_lex_cursor *cur)
+{
+ Item *item;
+ DBUG_ASSERT(sphead);
+ LEX_CSTRING name= {STRING_WITH_LEN("[implicit_cursor]") };
+ if (sp_declare_cursor(thd, &name, cur, NULL, true))
+ return true;
+ DBUG_ASSERT(thd->lex == this);
+ if (unlikely(!(bounds->m_index=
+ new (thd->mem_root) sp_assignment_lex(thd, this))))
+ return true;
+ bounds->m_index->sp_lex_in_use= true;
+ sphead->reset_lex(thd, bounds->m_index);
+ DBUG_ASSERT(thd->lex != this);
+ if (unlikely(!(item=
+ new (thd->mem_root) Item_field(thd,
+ thd->lex->current_context(),
+ NullS, NullS, &name))))
+ return true;
+ bounds->m_index->set_item_and_free_list(item, NULL);
+ if (thd->lex->sphead->restore_lex(thd))
+ return true;
+ DBUG_ASSERT(thd->lex == this);
+ bounds->m_direction= 1;
+ bounds->m_target_bound= NULL;
+ bounds->m_implicit_cursor= true;
+ return false;
+}
+
+sp_variable *
+LEX::sp_add_for_loop_cursor_variable(THD *thd,
+ const LEX_CSTRING *name,
+ const sp_pcursor *pcursor,
+ uint coffset,
+ sp_assignment_lex *param_lex,
+ Item_args *parameters)
+{
+ sp_variable *spvar= spcont->add_variable(thd, name);
+ if (!spvar)
+ return NULL;
+ spcont->declare_var_boundary(1);
+ sphead->fill_spvar_definition(thd, &spvar->field_def, &spvar->name);
+ if (unlikely(!(spvar->default_value= new (thd->mem_root) Item_null(thd))))
+ return NULL;
+
+ spvar->field_def.set_cursor_rowtype_ref(coffset);
+
+ if (unlikely(sphead->add_for_loop_open_cursor(thd, spcont, spvar, pcursor,
+ coffset,
+ param_lex, parameters)))
+ return NULL;
+
+ spcont->declare_var_boundary(0);
+ return spvar;
+}
+
+
+/**
+ Generate a code for a FOR loop condition:
+ - Make Item_splocal for the FOR loop index variable
+ - Make Item_splocal for the FOR loop upper bound variable
+ - Make a comparison function item on top of these two variables
+*/
+bool LEX::sp_for_loop_condition(THD *thd, const Lex_for_loop_st &loop)
+{
+ Item_splocal *args[2];
+ for (uint i= 0 ; i < 2; i++)
+ {
+ sp_variable *src= i == 0 ? loop.m_index : loop.m_target_bound;
+ args[i]= new (thd->mem_root)
+ Item_splocal(thd, &sp_rcontext_handler_local,
+ &src->name, src->offset, src->type_handler());
+ if (unlikely(args[i] == NULL))
+ return true;
+#ifdef DBUG_ASSERT_EXISTS
+ args[i]->m_sp= sphead;
+#endif
+ }
+
+ Item *expr= loop.m_direction > 0 ?
+ (Item *) new (thd->mem_root) Item_func_le(thd, args[0], args[1]) :
+ (Item *) new (thd->mem_root) Item_func_ge(thd, args[0], args[1]);
+ return unlikely(!expr) || unlikely(sp_while_loop_expression(thd, expr));
+}
+
+
+/**
+ Generate the FOR LOOP condition code in its own lex
+*/
+bool LEX::sp_for_loop_intrange_condition_test(THD *thd,
+ const Lex_for_loop_st &loop)
+{
+ spcont->set_for_loop(loop);
+ sphead->reset_lex(thd);
+ if (unlikely(thd->lex->sp_for_loop_condition(thd, loop)))
+ return true;
+ return thd->lex->sphead->restore_lex(thd);
+}
+
+
+bool LEX::sp_for_loop_cursor_condition_test(THD *thd,
+ const Lex_for_loop_st &loop)
+{
+ const LEX_CSTRING *cursor_name;
+ Item *expr;
+ spcont->set_for_loop(loop);
+ sphead->reset_lex(thd);
+ cursor_name= spcont->find_cursor(loop.m_cursor_offset);
+ DBUG_ASSERT(cursor_name);
+ if (unlikely(!(expr=
+ new (thd->mem_root)
+ Item_func_cursor_found(thd, cursor_name,
+ loop.m_cursor_offset))))
+ return true;
+ if (thd->lex->sp_while_loop_expression(thd, expr))
+ return true;
+ return thd->lex->sphead->restore_lex(thd);
+}
+
+
+bool LEX::sp_for_loop_intrange_declarations(THD *thd, Lex_for_loop_st *loop,
+ const LEX_CSTRING *index,
+ const Lex_for_loop_bounds_st &bounds)
+{
+ if (unlikely(!(loop->m_index=
+ bounds.m_index->
+ sp_add_for_loop_variable(thd, index,
+ bounds.m_index->get_item()))))
+ return true;
+ if (unlikely(!(loop->m_target_bound=
+ bounds.m_target_bound->
+ sp_add_for_loop_target_bound(thd,
+ bounds.
+ m_target_bound->get_item()))))
+ return true;
+ loop->m_direction= bounds.m_direction;
+ loop->m_implicit_cursor= 0;
+ return false;
+}
+
+
+bool LEX::sp_for_loop_cursor_declarations(THD *thd,
+ Lex_for_loop_st *loop,
+ const LEX_CSTRING *index,
+ const Lex_for_loop_bounds_st &bounds)
+{
+ Item *item= bounds.m_index->get_item();
+ Item_splocal *item_splocal;
+ Item_field *item_field;
+ Item_func_sp *item_func_sp= NULL;
+ LEX_CSTRING name;
+ uint coffs, param_count= 0;
+ const sp_pcursor *pcursor;
+
+ if ((item_splocal= item->get_item_splocal()))
+ 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)
+ name= item_field->field_name;
+ else if (item->type() == Item::FUNC_ITEM &&
+ static_cast<Item_func*>(item)->functype() == Item_func::FUNC_SP &&
+ !static_cast<Item_func_sp*>(item)->get_sp_name()->m_explicit_name)
+ {
+ /*
+ When a FOR LOOP for a cursor with parameters is parsed:
+ FOR index IN cursor(1,2,3) LOOP
+ statements;
+ END LOOP;
+ the parser scans "cursor(1,2,3)" using the "expr" rule,
+ so it thinks that cursor(1,2,3) is a stored function call.
+ It's not easy to implement this without using "expr" because
+ of grammar conflicts.
+ As a side effect, the Item_func_sp and its arguments in the parentheses
+ belong to the same LEX. This is different from an explicit
+ "OPEN cursor(1,2,3)" where every expression belongs to a separate LEX.
+ */
+ item_func_sp= static_cast<Item_func_sp*>(item);
+ name= item_func_sp->get_sp_name()->m_name;
+ param_count= item_func_sp->argument_count();
+ }
+ else
+ {
+ thd->parse_error();
+ return true;
+ }
+ if (unlikely(!(pcursor= spcont->find_cursor_with_error(&name, &coffs,
+ false)) ||
+ pcursor->check_param_count_with_error(param_count)))
+ return true;
+
+ if (!(loop->m_index= sp_add_for_loop_cursor_variable(thd, index,
+ pcursor, coffs,
+ bounds.m_index,
+ item_func_sp)))
+ return true;
+ loop->m_target_bound= NULL;
+ loop->m_direction= bounds.m_direction;
+ loop->m_cursor_offset= coffs;
+ loop->m_implicit_cursor= bounds.m_implicit_cursor;
+ return false;
+}
+
+
+/**
+ Generate a code for a FOR loop index increment
+*/
+bool LEX::sp_for_loop_increment(THD *thd, const Lex_for_loop_st &loop)
+{
+ Item_splocal *splocal= new (thd->mem_root)
+ Item_splocal(thd, &sp_rcontext_handler_local,
+ &loop.m_index->name, loop.m_index->offset,
+ loop.m_index->type_handler());
+ if (unlikely(splocal == NULL))
+ return true;
+#ifdef DBUG_ASSERT_EXISTS
+ splocal->m_sp= sphead;
+#endif
+ Item_int *inc= new (thd->mem_root) Item_int(thd, loop.m_direction);
+ if (unlikely(!inc))
+ return true;
+ Item *expr= new (thd->mem_root) Item_func_plus(thd, splocal, inc);
+ if (unlikely(!expr) ||
+ unlikely(sphead->set_local_variable(thd, spcont,
+ &sp_rcontext_handler_local,
+ loop.m_index, expr, this, true)))
+ return true;
+ return false;
+}
+
+
+bool LEX::sp_for_loop_intrange_finalize(THD *thd, const Lex_for_loop_st &loop)
+{
+ sphead->reset_lex(thd);
+
+ // Generate FOR LOOP index increment in its own lex
+ DBUG_ASSERT(this != thd->lex);
+ if (unlikely(thd->lex->sp_for_loop_increment(thd, loop) ||
+ thd->lex->sphead->restore_lex(thd)))
+ return true;
+
+ // Generate a jump to the beginning of the loop
+ DBUG_ASSERT(this == thd->lex);
+ return sp_while_loop_finalize(thd);
+}
+
+
+bool LEX::sp_for_loop_cursor_finalize(THD *thd, const Lex_for_loop_st &loop)
+{
+ sp_instr_cfetch *instr=
+ new (thd->mem_root) sp_instr_cfetch(sphead->instructions(),
+ spcont, loop.m_cursor_offset, false);
+ if (unlikely(instr == NULL) || unlikely(sphead->add_instr(instr)))
+ return true;
+ instr->add_to_varlist(loop.m_index);
+ // Generate a jump to the beginning of the loop
+ return sp_while_loop_finalize(thd);
+}
+
+bool LEX::sp_for_loop_outer_block_finalize(THD *thd,
+ const Lex_for_loop_st &loop)
+{
+ Lex_spblock tmp;
+ tmp.curs= MY_TEST(loop.m_implicit_cursor);
+ if (unlikely(sp_block_finalize(thd, tmp))) // The outer DECLARE..BEGIN..END
+ return true;
+ if (!loop.is_for_loop_explicit_cursor())
+ return false;
+ /*
+ Explicit cursor FOR loop must close the cursor automatically.
+ Note, implicit cursor FOR loop does not need to close the cursor,
+ it's closed by sp_instr_cpop.
+ */
+ sp_instr_cclose *ic= new (thd->mem_root)
+ sp_instr_cclose(sphead->instructions(), spcont,
+ loop.m_cursor_offset);
+ return ic == NULL || sphead->add_instr(ic);
+}
+
+/***************************************************************************/
+
+bool LEX::sp_declare_cursor(THD *thd, const LEX_CSTRING *name,
+ sp_lex_cursor *cursor_stmt,
+ sp_pcontext *param_ctx, bool add_cpush_instr)
+{
+ uint offp;
+ sp_instr_cpush *i;
+
+ if (spcont->find_cursor(name, &offp, true))
+ {
+ my_error(ER_SP_DUP_CURS, MYF(0), name->str);
+ return true;
+ }
+
+ if (unlikely(spcont->add_cursor(name, param_ctx, cursor_stmt)))
+ return true;
+
+ if (add_cpush_instr)
+ {
+ i= new (thd->mem_root)
+ sp_instr_cpush(sphead->instructions(), spcont, cursor_stmt,
+ spcont->current_cursor_count() - 1);
+ return unlikely(i == NULL) || unlikely(sphead->add_instr(i));
+ }
+ return false;
+}
+
+
+/**
+ Generate an SP code for an "OPEN cursor_name" statement.
+ @param thd
+ @param name - Name of the cursor
+ @param parameters - Cursor parameters, e.g. OPEN c(1,2,3)
+ @returns - false on success, true on error
+*/
+bool LEX::sp_open_cursor(THD *thd, const LEX_CSTRING *name,
+ List<sp_assignment_lex> *parameters)
+{
+ uint offset;
+ const sp_pcursor *pcursor;
+ uint param_count= parameters ? parameters->elements : 0;
+ return !(pcursor= spcont->find_cursor_with_error(name, &offset, false)) ||
+ pcursor->check_param_count_with_error(param_count) ||
+ sphead->add_open_cursor(thd, spcont, offset,
+ pcursor->param_context(), parameters);
+}
+
+
+bool LEX::sp_handler_declaration_init(THD *thd, int type)
+{
+ sp_handler *h= spcont->add_handler(thd, (sp_handler::enum_type) type);
+
+ spcont= spcont->push_context(thd, sp_pcontext::HANDLER_SCOPE);
+
+ sp_instr_hpush_jump *i=
+ new (thd->mem_root) sp_instr_hpush_jump(sphead->instructions(), spcont, h);
+
+ if (unlikely(i == NULL) || unlikely(sphead->add_instr(i)))
+ return true;
+
+ /* For continue handlers, mark end of handler scope. */
+ if (type == sp_handler::CONTINUE &&
+ unlikely(sphead->push_backpatch(thd, i, spcont->last_label())))
+ return true;
+
+ if (unlikely(sphead->push_backpatch(thd, i,
+ spcont->push_label(thd, &empty_clex_str,
+ 0))))
+ return true;
+
+ return false;
+}
+
+
+bool LEX::sp_handler_declaration_finalize(THD *thd, int type)
+{
+ sp_label *hlab= spcont->pop_label(); /* After this hdlr */
+ sp_instr_hreturn *i;
+
+ if (type == sp_handler::CONTINUE)
+ {
+ i= new (thd->mem_root) sp_instr_hreturn(sphead->instructions(), spcont);
+ if (unlikely(i == NULL) ||
+ unlikely(sphead->add_instr(i)))
+ return true;
+ }
+ else
+ { /* EXIT or UNDO handler, just jump to the end of the block */
+ i= new (thd->mem_root) sp_instr_hreturn(sphead->instructions(), spcont);
+ if (unlikely(i == NULL) ||
+ unlikely(sphead->add_instr(i)) ||
+ unlikely(sphead->push_backpatch(thd, i, spcont->last_label()))) /* Block end */
+ return true;
+ }
+ sphead->backpatch(hlab);
+ spcont= spcont->pop_context();
+ return false;
+}
+
+
+void LEX::sp_block_init(THD *thd, const LEX_CSTRING *label)
+{
+ spcont->push_label(thd, label, sphead->instructions(), sp_label::BEGIN);
+ spcont= spcont->push_context(thd, sp_pcontext::REGULAR_SCOPE);
+}
+
+
+bool LEX::sp_block_finalize(THD *thd, const Lex_spblock_st spblock,
+ class sp_label **splabel)
+{
+ sp_head *sp= sphead;
+ sp_pcontext *ctx= spcont;
+ sp_instr *i;
+
+ sp->backpatch(ctx->last_label()); /* We always have a label */
+ if (spblock.hndlrs)
+ {
+ i= new (thd->mem_root)
+ sp_instr_hpop(sp->instructions(), ctx, spblock.hndlrs);
+ if (unlikely(i == NULL) ||
+ unlikely(sp->add_instr(i)))
+ return true;
+ }
+ if (spblock.curs)
+ {
+ i= new (thd->mem_root)
+ sp_instr_cpop(sp->instructions(), ctx, spblock.curs);
+ if (unlikely(i == NULL) ||
+ unlikely(sp->add_instr(i)))
+ return true;
+ }
+ spcont= ctx->pop_context();
+ *splabel= spcont->pop_label();
+ return false;
+}
+
+
+bool LEX::sp_block_finalize(THD *thd, const Lex_spblock_st spblock,
+ const LEX_CSTRING *end_label)
+{
+ sp_label *splabel;
+ if (unlikely(sp_block_finalize(thd, spblock, &splabel)))
+ return true;
+ if (unlikely(end_label->str &&
+ lex_string_cmp(system_charset_info,
+ end_label, &splabel->name) != 0))
+ {
+ my_error(ER_SP_LABEL_MISMATCH, MYF(0), end_label->str);
+ return true;
+ }
+ return false;
+}
+
+
+sp_name *LEX::make_sp_name(THD *thd, const LEX_CSTRING *name)
+{
+ sp_name *res;
+ LEX_CSTRING db;
+ if (unlikely(check_routine_name(name)) ||
+ unlikely(copy_db_to(&db)) ||
+ unlikely((!(res= new (thd->mem_root) sp_name(&db, name, false)))))
+ return NULL;
+ return res;
+}
+
+
+/**
+ When a package routine name is stored in memory in Database_qualified_name,
+ the dot character is used to delimit package name from the routine name,
+ e.g.:
+ m_db= 'test'; -- database 'test'
+ m_name= 'p1.p1'; -- package 'p1', routine 'p1'
+ See database_qualified_name::make_package_routine_name() for details.
+ Disallow package routine names with dots,
+ to avoid ambiguity when interpreting m_name='p1.p1.p1', between:
+ a. package 'p1.p1' + routine 'p1'
+ b. package 'p1' + routine 'p1.p1'
+ m_name='p1.p1.p1' will always mean (a).
+*/
+sp_name *LEX::make_sp_name_package_routine(THD *thd, const LEX_CSTRING *name)
+{
+ sp_name *res= make_sp_name(thd, name);
+ if (likely(res) && unlikely(strchr(res->m_name.str, '.')))
+ {
+ my_error(ER_SP_WRONG_NAME, MYF(0), res->m_name.str);
+ res= NULL;
+ }
+ return res;
+}
+
+
+sp_name *LEX::make_sp_name(THD *thd, const LEX_CSTRING *name1,
+ const LEX_CSTRING *name2)
+{
+ sp_name *res;
+ LEX_CSTRING norm_name1;
+ if (unlikely(!name1->str) ||
+ unlikely(!thd->make_lex_string(&norm_name1, name1->str,
+ name1->length)) ||
+ unlikely(check_db_name((LEX_STRING *) &norm_name1)))
+ {
+ my_error(ER_WRONG_DB_NAME, MYF(0), name1->str);
+ return NULL;
+ }
+ if (unlikely(check_routine_name(name2)) ||
+ unlikely(!(res= new (thd->mem_root) sp_name(&norm_name1, name2, true))))
+ return NULL;
+ return res;
+}
+
+
+sp_head *LEX::make_sp_head(THD *thd, const sp_name *name,
+ const Sp_handler *sph)
+{
+ sp_package *package= get_sp_package();
+ sp_head *sp;
+
+ /* Order is important here: new - reset - init */
+ if (likely((sp= sp_head::create(package, sph))))
+ {
+ sp->reset_thd_mem_root(thd);
+ sp->init(this);
+ if (name)
+ {
+ if (package)
+ sp->make_package_routine_name(sp->get_main_mem_root(),
+ package->m_db,
+ package->m_name,
+ name->m_name);
+ else
+ sp->init_sp_name(name);
+ sp->make_qname(sp->get_main_mem_root(), &sp->m_qname);
+ }
+ sphead= sp;
+ }
+ sp_chistics.init();
+ return sp;
+}
+
+
+sp_head *LEX::make_sp_head_no_recursive(THD *thd, const sp_name *name,
+ const Sp_handler *sph)
+{
+ sp_package *package= thd->lex->get_sp_package();
+ /*
+ Sp_handler::sp_clone_and_link_routine() generates a standalone-alike
+ statement to clone package routines for recursion, e.g.:
+ CREATE PROCEDURE p1 AS BEGIN NULL; END;
+ Translate a standalone routine handler to the corresponding
+ package routine handler if we're cloning a package routine, e.g.:
+ sp_handler_procedure -> sp_handler_package_procedure
+ sp_handler_function -> sp_handler_package_function
+ */
+ if (package && package->m_is_cloning_routine)
+ sph= sph->package_routine_handler();
+ if (!sphead ||
+ (package &&
+ (sph == &sp_handler_package_procedure ||
+ sph == &sp_handler_package_function)))
+ return make_sp_head(thd, name, sph);
+ my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), sph->type_str());
+ return NULL;
+}
+
+
+bool LEX::sp_body_finalize_procedure(THD *thd)
+{
+ if (sphead->check_unresolved_goto())
+ return true;
+ sphead->set_stmt_end(thd);
+ sphead->restore_thd_mem_root(thd);
+ return false;
+}
+
+
+bool LEX::sp_body_finalize_function(THD *thd)
+{
+ if (sphead->is_not_allowed_in_function("function"))
+ return true;
+ if (!(sphead->m_flags & sp_head::HAS_RETURN))
+ {
+ my_error(ER_SP_NORETURN, MYF(0), ErrConvDQName(sphead).ptr());
+ return true;
+ }
+ if (sp_body_finalize_procedure(thd))
+ return true;
+ (void) is_native_function_with_warn(thd, &sphead->m_name);
+ return false;
+}
+
+
+bool LEX::sp_block_with_exceptions_finalize_declarations(THD *thd)
+{
+ /*
+ [ DECLARE declarations ]
+ BEGIN executable_section
+ [ EXCEPTION exceptions ]
+ END
+
+ We are now at the "BEGIN" keyword.
+ We have collected all declarations, including DECLARE HANDLER directives.
+ But there will be possibly more handlers in the EXCEPTION section.
+
+ Generate a forward jump from the end of the DECLARE section to the
+ beginning of the EXCEPTION section, over the executable section.
+ */
+ return sphead->add_instr_jump(thd, spcont);
+}
+
+
+bool
+LEX::sp_block_with_exceptions_finalize_executable_section(THD *thd,
+ uint executable_section_ip)
+{
+ /*
+ We're now at the end of "executable_section" of the block,
+ near the "EXCEPTION" or the "END" keyword.
+ Generate a jump to the END of the block over the EXCEPTION section.
+ */
+ if (sphead->add_instr_jump_forward_with_backpatch(thd, spcont))
+ return true;
+ /*
+ Set the destination for the jump that we added in
+ sp_block_with_exceptions_finalize_declarations().
+ */
+ sp_instr *instr= sphead->get_instr(executable_section_ip - 1);
+ instr->backpatch(sphead->instructions(), spcont);
+ return false;
+}
+
+
+bool
+LEX::sp_block_with_exceptions_finalize_exceptions(THD *thd,
+ uint executable_section_ip,
+ uint exception_count)
+{
+ if (!exception_count)
+ {
+ /*
+ The jump from the end of DECLARE section to
+ the beginning of the EXCEPTION section that we added in
+ sp_block_with_exceptions_finalize_declarations() is useless
+ if there were no exceptions.
+ Replace it to "no operation".
+ */
+ return sphead->replace_instr_to_nop(thd, executable_section_ip - 1);
+ }
+ /*
+ Generate a jump from the end of the EXCEPTION code
+ to the executable section.
+ */
+ return sphead->add_instr_jump(thd, spcont, executable_section_ip);
+}
+
+
+bool LEX::sp_block_with_exceptions_add_empty(THD *thd)
+{
+ uint ip= sphead->instructions();
+ return sp_block_with_exceptions_finalize_executable_section(thd, ip) ||
+ sp_block_with_exceptions_finalize_exceptions(thd, ip, 0);
+}
+
+
+bool LEX::sp_change_context(THD *thd, const sp_pcontext *ctx, bool exclusive)
+{
+ uint n;
+ uint ip= sphead->instructions();
+ if ((n= spcont->diff_handlers(ctx, exclusive)))
+ {
+ sp_instr_hpop *hpop= new (thd->mem_root) sp_instr_hpop(ip++, spcont, n);
+ if (unlikely(hpop == NULL) || unlikely(sphead->add_instr(hpop)))
+ return true;
+ }
+ if ((n= spcont->diff_cursors(ctx, exclusive)))
+ {
+ sp_instr_cpop *cpop= new (thd->mem_root) sp_instr_cpop(ip++, spcont, n);
+ if (unlikely(cpop == NULL) || unlikely(sphead->add_instr(cpop)))
+ return true;
+ }
+ return false;
+}
+
+
+bool LEX::sp_leave_statement(THD *thd, const LEX_CSTRING *label_name)
+{
+ sp_label *lab= spcont->find_label(label_name);
+ if (unlikely(!lab))
+ {
+ my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "LEAVE", label_name->str);
+ return true;
+ }
+ return sp_exit_block(thd, lab, NULL);
+}
+
+bool LEX::sp_goto_statement(THD *thd, const LEX_CSTRING *label_name)
+{
+ sp_label *lab= spcont->find_goto_label(label_name);
+ if (!lab || lab->ip == 0)
+ {
+ sp_label *delayedlabel;
+ if (!lab)
+ {
+ // Label not found --> add forward jump to an unknown label
+ spcont->push_goto_label(thd, label_name, 0, sp_label::GOTO);
+ delayedlabel= spcont->last_goto_label();
+ }
+ else
+ {
+ delayedlabel= lab;
+ }
+ return sphead->push_backpatch_goto(thd, spcont, delayedlabel);
+ }
+ else
+ {
+ // Label found (backward goto)
+ return sp_change_context(thd, lab->ctx, false) ||
+ sphead->add_instr_jump(thd, spcont, lab->ip); /* Jump back */
+ }
+ return false;
+}
+
+bool LEX::sp_push_goto_label(THD *thd, const LEX_CSTRING *label_name)
+{
+ sp_label *lab= spcont->find_goto_label(label_name, false);
+ if (lab)
+ {
+ if (unlikely(lab->ip != 0))
+ {
+ my_error(ER_SP_LABEL_REDEFINE, MYF(0), label_name->str);
+ return true;
+ }
+ lab->ip= sphead->instructions();
+
+ sp_label *beginblocklabel= spcont->find_label(&empty_clex_str);
+ sphead->backpatch_goto(thd, lab, beginblocklabel);
+ }
+ else
+ {
+ spcont->push_goto_label(thd, label_name, sphead->instructions());
+ }
+ return false;
+}
+
+bool LEX::sp_exit_block(THD *thd, sp_label *lab)
+{
+ /*
+ When jumping to a BEGIN-END block end, the target jump
+ points to the block hpop/cpop cleanup instructions,
+ so we should exclude the block context here.
+ When jumping to something else (i.e., SP_LAB_ITER),
+ there are no hpop/cpop at the jump destination,
+ so we should include the block context here for cleanup.
+ */
+ bool exclusive= (lab->type == sp_label::BEGIN);
+ return sp_change_context(thd, lab->ctx, exclusive) ||
+ sphead->add_instr_jump_forward_with_backpatch(thd, spcont, lab);
+}
+
+
+bool LEX::sp_exit_block(THD *thd, sp_label *lab, Item *when)
+{
+ if (!when)
+ return sp_exit_block(thd, lab);
+
+ 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);
+ if (unlikely(i == NULL) ||
+ unlikely(sphead->add_instr(i)) ||
+ unlikely(sp_exit_block(thd, lab)))
+ return true;
+ i->backpatch(sphead->instructions(), spcont);
+ return false;
+}
+
+
+bool LEX::sp_exit_statement(THD *thd, Item *item)
+{
+ sp_label *lab= spcont->find_label_current_loop_start();
+ if (unlikely(!lab))
+ {
+ my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "EXIT", "");
+ return true;
+ }
+ DBUG_ASSERT(lab->type == sp_label::ITERATION);
+ return sp_exit_block(thd, lab, item);
+}
+
+
+bool LEX::sp_exit_statement(THD *thd, const LEX_CSTRING *label_name, Item *item)
+{
+ sp_label *lab= spcont->find_label(label_name);
+ if (unlikely(!lab || lab->type != sp_label::ITERATION))
+ {
+ my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "EXIT", label_name->str);
+ return true;
+ }
+ return sp_exit_block(thd, lab, item);
+}
+
+
+bool LEX::sp_iterate_statement(THD *thd, const LEX_CSTRING *label_name)
+{
+ sp_label *lab= spcont->find_label(label_name);
+ if (unlikely(!lab || lab->type != sp_label::ITERATION))
+ {
+ my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "ITERATE", label_name->str);
+ return true;
+ }
+ return sp_continue_loop(thd, lab);
+}
+
+
+bool LEX::sp_continue_loop(THD *thd, sp_label *lab)
+{
+ if (lab->ctx->for_loop().m_index)
+ {
+ // We're in a FOR loop, increment the index variable before backward jump
+ sphead->reset_lex(thd);
+ DBUG_ASSERT(this != thd->lex);
+ if (thd->lex->sp_for_loop_increment(thd, lab->ctx->for_loop()) ||
+ thd->lex->sphead->restore_lex(thd))
+ return true;
+ }
+ return sp_change_context(thd, lab->ctx, false) ||
+ sphead->add_instr_jump(thd, spcont, lab->ip); /* Jump back */
+}
+
+
+bool LEX::sp_continue_loop(THD *thd, sp_label *lab, Item *when)
+{
+ if (!when)
+ return sp_continue_loop(thd, lab);
+
+ 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);
+ if (unlikely(i == NULL) ||
+ unlikely(sphead->add_instr(i)) ||
+ unlikely(sp_continue_loop(thd, lab)))
+ return true;
+ i->backpatch(sphead->instructions(), spcont);
+ return false;
+}
+
+
+bool LEX::sp_continue_statement(THD *thd, Item *when)
+{
+ 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, when);
+}
+
+
+bool LEX::sp_continue_statement(THD *thd, const LEX_CSTRING *label_name,
+ Item *when)
+{
+ 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, when);
+}
+
+
+bool LEX::maybe_start_compound_statement(THD *thd)
+{
+ if (!sphead)
+ {
+ if (!make_sp_head(thd, NULL, &sp_handler_procedure))
+ return true;
+ sphead->set_suid(SP_IS_NOT_SUID);
+ sphead->set_body_start(thd, thd->m_parser_state->m_lip.get_cpp_ptr());
+ }
+ return false;
+}
+
+
+bool LEX::sp_push_loop_label(THD *thd, const LEX_CSTRING *label_name)
+{
+ sp_label *lab= spcont->find_label(label_name);
+ if (lab)
+ {
+ my_error(ER_SP_LABEL_REDEFINE, MYF(0), label_name->str);
+ return true;
+ }
+ spcont->push_label(thd, label_name, sphead->instructions(),
+ sp_label::ITERATION);
+ return false;
+}
+
+
+bool LEX::sp_push_loop_empty_label(THD *thd)
+{
+ if (maybe_start_compound_statement(thd))
+ return true;
+ /* Unlabeled controls get an empty label. */
+ spcont->push_label(thd, &empty_clex_str, sphead->instructions(),
+ sp_label::ITERATION);
+ return false;
+}
+
+
+bool LEX::sp_pop_loop_label(THD *thd, const LEX_CSTRING *label_name)
+{
+ sp_label *lab= spcont->pop_label();
+ sphead->backpatch(lab);
+ if (label_name->str &&
+ lex_string_cmp(system_charset_info, label_name,
+ &lab->name) != 0)
+ {
+ my_error(ER_SP_LABEL_MISMATCH, MYF(0), label_name->str);
+ return true;
+ }
+ return false;
+}
+
+
+void LEX::sp_pop_loop_empty_label(THD *thd)
+{
+ sp_label *lab= spcont->pop_label();
+ sphead->backpatch(lab);
+ DBUG_ASSERT(lab->name.length == 0);
+}
+
+
+bool LEX::sp_while_loop_expression(THD *thd, Item *expr)
+{
+ sp_instr_jump_if_not *i= new (thd->mem_root)
+ sp_instr_jump_if_not(sphead->instructions(), spcont, expr, this);
+ return (unlikely(i == NULL) ||
+ /* Jumping forward */
+ unlikely(sphead->push_backpatch(thd, i, spcont->last_label())) ||
+ unlikely(sphead->new_cont_backpatch(i)) ||
+ unlikely(sphead->add_instr(i)));
+}
+
+
+bool LEX::sp_while_loop_finalize(THD *thd)
+{
+ sp_label *lab= spcont->last_label(); /* Jumping back */
+ sp_instr_jump *i= new (thd->mem_root)
+ sp_instr_jump(sphead->instructions(), spcont, lab->ip);
+ if (unlikely(i == NULL) ||
+ unlikely(sphead->add_instr(i)))
+ return true;
+ sphead->do_cont_backpatch();
+ return false;
+}
+
+
+Item *LEX::create_and_link_Item_trigger_field(THD *thd,
+ const LEX_CSTRING *name,
+ bool new_row)
+{
+ Item_trigger_field *trg_fld;
+
+ if (unlikely(trg_chistics.event == TRG_EVENT_INSERT && !new_row))
+ {
+ my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "OLD", "on INSERT");
+ return NULL;
+ }
+
+ if (unlikely(trg_chistics.event == TRG_EVENT_DELETE && new_row))
+ {
+ my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "NEW", "on DELETE");
+ return NULL;
+ }
+
+ DBUG_ASSERT(!new_row ||
+ (trg_chistics.event == TRG_EVENT_INSERT ||
+ trg_chistics.event == TRG_EVENT_UPDATE));
+
+ const bool tmp_read_only=
+ !(new_row && trg_chistics.action_time == TRG_ACTION_BEFORE);
+ trg_fld= new (thd->mem_root)
+ Item_trigger_field(thd, current_context(),
+ new_row ?
+ Item_trigger_field::NEW_ROW:
+ Item_trigger_field::OLD_ROW,
+ name, SELECT_ACL, tmp_read_only);
+ /*
+ Let us add this item to list of all Item_trigger_field objects
+ in trigger.
+ */
+ if (likely(trg_fld))
+ trg_table_fields.link_in_list(trg_fld, &trg_fld->next_trg_field);
+
+ return trg_fld;
+}
+
+
+Item *LEX::make_item_colon_ident_ident(THD *thd,
+ const Lex_ident_cli_st *ca,
+ const Lex_ident_cli_st *cb)
+{
+ Lex_ident_sys a(thd, ca), b(thd, cb);
+ if (a.is_null() || b.is_null())
+ return NULL; // OEM
+ if (!is_trigger_new_or_old_reference(&a))
+ {
+ thd->parse_error();
+ return NULL;
+ }
+ bool new_row= (a.str[0] == 'N' || a.str[0] == 'n');
+ return create_and_link_Item_trigger_field(thd, &b, new_row);
+}
+
+
+Item *LEX::make_item_plsql_cursor_attr(THD *thd, const LEX_CSTRING *name,
+ plsql_cursor_attr_t attr)
+{
+ uint offset;
+ if (unlikely(!spcont || !spcont->find_cursor(name, &offset, false)))
+ {
+ my_error(ER_SP_CURSOR_MISMATCH, MYF(0), name->str);
+ return NULL;
+ }
+ switch (attr) {
+ case PLSQL_CURSOR_ATTR_ISOPEN:
+ return new (thd->mem_root) Item_func_cursor_isopen(thd, name, offset);
+ case PLSQL_CURSOR_ATTR_FOUND:
+ return new (thd->mem_root) Item_func_cursor_found(thd, name, offset);
+ case PLSQL_CURSOR_ATTR_NOTFOUND:
+ return new (thd->mem_root) Item_func_cursor_notfound(thd, name, offset);
+ case PLSQL_CURSOR_ATTR_ROWCOUNT:
+ return new (thd->mem_root) Item_func_cursor_rowcount(thd, name, offset);
+ }
+ DBUG_ASSERT(0);
+ return NULL;
+}
+
+
+Item *LEX::make_item_sysvar(THD *thd,
+ enum_var_type type,
+ const LEX_CSTRING *name,
+ const LEX_CSTRING *component)
+
+{
+ Item *item;
+ DBUG_ASSERT(name->str);
+ /*
+ "SELECT @@global.global.variable" is not allowed
+ Note, "global" can come through TEXT_STRING_sys.
+ */
+ if (component->str && unlikely(check_reserved_words(name)))
+ {
+ thd->parse_error();
+ return NULL;
+ }
+ if (unlikely(!(item= get_system_var(thd, type, name, component))))
+ return NULL;
+ if (!((Item_func_get_system_var*) item)->is_written_to_binlog())
+ set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_VARIABLE);
+ return item;
+}
+
+
+static bool param_push_or_clone(THD *thd, LEX *lex, Item_param *item)
+{
+ return !lex->clone_spec_offset ?
+ lex->param_list.push_back(item, thd->mem_root) :
+ item->add_as_clone(thd);
+}
+
+
+Item_param *LEX::add_placeholder(THD *thd, const LEX_CSTRING *name,
+ const char *start, const char *end)
+{
+ if (unlikely(!thd->m_parser_state->m_lip.stmt_prepare_mode))
+ {
+ thd->parse_error(ER_SYNTAX_ERROR, start);
+ return NULL;
+ }
+ if (unlikely(!parsing_options.allows_variable))
+ {
+ my_error(ER_VIEW_SELECT_VARIABLE, MYF(0));
+ return NULL;
+ }
+
+ Query_fragment pos(thd, sphead, start, end);
+ Item_param *item= new (thd->mem_root) Item_param(thd, name,
+ pos.pos(), pos.length());
+ if (unlikely(!item) || unlikely(param_push_or_clone(thd, this, item)))
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ return NULL;
+ }
+ return item;
+}
+
+
+bool LEX::add_signal_statement(THD *thd, const sp_condition_value *v)
+{
+ Yacc_state *state= &thd->m_parser_state->m_yacc;
+ sql_command= SQLCOM_SIGNAL;
+ m_sql_cmd= new (thd->mem_root) Sql_cmd_signal(v, state->m_set_signal_info);
+ return m_sql_cmd == NULL;
+}
+
+
+bool LEX::add_resignal_statement(THD *thd, const sp_condition_value *v)
+{
+ Yacc_state *state= &thd->m_parser_state->m_yacc;
+ sql_command= SQLCOM_RESIGNAL;
+ m_sql_cmd= new (thd->mem_root) Sql_cmd_resignal(v, state->m_set_signal_info);
+ return m_sql_cmd == NULL;
+}
+
+
+Item *LEX::create_item_ident_nospvar(THD *thd,
+ const Lex_ident_sys_st *a,
+ const Lex_ident_sys_st *b)
+{
+ DBUG_ASSERT(this == thd->lex);
+ /*
+ FIXME This will work ok in simple_ident_nospvar case because
+ we can't meet simple_ident_nospvar in trigger now. But it
+ should be changed in future.
+ */
+ if (is_trigger_new_or_old_reference(a))
+ {
+ bool new_row= (a->str[0]=='N' || a->str[0]=='n');
+
+ return create_and_link_Item_trigger_field(thd, b, new_row);
+ }
+
+ if (unlikely(current_select->no_table_names_allowed))
+ {
+ my_error(ER_TABLENAME_NOT_ALLOWED_HERE, MYF(0), a->str, thd->where);
+ return NULL;
+ }
+ if ((current_select->parsing_place != IN_HAVING) ||
+ (current_select->get_in_sum_expr() > 0))
+ return new (thd->mem_root) Item_field(thd, current_context(),
+ NullS, a->str, b);
+ return new (thd->mem_root) Item_ref(thd, current_context(),
+ NullS, a->str, b);
+}
+
+
+Item_splocal *LEX::create_item_spvar_row_field(THD *thd,
+ const Sp_rcontext_handler *rh,
+ const Lex_ident_sys *a,
+ const Lex_ident_sys *b,
+ sp_variable *spv,
+ const char *start,
+ const char *end)
+{
+ if (unlikely(!parsing_options.allows_variable))
+ {
+ my_error(ER_VIEW_SELECT_VARIABLE, MYF(0));
+ return NULL;
+ }
+
+ Query_fragment pos(thd, sphead, start, end);
+ Item_splocal *item;
+ if (spv->field_def.is_table_rowtype_ref() ||
+ spv->field_def.is_cursor_rowtype_ref())
+ {
+ if (unlikely(!(item= new (thd->mem_root)
+ Item_splocal_row_field_by_name(thd, rh, a, b, spv->offset,
+ &type_handler_null,
+ pos.pos(), pos.length()))))
+ return NULL;
+ }
+ else
+ {
+ uint row_field_offset;
+ const Spvar_definition *def;
+ if (unlikely(!(def= spv->find_row_field(a, b, &row_field_offset))))
+ return NULL;
+
+ if (unlikely(!(item= new (thd->mem_root)
+ Item_splocal_row_field(thd, rh, a, b,
+ spv->offset, row_field_offset,
+ def->type_handler(),
+ pos.pos(), pos.length()))))
+ return NULL;
+ }
+#ifdef DBUG_ASSERT_EXISTS
+ item->m_sp= sphead;
+#endif
+ safe_to_cache_query=0;
+ return item;
+}
+
+
+my_var *LEX::create_outvar(THD *thd, const LEX_CSTRING *name)
+{
+ const Sp_rcontext_handler *rh;
+ sp_variable *spv;
+ if (likely((spv= find_variable(name, &rh))))
+ return result ? new (thd->mem_root)
+ my_var_sp(rh, name, spv->offset,
+ spv->type_handler(), sphead) :
+ NULL /* EXPLAIN */;
+ my_error(ER_SP_UNDECLARED_VAR, MYF(0), name->str);
+ return NULL;
+}
+
+
+my_var *LEX::create_outvar(THD *thd,
+ const LEX_CSTRING *a,
+ const LEX_CSTRING *b)
+{
+ const Sp_rcontext_handler *rh;
+ sp_variable *t;
+ if (unlikely(!(t= find_variable(a, &rh))))
+ {
+ my_error(ER_SP_UNDECLARED_VAR, MYF(0), a->str);
+ return NULL;
+ }
+ uint row_field_offset;
+ if (!t->find_row_field(a, b, &row_field_offset))
+ return NULL;
+ return result ?
+ new (thd->mem_root) my_var_sp_row_field(rh, a, b, t->offset,
+ row_field_offset, sphead) :
+ NULL /* EXPLAIN */;
+}
+
+
+Item *LEX::create_item_func_nextval(THD *thd, Table_ident *table_ident)
+{
+ TABLE_LIST *table;
+ if (unlikely(!(table= current_select->add_table_to_list(thd, table_ident, 0,
+ TL_OPTION_SEQUENCE,
+ TL_WRITE_ALLOW_WRITE,
+ MDL_SHARED_WRITE))))
+ return NULL;
+ thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
+ return new (thd->mem_root) Item_func_nextval(thd, table);
+}
+
+
+Item *LEX::create_item_func_lastval(THD *thd, Table_ident *table_ident)
+{
+ TABLE_LIST *table;
+ if (unlikely(!(table= current_select->add_table_to_list(thd, table_ident, 0,
+ TL_OPTION_SEQUENCE,
+ TL_READ,
+ MDL_SHARED_READ))))
+ return NULL;
+ thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
+ return new (thd->mem_root) Item_func_lastval(thd, table);
+}
+
+
+Item *LEX::create_item_func_nextval(THD *thd,
+ const LEX_CSTRING *db,
+ const LEX_CSTRING *name)
+{
+ Table_ident *table_ident;
+ if (unlikely(!(table_ident=
+ new (thd->mem_root) Table_ident(thd, db, name, false))))
+ return NULL;
+ return create_item_func_nextval(thd, table_ident);
+}
+
+
+Item *LEX::create_item_func_lastval(THD *thd,
+ const LEX_CSTRING *db,
+ const LEX_CSTRING *name)
+{
+ Table_ident *table_ident;
+ if (unlikely(!(table_ident=
+ new (thd->mem_root) Table_ident(thd, db, name, false))))
+ return NULL;
+ return create_item_func_lastval(thd, table_ident);
+}
+
+
+Item *LEX::create_item_func_setval(THD *thd, Table_ident *table_ident,
+ longlong nextval, ulonglong round,
+ bool is_used)
+{
+ TABLE_LIST *table;
+ if (unlikely(!(table= current_select->add_table_to_list(thd, table_ident, 0,
+ TL_OPTION_SEQUENCE,
+ TL_WRITE_ALLOW_WRITE,
+ MDL_SHARED_WRITE))))
+ return NULL;
+ return new (thd->mem_root) Item_func_setval(thd, table, nextval, round,
+ is_used);
+}
+
+
+Item *LEX::create_item_ident(THD *thd,
+ const Lex_ident_cli_st *ca,
+ const Lex_ident_cli_st *cb)
+{
+ const char *start= ca->pos();
+ const char *end= cb->end();
+ const Sp_rcontext_handler *rh;
+ sp_variable *spv;
+ DBUG_ASSERT(thd->m_parser_state->m_lip.get_buf() <= start);
+ DBUG_ASSERT(start <= end);
+ DBUG_ASSERT(end <= thd->m_parser_state->m_lip.get_end_of_query());
+ Lex_ident_sys a(thd, ca), b(thd, cb);
+ if (a.is_null() || b.is_null())
+ return NULL; // OEM
+ if ((spv= find_variable(&a, &rh)) &&
+ (spv->field_def.is_row() ||
+ spv->field_def.is_table_rowtype_ref() ||
+ spv->field_def.is_cursor_rowtype_ref()))
+ return create_item_spvar_row_field(thd, rh, &a, &b, spv, start, end);
+
+ if ((thd->variables.sql_mode & MODE_ORACLE) && b.length == 7)
+ {
+ if (!my_strnncoll(system_charset_info,
+ (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,
+ (const uchar *) b.str, 7,
+ (const uchar *) "CURRVAL", 7))
+ return create_item_func_lastval(thd, &null_clex_str, &a);
+ }
+
+ return create_item_ident_nospvar(thd, &a, &b);
+}
+
+
+Item *LEX::create_item_ident(THD *thd,
+ const Lex_ident_sys_st *a,
+ const Lex_ident_sys_st *b,
+ const Lex_ident_sys_st *c)
+{
+ const char *schema= (thd->client_capabilities & CLIENT_NO_SCHEMA ?
+ NullS : a->str);
+
+ if ((thd->variables.sql_mode & MODE_ORACLE) && c->length == 7)
+ {
+ if (!my_strnncoll(system_charset_info,
+ (const uchar *) c->str, 7,
+ (const uchar *) "NEXTVAL", 7))
+ return create_item_func_nextval(thd, a, b);
+ else if (!my_strnncoll(system_charset_info,
+ (const uchar *) c->str, 7,
+ (const uchar *) "CURRVAL", 7))
+ return create_item_func_lastval(thd, a, b);
+ }
+
+ if (current_select->no_table_names_allowed)
+ {
+ my_error(ER_TABLENAME_NOT_ALLOWED_HERE, MYF(0), b->str, thd->where);
+ return NULL;
+ }
+ if (current_select->parsing_place != IN_HAVING ||
+ current_select->get_in_sum_expr() > 0)
+ return new (thd->mem_root) Item_field(thd, current_context(),
+ schema, b->str, c);
+ return new (thd->mem_root) Item_ref(thd, current_context(),
+ schema, b->str, c);
+}
+
+
+Item *LEX::create_item_limit(THD *thd, const Lex_ident_cli_st *ca)
+{
+ DBUG_ASSERT(thd->m_parser_state->m_lip.get_buf() <= ca->pos());
+ DBUG_ASSERT(ca->pos() <= ca->end());
+ DBUG_ASSERT(ca->end() <= thd->m_parser_state->m_lip.get_end_of_query());
+
+ const Sp_rcontext_handler *rh;
+ sp_variable *spv;
+ Lex_ident_sys sa(thd, ca);
+ if (sa.is_null())
+ return NULL; // EOM
+ if (!(spv= find_variable(&sa, &rh)))
+ {
+ my_error(ER_SP_UNDECLARED_VAR, MYF(0), sa.str);
+ return NULL;
+ }
+
+ Query_fragment pos(thd, sphead, ca->pos(), ca->end());
+ Item_splocal *item;
+ if (unlikely(!(item= new (thd->mem_root)
+ Item_splocal(thd, rh, &sa,
+ spv->offset, spv->type_handler(),
+ clone_spec_offset ? 0 : pos.pos(),
+ clone_spec_offset ? 0 : pos.length()))))
+ return NULL;
+#ifdef DBUG_ASSERT_EXISTS
+ item->m_sp= sphead;
+#endif
+ safe_to_cache_query= 0;
+
+ if (unlikely(item->type() != Item::INT_ITEM))
+ {
+ my_error(ER_WRONG_SPVAR_TYPE_IN_LIMIT, MYF(0));
+ return NULL;
+ }
+ item->limit_clause_param= true;
+ return item;
+}
+
+
+Item *LEX::create_item_limit(THD *thd,
+ const Lex_ident_cli_st *ca,
+ const Lex_ident_cli_st *cb)
+{
+ DBUG_ASSERT(thd->m_parser_state->m_lip.get_buf() <= ca->pos());
+ DBUG_ASSERT(ca->pos() <= cb->end());
+ DBUG_ASSERT(cb->end() <= thd->m_parser_state->m_lip.get_end_of_query());
+
+ const Sp_rcontext_handler *rh;
+ sp_variable *spv;
+ Lex_ident_sys sa(thd, ca), sb(thd, cb);
+ if (unlikely(sa.is_null() || sb.is_null()))
+ return NULL; // EOM
+ if (!(spv= find_variable(&sa, &rh)))
+ {
+ my_error(ER_SP_UNDECLARED_VAR, MYF(0), sa.str);
+ return NULL;
+ }
+ // Qualified %TYPE variables are not possible
+ DBUG_ASSERT(!spv->field_def.column_type_ref());
+ Item_splocal *item;
+ if (unlikely(!(item= create_item_spvar_row_field(thd, rh, &sa, &sb, spv,
+ ca->pos(), cb->end()))))
+ return NULL;
+ if (unlikely(item->type() != Item::INT_ITEM))
+ {
+ my_error(ER_WRONG_SPVAR_TYPE_IN_LIMIT, MYF(0));
+ return NULL;
+ }
+ item->limit_clause_param= true;
+ return item;
+}
+
+
+bool LEX::set_user_variable(THD *thd, const LEX_CSTRING *name, Item *val)
+{
+ Item_func_set_user_var *item;
+ set_var_user *var;
+ if (unlikely(!(item= new (thd->mem_root) Item_func_set_user_var(thd, name,
+ val))) ||
+ unlikely(!(var= new (thd->mem_root) set_var_user(item))))
+ return true;
+ if (unlikely(var_list.push_back(var, thd->mem_root)))
+ return true;
+ return false;
+}
+
+
+Item *LEX::create_item_ident_nosp(THD *thd, Lex_ident_sys_st *name)
+{
+ if (current_select->parsing_place != IN_HAVING ||
+ current_select->get_in_sum_expr() > 0)
+ return new (thd->mem_root) Item_field(thd, current_context(),
+ NullS, NullS, name);
+
+ return new (thd->mem_root) Item_ref(thd, current_context(),
+ NullS, NullS, name);
+}
+
+
+Item *LEX::create_item_ident_sp(THD *thd, Lex_ident_sys_st *name,
+ const char *start,
+ const char *end)
+{
+ DBUG_ASSERT(thd->m_parser_state->m_lip.get_buf() <= start);
+ DBUG_ASSERT(start <= end);
+ DBUG_ASSERT(end <= thd->m_parser_state->m_lip.get_end_of_query());
+
+ const Sp_rcontext_handler *rh;
+ sp_variable *spv;
+ DBUG_ASSERT(spcont);
+ DBUG_ASSERT(sphead);
+ if ((spv= find_variable(name, &rh)))
+ {
+ /* We're compiling a stored procedure and found a variable */
+ if (!parsing_options.allows_variable)
+ {
+ my_error(ER_VIEW_SELECT_VARIABLE, MYF(0));
+ return NULL;
+ }
+
+ Query_fragment pos(thd, sphead, start, end);
+ uint f_pos= clone_spec_offset ? 0 : pos.pos();
+ uint f_length= clone_spec_offset ? 0 : pos.length();
+ Item_splocal *splocal= spv->field_def.is_column_type_ref() ?
+ new (thd->mem_root) Item_splocal_with_delayed_data_type(thd, rh, name,
+ spv->offset,
+ f_pos, f_length) :
+ new (thd->mem_root) Item_splocal(thd, rh, name,
+ spv->offset, spv->type_handler(),
+ f_pos, f_length);
+ if (unlikely(splocal == NULL))
+ return NULL;
+#ifdef DBUG_ASSERT_EXISTS
+ splocal->m_sp= sphead;
+#endif
+ safe_to_cache_query= 0;
+ return splocal;
+ }
+
+ if (thd->variables.sql_mode & MODE_ORACLE)
+ {
+ if (lex_string_eq(name, STRING_WITH_LEN("SQLCODE")))
+ return new (thd->mem_root) Item_func_sqlcode(thd);
+ if (lex_string_eq(name, STRING_WITH_LEN("SQLERRM")))
+ return new (thd->mem_root) Item_func_sqlerrm(thd);
+ }
+ return create_item_ident_nosp(thd, name);
+}
+
+
+
+bool LEX::set_variable(const LEX_CSTRING *name, Item *item)
+{
+ sp_pcontext *ctx;
+ const Sp_rcontext_handler *rh;
+ sp_variable *spv= find_variable(name, &ctx, &rh);
+ return spv ? sphead->set_local_variable(thd, ctx, rh, spv, item, this, true) :
+ set_system_variable(option_type, name, item);
+}
+
+
+/**
+ Generate instructions for:
+ SET x.y= expr;
+*/
+bool LEX::set_variable(const LEX_CSTRING *name1,
+ const LEX_CSTRING *name2,
+ Item *item)
+{
+ const Sp_rcontext_handler *rh;
+ sp_pcontext *ctx;
+ sp_variable *spv;
+ if (spcont && (spv= find_variable(name1, &ctx, &rh)))
+ {
+ if (spv->field_def.is_table_rowtype_ref() ||
+ spv->field_def.is_cursor_rowtype_ref())
+ return sphead->set_local_variable_row_field_by_name(thd, ctx,
+ rh,
+ spv, name2,
+ item, this);
+ // A field of a ROW variable
+ uint row_field_offset;
+ return !spv->find_row_field(name1, name2, &row_field_offset) ||
+ sphead->set_local_variable_row_field(thd, ctx, rh,
+ spv, row_field_offset,
+ item, this);
+ }
+
+ if (is_trigger_new_or_old_reference(name1))
+ return set_trigger_field(name1, name2, item);
+
+ return set_system_variable(thd, option_type, name1, name2, item);
+}
+
+
+bool LEX::set_default_system_variable(enum_var_type var_type,
+ const LEX_CSTRING *name,
+ Item *val)
+{
+ static LEX_CSTRING default_base_name= {STRING_WITH_LEN("default")};
+ sys_var *var= find_sys_var(thd, name->str, name->length);
+ if (!var)
+ return true;
+ if (unlikely(!var->is_struct()))
+ {
+ my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), name->str);
+ return true;
+ }
+ return set_system_variable(var_type, var, &default_base_name, val);
+}
+
+
+bool LEX::set_system_variable(enum_var_type var_type,
+ const LEX_CSTRING *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;
+}
+
+
+bool LEX::set_system_variable(THD *thd, enum_var_type var_type,
+ const LEX_CSTRING *name1,
+ const LEX_CSTRING *name2,
+ Item *val)
+{
+ sys_var *tmp;
+ if (unlikely(check_reserved_words(name1)) ||
+ unlikely(!(tmp= find_sys_var(thd, name2->str, name2->length, true))))
+ {
+ my_error(ER_UNKNOWN_STRUCTURED_VARIABLE, MYF(0),
+ (int) name1->length, name1->str);
+ return true;
+ }
+ if (unlikely(!tmp->is_struct()))
+ {
+ my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), name2->str);
+ return true;
+ }
+ return set_system_variable(var_type, tmp, name1, val);
+}
+
+
+bool LEX::set_trigger_field(const LEX_CSTRING *name1, const LEX_CSTRING *name2,
+ Item *val)
+{
+ DBUG_ASSERT(is_trigger_new_or_old_reference(name1));
+ if (unlikely(name1->str[0]=='O' || name1->str[0]=='o'))
+ {
+ my_error(ER_TRG_CANT_CHANGE_ROW, MYF(0), "OLD", "");
+ return true;
+ }
+ if (unlikely(trg_chistics.event == TRG_EVENT_DELETE))
+ {
+ my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "NEW", "on DELETE");
+ return true;
+ }
+ if (unlikely(trg_chistics.action_time == TRG_ACTION_AFTER))
+ {
+ my_error(ER_TRG_CANT_CHANGE_ROW, MYF(0), "NEW", "after ");
+ return true;
+ }
+ return set_trigger_new_row(name2, val);
}
@@ -4858,7 +7343,7 @@ uint binlog_unsafe_map[256];
#define UNSAFE(a, b, c) \
{ \
- DBUG_PRINT("unsafe_mixed_statement", ("SETTING BASE VALUES: %s, %s, %02X\n", \
+ DBUG_PRINT("unsafe_mixed_statement", ("SETTING BASE VALUES: %s, %s, %02X", \
LEX::stmt_accessed_table_string(a), \
LEX::stmt_accessed_table_string(b), \
c)); \
@@ -5012,20 +7497,21 @@ void binlog_unsafe_map_init()
st_select_lex and saves this fields.
*/
-void st_select_lex::collect_grouping_fields(THD *thd)
+void st_select_lex::collect_grouping_fields(THD *thd,
+ ORDER *grouping_list)
{
grouping_tmp_fields.empty();
List_iterator<Item> li(join->fields_list);
Item *item= li++;
for (uint i= 0; i < master_unit()->derived->table->s->fields; i++, (item=li++))
{
- for (ORDER *ord= join->group_list; ord; ord= ord->next)
+ for (ORDER *ord= grouping_list; ord; ord= ord->next)
{
if ((*ord->item)->eq((Item*)item, 0))
{
- Grouping_tmp_field *grouping_tmp_field=
- new Grouping_tmp_field(master_unit()->derived->table->field[i], item);
- grouping_tmp_fields.push_back(grouping_tmp_field);
+ Grouping_tmp_field *grouping_tmp_field=
+ new Grouping_tmp_field(master_unit()->derived->table->field[i], item);
+ grouping_tmp_fields.push_back(grouping_tmp_field);
}
}
}
@@ -5131,14 +7617,14 @@ st_select_lex::check_cond_extraction_for_grouping_fields(Item *cond,
*/
Item *st_select_lex::build_cond_for_grouping_fields(THD *thd, Item *cond,
- bool no_top_clones)
+ bool no_top_clones)
{
if (cond->get_extraction_flag() == FULL_EXTRACTION_FL)
{
if (no_top_clones)
return cond;
cond->clear_extraction_flag();
- return cond->build_clone(thd, thd->mem_root);
+ return cond->build_clone(thd);
}
if (cond->type() == Item::COND_ITEM)
{
@@ -5151,25 +7637,25 @@ Item *st_select_lex::build_cond_for_grouping_fields(THD *thd, Item *cond,
}
else
new_cond= new (thd->mem_root) Item_cond_or(thd);
- if (!new_cond)
- return 0;
+ if (unlikely(!new_cond))
+ return 0;
List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
Item *item;
while ((item=li++))
{
if (item->get_extraction_flag() == NO_EXTRACTION_FL)
{
- DBUG_ASSERT(cond_and);
- item->clear_extraction_flag();
- continue;
+ DBUG_ASSERT(cond_and);
+ item->clear_extraction_flag();
+ continue;
}
Item *fix= build_cond_for_grouping_fields(thd, item,
- no_top_clones & cond_and);
- if (!fix)
+ no_top_clones & cond_and);
+ if (unlikely(!fix))
{
- if (cond_and)
- continue;
- break;
+ if (cond_and)
+ continue;
+ break;
}
new_cond->argument_list()->push_back(fix, thd->mem_root);
}
@@ -5177,13 +7663,13 @@ Item *st_select_lex::build_cond_for_grouping_fields(THD *thd, Item *cond,
if (!cond_and && item)
{
while((item= li++))
- item->clear_extraction_flag();
+ item->clear_extraction_flag();
return 0;
}
switch (new_cond->argument_list()->elements)
{
case 0:
- return 0;
+ return 0;
case 1:
return new_cond->argument_list()->head();
default:
@@ -5192,3 +7678,653 @@ Item *st_select_lex::build_cond_for_grouping_fields(THD *thd, Item *cond,
}
return 0;
}
+
+
+int set_statement_var_if_exists(THD *thd, const char *var_name,
+ size_t var_name_length, ulonglong value)
+{
+ sys_var *sysvar;
+ if (unlikely(thd->lex->sql_command == SQLCOM_CREATE_VIEW))
+ {
+ my_error(ER_VIEW_SELECT_CLAUSE, MYF(0), "[NO]WAIT");
+ return 1;
+ }
+ if (unlikely(thd->lex->sphead))
+ {
+ my_error(ER_SP_BADSTATEMENT, MYF(0), "[NO]WAIT");
+ return 1;
+ }
+ if ((sysvar= find_sys_var(thd, var_name, var_name_length, true)))
+ {
+ Item *item= new (thd->mem_root) Item_uint(thd, value);
+ set_var *var= new (thd->mem_root) set_var(thd, OPT_SESSION, sysvar,
+ &null_clex_str, item);
+
+ if (unlikely(!item) || unlikely(!var) ||
+ unlikely(thd->lex->stmt_var_list.push_back(var, thd->mem_root)))
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+bool LEX::sp_add_cfetch(THD *thd, const LEX_CSTRING *name)
+{
+ uint offset;
+ sp_instr_cfetch *i;
+
+ if (!spcont->find_cursor(name, &offset, false))
+ {
+ my_error(ER_SP_CURSOR_MISMATCH, MYF(0), name->str);
+ return true;
+ }
+ i= new (thd->mem_root)
+ sp_instr_cfetch(sphead->instructions(), spcont, offset,
+ !(thd->variables.sql_mode & MODE_ORACLE));
+ if (unlikely(i == NULL) || unlikely(sphead->add_instr(i)))
+ return true;
+ return false;
+}
+
+
+bool LEX::create_or_alter_view_finalize(THD *thd, Table_ident *table_ident)
+{
+ sql_command= SQLCOM_CREATE_VIEW;
+ /* first table in list is target VIEW name */
+ if (unlikely(!select_lex.add_table_to_list(thd, table_ident, NULL,
+ TL_OPTION_UPDATING,
+ TL_IGNORE,
+ MDL_EXCLUSIVE)))
+ return true;
+ query_tables->open_strategy= TABLE_LIST::OPEN_STUB;
+ return false;
+}
+
+
+bool LEX::add_alter_view(THD *thd, uint16 algorithm,
+ enum_view_suid suid,
+ Table_ident *table_ident)
+{
+ if (unlikely(sphead))
+ {
+ my_error(ER_SP_BADSTATEMENT, MYF(0), "ALTER VIEW");
+ return true;
+ }
+ if (unlikely(!(create_view= new (thd->mem_root)
+ Create_view_info(VIEW_ALTER, algorithm, suid))))
+ return true;
+ return create_or_alter_view_finalize(thd, table_ident);
+}
+
+
+bool LEX::add_create_view(THD *thd, DDL_options_st ddl,
+ uint16 algorithm, enum_view_suid suid,
+ Table_ident *table_ident)
+{
+ if (unlikely(set_create_options_with_check(ddl)))
+ return true;
+ if (unlikely(!(create_view= new (thd->mem_root)
+ Create_view_info(ddl.or_replace() ?
+ VIEW_CREATE_OR_REPLACE :
+ VIEW_CREATE_NEW,
+ algorithm, suid))))
+ return true;
+ return create_or_alter_view_finalize(thd, table_ident);
+}
+
+
+bool LEX::call_statement_start(THD *thd, sp_name *name)
+{
+ Database_qualified_name pkgname(&null_clex_str, &null_clex_str);
+ const Sp_handler *sph= &sp_handler_procedure;
+ sql_command= SQLCOM_CALL;
+ value_list.empty();
+ if (unlikely(sph->sp_resolve_package_routine(thd, thd->lex->sphead,
+ name, &sph, &pkgname)))
+ return true;
+ if (unlikely(!(m_sql_cmd= new (thd->mem_root) Sql_cmd_call(name, sph))))
+ return true;
+ sph->add_used_routine(this, thd, name);
+ if (pkgname.m_name.length)
+ sp_handler_package_body.add_used_routine(this, thd, &pkgname);
+ return false;
+}
+
+
+bool LEX::call_statement_start(THD *thd, const LEX_CSTRING *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)
+{
+ sp_name *spname= make_sp_name(thd, name1, name2);
+ return unlikely(!spname) || call_statement_start(thd, spname);
+}
+
+
+sp_package *LEX::get_sp_package() const
+{
+ return sphead ? sphead->get_package() : NULL;
+}
+
+
+sp_package *LEX::create_package_start(THD *thd,
+ enum_sql_command command,
+ const Sp_handler *sph,
+ const sp_name *name_arg,
+ DDL_options_st options)
+{
+ sp_package *pkg;
+
+ if (unlikely(sphead))
+ {
+ my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), sph->type_str());
+ return NULL;
+ }
+ if (unlikely(set_command_with_check(command, options)))
+ return NULL;
+ if (sph->type() == TYPE_ENUM_PACKAGE_BODY)
+ {
+ /*
+ If we start parsing a "CREATE PACKAGE BODY", we need to load
+ the corresponding "CREATE PACKAGE", for the following reasons:
+ 1. "CREATE PACKAGE BODY" is allowed only if "CREATE PACKAGE"
+ was done earlier for the same package name.
+ So if "CREATE PACKAGE" does not exist, we throw an error here.
+ 2. When parsing "CREATE PACKAGE BODY", we need to know all package
+ public and private routine names, to translate procedure and
+ function calls correctly.
+ For example, this statement inside a package routine:
+ CALL p;
+ can be translated to:
+ CALL db.pkg.p; -- p is a known (public or private) package routine
+ CALL db.p; -- p is not a known package routine
+ */
+ sp_head *spec;
+ int ret= sp_handler_package_spec.
+ sp_cache_routine_reentrant(thd, name_arg, &spec);
+ if (unlikely(!spec))
+ {
+ if (!ret)
+ my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
+ "PACKAGE", ErrConvDQName(name_arg).ptr());
+ return 0;
+ }
+ }
+ if (unlikely(!(pkg= sp_package::create(this, name_arg, sph))))
+ return NULL;
+ pkg->reset_thd_mem_root(thd);
+ pkg->init(this);
+ pkg->make_qname(pkg->get_main_mem_root(), &pkg->m_qname);
+ sphead= pkg;
+ return pkg;
+}
+
+
+bool LEX::create_package_finalize(THD *thd,
+ const sp_name *name,
+ const sp_name *name2,
+ const char *body_start,
+ const char *body_end)
+{
+ if (name2 &&
+ (name2->m_explicit_name != name->m_explicit_name ||
+ strcmp(name2->m_db.str, name->m_db.str) ||
+ !Sp_handler::eq_routine_name(name2->m_name, name->m_name)))
+ {
+ bool exp= name2->m_explicit_name || name->m_explicit_name;
+ my_error(ER_END_IDENTIFIER_DOES_NOT_MATCH, MYF(0),
+ exp ? ErrConvDQName(name2).ptr() : name2->m_name.str,
+ exp ? ErrConvDQName(name).ptr() : name->m_name.str);
+ return true;
+ }
+ sphead->m_body.length= body_end - body_start;
+ if (unlikely(!(sphead->m_body.str= thd->strmake(body_start,
+ sphead->m_body.length))))
+ return true;
+
+ size_t not_used;
+ Lex_input_stream *lip= & thd->m_parser_state->m_lip;
+ sphead->m_defstr.length= lip->get_cpp_ptr() - lip->get_cpp_buf();
+ sphead->m_defstr.str= thd->strmake(lip->get_cpp_buf(), sphead->m_defstr.length);
+ trim_whitespace(thd->charset(), &sphead->m_defstr, &not_used);
+
+ sphead->restore_thd_mem_root(thd);
+ sp_package *pkg= sphead->get_package();
+ DBUG_ASSERT(pkg);
+ return pkg->validate_after_parser(thd);
+}
+
+
+bool LEX::add_grant_command(THD *thd, enum_sql_command sql_command_arg,
+ stored_procedure_type type_arg)
+{
+ if (columns.elements)
+ {
+ thd->parse_error();
+ return true;
+ }
+ sql_command= sql_command_arg,
+ type= type_arg;
+ return false;
+}
+
+
+Item *LEX::make_item_func_substr(THD *thd, Item *a, Item *b, Item *c)
+{
+ return (thd->variables.sql_mode & MODE_ORACLE) ?
+ new (thd->mem_root) Item_func_substr_oracle(thd, a, b, c) :
+ new (thd->mem_root) Item_func_substr(thd, a, b, c);
+}
+
+
+Item *LEX::make_item_func_substr(THD *thd, Item *a, Item *b)
+{
+ return (thd->variables.sql_mode & MODE_ORACLE) ?
+ new (thd->mem_root) Item_func_substr_oracle(thd, a, b) :
+ new (thd->mem_root) Item_func_substr(thd, a, b);
+}
+
+
+Item *LEX::make_item_func_replace(THD *thd,
+ Item *org,
+ Item *find,
+ Item *replace)
+{
+ return (thd->variables.sql_mode & MODE_ORACLE) ?
+ new (thd->mem_root) Item_func_replace_oracle(thd, org, find, replace) :
+ new (thd->mem_root) Item_func_replace(thd, org, find, replace);
+}
+
+
+bool SELECT_LEX::vers_push_field(THD *thd, TABLE_LIST *table,
+ const LEX_CSTRING field_name)
+{
+ DBUG_ASSERT(field_name.str);
+ Item_field *fld= new (thd->mem_root) Item_field(thd, &context,
+ table->db.str,
+ table->alias.str,
+ &field_name);
+ if (unlikely(!fld) || unlikely(item_list.push_back(fld)))
+ return true;
+
+ if (thd->lex->view_list.elements)
+ {
+ LEX_CSTRING *l;
+ if (unlikely(!(l= thd->make_clex_string(field_name.str,
+ field_name.length))) ||
+ unlikely(thd->lex->view_list.push_back(l)))
+ return true;
+ }
+
+ return false;
+}
+
+
+Item *Lex_trim_st::make_item_func_trim_std(THD *thd) const
+{
+ if (m_remove)
+ {
+ switch (m_spec) {
+ case TRIM_BOTH:
+ return new (thd->mem_root) Item_func_trim(thd, m_source, m_remove);
+ case TRIM_LEADING:
+ return new (thd->mem_root) Item_func_ltrim(thd, m_source, m_remove);
+ case TRIM_TRAILING:
+ return new (thd->mem_root) Item_func_rtrim(thd, m_source, m_remove);
+ }
+ }
+
+ switch (m_spec) {
+ case TRIM_BOTH:
+ return new (thd->mem_root) Item_func_trim(thd, m_source);
+ case TRIM_LEADING:
+ return new (thd->mem_root) Item_func_ltrim(thd, m_source);
+ case TRIM_TRAILING:
+ return new (thd->mem_root) Item_func_rtrim(thd, m_source);
+ }
+ DBUG_ASSERT(0);
+ return NULL;
+}
+
+
+Item *Lex_trim_st::make_item_func_trim_oracle(THD *thd) const
+{
+ if (m_remove)
+ {
+ switch (m_spec) {
+ case TRIM_BOTH:
+ return new (thd->mem_root) Item_func_trim_oracle(thd, m_source, m_remove);
+ case TRIM_LEADING:
+ return new (thd->mem_root) Item_func_ltrim_oracle(thd, m_source, m_remove);
+ case TRIM_TRAILING:
+ return new (thd->mem_root) Item_func_rtrim_oracle(thd, m_source, m_remove);
+ }
+ }
+
+ switch (m_spec) {
+ case TRIM_BOTH:
+ return new (thd->mem_root) Item_func_trim_oracle(thd, m_source);
+ case TRIM_LEADING:
+ return new (thd->mem_root) Item_func_ltrim_oracle(thd, m_source);
+ case TRIM_TRAILING:
+ return new (thd->mem_root) Item_func_rtrim_oracle(thd, m_source);
+ }
+ DBUG_ASSERT(0);
+ return NULL;
+}
+
+
+Item *Lex_trim_st::make_item_func_trim(THD *thd) const
+{
+ return (thd->variables.sql_mode & MODE_ORACLE) ?
+ make_item_func_trim_oracle(thd) :
+ make_item_func_trim_std(thd);
+}
+
+
+Item *LEX::make_item_func_call_generic(THD *thd, Lex_ident_cli_st *cdb,
+ Lex_ident_cli_st *cname, List<Item> *args)
+{
+ Lex_ident_sys db(thd, cdb), name(thd, cname);
+ if (db.is_null() || name.is_null())
+ return NULL; // EOM
+ /*
+ The following in practice calls:
+ <code>Create_sp_func::create()</code>
+ and builds a stored function.
+
+ However, it's important to maintain the interface between the
+ parser and the implementation in item_create.cc clean,
+ since this will change with WL#2128 (SQL PATH):
+ - INFORMATION_SCHEMA.version() is the SQL 99 syntax for the native
+ function version(),
+ - MySQL.version() is the SQL 2003 syntax for the native function
+ version() (a vendor can specify any schema).
+ */
+
+ if (!name.str || check_db_name((LEX_STRING*) static_cast<LEX_CSTRING*>(&db)))
+ {
+ my_error(ER_WRONG_DB_NAME, MYF(0), db.str);
+ return NULL;
+ }
+ if (check_routine_name(&name))
+ return NULL;
+
+ Create_qfunc *builder= find_qualified_function_builder(thd);
+ DBUG_ASSERT(builder);
+ return builder->create_with_db(thd, &db, &name, true, args);
+}
+
+
+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)))
+ return NULL;
+ current_select->with_wild++;
+ return item;
+}
+
+
+Item *LEX::create_item_qualified_asterisk(THD *thd,
+ const Lex_ident_sys_st *a,
+ const Lex_ident_sys_st *b)
+{
+ Item *item;
+ const char* schema= thd->client_capabilities & CLIENT_NO_SCHEMA ?
+ NullS : a->str;
+ if (!(item= new (thd->mem_root) Item_field(thd, current_context(),
+ schema, b->str,
+ &star_clex_str)))
+ return NULL;
+ current_select->with_wild++;
+ return item;
+}
+
+
+bool Lex_ident_sys_st::copy_ident_cli(THD *thd, const Lex_ident_cli_st *str)
+{
+ return thd->to_ident_sys_alloc(this, str);
+}
+
+bool Lex_ident_sys_st::copy_keyword(THD *thd, const Lex_ident_cli_st *str)
+{
+ return thd->make_lex_string(static_cast<LEX_CSTRING*>(this),
+ str->str, str->length) == NULL;
+}
+
+bool Lex_ident_sys_st::copy_or_convert(THD *thd,
+ const Lex_ident_cli_st *src,
+ CHARSET_INFO *cs)
+{
+ if (!src->is_8bit())
+ return copy_keyword(thd, src); // 7bit string makes a wellformed identifier
+ return convert(thd, src, cs);
+}
+
+
+bool Lex_ident_sys_st::copy_sys(THD *thd, const LEX_CSTRING *src)
+{
+ if (thd->check_string_for_wellformedness(src->str, src->length,
+ system_charset_info))
+ return true;
+ return thd->make_lex_string(this, src->str, src->length) == NULL;
+}
+
+
+bool Lex_ident_sys_st::convert(THD *thd,
+ const LEX_CSTRING *src, CHARSET_INFO *cs)
+{
+ LEX_STRING tmp;
+ if (thd->convert_with_error(system_charset_info, &tmp, cs,
+ src->str, src->length))
+ return true;
+ str= tmp.str;
+ length= tmp.length;
+ return false;
+}
+
+
+bool Lex_ident_sys_st::to_size_number(ulonglong *to) const
+{
+ ulonglong number;
+ uint text_shift_number= 0;
+ longlong prefix_number;
+ const char *start_ptr= str;
+ size_t str_len= length;
+ const char *end_ptr= start_ptr + str_len;
+ int error;
+ prefix_number= my_strtoll10(start_ptr, (char**) &end_ptr, &error);
+ if (likely((start_ptr + str_len - 1) == end_ptr))
+ {
+ switch (end_ptr[0])
+ {
+ case 'g':
+ case 'G': text_shift_number+=30; break;
+ case 'm':
+ case 'M': text_shift_number+=20; break;
+ case 'k':
+ case 'K': text_shift_number+=10; break;
+ default:
+ my_error(ER_WRONG_SIZE_NUMBER, MYF(0));
+ return true;
+ }
+ if (unlikely(prefix_number >> 31))
+ {
+ my_error(ER_SIZE_OVERFLOW_ERROR, MYF(0));
+ return true;
+ }
+ number= prefix_number << text_shift_number;
+ }
+ else
+ {
+ my_error(ER_WRONG_SIZE_NUMBER, MYF(0));
+ return true;
+ }
+ *to= number;
+ return false;
+}
+
+
+bool LEX::part_values_current(THD *thd)
+{
+ partition_element *elem= part_info->curr_part_elem;
+ if (!is_partition_management())
+ {
+ if (unlikely(part_info->part_type != VERSIONING_PARTITION))
+ {
+ my_error(ER_PARTITION_WRONG_TYPE, MYF(0), "SYSTEM_TIME");
+ return true;
+ }
+ }
+ else
+ {
+ DBUG_ASSERT(create_last_non_select_table);
+ DBUG_ASSERT(create_last_non_select_table->table_name.str);
+ // FIXME: other ALTER commands?
+ my_error(ER_VERS_WRONG_PARTS, MYF(0),
+ create_last_non_select_table->table_name.str);
+ return true;
+ }
+ elem->type= partition_element::CURRENT;
+ DBUG_ASSERT(part_info->vers_info);
+ part_info->vers_info->now_part= elem;
+ return false;
+}
+
+
+bool LEX::part_values_history(THD *thd)
+{
+ partition_element *elem= part_info->curr_part_elem;
+ if (!is_partition_management())
+ {
+ if (unlikely(part_info->part_type != VERSIONING_PARTITION))
+ {
+ my_error(ER_PARTITION_WRONG_TYPE, MYF(0), "SYSTEM_TIME");
+ return true;
+ }
+ }
+ else
+ {
+ part_info->vers_init_info(thd);
+ elem->id= UINT_MAX32;
+ }
+ DBUG_ASSERT(part_info->vers_info);
+ if (unlikely(part_info->vers_info->now_part))
+ {
+ DBUG_ASSERT(create_last_non_select_table);
+ DBUG_ASSERT(create_last_non_select_table->table_name.str);
+ my_error(ER_VERS_WRONG_PARTS, MYF(0),
+ create_last_non_select_table->table_name.str);
+ return true;
+ }
+ elem->type= partition_element::HISTORY;
+ return false;
+}
+
+
+bool LEX::last_field_generated_always_as_row_start_or_end(Lex_ident *p,
+ const char *type,
+ uint flag)
+{
+ if (unlikely(p->str))
+ {
+ my_error(ER_VERS_DUPLICATE_ROW_START_END, MYF(0), type,
+ last_field->field_name.str);
+ return true;
+ }
+ last_field->flags|= (flag | NOT_NULL_FLAG);
+ DBUG_ASSERT(p);
+ *p= last_field->field_name;
+ return false;
+}
+
+
+
+bool LEX::last_field_generated_always_as_row_start()
+{
+ Vers_parse_info &info= vers_get_info();
+ Lex_ident *p= &info.as_row.start;
+ return last_field_generated_always_as_row_start_or_end(p, "START",
+ VERS_SYS_START_FLAG);
+}
+
+
+bool LEX::last_field_generated_always_as_row_end()
+{
+ Vers_parse_info &info= vers_get_info();
+ Lex_ident *p= &info.as_row.end;
+ return last_field_generated_always_as_row_start_or_end(p, "END",
+ VERS_SYS_END_FLAG);
+}
+
+
+bool LEX::tvc_finalize()
+{
+ mysql_init_select(this);
+ if (unlikely(!(current_select->tvc=
+ new (thd->mem_root)
+ table_value_constr(many_values,
+ current_select,
+ current_select->options))))
+ return true;
+ many_values.empty();
+ if (!current_select->master_unit()->fake_select_lex)
+ current_select->master_unit()->add_fake_select_lex(thd);
+ return false;
+}
+
+
+bool LEX::tvc_finalize_derived()
+{
+ derived_tables|= DERIVED_SUBQUERY;
+ if (unlikely(!expr_allows_subselect || sql_command == (int)SQLCOM_PURGE))
+ {
+ thd->parse_error();
+ return true;
+ }
+ if (current_select->linkage == GLOBAL_OPTIONS_TYPE ||
+ unlikely(mysql_new_select(this, 1, NULL)))
+ return true;
+ current_select->linkage= DERIVED_TABLE_TYPE;
+ return tvc_finalize();
+}
+
+
+bool LEX::map_data_type(const Lex_ident_sys_st &schema_name,
+ Lex_field_type_st *type) const
+{
+ const Schema *schema= schema_name.str ?
+ Schema::find_by_name(schema_name) :
+ Schema::find_implied(thd);
+ if (!schema)
+ {
+ char buf[128];
+ const Name type_name= type->type_handler()->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());
+ type->set_handler(mapped);
+ return false;
+}
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 965c3f29834..55929ed7df6 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -23,16 +23,176 @@
#include "violite.h" /* SSL_type */
#include "sql_trigger.h"
-#include "item.h" /* From item_subselect.h: subselect_union_engine */
#include "thr_lock.h" /* thr_lock_type, TL_UNLOCK */
#include "mem_root_array.h"
#include "sql_cmd.h"
#include "sql_alter.h" // Alter_info
#include "sql_window.h"
#include "sql_trigger.h"
+#include "sp.h" // enum stored_procedure_type
+#include "sql_tvc.h"
+#include "item.h"
+#include "sql_schema.h"
+
+/* Used for flags of nesting constructs */
+#define SELECT_NESTING_MAP_SIZE 64
+typedef Bitmap<SELECT_NESTING_MAP_SIZE> nesting_map;
/* YACC and LEX Definitions */
+
+/**
+ A string with metadata. Usually points to a string in the client
+ character set, but unlike Lex_ident_cli_st (see below) it does not
+ necessarily point to a query fragment. It can also point to memory
+ of other kinds (e.g. an additional THD allocated memory buffer
+ not overlapping with the current query text).
+
+ We'll add more flags here eventually, to know if the string has, e.g.:
+ - multi-byte characters
+ - bad byte sequences
+ - backslash escapes: 'a\nb'
+ and reuse the original query fragments instead of making the string
+ copy too early, in Lex_input_stream::get_text().
+ This will allow to avoid unnecessary copying, as well as
+ create more optimal Item types in sql_yacc.yy
+*/
+struct Lex_string_with_metadata_st: public LEX_CSTRING
+{
+private:
+ bool m_is_8bit; // True if the string has 8bit characters
+ char m_quote; // Quote character, or 0 if not quoted
+public:
+ void set_8bit(bool is_8bit) { m_is_8bit= is_8bit; }
+ void set_metadata(bool is_8bit, char quote)
+ {
+ m_is_8bit= is_8bit;
+ m_quote= quote;
+ }
+ void set(const char *s, size_t len, bool is_8bit, char quote)
+ {
+ str= s;
+ length= len;
+ set_metadata(is_8bit, quote);
+ }
+ void set(const LEX_CSTRING *s, bool is_8bit, char quote)
+ {
+ ((LEX_CSTRING &)*this)= *s;
+ set_metadata(is_8bit, quote);
+ }
+ bool is_8bit() const { return m_is_8bit; }
+ 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
+ {
+ 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
+ {
+ return !m_is_8bit ? MY_REPERTOIRE_ASCII : MY_REPERTOIRE_UNICODE30;
+ }
+};
+
+
+/*
+ Used to store identifiers in the client character set.
+ Points to a query fragment.
+*/
+struct Lex_ident_cli_st: public Lex_string_with_metadata_st
+{
+public:
+ void set_keyword(const char *s, size_t len)
+ {
+ set(s, len, false, '\0');
+ }
+ void set_ident(const char *s, size_t len, bool is_8bit)
+ {
+ set(s, len, is_8bit, '\0');
+ }
+ void set_ident_quoted(const char *s, size_t len, bool is_8bit, char quote)
+ {
+ set(s, len, is_8bit, quote);
+ }
+ void set_unquoted(const LEX_CSTRING *s, bool is_8bit)
+ {
+ set(s, is_8bit, '\0');
+ }
+ const char *pos() const { return str - is_quoted(); }
+ const char *end() const { return str + length + is_quoted(); }
+};
+
+
+class Lex_ident_cli: public Lex_ident_cli_st
+{
+public:
+ Lex_ident_cli(const LEX_CSTRING *s, bool is_8bit)
+ {
+ set_unquoted(s, is_8bit);
+ }
+ Lex_ident_cli(const char *s, size_t len)
+ {
+ set_ident(s, len, false);
+ }
+};
+
+
+struct Lex_ident_sys_st: public LEX_CSTRING
+{
+public:
+ bool copy_ident_cli(THD *thd, const Lex_ident_cli_st *str);
+ bool copy_keyword(THD *thd, const Lex_ident_cli_st *str);
+ bool copy_sys(THD *thd, const LEX_CSTRING *str);
+ bool convert(THD *thd, const LEX_CSTRING *str, CHARSET_INFO *cs);
+ bool copy_or_convert(THD *thd, const Lex_ident_cli_st *str, CHARSET_INFO *cs);
+ bool is_null() const { return str == NULL; }
+ bool to_size_number(ulonglong *to) const;
+};
+
+
+class Lex_ident_sys: public Lex_ident_sys_st
+{
+public:
+ Lex_ident_sys(THD *thd, const Lex_ident_cli_st *str)
+ {
+ if (copy_ident_cli(thd, str))
+ ((LEX_CSTRING &) *this)= null_clex_str;
+ }
+ Lex_ident_sys()
+ {
+ ((LEX_CSTRING &) *this)= null_clex_str;
+ }
+};
+
+
+enum sub_select_type
+{
+ UNSPECIFIED_TYPE,
+ /* following 3 enums should be as they are*/
+ UNION_TYPE, INTERSECT_TYPE, EXCEPT_TYPE,
+ GLOBAL_OPTIONS_TYPE, DERIVED_TABLE_TYPE, OLAP_TYPE
+};
+enum unit_common_op {OP_MIX, OP_UNION, OP_INTERSECT, OP_EXCEPT};
+
+enum enum_view_suid
+{
+ VIEW_SUID_INVOKER= 0,
+ VIEW_SUID_DEFINER= 1,
+ VIEW_SUID_DEFAULT= 2
+};
+
+
+enum plsql_cursor_attr_t
+{
+ PLSQL_CURSOR_ATTR_ISOPEN,
+ PLSQL_CURSOR_ATTR_FOUND,
+ PLSQL_CURSOR_ATTR_NOTFOUND,
+ PLSQL_CURSOR_ATTR_ROWCOUNT
+};
+
+
/* These may not be declared yet */
class Table_ident;
class sql_exchange;
@@ -41,6 +201,8 @@ class sp_head;
class sp_name;
class sp_instr;
class sp_pcontext;
+class sp_variable;
+class sp_assignment_lex;
class st_alter_tablespace;
class partition_info;
class Event_parse_data;
@@ -52,7 +214,7 @@ class Key_part_spec;
class Item_window_func;
struct sql_digest_state;
class With_clause;
-
+class my_var;
#define ALLOC_ROOT_SET 1024
@@ -103,15 +265,6 @@ extern uint binlog_unsafe_map[256];
void binlog_unsafe_map_init();
#endif
-/**
- used by the parser to store internal variable name
-*/
-struct sys_var_with_base
-{
- sys_var *var;
- LEX_STRING base_name;
-};
-
struct LEX_TYPE
{
enum enum_field_types type;
@@ -130,11 +283,11 @@ struct LEX_TYPE
#define LEX_YYSTYPE void *
#else
#include "lex_symbol.h"
-#if MYSQL_LEX
-#include "item_func.h" /* Cast_target used in sql_yacc.h */
-#include "sql_get_diagnostics.h" /* Types used in sql_yacc.h */
+#ifdef MYSQL_LEX
+#include "item_func.h" /* Cast_target used in sql_yacc.hh */
+#include "sql_get_diagnostics.h" /* Types used in sql_yacc.hh */
#include "sp_pcontext.h"
-#include "sql_yacc.h"
+#include "sql_yacc.hh"
#define LEX_YYSTYPE YYSTYPE *
#else
#define LEX_YYSTYPE void *
@@ -143,18 +296,20 @@ struct LEX_TYPE
#endif
// describe/explain types
-#define DESCRIBE_NORMAL 1
-#define DESCRIBE_EXTENDED 2
+#define DESCRIBE_NORMAL 1
+#define DESCRIBE_EXTENDED 2
/*
This is not within #ifdef because we want "EXPLAIN PARTITIONS ..." to produce
additional "partitions" column even if partitioning is not compiled in.
*/
-#define DESCRIBE_PARTITIONS 4
+#define DESCRIBE_PARTITIONS 4
#ifdef MYSQL_SERVER
-extern const LEX_STRING null_lex_str;
-extern const LEX_STRING empty_lex_str;
+extern const LEX_STRING empty_lex_str;
+extern MYSQL_PLUGIN_IMPORT const LEX_CSTRING empty_clex_str;
+extern const LEX_CSTRING star_clex_str;
+extern const LEX_CSTRING param_clex_str;
enum enum_sp_suid_behaviour
{
@@ -172,26 +327,54 @@ enum enum_sp_data_access
SP_MODIFIES_SQL_DATA
};
-const LEX_STRING sp_data_access_name[]=
+enum enum_sp_aggregate_type
{
- { C_STRING_WITH_LEN("") },
- { C_STRING_WITH_LEN("CONTAINS SQL") },
- { C_STRING_WITH_LEN("NO SQL") },
- { C_STRING_WITH_LEN("READS SQL DATA") },
- { C_STRING_WITH_LEN("MODIFIES SQL DATA") }
+ DEFAULT_AGGREGATE= 0,
+ NOT_AGGREGATE,
+ GROUP_AGGREGATE
};
-#define DERIVED_SUBQUERY 1
-#define DERIVED_VIEW 2
+const LEX_CSTRING sp_data_access_name[]=
+{
+ { STRING_WITH_LEN("") },
+ { STRING_WITH_LEN("CONTAINS SQL") },
+ { STRING_WITH_LEN("NO SQL") },
+ { STRING_WITH_LEN("READS SQL DATA") },
+ { STRING_WITH_LEN("MODIFIES SQL DATA") }
+};
+
+#define DERIVED_SUBQUERY 1
+#define DERIVED_VIEW 2
#define DERIVED_WITH 4
enum enum_view_create_mode
{
- VIEW_CREATE_NEW, // check that there are not such VIEW/table
- VIEW_ALTER, // check that VIEW .frm with such name exists
- VIEW_CREATE_OR_REPLACE // check only that there are not such table
+ VIEW_CREATE_NEW, // check that there are not such VIEW/table
+ VIEW_ALTER, // check that VIEW .frm with such name exists
+ VIEW_CREATE_OR_REPLACE // check only that there are not such table
};
+
+class Create_view_info: public Sql_alloc
+{
+public:
+ LEX_CSTRING select; // The SELECT statement of CREATE VIEW
+ enum enum_view_create_mode mode;
+ uint16 algorithm;
+ uint8 check;
+ enum enum_view_suid suid;
+ Create_view_info(enum_view_create_mode mode_arg,
+ uint16 algorithm_arg,
+ enum_view_suid suid_arg)
+ :select(null_clex_str),
+ mode(mode_arg),
+ algorithm(algorithm_arg),
+ check(VIEW_CHECK_NONE),
+ suid(suid_arg)
+ { }
+};
+
+
enum enum_drop_mode
{
DROP_DEFAULT, // mode is not specified
@@ -200,10 +383,11 @@ enum enum_drop_mode
};
/* Options to add_table_to_list() */
-#define TL_OPTION_UPDATING 1
-#define TL_OPTION_FORCE_INDEX 2
+#define TL_OPTION_UPDATING 1
+#define TL_OPTION_FORCE_INDEX 2
#define TL_OPTION_IGNORE_LEAVES 4
#define TL_OPTION_ALIAS 8
+#define TL_OPTION_SEQUENCE 16
typedef List<Item> List_item;
typedef Mem_root_array<ORDER*, true> Group_list_ptrs;
@@ -212,11 +396,11 @@ typedef Mem_root_array<ORDER*, true> Group_list_ptrs;
typedef struct st_lex_server_options
{
long port;
- LEX_STRING server_name, host, db, username, password, scheme, socket, owner;
- void reset(LEX_STRING name)
+ LEX_CSTRING server_name, host, db, username, password, scheme, socket, owner;
+ void reset(LEX_CSTRING name)
{
server_name= name;
- host= db= username= password= scheme= socket= owner= null_lex_str;
+ host= db= username= password= scheme= socket= owner= null_clex_str;
port= -1;
}
} LEX_SERVER_OPTIONS;
@@ -234,13 +418,13 @@ struct LEX_MASTER_INFO
DYNAMIC_ARRAY repl_ignore_server_ids;
DYNAMIC_ARRAY repl_do_domain_ids;
DYNAMIC_ARRAY repl_ignore_domain_ids;
- char *host, *user, *password, *log_file_name;
- char *ssl_key, *ssl_cert, *ssl_ca, *ssl_capath, *ssl_cipher;
- char *ssl_crl, *ssl_crlpath;
- char *relay_log_name;
- LEX_STRING connection_name;
+ const char *host, *user, *password, *log_file_name;
+ const char *ssl_key, *ssl_cert, *ssl_ca, *ssl_capath, *ssl_cipher;
+ const char *ssl_crl, *ssl_crlpath;
+ const char *relay_log_name;
+ LEX_CSTRING connection_name;
/* Value in START SLAVE UNTIL master_gtid_pos=xxx */
- LEX_STRING gtid_pos_str;
+ LEX_CSTRING gtid_pos_str;
ulonglong pos;
ulong relay_log_pos;
ulong server_id;
@@ -286,7 +470,7 @@ struct LEX_MASTER_INFO
ssl= ssl_verify_server_cert= heartbeat_opt=
repl_ignore_server_ids_opt= repl_do_domain_ids_opt=
repl_ignore_domain_ids_opt= LEX_MI_UNCHANGED;
- gtid_pos_str= null_lex_str;
+ gtid_pos_str= null_clex_str;
use_gtid_opt= LEX_GTID_UNCHANGED;
sql_delay= -1;
}
@@ -297,12 +481,6 @@ typedef struct st_lex_reset_slave
bool all;
} LEX_RESET_SLAVE;
-enum sub_select_type
-{
- UNSPECIFIED_TYPE,UNION_TYPE, INTERSECT_TYPE,
- EXCEPT_TYPE, GLOBAL_OPTIONS_TYPE, DERIVED_TABLE_TYPE, OLAP_TYPE
-};
-
enum olap_type
{
UNSPECIFIED_OLAP_TYPE, CUBE_TYPE, ROLLUP_TYPE
@@ -340,10 +518,10 @@ public:
The index name. Empty (str=NULL) name represents an empty list
USE INDEX () clause
*/
- LEX_STRING key_name;
+ LEX_CSTRING key_name;
Index_hint (enum index_hint_type type_arg, index_clause_map clause_arg,
- char *str, uint length) :
+ const char *str, size_t length) :
type(type_arg), clause(clause_arg)
{
key_name.str= str;
@@ -481,6 +659,8 @@ protected:
st_select_lex_node *next, **prev, /* neighbor list */
*master, *slave, /* vertical links */
*link_next, **link_prev; /* list of whole SELECT_LEX */
+
+ void init_query_common();
public:
ulonglong options;
@@ -503,6 +683,10 @@ public:
*/
uint8 uncacheable;
enum sub_select_type linkage;
+ bool is_linkage_set() const
+ {
+ return linkage == UNION_TYPE || linkage == INTERSECT_TYPE || linkage == EXCEPT_TYPE;
+ }
bool no_table_names_allowed; /* used for global order by */
static void *operator new(size_t size, MEM_ROOT *mem_root) throw ()
@@ -518,11 +702,8 @@ public:
linkage(UNSPECIFIED_TYPE)
{
}
- virtual ~st_select_lex_node() {}
inline st_select_lex_node* get_master() { return master; }
- virtual void init_query();
- virtual void init_select();
void include_down(st_select_lex_node *upper);
void add_slave(st_select_lex_node *slave_arg);
void include_neighbour(st_select_lex_node *before);
@@ -531,23 +712,6 @@ public:
void exclude();
void exclude_from_tree();
- virtual st_select_lex* outer_select()= 0;
- virtual st_select_lex* return_after_parsing()= 0;
-
- virtual bool inc_in_sum_expr();
- virtual uint get_in_sum_expr();
- virtual TABLE_LIST* get_table_list();
- virtual List<Item>* get_item_list();
- virtual ulong get_table_join_options();
- virtual TABLE_LIST *add_table_to_list(THD *thd, Table_ident *table,
- LEX_STRING *alias,
- ulong table_options,
- thr_lock_type flags= TL_UNLOCK,
- enum_mdl_type mdl_type= MDL_SHARED_READ,
- List<Index_hint> *hints= 0,
- List<String> *partition_names= 0,
- LEX_STRING *option= 0);
- virtual void set_lock_for_tables(thr_lock_type lock_type, bool for_update) {}
void set_slave(st_select_lex_node *slave_arg) { slave= slave_arg; }
void move_node(st_select_lex_node *where_to_move)
{
@@ -563,14 +727,9 @@ public:
st_select_lex_node *end_chain_node);
void move_as_slave(st_select_lex_node *new_master);
friend class st_select_lex_unit;
- friend bool mysql_new_select(LEX *lex, bool move_down);
+ friend bool mysql_new_select(LEX *lex, bool move_down, SELECT_LEX *sel);
friend bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
bool open_view_no_parse);
- friend bool mysql_derived_prepare(THD *thd, LEX *lex,
- TABLE_LIST *orig_table_list);
- friend bool mysql_derived_merge(THD *thd, LEX *lex,
- TABLE_LIST *orig_table_list);
- friend bool TABLE_LIST::init_derived(THD *thd, bool init_view);
private:
void fast_exclude();
};
@@ -583,7 +742,7 @@ typedef class st_select_lex_node SELECT_LEX_NODE;
class THD;
class select_result;
class JOIN;
-class select_union;
+class select_unit;
class Procedure;
class Explain_query;
@@ -595,10 +754,18 @@ 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;
- select_union *union_result;
+ select_unit *union_result;
ulonglong found_rows_for_union;
bool saved_error;
+ bool prepare_join(THD *thd, SELECT_LEX *sl, select_result *result,
+ ulong additional_options,
+ bool is_union_select);
+ bool join_union_item_types(THD *thd, List<Item> &types, uint count);
+ bool join_union_type_handlers(THD *thd,
+ class Type_holder *holders, uint count);
+ bool join_union_type_attributes(THD *thd,
+ class Type_holder *holders, uint count);
public:
// Ensures that at least all members used during cleanup() are initialized.
st_select_lex_unit()
@@ -613,6 +780,7 @@ public:
select_result *result;
bool prepared, // prepare phase already performed for UNION (unit)
optimized, // optimize phase already performed for UNION (unit)
+ optimized_2,
executed, // already executed
cleaned;
@@ -629,6 +797,17 @@ 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
+ */
+ bool with_wrapped_tvc;
+ /**
Pointer to 'last' select, or pointer to select where we stored
global parameters for union.
@@ -669,7 +848,7 @@ public:
/* thread handler */
THD *thd;
/*
- SELECT_LEX for hidden SELECT in onion which process global
+ SELECT_LEX for hidden SELECT in union which process global
ORDER BY and LIMIT
*/
st_select_lex *fake_select_lex;
@@ -681,7 +860,7 @@ public:
st_select_lex *union_distinct; /* pointer to the last UNION DISTINCT */
bool describe; /* union exec() called for EXPLAIN */
- Procedure *last_procedure; /* Pointer to procedure, if such exists */
+ Procedure *last_procedure; /* Pointer to procedure, if such exists */
bool columns_are_renamed;
@@ -691,7 +870,7 @@ public:
{
return reinterpret_cast<st_select_lex*>(slave);
}
- void set_with_clause(With_clause *with_cl);
+ inline void set_with_clause(With_clause *with_cl);
st_select_lex_unit* next_unit()
{
return reinterpret_cast<st_select_lex_unit*>(next);
@@ -702,7 +881,8 @@ public:
bool is_excluded() { return prev == NULL; }
/* UNION methods */
- bool prepare(THD *thd, select_result *result, ulong additional_options);
+ bool prepare(TABLE_LIST *derived_arg, select_result *sel_result,
+ ulong additional_options);
bool optimize();
bool exec();
bool exec_recursive();
@@ -719,19 +899,21 @@ public:
select_result_interceptor *old_result);
void set_limit(st_select_lex *values);
void set_thd(THD *thd_arg) { thd= thd_arg; }
- inline bool is_union ();
+ inline bool is_unit_op ();
bool union_needs_tmp_table();
void set_unique_exclude();
+ bool check_distinct_in_union();
- friend void lex_start(THD *thd);
+ friend struct LEX;
friend int subselect_union_engine::exec();
List<Item> *get_column_types(bool for_cursor);
- select_union *get_union_result() { return union_result; }
+ select_unit *get_union_result() { return union_result; }
int save_union_explain(Explain_query *output);
int save_union_explain_part2(Explain_query *output);
+ unit_common_op common_op();
};
typedef class st_select_lex_unit SELECT_LEX_UNIT;
@@ -763,7 +945,7 @@ class st_select_lex: public st_select_lex_node
{
public:
Name_resolution_context context;
- char *db;
+ LEX_CSTRING db;
Item *where, *having; /* WHERE & HAVING clauses */
Item *prep_where; /* saved WHERE clause for prepared statement processing */
Item *prep_having;/* saved HAVING clause for prepared statement processing */
@@ -794,7 +976,7 @@ public:
List<Item> item_list; /* list of fields & expressions */
List<Item> pre_fix; /* above list before fix_fields */
- bool is_item_list_lookup;
+ bool is_item_list_lookup;
/*
Usualy it is pointer to ftfunc_list_alloc, but in union used to create fake
select_lex for calling mysql_select under results of union
@@ -825,6 +1007,17 @@ public:
those converted to jtbm nests. The list is emptied when conversion is done.
*/
List<Item_in_subselect> sj_subselects;
+ /*
+ List of IN-predicates in this st_select_lex that
+ can be transformed into IN-subselect defined with TVC.
+ */
+ List<Item_func_in> in_funcs;
+ /*
+ Number of current derived table made with TVC during the
+ transformation of IN-predicate into IN-subquery for this
+ st_select_lex.
+ */
+ uint curr_tvc_name;
/*
Needed to correctly generate 'PRIMARY' or 'SIMPLE' for select_type column
@@ -896,7 +1089,8 @@ public:
int nest_level; /* nesting level of select */
Item_sum *inner_sum_func_list; /* list of sum func in nested selects */
uint with_wild; /* item list contain '*' */
- bool braces; /* SELECT ... UNION (SELECT ... ) <- this braces */
+ bool braces; /* SELECT ... UNION (SELECT ... ) <- this braces */
+ bool automatic_brackets; /* dummy select for INTERSECT precedence */
/* TRUE when having fix field called in processing of this SELECT */
bool having_fix_field;
/*
@@ -980,6 +1174,16 @@ public:
/* it is for correct printing SELECT options */
thr_lock_type lock_type;
+
+ table_value_constr *tvc;
+ bool in_tvc;
+
+ /** System Versioning */
+public:
+ uint versioned_tables;
+ int vers_setup_conds(THD *thd, TABLE_LIST *tables);
+ /* push new Item_field into item_list */
+ bool vers_push_field(THD *thd, TABLE_LIST *table, const LEX_CSTRING field_name);
void init_query();
void init_select();
@@ -1019,11 +1223,11 @@ public:
bool add_order_to_list(THD *thd, Item *item, bool asc);
bool add_gorder_to_list(THD *thd, Item *item, bool asc);
TABLE_LIST* add_table_to_list(THD *thd, Table_ident *table,
- LEX_STRING *alias,
- ulong table_options,
- thr_lock_type flags= TL_UNLOCK,
+ LEX_CSTRING *alias,
+ ulong table_options,
+ thr_lock_type flags= TL_UNLOCK,
enum_mdl_type mdl_type= MDL_SHARED_READ,
- List<Index_hint> *hints= 0,
+ List<Index_hint> *hints= 0,
List<String> *partition_names= 0,
LEX_STRING *option= 0);
TABLE_LIST* get_table_list();
@@ -1068,8 +1272,8 @@ public:
*/
ha_rows get_limit();
- friend void lex_start(THD *thd);
- st_select_lex() : group_list_ptrs(NULL), braces(0),
+ friend struct LEX;
+ st_select_lex() : group_list_ptrs(NULL), braces(0), automatic_brackets(0),
n_sum_items(0), n_child_sum_items(0)
{}
void make_empty_select()
@@ -1101,7 +1305,7 @@ public:
Add a index hint to the tagged list of hints. The type and clause of the
hint will be the current ones (set by set_index_hint())
*/
- bool add_index_hint (THD *thd, char *str, uint length);
+ bool add_index_hint (THD *thd, const char *str, size_t length);
/* make a list to hold index hints */
void alloc_index_hints (THD *thd);
@@ -1114,7 +1318,7 @@ public:
}
void clear_index_hints(void) { index_hints= NULL; }
- bool is_part_of_union() { return master_unit()->is_union(); }
+ bool is_part_of_union() { return master_unit()->is_unit_op(); }
bool is_top_level_node()
{
return (select_number == 1) && !is_part_of_union();
@@ -1156,10 +1360,12 @@ public:
*/
bool non_agg_field_used() const { return m_non_agg_field_used; }
bool agg_func_used() const { return m_agg_func_used; }
+ bool custom_agg_func_used() const { return m_custom_agg_func_used; }
void set_non_agg_field_used(bool val) { m_non_agg_field_used= val; }
void set_agg_func_used(bool val) { m_agg_func_used= val; }
- void set_with_clause(With_clause *with_clause);
+ void set_custom_agg_func_used(bool val) { m_custom_agg_func_used= val; }
+ inline void set_with_clause(With_clause *with_clause);
With_clause *get_with_clause()
{
return master_unit()->with_clause;
@@ -1171,19 +1377,19 @@ public:
With_element *find_table_def_in_with_clauses(TABLE_LIST *table);
bool check_unrestricted_recursive(bool only_standard_compliant);
bool check_subqueries_with_recursive_references();
- void collect_grouping_fields(THD *thd);
+ void collect_grouping_fields(THD *thd, ORDER *grouping_list);
void check_cond_extraction_for_grouping_fields(Item *cond,
TABLE_LIST *derived);
Item *build_cond_for_grouping_fields(THD *thd, Item *cond,
- bool no_to_clones);
+ bool no_to_clones);
List<Window_spec> window_specs;
void prepare_add_window_spec(THD *thd);
- bool add_window_def(THD *thd, LEX_STRING *win_name, LEX_STRING *win_ref,
+ bool add_window_def(THD *thd, LEX_CSTRING *win_name, LEX_CSTRING *win_ref,
SQL_I_List<ORDER> win_partition_list,
SQL_I_List<ORDER> win_order_list,
Window_frame *win_frame);
- bool add_window_spec(THD *thd, LEX_STRING *win_ref,
+ bool add_window_spec(THD *thd, LEX_CSTRING *win_ref,
SQL_I_List<ORDER> win_partition_list,
SQL_I_List<ORDER> win_order_list,
Window_frame *win_frame);
@@ -1191,13 +1397,15 @@ public:
bool add_window_func(Item_window_func *win_func);
bool have_window_funcs() const { return (window_funcs.elements !=0); }
+ ORDER *find_common_window_func_partition_fields(THD *thd);
bool cond_pushdown_is_allowed() const
- { return !have_window_funcs() && !olap && !explicit_limit; }
+ { return !olap && !explicit_limit && !tvc; }
private:
bool m_non_agg_field_used;
bool m_agg_func_used;
+ bool m_custom_agg_func_used;
/* current index hint kind. used in filling up index_hints */
enum index_hint_type current_index_hint_type;
@@ -1214,22 +1422,41 @@ public:
};
typedef class st_select_lex SELECT_LEX;
-inline bool st_select_lex_unit::is_union ()
-{
- return first_select()->next_select() &&
- first_select()->next_select()->linkage == UNION_TYPE;
+inline bool st_select_lex_unit::is_unit_op ()
+{
+ if (!first_select()->next_select())
+ {
+ if (first_select()->tvc)
+ return 1;
+ else
+ return 0;
+ }
+
+ enum sub_select_type linkage= first_select()->next_select()->linkage;
+ return linkage == UNION_TYPE || linkage == INTERSECT_TYPE ||
+ linkage == EXCEPT_TYPE;
}
struct st_sp_chistics
{
- LEX_STRING comment;
+ LEX_CSTRING comment;
enum enum_sp_suid_behaviour suid;
bool detistic;
enum enum_sp_data_access daccess;
+ enum enum_sp_aggregate_type agg_type;
+ void init() { bzero(this, sizeof(*this)); }
+ void set(const st_sp_chistics &other) { *this= other; }
+ bool read_from_mysql_proc_row(THD *thd, TABLE *table);
};
+class Sp_chistics: public st_sp_chistics
+{
+public:
+ Sp_chistics() { init(); }
+};
+
struct st_trg_chistics: public st_trg_execution_order
{
@@ -1241,8 +1468,6 @@ struct st_trg_chistics: public st_trg_execution_order
};
-extern sys_var *trg_new_row_fake_var;
-
enum xa_option_words {XA_NONE, XA_JOIN, XA_RESUME, XA_ONE_PHASE,
XA_SUSPEND, XA_FOR_MIGRATE};
@@ -1777,6 +2002,14 @@ public:
((1U << STMT_READS_TEMP_TRANS_TABLE) |
(1U << STMT_WRITES_TEMP_TRANS_TABLE))) != 0);
}
+ inline bool stmt_writes_to_non_temp_table()
+ {
+ DBUG_ENTER("THD::stmt_writes_to_non_temp_table");
+
+ DBUG_RETURN((stmt_accessed_table_flag &
+ ((1U << STMT_WRITES_TRANS_TABLE) |
+ (1U << STMT_WRITES_NON_TRANS_TABLE))));
+ }
/**
Checks if a temporary non-transactional table is about to be accessed
@@ -1828,7 +2061,7 @@ public:
unsafe= (binlog_unsafe_map[stmt_accessed_table_flag] & condition);
#if !defined(DBUG_OFF)
- DBUG_PRINT("LEX::is_mixed_stmt_unsafe", ("RESULT %02X %02X %02X\n", condition,
+ DBUG_PRINT("LEX::is_mixed_stmt_unsafe", ("RESULT %02X %02X %02X", condition,
binlog_unsafe_map[stmt_accessed_table_flag],
(binlog_unsafe_map[stmt_accessed_table_flag] & condition)));
@@ -1912,6 +2145,7 @@ private:
struct st_parsing_options
{
bool allows_variable;
+ bool lookup_keywords_after_qualifier;
st_parsing_options() { reset(); }
void reset();
@@ -1976,10 +2210,20 @@ public:
@retval FALSE OK
@retval TRUE Error
*/
- bool init(THD *thd, char *buff, unsigned int length);
+ bool init(THD *thd, char *buff, size_t length);
+
+ void reset(char *buff, size_t length);
+
+ /**
+ The main method to scan the next token, with token contraction processing
+ for LALR(2) resolution, e.g. translate "WITH" followed by "ROLLUP"
+ to a single token WITH_ROLLUP_SYM.
+ */
+ int lex_token(union YYSTYPE *yylval, THD *thd);
- void reset(char *buff, unsigned int length);
+ void reduce_digest_token(uint token_left, uint token_right);
+private:
/**
Set the echo mode.
@@ -2109,15 +2353,6 @@ public:
/**
End of file indicator for the query text to parse.
- @return true if there are no more characters to parse
- */
- bool eof()
- {
- return (m_ptr >= m_end_of_query);
- }
-
- /**
- End of file indicator for the query text to parse.
@param n number of characters expected
@return true if there are less than n characters to parse
*/
@@ -2126,24 +2361,6 @@ public:
return ((m_ptr + n) >= m_end_of_query);
}
- /** Get the raw query buffer. */
- const char *get_buf()
- {
- return m_buf;
- }
-
- /** Get the pre-processed query buffer. */
- const char *get_cpp_buf()
- {
- return m_cpp_buf;
- }
-
- /** Get the end of the raw query buffer. */
- const char *get_end_of_query()
- {
- return m_end_of_query;
- }
-
/** Mark the stream position as the start of a new token. */
void start_token()
{
@@ -2166,10 +2383,65 @@ public:
m_cpp_tok_start= m_cpp_ptr;
}
+ /**
+ Get the maximum length of the utf8-body buffer.
+ The utf8 body can grow because of the character set conversion and escaping.
+ */
+ size_t get_body_utf8_maximum_length(THD *thd);
+
+ /** Get the length of the current token, in the raw buffer. */
+ uint yyLength()
+ {
+ /*
+ The assumption is that the lexical analyser is always 1 character ahead,
+ which the -1 account for.
+ */
+ DBUG_ASSERT(m_ptr > m_tok_start);
+ return (uint) ((m_ptr - m_tok_start) - 1);
+ }
+
+ /**
+ Test if a lookahead token was already scanned by lex_token(),
+ for LALR(2) resolution.
+ */
+ bool has_lookahead() const
+ {
+ return lookahead_token >= 0;
+ }
+
+public:
+
+ /**
+ End of file indicator for the query text to parse.
+ @return true if there are no more characters to parse
+ */
+ bool eof()
+ {
+ return (m_ptr >= m_end_of_query);
+ }
+
+ /** Get the raw query buffer. */
+ const char *get_buf()
+ {
+ return m_buf;
+ }
+
+ /** Get the pre-processed query buffer. */
+ const char *get_cpp_buf()
+ {
+ return m_cpp_buf;
+ }
+
+ /** Get the end of the raw query buffer. */
+ const char *get_end_of_query()
+ {
+ return m_end_of_query;
+ }
+
/** Get the token start position, in the raw buffer. */
const char *get_tok_start()
{
- return m_tok_start;
+ return has_lookahead() ? m_tok_start_prev : m_tok_start;
}
void set_cpp_tok_start(const char *pos)
@@ -2183,39 +2455,16 @@ public:
return m_tok_end;
}
- /** Get the previous token start position, in the raw buffer. */
- const char *get_tok_start_prev()
- {
- return m_tok_start_prev;
- }
-
/** Get the current stream pointer, in the raw buffer. */
const char *get_ptr()
{
return m_ptr;
}
- /** Get the length of the current token, in the raw buffer. */
- uint yyLength()
- {
- /*
- The assumption is that the lexical analyser is always 1 character ahead,
- which the -1 account for.
- */
- DBUG_ASSERT(m_ptr > m_tok_start);
- return (uint) ((m_ptr - m_tok_start) - 1);
- }
-
- /** Get the previus token start position, in the pre-processed buffer. */
- const char *get_cpp_start_prev()
- {
- return m_cpp_tok_start_prev;
- }
-
/** Get the token start position, in the pre-processed buffer. */
const char *get_cpp_tok_start()
{
- return m_cpp_tok_start;
+ return has_lookahead() ? m_cpp_tok_start_prev : m_cpp_tok_start;
}
/** Get the token end position, in the pre-processed buffer. */
@@ -2224,12 +2473,39 @@ public:
return m_cpp_tok_end;
}
+ /**
+ Get the token end position in the pre-processed buffer,
+ with trailing spaces removed.
+ */
+ const char *get_cpp_tok_end_rtrim()
+ {
+ const char *p;
+ for (p= m_cpp_tok_end;
+ p > m_cpp_buf && my_isspace(system_charset_info, p[-1]);
+ p--)
+ { }
+ return p;
+ }
+
/** Get the current stream pointer, in the pre-processed buffer. */
const char *get_cpp_ptr()
{
return m_cpp_ptr;
}
+ /**
+ Get the current stream pointer, in the pre-processed buffer,
+ with traling spaces removed.
+ */
+ const char *get_cpp_ptr_rtrim()
+ {
+ const char *p;
+ for (p= m_cpp_ptr;
+ p > m_cpp_buf && my_isspace(system_charset_info, p[-1]);
+ p--)
+ { }
+ return p;
+ }
/** Get the utf8-body string. */
const char *get_body_utf8_str()
{
@@ -2237,37 +2513,24 @@ public:
}
/** Get the utf8-body length. */
- uint get_body_utf8_length()
+ size_t get_body_utf8_length()
{
- return (uint) (m_body_utf8_ptr - m_body_utf8);
+ return (size_t) (m_body_utf8_ptr - m_body_utf8);
}
- /**
- Get the maximum length of the utf8-body buffer.
- The utf8 body can grow because of the character set conversion and escaping.
- */
- uint get_body_utf8_maximum_length(THD *thd);
-
void body_utf8_start(THD *thd, const char *begin_ptr);
void body_utf8_append(const char *ptr);
void body_utf8_append(const char *ptr, const char *end_ptr);
void body_utf8_append_ident(THD *thd,
- const LEX_STRING *txt,
+ const Lex_string_with_metadata_st *txt,
const char *end_ptr);
void body_utf8_append_escape(THD *thd,
- const LEX_STRING *txt,
+ const LEX_CSTRING *txt,
CHARSET_INFO *txt_cs,
const char *end_ptr,
my_wc_t sep);
- /** Current thread. */
- THD *m_thd;
-
- /** Current line number. */
- uint yylineno;
-
- /** Interface with bison, value of the last token parsed. */
- LEX_YYSTYPE yylval;
+private:
/**
LALR(2) resolution, look ahead token.
Value of the next token to return, if any,
@@ -2279,13 +2542,25 @@ public:
/** LALR(2) resolution, value of the look ahead token.*/
LEX_YYSTYPE lookahead_yylval;
- bool get_text(LEX_STRING *to, uint sep, int pre_skip, int post_skip);
+ bool get_text(Lex_string_with_metadata_st *to,
+ uint sep, int pre_skip, int post_skip);
void add_digest_token(uint token, LEX_YYSTYPE yylval);
- void reduce_digest_token(uint token_left, uint token_right);
+ bool consume_comment(int remaining_recursions_permitted);
+ int lex_one_token(union YYSTYPE *yylval, THD *thd);
+ int find_keyword(Lex_ident_cli_st *str, uint len, bool function);
+ LEX_CSTRING get_token(uint skip, uint length);
+ int scan_ident_sysvar(THD *thd, Lex_ident_cli_st *str);
+ int scan_ident_start(THD *thd, Lex_ident_cli_st *str);
+ int scan_ident_middle(THD *thd, Lex_ident_cli_st *str,
+ CHARSET_INFO **cs, my_lex_states *);
+ int scan_ident_delimited(THD *thd, Lex_ident_cli_st *str);
+ bool get_7bit_or_8bit_ident(THD *thd, uchar *last_char);
+
+ /** Current thread. */
+ THD *m_thd;
-private:
/** Pointer to the current position in the raw input stream. */
char *m_ptr;
@@ -2305,7 +2580,7 @@ private:
const char *m_buf;
/** Length of the raw buffer. */
- uint m_buf_length;
+ size_t m_buf_length;
/** Echo the parsed stream to the pre-processed buffer. */
bool m_echo;
@@ -2358,9 +2633,6 @@ public:
*/
const char *found_semicolon;
- /** Token character bitmaps, to detect 7bit strings. */
- uchar tok_bitmap;
-
/** SQL_MODE = IGNORE_SPACE. */
bool ignore_space;
@@ -2374,6 +2646,15 @@ public:
*/
bool multi_statements;
+ /** Current line number. */
+ uint yylineno;
+
+ /**
+ Current statement digest instrumentation.
+ */
+ sql_digest_state* m_digest;
+
+private:
/** State of the lexical analyser for comments. */
enum_comment_state in_comment;
enum_comment_state in_comment_saved;
@@ -2400,13 +2681,9 @@ public:
NOTE: this member must be used within MYSQLlex() function only.
*/
CHARSET_INFO *m_underscore_cs;
-
- /**
- Current statement digest instrumentation.
- */
- sql_digest_state* m_digest;
};
+
/**
Abstract representation of a statement.
This class is an interface between the parser and the runtime.
@@ -2515,7 +2792,7 @@ public:
Explain_update* save_explain_update_data(MEM_ROOT *mem_root, THD *thd);
protected:
- void save_explain_data_intern(MEM_ROOT *mem_root, Explain_update *eu, bool is_analyze);
+ bool save_explain_data_intern(MEM_ROOT *mem_root, Explain_update *eu, bool is_analyze);
public:
virtual ~Update_plan() {}
@@ -2578,7 +2855,9 @@ struct LEX: public Query_tables_list
When a copy of a with element is parsed this is set to the offset of
the with element in the input string, otherwise it's set to 0
*/
- uint clone_spec_offset;
+ my_ptrdiff_t clone_spec_offset;
+
+ Create_view_info *create_view;
/* Query Plan Footprint of a currently running select */
Explain_query *explain;
@@ -2601,11 +2880,11 @@ struct LEX: public Query_tables_list
*/
LEX *stmt_lex;
- LEX_STRING name;
- char *help_arg;
- char *backup_dir; /* For RESTORE/BACKUP */
- char* to_log; /* For PURGE MASTER LOGS TO */
- char* x509_subject,*x509_issuer,*ssl_cipher;
+ LEX_CSTRING name;
+ const char *help_arg;
+ const char *backup_dir; /* For RESTORE/BACKUP */
+ const char* to_log; /* For PURGE MASTER LOGS TO */
+ const char* x509_subject,*x509_issuer,*ssl_cipher;
String *wild; /* Wildcard in SHOW {something} LIKE 'wild'*/
sql_exchange *exchange;
select_result *result;
@@ -2613,7 +2892,7 @@ struct LEX: public Query_tables_list
@c the two may also hold BINLOG arguments: either comment holds a
base64-char string or both represent the BINLOG fragment user variables.
*/
- LEX_STRING comment, ident;
+ LEX_CSTRING comment, ident;
LEX_USER *grant_user;
XID *xid;
THD *thd;
@@ -2622,8 +2901,6 @@ struct LEX: public Query_tables_list
DYNAMIC_ARRAY plugins;
plugin_ref plugins_static_buffer[INITIAL_LEX_PLUGIN_LIST_SIZE];
- bool text_string_is_7bit;
-
/** SELECT of CREATE VIEW statement */
LEX_STRING create_view_select;
@@ -2643,10 +2920,11 @@ struct LEX: public Query_tables_list
*/
LEX_USER *definer;
+ 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<Item> *insert_list,field_list,value_list,update_list;
List<List_item> many_values;
List<set_var_base> var_list;
List<set_var_base> stmt_var_list; //SET_STATEMENT values
@@ -2654,8 +2932,20 @@ struct LEX: public Query_tables_list
private:
Query_arena_memroot *arena_for_set_stmt;
MEM_ROOT *mem_root_for_set_stmt;
- void parse_error();
+ bool sp_block_finalize(THD *thd, const Lex_spblock_st spblock,
+ class sp_label **splabel);
+ bool sp_change_context(THD *thd, const sp_pcontext *ctx, bool exclusive);
+ bool sp_exit_block(THD *thd, sp_label *lab);
+ 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);
+
public:
+ void parse_error(uint err_number= ER_SYNTAX_ERROR);
inline bool is_arena_for_set_stmt() {return arena_for_set_stmt != 0;}
bool set_arena_for_set_stmt(Query_arena *backup);
void reset_arena_for_set_stmt(Query_arena *backup);
@@ -2663,8 +2953,8 @@ public:
List<Item_func_set_user_var> set_var_list; // in-query assignment list
List<Item_param> param_list;
- List<LEX_STRING> view_list; // view list (list of field names in view)
- List<LEX_STRING> with_column_list; // list of column names in with_list_element
+ 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)
/*
@@ -2687,12 +2977,12 @@ public:
Column_definition *last_field;
Item_sum *in_sum_func;
udf_func udf;
- HA_CHECK_OPT check_opt; // check/repair options
+ HA_CHECK_OPT check_opt; // check/repair options
Table_specification_st create_info;
Key *last_key;
- LEX_MASTER_INFO mi; // used by CHANGE MASTER
+ LEX_MASTER_INFO mi; // used by CHANGE MASTER
LEX_SERVER_OPTIONS server_options;
- LEX_STRING relay_log_connection_name;
+ LEX_CSTRING relay_log_connection_name;
USER_RESOURCES mqh;
LEX_RESET_SLAVE reset_slave_info;
ulonglong type;
@@ -2729,7 +3019,7 @@ public:
*/
bool parse_vcol_expr;
- enum SSL_type ssl_type; /* defined in violite.h */
+ enum SSL_type ssl_type; // defined in violite.h
enum enum_duplicates duplicates;
enum enum_tx_isolation tx_isolation;
enum enum_ha_read_modes ha_read_mode;
@@ -2740,7 +3030,6 @@ public:
bool with_persistent_for_clause; // uses PERSISTENT FOR clause (in ANALYZE)
};
enum enum_var_type option_type;
- enum enum_view_create_mode create_view_mode;
enum enum_drop_mode drop_mode;
uint profile_query_id;
@@ -2766,8 +3055,6 @@ public:
DERIVED_SUBQUERY and DERIVED_VIEW).
*/
uint8 derived_tables;
- uint16 create_view_algorithm;
- uint8 create_view_check;
uint8 context_analysis_only;
bool local_file;
bool check_exists;
@@ -2776,7 +3063,7 @@ public:
enum enum_yes_no_unknown tx_chain, tx_release;
bool safe_to_cache_query;
- bool subqueries, ignore;
+ bool ignore;
st_parsing_options parsing_options;
Alter_info alter_info;
/*
@@ -2786,16 +3073,15 @@ public:
*/
TABLE_LIST *create_last_non_select_table;
/* Prepared statements SQL syntax:*/
- LEX_STRING prepared_stmt_name; /* Statement name (in all queries) */
+ LEX_CSTRING prepared_stmt_name; /* Statement name (in all queries) */
/* PREPARE or EXECUTE IMMEDIATE source expression */
Item *prepared_stmt_code;
/* Names of user variables holding parameters (in EXECUTE) */
List<Item> prepared_stmt_params;
sp_head *sphead;
sp_name *spname;
- bool sp_lex_in_use; /* Keep track on lex usage in SPs for error handling */
+ bool sp_lex_in_use; // Keep track on lex usage in SPs for error handling
bool all_privileges;
- bool proxy_priv;
sp_pcontext *spcont;
@@ -2803,16 +3089,11 @@ public:
Event_parse_data *event_parse_data;
- bool only_view; /* used for SHOW CREATE TABLE/VIEW */
/*
field_list was created for view and should be removed before PS/SP
rexecuton
*/
bool empty_field_list_on_rset;
- /*
- view created to be run from definer (standard behaviour)
- */
- uint8 create_view_suid;
/* Characterstics of trigger being created */
st_trg_chistics trg_chistics;
/*
@@ -2877,6 +3158,7 @@ public:
st_alter_tablespace *alter_tablespace_info;
bool escape_used;
+ bool default_used; /* using default() function */
bool is_lex_started; /* If lex_start() did run. For debugging. */
/*
@@ -2915,12 +3197,15 @@ public:
SQL_I_List<ORDER> save_group_list;
SQL_I_List<ORDER> save_order_list;
- LEX_STRING *win_ref;
+ LEX_CSTRING *win_ref;
Window_frame *win_frame;
Window_frame_bound *frame_top_bound;
Window_frame_bound *frame_bottom_bound;
Window_spec *win_spec;
+ /* System Versioning */
+ vers_select_conds_t vers_conditions;
+
inline void free_set_stmt_mem_root()
{
DBUG_ASSERT(!is_arena_for_set_stmt());
@@ -2942,6 +3227,14 @@ public:
delete_dynamic(&plugins);
}
+ virtual class Query_arena *query_arena()
+ {
+ DBUG_ASSERT(0);
+ return NULL;
+ }
+
+ void start(THD *thd);
+
inline bool is_ps_or_view_context_analysis()
{
return (context_analysis_only &
@@ -3030,7 +3323,7 @@ public:
return context_stack.pop();
}
- bool copy_db_to(char **p_db, size_t *p_db_length) const;
+ bool copy_db_to(LEX_CSTRING *to);
Name_resolution_context *current_context()
{
@@ -3045,7 +3338,10 @@ public:
void restore_backup_query_tables_list(Query_tables_list *backup);
bool table_or_sp_used();
+
bool is_partition_management() const;
+ bool part_values_current(THD *thd);
+ bool part_values_history(THD *thd);
/**
@brief check if the statement is a single-level join
@@ -3075,8 +3371,13 @@ public:
bool is_analyze, bool *printed_anything);
void restore_set_statement_var();
- void init_last_field(Column_definition *field, const char *name, CHARSET_INFO *cs);
- void set_last_field_type(const Lex_field_type_st &type);
+ void init_last_field(Column_definition *field, const LEX_CSTRING *name,
+ const CHARSET_INFO *cs);
+ bool last_field_generated_always_as_row_start_or_end(Lex_ident *p,
+ const char *type,
+ uint flags);
+ bool last_field_generated_always_as_row_start();
+ bool last_field_generated_always_as_row_end();
bool set_bincmp(CHARSET_INFO *cs, bool bin);
bool get_dynamic_sql_string(LEX_CSTRING *dst, String *buffer);
@@ -3086,11 +3387,484 @@ public:
List_iterator_fast<Item> param_it(prepared_stmt_params);
while (Item *param= param_it++)
{
- if (param->fix_fields(thd, 0) || param->check_cols(1))
+ if (param->fix_fields_if_needed_for_scalar(thd, 0))
return true;
}
return false;
}
+ sp_variable *sp_param_init(LEX_CSTRING *name);
+ bool sp_param_fill_definition(sp_variable *spvar);
+
+ int case_stmt_action_expr(Item* expr);
+ int case_stmt_action_when(Item *when, bool simple);
+ int case_stmt_action_then();
+ bool add_select_to_union_list(bool is_union_distinct,
+ enum sub_select_type type,
+ bool is_top_level);
+ 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);
+ bool set_system_variable(THD *thd, enum_var_type var_type,
+ const LEX_CSTRING *name1,
+ const LEX_CSTRING *name2,
+ Item *val);
+ bool set_default_system_variable(enum_var_type var_type,
+ const LEX_CSTRING *name,
+ Item *val);
+ bool set_user_variable(THD *thd, const LEX_CSTRING *name, Item *val);
+ void set_stmt_init();
+ sp_name *make_sp_name(THD *thd, const LEX_CSTRING *name);
+ sp_name *make_sp_name(THD *thd, const LEX_CSTRING *name1,
+ const LEX_CSTRING *name2);
+ sp_name *make_sp_name_package_routine(THD *thd, const LEX_CSTRING *name);
+ sp_head *make_sp_head(THD *thd, const sp_name *name, const Sp_handler *sph);
+ sp_head *make_sp_head_no_recursive(THD *thd, const sp_name *name,
+ const Sp_handler *sph);
+ sp_head *make_sp_head_no_recursive(THD *thd,
+ DDL_options_st options, sp_name *name,
+ const Sp_handler *sph)
+ {
+ if (add_create_options_with_check(options))
+ return NULL;
+ return make_sp_head_no_recursive(thd, name, sph);
+ }
+ bool sp_body_finalize_function(THD *);
+ bool sp_body_finalize_procedure(THD *);
+ sp_package *create_package_start(THD *thd,
+ enum_sql_command command,
+ const Sp_handler *sph,
+ const sp_name *name,
+ DDL_options_st options);
+ bool create_package_finalize(THD *thd,
+ const sp_name *name,
+ const sp_name *name2,
+ 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);
+ sp_variable *find_variable(const LEX_CSTRING *name,
+ sp_pcontext **ctx,
+ const Sp_rcontext_handler **rh) const;
+ sp_variable *find_variable(const LEX_CSTRING *name,
+ const Sp_rcontext_handler **rh) const
+ {
+ 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);
+ void sp_variable_declarations_init(THD *thd, int nvars);
+ bool sp_variable_declarations_finalize(THD *thd, int nvars,
+ const Column_definition *cdef,
+ Item *def);
+ bool sp_variable_declarations_set_default(THD *thd, int nvars, Item *def);
+ bool sp_variable_declarations_row_finalize(THD *thd, int nvars,
+ Row_definition_list *row,
+ Item *def);
+ bool sp_variable_declarations_with_ref_finalize(THD *thd, int nvars,
+ Qualified_column_ident *col,
+ Item *def);
+ bool sp_variable_declarations_rowtype_finalize(THD *thd, int nvars,
+ Qualified_column_ident *,
+ Item *def);
+ bool sp_variable_declarations_cursor_rowtype_finalize(THD *thd, int nvars,
+ uint offset,
+ Item *def);
+ bool sp_variable_declarations_table_rowtype_finalize(THD *thd, int nvars,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &table,
+ Item *def);
+ bool sp_variable_declarations_column_type_finalize(THD *thd, int nvars,
+ Qualified_column_ident *ref,
+ Item *def);
+ bool sp_variable_declarations_vartype_finalize(THD *thd, int nvars,
+ const LEX_CSTRING &name,
+ Item *def);
+ bool sp_variable_declarations_copy_type_finalize(THD *thd, int nvars,
+ const Column_definition &ref,
+ Row_definition_list *fields,
+ Item *def);
+ bool sp_handler_declaration_init(THD *thd, int type);
+ bool sp_handler_declaration_finalize(THD *thd, int type);
+
+ bool sp_declare_cursor(THD *thd, const LEX_CSTRING *name,
+ class sp_lex_cursor *cursor_stmt,
+ sp_pcontext *param_ctx, bool add_cpush_instr);
+
+ bool sp_open_cursor(THD *thd, const LEX_CSTRING *name,
+ List<sp_assignment_lex> *parameters);
+ Item_splocal *create_item_for_sp_var(const Lex_ident_cli_st *name,
+ sp_variable *spvar);
+
+ Item *create_item_qualified_asterisk(THD *thd, const Lex_ident_sys_st *name);
+ Item *create_item_qualified_asterisk(THD *thd,
+ const Lex_ident_sys_st *a,
+ const Lex_ident_sys_st *b);
+ Item *create_item_qualified_asterisk(THD *thd, const Lex_ident_cli_st *cname)
+ {
+ Lex_ident_sys name(thd, cname);
+ if (name.is_null())
+ return NULL; // EOM
+ return create_item_qualified_asterisk(thd, &name);
+ }
+ Item *create_item_qualified_asterisk(THD *thd,
+ const Lex_ident_cli_st *ca,
+ const Lex_ident_cli_st *cb)
+ {
+ Lex_ident_sys a(thd, ca), b(thd, cb);
+ if (a.is_null() || b.is_null())
+ return NULL; // EOM
+ return create_item_qualified_asterisk(thd, &a, &b);
+ }
+
+ Item *create_item_ident_nosp(THD *thd, Lex_ident_sys_st *name);
+ Item *create_item_ident_sp(THD *thd, Lex_ident_sys_st *name,
+ const char *start, const char *end);
+ Item *create_item_ident(THD *thd, Lex_ident_cli_st *cname)
+ {
+ Lex_ident_sys name(thd, cname);
+ if (name.is_null())
+ return NULL; // EOM
+ return sphead ?
+ create_item_ident_sp(thd, &name, cname->pos(), cname->end()) :
+ create_item_ident_nosp(thd, &name);
+ }
+ /*
+ Create an Item corresponding to a qualified name: a.b
+ when the parser is out of an SP context.
+ @param THD - THD, for mem_root
+ @param a - the first name
+ @param b - the second name
+ @retval - a pointer to a created item, or NULL on error.
+
+ Possible Item types that can be created:
+ - Item_trigger_field
+ - Item_field
+ - Item_ref
+ */
+ Item *create_item_ident_nospvar(THD *thd,
+ const Lex_ident_sys_st *a,
+ const Lex_ident_sys_st *b);
+ /*
+ Create an Item corresponding to a ROW field valiable: var.field
+ @param THD - THD, for mem_root
+ @param rh [OUT] - the rcontext handler (local vs package variables)
+ @param var - the ROW variable name
+ @param field - the ROW variable field name
+ @param spvar - the variable that was previously found by name
+ using "var_name".
+ @param start - position in the query (for binary log)
+ @param end - end in the query (for binary log)
+ */
+ Item_splocal *create_item_spvar_row_field(THD *thd,
+ const Sp_rcontext_handler *rh,
+ const Lex_ident_sys *var,
+ const Lex_ident_sys *field,
+ sp_variable *spvar,
+ const char *start,
+ const char *end);
+ /*
+ Create an item from its qualified name.
+ Depending on context, it can be either a ROW variable field,
+ or trigger, table field, table field reference.
+ See comments to create_item_spvar_row_field() and
+ create_item_ident_nospvar().
+ @param thd - THD, for mem_root
+ @param a - the first name
+ @param b - the second name
+ @retval - NULL on error, or a pointer to a new Item.
+ */
+ Item *create_item_ident(THD *thd,
+ const Lex_ident_cli_st *a,
+ const Lex_ident_cli_st *b);
+ /*
+ Create an item from its qualified name.
+ Depending on context, it can be a table field, a table field reference,
+ or a sequence NEXTVAL and CURRVAL.
+ @param thd - THD, for mem_root
+ @param a - the first name
+ @param b - the second name
+ @param c - the third name
+ @retval - NULL on error, or a pointer to a new Item.
+ */
+ Item *create_item_ident(THD *thd,
+ const Lex_ident_sys_st *a,
+ const Lex_ident_sys_st *b,
+ const Lex_ident_sys_st *c);
+
+ Item *create_item_ident(THD *thd,
+ const Lex_ident_cli_st *ca,
+ const Lex_ident_cli_st *cb,
+ const Lex_ident_cli_st *cc)
+ {
+ Lex_ident_sys b(thd, cb), c(thd, cc);
+ if (b.is_null() || c.is_null())
+ return NULL;
+ if (ca->pos() == cb->pos()) // SELECT .t1.col1
+ {
+ DBUG_ASSERT(ca->length == 0);
+ Lex_ident_sys none;
+ return create_item_ident(thd, &none, &b, &c);
+ }
+ Lex_ident_sys a(thd, ca);
+ return a.is_null() ? NULL : create_item_ident(thd, &a, &b, &c);
+ }
+
+ /*
+ Create an item for "NEXT VALUE FOR sequence_name"
+ */
+ Item *create_item_func_nextval(THD *thd, Table_ident *ident);
+ Item *create_item_func_nextval(THD *thd, const LEX_CSTRING *db,
+ const LEX_CSTRING *name);
+ /*
+ Create an item for "PREVIOUS VALUE FOR sequence_name"
+ */
+ Item *create_item_func_lastval(THD *thd, Table_ident *ident);
+ Item *create_item_func_lastval(THD *thd, const LEX_CSTRING *db,
+ const LEX_CSTRING *name);
+
+ /*
+ Create an item for "SETVAL(sequence_name, value [, is_used [, round]])
+ */
+ Item *create_item_func_setval(THD *thd, Table_ident *ident, longlong value,
+ ulonglong round, bool is_used);
+
+ /*
+ Create an item for a name in LIMIT clause: LIMIT var
+ @param THD - THD, for mem_root
+ @param var_name - the variable name
+ @retval - a new Item corresponding to the SP variable,
+ or NULL on error
+ (non in SP, unknown variable, wrong data type).
+ */
+ Item *create_item_limit(THD *thd, const Lex_ident_cli_st *var_name);
+
+ /*
+ Create an item for a qualified name in LIMIT clause: LIMIT var.field
+ @param THD - THD, for mem_root
+ @param var_name - the variable name
+ @param field_name - the variable field name
+ @param start - start in the query (for binary log)
+ @param end - end in the query (for binary log)
+ @retval - a new Item corresponding to the SP variable,
+ or NULL on error
+ (non in SP, unknown variable, unknown ROW field,
+ wrong data type).
+ */
+ Item *create_item_limit(THD *thd,
+ const Lex_ident_cli_st *var_name,
+ const Lex_ident_cli_st *field_name);
+
+ Item *make_item_func_replace(THD *thd, Item *org, Item *find, Item *replace);
+ Item *make_item_func_substr(THD *thd, Item *a, Item *b, Item *c);
+ 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);
+ my_var *create_outvar(THD *thd, const LEX_CSTRING *name);
+
+ /*
+ Create a my_var instance for a ROW field variable that was used
+ as an OUT SP parameter: CALL p1(var.field);
+ @param THD - THD, for mem_root
+ @param var_name - the variable name
+ @param field_name - the variable field name
+ */
+ my_var *create_outvar(THD *thd,
+ const LEX_CSTRING *var_name,
+ const LEX_CSTRING *field_name);
+
+ bool is_trigger_new_or_old_reference(const LEX_CSTRING *name) const;
+
+ Item *create_and_link_Item_trigger_field(THD *thd, const LEX_CSTRING *name,
+ bool new_row);
+ // For syntax with colon, e.g. :NEW.a or :OLD.a
+ Item *make_item_colon_ident_ident(THD *thd,
+ const Lex_ident_cli_st *a,
+ const Lex_ident_cli_st *b);
+ // PLSQL: cursor%ISOPEN etc
+ Item *make_item_plsql_cursor_attr(THD *thd, const LEX_CSTRING *name,
+ plsql_cursor_attr_t attr);
+
+ // For "SELECT @@var", "SELECT @@var.field"
+ Item *make_item_sysvar(THD *thd,
+ enum_var_type type,
+ const LEX_CSTRING *name)
+ {
+ return make_item_sysvar(thd, type, name, &null_clex_str);
+ }
+ Item *make_item_sysvar(THD *thd,
+ enum_var_type type,
+ const LEX_CSTRING *name,
+ const LEX_CSTRING *component);
+ void sp_block_init(THD *thd, const LEX_CSTRING *label);
+ void sp_block_init(THD *thd)
+ {
+ // Unlabeled blocks get an empty label
+ sp_block_init(thd, &empty_clex_str);
+ }
+ bool sp_block_finalize(THD *thd, const Lex_spblock_st spblock)
+ {
+ class sp_label *tmp;
+ return sp_block_finalize(thd, spblock, &tmp);
+ }
+ bool sp_block_finalize(THD *thd)
+ {
+ return sp_block_finalize(thd, Lex_spblock());
+ }
+ bool sp_block_finalize(THD *thd, const Lex_spblock_st spblock,
+ const LEX_CSTRING *end_label);
+ bool sp_block_finalize(THD *thd, const LEX_CSTRING *end_label)
+ {
+ return sp_block_finalize(thd, Lex_spblock(), end_label);
+ }
+ bool sp_declarations_join(Lex_spblock_st *res,
+ const Lex_spblock_st b1,
+ const Lex_spblock_st b2) const
+ {
+ if ((b2.vars || b2.conds) && (b1.curs || b1.hndlrs))
+ {
+ my_error(ER_SP_VARCOND_AFTER_CURSHNDLR, MYF(0));
+ return true;
+ }
+ if (b2.curs && b1.hndlrs)
+ {
+ my_error(ER_SP_CURSOR_AFTER_HANDLER, MYF(0));
+ return true;
+ }
+ res->join(b1, b2);
+ return false;
+ }
+ bool sp_block_with_exceptions_finalize_declarations(THD *thd);
+ bool sp_block_with_exceptions_finalize_executable_section(THD *thd,
+ uint executable_section_ip);
+ bool sp_block_with_exceptions_finalize_exceptions(THD *thd,
+ uint executable_section_ip,
+ uint exception_count);
+ bool sp_block_with_exceptions_add_empty(THD *thd);
+ bool sp_exit_statement(THD *thd, Item *when);
+ bool sp_exit_statement(THD *thd, const LEX_CSTRING *label_name, Item *item);
+ 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_iterate_statement(THD *thd, const LEX_CSTRING *label_name);
+
+ bool maybe_start_compound_statement(THD *thd);
+ bool sp_push_loop_label(THD *thd, const LEX_CSTRING *label_name);
+ bool sp_push_loop_empty_label(THD *thd);
+ bool sp_pop_loop_label(THD *thd, const LEX_CSTRING *label_name);
+ 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_push_goto_label(THD *thd, const LEX_CSTRING *label_name);
+
+ Item_param *add_placeholder(THD *thd, const LEX_CSTRING *name,
+ const char *start, const char *end);
+
+ /* Integer range FOR LOOP methods */
+ sp_variable *sp_add_for_loop_variable(THD *thd, const LEX_CSTRING *name,
+ Item *value);
+ sp_variable *sp_add_for_loop_target_bound(THD *thd, Item *value)
+ {
+ LEX_CSTRING name= { STRING_WITH_LEN("[target_bound]") };
+ return sp_add_for_loop_variable(thd, &name, value);
+ }
+ bool sp_for_loop_intrange_declarations(THD *thd, Lex_for_loop_st *loop,
+ const LEX_CSTRING *index,
+ const Lex_for_loop_bounds_st &bounds);
+ bool sp_for_loop_intrange_condition_test(THD *thd, const Lex_for_loop_st &loop);
+ bool sp_for_loop_intrange_finalize(THD *thd, const Lex_for_loop_st &loop);
+
+ /* Cursor FOR LOOP methods */
+ bool sp_for_loop_cursor_declarations(THD *thd, Lex_for_loop_st *loop,
+ const LEX_CSTRING *index,
+ const Lex_for_loop_bounds_st &bounds);
+ sp_variable *sp_add_for_loop_cursor_variable(THD *thd,
+ const LEX_CSTRING *name,
+ const class sp_pcursor *cur,
+ uint coffset,
+ sp_assignment_lex *param_lex,
+ Item_args *parameters);
+ bool sp_for_loop_implicit_cursor_statement(THD *thd,
+ Lex_for_loop_bounds_st *bounds,
+ sp_lex_cursor *cur);
+ bool sp_for_loop_cursor_condition_test(THD *thd, const Lex_for_loop_st &loop);
+ bool sp_for_loop_cursor_finalize(THD *thd, const Lex_for_loop_st &);
+
+ /* Generic FOR LOOP methods*/
+
+ /*
+ Generate FOR loop declarations and
+ initialize "loop" from "index" and "bounds".
+
+ @param [IN] thd - current THD, for mem_root and error reporting
+ @param [OUT] loop - the loop generated SP variables are stored here,
+ together with additional loop characteristics.
+ @param [IN] index - the loop index variable name
+ @param [IN] bounds - the loop bounds (in sp_assignment_lex format)
+ and additional loop characteristics,
+ as created by the sp_for_loop_bounds rule.
+ @retval true - on error
+ @retval false - on success
+
+ This methods adds declarations:
+ - An explicit integer or cursor%ROWTYPE "index" variable
+ - An implicit integer upper bound variable, in case of integer range loops
+ - A CURSOR, in case of an implicit CURSOR loops
+ The generated variables are stored into "loop".
+ Additional loop characteristics are copied from "bounds" to "loop".
+ */
+ bool sp_for_loop_declarations(THD *thd, Lex_for_loop_st *loop,
+ const LEX_CSTRING *index,
+ const Lex_for_loop_bounds_st &bounds)
+ {
+ return bounds.is_for_loop_cursor() ?
+ sp_for_loop_cursor_declarations(thd, loop, index, bounds) :
+ sp_for_loop_intrange_declarations(thd, loop, index, bounds);
+ }
+
+ /*
+ Generate a conditional jump instruction to leave the loop,
+ using a proper condition depending on the loop type:
+ - Item_func_le -- integer range loops
+ - Item_func_ge -- integer range reverse loops
+ - Item_func_cursor_found -- cursor loops
+ */
+ bool sp_for_loop_condition_test(THD *thd, const Lex_for_loop_st &loop)
+ {
+ return loop.is_for_loop_cursor() ?
+ sp_for_loop_cursor_condition_test(thd, loop) :
+ sp_for_loop_intrange_condition_test(thd, loop);
+ }
+
+ /*
+ Generate "increment" instructions followed by a jump to the
+ condition test in the beginnig of the loop.
+ "Increment" depends on the loop type and can be:
+ - index:= index + 1; -- integer range loops
+ - index:= index - 1; -- integer range reverse loops
+ - FETCH cursor INTO index; -- cursor loops
+ */
+ bool sp_for_loop_finalize(THD *thd, const Lex_for_loop_st &loop)
+ {
+ return loop.is_for_loop_cursor() ?
+ sp_for_loop_cursor_finalize(thd, loop) :
+ sp_for_loop_intrange_finalize(thd, loop);
+ }
+ bool sp_for_loop_outer_block_finalize(THD *thd, const Lex_for_loop_st &loop);
+ /* End of FOR LOOP methods */
+
+ bool add_signal_statement(THD *thd, const class sp_condition_value *value);
+ bool add_resignal_statement(THD *thd, const class sp_condition_value *value);
// Check if "KEY IF NOT EXISTS name" used outside of ALTER context
bool check_add_key(DDL_options_st ddl)
@@ -3103,7 +3877,7 @@ public:
return false;
}
// Add a key as a part of CREATE TABLE or ALTER TABLE
- bool add_key(Key::Keytype key_type, const LEX_STRING &key_name,
+ bool add_key(Key::Keytype key_type, const LEX_CSTRING *key_name,
ha_key_alg algorithm, DDL_options_st ddl)
{
if (check_add_key(ddl) ||
@@ -3113,7 +3887,7 @@ public:
return false;
}
// Add a key for a CREATE INDEX statement
- bool add_create_index(Key::Keytype key_type, const LEX_STRING &key_name,
+ bool add_create_index(Key::Keytype key_type, const LEX_CSTRING *key_name,
ha_key_alg algorithm, DDL_options_st ddl)
{
if (check_create_options(ddl) ||
@@ -3122,8 +3896,27 @@ public:
alter_info.key_list.push_back(last_key);
return false;
}
+ bool add_create_index_prepare(Table_ident *table)
+ {
+ sql_command= SQLCOM_CREATE_INDEX;
+ if (!current_select->add_table_to_list(thd, table, NULL,
+ TL_OPTION_UPDATING,
+ TL_READ_NO_INSERT,
+ MDL_SHARED_UPGRADABLE))
+ return true;
+ alter_info.reset();
+ alter_info.flags= ALTER_ADD_INDEX;
+ option_list= NULL;
+ return false;
+ }
+ /*
+ Add an UNIQUE or PRIMARY key which is a part of a column definition:
+ CREATE TABLE t1 (a INT PRIMARY KEY);
+ */
+ void add_key_to_list(LEX_CSTRING *field_name,
+ enum Key::Keytype type, bool check_exists);
// Add a constraint as a part of CREATE TABLE or ALTER TABLE
- bool add_constraint(LEX_STRING *name, Virtual_column_info *constr,
+ bool add_constraint(LEX_CSTRING *name, Virtual_column_info *constr,
bool if_not_exists)
{
constr->name= *name;
@@ -3132,6 +3925,8 @@ public:
alter_info.check_constraint_list.push_back(constr);
return false;
}
+ bool add_alter_list(const char *par_name, Virtual_column_info *expr,
+ bool par_exists);
void set_command(enum_sql_command command,
DDL_options_st options)
{
@@ -3154,11 +3949,18 @@ public:
}
return false;
}
+ bool set_create_options_with_check(DDL_options_st options)
+ {
+ create_info.set(options);
+ return check_create_options(create_info);
+ }
bool add_create_options_with_check(DDL_options_st options)
{
create_info.add(options);
return check_create_options(create_info);
}
+ bool sp_add_cfetch(THD *thd, const LEX_CSTRING *name);
+
bool set_command_with_check(enum_sql_command command,
uint scope,
DDL_options_st options)
@@ -3204,6 +4006,58 @@ public:
}
return false;
}
+
+ SELECT_LEX *exclude_last_select();
+ bool add_unit_in_brackets(SELECT_LEX *nselect);
+ void check_automatic_up(enum sub_select_type type);
+ bool create_or_alter_view_finalize(THD *thd, Table_ident *table_ident);
+ bool add_alter_view(THD *thd, uint16 algorithm, enum_view_suid suid,
+ Table_ident *table_ident);
+ 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);
+
+ Vers_parse_info &vers_get_info()
+ {
+ return create_info.vers_info;
+ }
+ sp_package *get_sp_package() const;
+
+ /**
+ Check if the select is a simple select (not an union).
+ @retval
+ 0 ok
+ @retval
+ 1 error ; In this case the error messege is sent to the client
+ */
+ bool check_simple_select(const LEX_CSTRING *option)
+ {
+ if (current_select != &select_lex)
+ {
+ char command[80];
+ strmake(command, option->str, MY_MIN(option->length, sizeof(command)-1));
+ my_error(ER_CANT_USE_OPTION_HERE, MYF(0), command);
+ return true;
+ }
+ return false;
+ }
+
+ void tvc_start()
+ {
+ field_list.empty();
+ many_values.empty();
+ insert_list= 0;
+ }
+ bool tvc_finalize();
+ bool tvc_finalize_derived();
+
+ bool map_data_type(const Lex_ident_sys_st &schema,
+ Lex_field_type_st *type) const;
+
+ void mark_first_table_as_inserting();
};
@@ -3323,18 +4177,6 @@ public:
};
/**
- Input parameters to the parser.
-*/
-struct Parser_input
-{
- bool m_compute_digest;
-
- Parser_input()
- : m_compute_digest(false)
- {}
-};
-
-/**
Internal state of the parser.
The complete state consist of:
- state data used during lexical parsing,
@@ -3353,7 +4195,7 @@ public:
@retval FALSE OK
@retval TRUE Error
*/
- bool init(THD *thd, char *buff, unsigned int length)
+ bool init(THD *thd, char *buff, size_t length)
{
return m_lip.init(thd, buff, length);
}
@@ -3361,7 +4203,6 @@ public:
~Parser_state()
{}
- Parser_input m_input;
Lex_input_stream m_lip;
Yacc_state m_yacc;
@@ -3388,6 +4229,92 @@ struct st_lex_local: public LEX, public Sql_alloc
{
};
+
+/**
+ An st_lex_local extension with automatic initialization for SP purposes.
+ Used to parse sub-expressions and SP sub-statements.
+
+ This class is reused for:
+ 1. sp_head::reset_lex() based constructs
+ - SP variable assignments (e.g. SET x=10;)
+ - FOR loop conditions and index variable increments
+ - Cursor statements
+ - SP statements
+ - SP function RETURN statements
+ - CASE statements
+ - REPEAT..UNTIL expressions
+ - WHILE expressions
+ - EXIT..WHEN and CONTINUE..WHEN statements
+ 2. sp_assignment_lex based constructs:
+ - CURSOR parameter assignments
+*/
+class sp_lex_local: public st_lex_local
+{
+public:
+ sp_lex_local(THD *thd, const LEX *oldlex)
+ {
+ /* Reset most stuff. */
+ start(thd);
+ /* Keep the parent SP stuff */
+ sphead= oldlex->sphead;
+ spcont= oldlex->spcont;
+ /* Keep the parent trigger stuff too */
+ trg_chistics= oldlex->trg_chistics;
+ trg_table_fields.empty();
+ sp_lex_in_use= false;
+ }
+};
+
+
+/**
+ An assignment specific LEX, which additionally has an Item (an expression)
+ and an associated with the Item free_list, which is usually freed
+ after the expression is calculated.
+
+ Note, consider changing some of sp_lex_local to sp_assignment_lex,
+ as the latter allows to use a simpler grammar in sql_yacc.yy (IMO).
+
+ If the expression is simple (e.g. does not have function calls),
+ then m_item and m_free_list point to the same Item.
+
+ If the expressions is complex (e.g. have function calls),
+ then m_item points to the leftmost Item, while m_free_list points
+ to the rightmost item.
+ For example:
+ f1(COALESCE(f2(10), f2(20)))
+ - m_item points to Item_func_sp for f1 (the leftmost Item)
+ - m_free_list points to Item_int for 20 (the rightmost Item)
+
+ Note, we could avoid storing m_item at all, as we can always reach
+ the leftmost item from the rightmost item by iterating through m_free_list.
+ But with a separate m_item the code should be faster.
+*/
+class sp_assignment_lex: public sp_lex_local
+{
+ Item *m_item; // The expression
+ Item *m_free_list; // The associated free_list (sub-expressions)
+public:
+ sp_assignment_lex(THD *thd, LEX *oldlex)
+ :sp_lex_local(thd, oldlex),
+ m_item(NULL),
+ m_free_list(NULL)
+ { }
+ void set_item_and_free_list(Item *item, Item *free_list)
+ {
+ m_item= item;
+ m_free_list= free_list;
+ }
+ Item *get_item() const
+ {
+ return m_item;
+ }
+ Item *get_free_list() const
+ {
+ return m_free_list;
+ }
+};
+
+
extern void lex_init(void);
extern void lex_free(void);
extern void lex_start(THD *thd);
@@ -3397,18 +4324,29 @@ extern void lex_end_stage2(LEX *lex);
void end_lex_with_single_table(THD *thd, TABLE *table, LEX *old_lex);
int init_lex_with_single_table(THD *thd, TABLE *table, LEX *lex);
extern int MYSQLlex(union YYSTYPE *yylval, THD *thd);
+extern int ORAlex(union YYSTYPE *yylval, THD *thd);
-extern void trim_whitespace(CHARSET_INFO *cs, LEX_STRING *str,
- uint *prefix_removed);
+extern void trim_whitespace(CHARSET_INFO *cs, LEX_CSTRING *str, size_t * prefix_length = 0);
-extern bool is_lex_native_function(const LEX_STRING *name);
+extern bool is_lex_native_function(const LEX_CSTRING *name);
+extern bool is_native_function(THD *thd, const LEX_CSTRING *name);
+extern bool is_native_function_with_warn(THD *thd, const LEX_CSTRING *name);
/**
@} (End of group Semantic_Analysis)
*/
-void my_missing_function_error(const LEX_STRING &token, const char *name);
+void my_missing_function_error(const LEX_CSTRING &token, const char *name);
bool is_keyword(const char *name, uint len);
+int set_statement_var_if_exists(THD *thd, const char *var_name,
+ size_t var_name_length, ulonglong value);
+
+Virtual_column_info *add_virtual_expression(THD *thd, Item *expr);
+Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal,
+ Item *expr);
+
+void sp_create_assignment_lex(THD *thd, bool no_lookahead);
+bool sp_create_assignment_instr(THD *thd, bool no_lookahead);
#endif /* MYSQL_SERVER */
#endif /* SQL_LEX_INCLUDED */
diff --git a/sql/sql_lifo_buffer.h b/sql/sql_lifo_buffer.h
index 4d2d0bd3b27..0347030e4c6 100644
--- a/sql/sql_lifo_buffer.h
+++ b/sql/sql_lifo_buffer.h
@@ -84,7 +84,7 @@ public:
start= start_arg;
end= end_arg;
if (end != start)
- TRASH_ALLOC(start, end - start);
+ TRASH_ALLOC(start, size_t(end - start));
reset();
}
@@ -224,7 +224,7 @@ public:
{
DBUG_ASSERT(unused_end >= unused_start);
DBUG_ASSERT(end == unused_start);
- TRASH_ALLOC(unused_start, unused_end - unused_start);
+ TRASH_ALLOC(unused_start, size_t(unused_end - unused_start));
end= unused_end;
}
/* Return pointer to start of the memory area that is occupied by the data */
diff --git a/sql/sql_list.cc b/sql/sql_list.cc
index 40cff446e0e..92664898718 100644
--- a/sql/sql_list.cc
+++ b/sql/sql_list.cc
@@ -18,6 +18,7 @@
#pragma implementation // gcc: Class implementation
#endif
+#include "mariadb.h"
#include "sql_list.h"
list_node end_of_list;
diff --git a/sql/sql_list.h b/sql/sql_list.h
index a7ece69acb6..a47acf57b54 100644
--- a/sql/sql_list.h
+++ b/sql/sql_list.h
@@ -20,46 +20,7 @@
#pragma interface /* gcc class implementation */
#endif
-#include "my_sys.h" /* alloc_root, TRASH, MY_WME,
- MY_FAE, MY_ALLOW_ZERO_PTR */
-#include "m_string.h" /* bfill */
-
-THD *thd_get_current_thd();
-
-/* mysql standard class memory allocator */
-
-class Sql_alloc
-{
-public:
- static void *operator new(size_t size) throw ()
- {
- return thd_alloc(thd_get_current_thd(), size);
- }
- static void *operator new[](size_t size) throw ()
- {
- return thd_alloc(thd_get_current_thd(), size);
- }
- static void *operator new[](size_t size, MEM_ROOT *mem_root) throw ()
- { return alloc_root(mem_root, size); }
- static void *operator new(size_t size, MEM_ROOT *mem_root) throw ()
- { return alloc_root(mem_root, size); }
- static void operator delete(void *ptr, size_t size) { TRASH_FREE(ptr, size); }
- static void operator delete(void *ptr, MEM_ROOT *mem_root)
- { /* never called */ }
- static void operator delete[](void *ptr, MEM_ROOT *mem_root)
- { /* never called */ }
- static void operator delete[](void *ptr, size_t size) { TRASH_FREE(ptr, size); }
-#ifdef HAVE_valgrind
- bool dummy_for_valgrind;
- inline Sql_alloc() :dummy_for_valgrind(0) {}
- inline ~Sql_alloc() {}
-#else
- inline Sql_alloc() {}
- inline ~Sql_alloc() {}
-#endif
-
-};
-
+#include "sql_alloc.h"
/**
Simple intrusive linked list.
@@ -68,6 +29,7 @@ public:
a pointer to the first element in the list and a indirect
reference to the last element.
*/
+
template <typename T>
class SQL_I_List :public Sql_alloc
{
@@ -352,16 +314,15 @@ public:
friend class error_list;
friend class error_list_iterator;
-#ifndef DBUG_OFF
/*
- Debugging help: return N-th element in the list, or NULL if the list has
+ Return N-th element in the list, or NULL if the list has
less than N elements.
*/
- void *elem(int n)
+ void *elem(uint n)
{
list_node *node= first;
void *data= NULL;
- for (int i=0; i <= n; i++)
+ for (uint i= 0; i <= n; i++)
{
if (node == &end_of_list)
{
@@ -373,7 +334,6 @@ public:
}
return data;
}
-#endif
#ifdef LIST_EXTRA_DEBUG
/*
@@ -541,10 +501,10 @@ public:
base_list(tmp, mem_root) {}
inline bool push_back(T *a) { return base_list::push_back(a); }
inline bool push_back(T *a, MEM_ROOT *mem_root)
- { return base_list::push_back(a, mem_root); }
+ { return base_list::push_back((void*) a, mem_root); }
inline bool push_front(T *a) { return base_list::push_front(a); }
inline bool push_front(T *a, MEM_ROOT *mem_root)
- { return base_list::push_front(a, mem_root); }
+ { return base_list::push_front((void*) a, mem_root); }
inline T* head() {return (T*) base_list::head(); }
inline T** head_ref() {return (T**) base_list::head_ref(); }
inline T* pop() {return (T*) base_list::pop(); }
@@ -565,9 +525,7 @@ public:
}
empty();
}
-#ifndef DBUG_OFF
- T *elem(int n) { return (T*)base_list::elem(n); }
-#endif
+ T *elem(uint n) { return (T*) base_list::elem(n); }
};
@@ -591,10 +549,10 @@ public:
template <class T> class List_iterator_fast :public base_list_iterator
{
protected:
- inline T *replace(T *a) { return (T*) 0; }
- inline T *replace(List<T> &a) { return (T*) 0; }
- inline void remove(void) { }
- inline void after(T *a) { }
+ inline T *replace(T *) { return (T*) 0; }
+ inline T *replace(List<T> &) { return (T*) 0; }
+ inline void remove(void) {}
+ inline void after(T *) {}
inline T** ref(void) { return (T**) 0; }
public:
@@ -663,7 +621,7 @@ struct ilink
{
return (void*)my_malloc((uint)size, MYF(MY_WME | MY_FAE | ME_FATALERROR));
}
- static void operator delete(void* ptr_arg, size_t size)
+ static void operator delete(void* ptr_arg, size_t)
{
my_free(ptr_arg);
}
@@ -683,6 +641,10 @@ struct ilink
{
DBUG_ASSERT(prev != 0 && next != 0);
}
+ inline void assert_not_linked()
+ {
+ DBUG_ASSERT(prev == 0 && next == 0);
+ }
virtual ~ilink() { unlink(); } /*lint -e1740 */
};
diff --git a/sql/sql_load.cc b/sql/sql_load.cc
index e2dbbdb6782..09af120e6cf 100644
--- a/sql/sql_load.cc
+++ b/sql/sql_load.cc
@@ -19,7 +19,7 @@
/* Copy data from a textfile to table */
/* 2006-12 Erik Wetterberg : LOAD XML added */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#include "sql_load.h"
@@ -298,7 +298,7 @@ static int read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
bool ignore_check_option_errors);
#ifndef EMBEDDED_LIBRARY
-static bool write_execute_load_query_log_event(THD *, sql_exchange*, const
+static bool write_execute_load_query_log_event(THD *, const sql_exchange*, const
char*, const char*, bool, enum enum_duplicates, bool, bool, int);
#endif /* EMBEDDED_LIBRARY */
@@ -348,7 +348,7 @@ bool Load_data_param::add_outvar_user_var(THD *thd)
TRUE - error / FALSE - success
*/
-int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
+int mysql_load(THD *thd, const sql_exchange *ex, TABLE_LIST *table_list,
List<Item> &fields_vars, List<Item> &set_fields,
List<Item> &set_values,
enum enum_duplicates handle_duplicates, bool ignore,
@@ -363,13 +363,13 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
killed_state killed_status;
bool is_concurrent;
#endif
- char *db = table_list->db; // This is never null
+ const char *db= table_list->db.str; // This is never null
/*
If path for file is not defined, we will use the current database.
If this is not set, we will use the directory where the table to be
loaded is located
*/
- char *tdb= thd->db ? thd->db : db; // Result is never null
+ const char *tdb= thd->db.str ? thd->db.str : db; // Result is never null
ulong skip_lines= ex->skip_lines;
bool transactional_table __attribute__((unused));
DBUG_ENTER("mysql_load");
@@ -421,7 +421,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
!table_list->single_table_updatable() || // and derived tables
check_key_in_view(thd, table_list))
{
- my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "LOAD");
+ my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "LOAD");
DBUG_RETURN(TRUE);
}
if (table_list->is_multitable())
@@ -434,7 +434,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
{
DBUG_RETURN(TRUE);
}
- thd_proc_info(thd, "executing");
+ thd_proc_info(thd, "Executing");
/*
Let us emit an error if we are loading data to table which is used
in subselect in SET clause like we do it for INSERT.
@@ -445,7 +445,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
*/
if (unique_table(thd, table_list, table_list->next_global, 0))
{
- my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->table_name,
+ my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->table_name.str,
"LOAD DATA");
DBUG_RETURN(TRUE);
}
@@ -462,6 +462,9 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
field_iterator.set(table_list);
for (; !field_iterator.end_of_fields(); field_iterator.next())
{
+ if (field_iterator.field() &&
+ field_iterator.field()->invisible > VISIBLE)
+ continue;
Item *item;
if (!(item= field_iterator.create_item(thd)))
DBUG_RETURN(TRUE);
@@ -611,7 +614,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
*ex->field_term, *ex->line_start,
*ex->line_term, *ex->enclosed,
info.escape_char, read_file_from_client, is_fifo);
- if (read_info.error)
+ if (unlikely(read_info.error))
{
if (file >= 0)
mysql_file_close(file, MYF(0)); // no files in net reading
@@ -642,8 +645,8 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
}
}
- thd_proc_info(thd, "reading file");
- if (!(error= MY_TEST(read_info.error)))
+ thd_proc_info(thd, "Reading file");
+ if (likely(!(error= MY_TEST(read_info.error))))
{
table->reset_default_fields();
table->next_number_field=table->found_next_number_field;
@@ -687,7 +690,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
table_list->table->file->ha_rnd_end();
thd_proc_info(thd, "End bulk insert");
- if (!error)
+ if (likely(!error))
thd_progress_next_stage(thd);
if (thd->locked_tables_mode <= LTM_LOCK_TABLES &&
table->file->ha_end_bulk_insert() && !error)
@@ -746,8 +749,8 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
writing binary log will be ignored */
if (thd->transaction.stmt.modified_non_trans_table)
(void) write_execute_load_query_log_event(thd, ex,
- table_list->db,
- table_list->table_name,
+ table_list->db.str,
+ table_list->table_name.str,
is_concurrent,
handle_duplicates, ignore,
transactional_table,
@@ -797,7 +800,8 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
{
int errcode= query_error_code(thd, killed_status == NOT_KILLED);
error= write_execute_load_query_log_event(thd, ex,
- table_list->db, table_list->table_name,
+ table_list->db.str,
+ table_list->table_name.str,
is_concurrent,
handle_duplicates, ignore,
transactional_table,
@@ -811,7 +815,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
*/
error= error || mysql_bin_log.get_log_file()->error;
}
- if (error)
+ if (unlikely(error))
goto err;
}
#endif /*!EMBEDDED_LIBRARY*/
@@ -831,7 +835,7 @@ err:
#ifndef EMBEDDED_LIBRARY
/* Not a very useful function; just to avoid duplication of code */
-static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex,
+static bool write_execute_load_query_log_event(THD *thd, const sql_exchange* ex,
const char* db_arg, /* table's database */
const char* table_name_arg,
bool is_concurrent,
@@ -846,7 +850,7 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex,
List<Item> fv;
Item *item, *val;
int n;
- const char *tdb= (thd->db != NULL ? thd->db : db_arg);
+ const char *tdb= (thd->db.str != NULL ? thd->db.str : db_arg);
const char *qualify_db= NULL;
char command_buffer[1024];
String query_str(command_buffer, sizeof(command_buffer),
@@ -862,7 +866,7 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex,
lle.set_fname_outside_temp_buf(ex->file_name, strlen(ex->file_name));
query_str.length(0);
- if (!thd->db || strcmp(db_arg, thd->db))
+ if (!thd->db.str || strcmp(db_arg, thd->db.str))
{
/*
If used database differs from table's database,
@@ -908,8 +912,8 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex,
val= lv++;
if (n++)
query_str.append(STRING_WITH_LEN(", "));
- append_identifier(thd, &query_str, item->name, strlen(item->name));
- query_str.append(val->name);
+ append_identifier(thd, &query_str, &item->name);
+ query_str.append(&val->name);
}
}
@@ -1131,11 +1135,11 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
}
}
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
read_info.error= 1;
-
- if (read_info.error)
+ if (unlikely(read_info.error))
break;
+
if (skip_lines)
{
skip_lines--;
@@ -1155,11 +1159,11 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
}
}
- if (thd->killed ||
- fill_record_n_invoke_before_triggers(thd, table, set_fields,
- set_values,
- ignore_check_option_errors,
- TRG_EVENT_INSERT))
+ if (unlikely(thd->killed) ||
+ unlikely(fill_record_n_invoke_before_triggers(thd, table, set_fields,
+ set_values,
+ ignore_check_option_errors,
+ TRG_EVENT_INSERT)))
DBUG_RETURN(1);
switch (table_list->view_check_option(thd,
@@ -1253,7 +1257,7 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
xmlit.rewind();
tag= xmlit++;
- while(tag && strcmp(tag->field.c_ptr(), item->name) != 0)
+ while(tag && strcmp(tag->field.c_ptr(), item->name.str) != 0)
tag= xmlit++;
Load_data_outvar *dst= item->get_load_data_outvar_or_error();
@@ -1265,7 +1269,7 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
DBUG_RETURN(1);
}
- if (read_info.error)
+ if (unlikely(read_info.error))
break;
if (skip_lines)
@@ -2027,8 +2031,8 @@ int READ_INFO::read_xml(THD *thd)
case '=': /* attribute name end - read the value */
//check for tag field and attribute name
- if(!memcmp(tag.c_ptr_safe(), STRING_WITH_LEN("field")) &&
- !memcmp(attribute.c_ptr_safe(), STRING_WITH_LEN("name")))
+ if(!strcmp(tag.c_ptr_safe(), "field") &&
+ !strcmp(attribute.c_ptr_safe(), "name"))
{
/*
this is format <field name="xx">xx</field>
@@ -2059,7 +2063,7 @@ int READ_INFO::read_xml(THD *thd)
chr= read_value(delim, &value);
if (attribute.length() > 0 && value.length() > 0)
{
- DBUG_PRINT("read_xml", ("lev:%i att:%s val:%s\n",
+ DBUG_PRINT("read_xml", ("lev:%i att:%s val:%s",
level + 1,
attribute.c_ptr_safe(),
value.c_ptr_safe()));
diff --git a/sql/sql_load.h b/sql/sql_load.h
index 8ff2f1ab8f3..8413d27805c 100644
--- a/sql/sql_load.h
+++ b/sql/sql_load.h
@@ -24,7 +24,7 @@ class Item;
class sql_exchange;
-int mysql_load(THD *thd, sql_exchange *ex, TABLE_LIST *table_list,
+int mysql_load(THD *thd, const sql_exchange *ex, TABLE_LIST *table_list,
List<Item> &fields_vars, List<Item> &set_fields,
List<Item> &set_values_list,
enum enum_duplicates handle_duplicates, bool ignore,
diff --git a/sql/sql_locale.cc b/sql/sql_locale.cc
index cc4bc0c175d..45f81da80c9 100644
--- a/sql/sql_locale.cc
+++ b/sql/sql_locale.cc
@@ -20,12 +20,11 @@
!! This file is built from my_locale.pl !!
*/
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#include "sql_locale.h"
#include "sql_class.h" // THD
-#include "my_sys.h" // MY_*, NullS, NULL
enum err_msgs_index
diff --git a/sql/sql_locale.h b/sql/sql_locale.h
index 270540ee5c3..87145a106cc 100644
--- a/sql/sql_locale.h
+++ b/sql/sql_locale.h
@@ -22,7 +22,6 @@ typedef struct my_locale_errmsgs
const char ***errmsgs;
} MY_LOCALE_ERRMSGS;
-#include "my_global.h" /* uint */
typedef struct st_typelib TYPELIB;
diff --git a/sql/sql_manager.cc b/sql/sql_manager.cc
index 7fa9a78f06f..2ad8d8a914a 100644
--- a/sql/sql_manager.cc
+++ b/sql/sql_manager.cc
@@ -21,7 +21,7 @@
* o Berkeley DB: removing unneeded log files.
*/
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "sql_manager.h"
#include "sql_base.h" // flush_tables
diff --git a/sql/sql_mode.cc b/sql/sql_mode.cc
index 9eea6ccdb52..32d19cecbdb 100644
--- a/sql/sql_mode.cc
+++ b/sql/sql_mode.cc
@@ -14,7 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include "mysqld.h"
+#include "mariadb.h"
#include "set_var.h"
void Sql_mode_dependency::push_dependency_warnings(THD *thd)
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 543c877b7f1..6802816caaf 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -15,7 +15,7 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
#define MYSQL_LEX 1
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "sql_parse.h" // sql_kill, *_precheck, *_prepare
#include "lock.h" // try_transactional_lock,
@@ -82,7 +82,6 @@
#include <m_ctype.h>
#include <myisam.h>
#include <my_dir.h>
-#include "rpl_handler.h"
#include "rpl_mi.h"
#include "sql_digest.h"
@@ -99,8 +98,8 @@
#include "debug_sync.h"
#include "probes_mysql.h"
#include "set_var.h"
-#include "log_slow.h"
#include "sql_bootstrap.h"
+#include "sql_sequence.h"
#include "my_json_writer.h"
@@ -123,14 +122,6 @@ static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length,
@{
*/
-/* Used in error handling only */
-#define SP_COM_STRING(LP) \
- ((LP)->sql_command == SQLCOM_CREATE_SPFUNCTION || \
- (LP)->sql_command == SQLCOM_ALTER_FUNCTION || \
- (LP)->sql_command == SQLCOM_SHOW_CREATE_FUNC || \
- (LP)->sql_command == SQLCOM_DROP_FUNCTION ? \
- "FUNCTION" : "PROCEDURE")
-
static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables);
static void sql_kill(THD *thd, longlong id, killed_state state, killed_type type);
static void sql_kill_user(THD *thd, LEX_USER *user, killed_state state);
@@ -140,39 +131,39 @@ static bool check_rename_table(THD *, TABLE_LIST *, TABLE_LIST *);
const char *any_db="*any*"; // Special symbol for check_access
-const LEX_STRING command_name[257]={
- { C_STRING_WITH_LEN("Sleep") }, //0
- { C_STRING_WITH_LEN("Quit") }, //1
- { C_STRING_WITH_LEN("Init DB") }, //2
- { C_STRING_WITH_LEN("Query") }, //3
- { C_STRING_WITH_LEN("Field List") }, //4
- { C_STRING_WITH_LEN("Create DB") }, //5
- { C_STRING_WITH_LEN("Drop DB") }, //6
- { C_STRING_WITH_LEN("Refresh") }, //7
- { C_STRING_WITH_LEN("Shutdown") }, //8
- { C_STRING_WITH_LEN("Statistics") }, //9
- { C_STRING_WITH_LEN("Processlist") }, //10
- { C_STRING_WITH_LEN("Connect") }, //11
- { C_STRING_WITH_LEN("Kill") }, //12
- { C_STRING_WITH_LEN("Debug") }, //13
- { C_STRING_WITH_LEN("Ping") }, //14
- { C_STRING_WITH_LEN("Time") }, //15
- { C_STRING_WITH_LEN("Delayed insert") }, //16
- { C_STRING_WITH_LEN("Change user") }, //17
- { C_STRING_WITH_LEN("Binlog Dump") }, //18
- { C_STRING_WITH_LEN("Table Dump") }, //19
- { C_STRING_WITH_LEN("Connect Out") }, //20
- { C_STRING_WITH_LEN("Register Slave") }, //21
- { C_STRING_WITH_LEN("Prepare") }, //22
- { C_STRING_WITH_LEN("Execute") }, //23
- { C_STRING_WITH_LEN("Long Data") }, //24
- { C_STRING_WITH_LEN("Close stmt") }, //25
- { C_STRING_WITH_LEN("Reset stmt") }, //26
- { C_STRING_WITH_LEN("Set option") }, //27
- { C_STRING_WITH_LEN("Fetch") }, //28
- { C_STRING_WITH_LEN("Daemon") }, //29
- { C_STRING_WITH_LEN("Unimpl get tid") }, //30
- { C_STRING_WITH_LEN("Reset connection") },//31
+const LEX_CSTRING command_name[257]={
+ { STRING_WITH_LEN("Sleep") }, //0
+ { STRING_WITH_LEN("Quit") }, //1
+ { STRING_WITH_LEN("Init DB") }, //2
+ { STRING_WITH_LEN("Query") }, //3
+ { STRING_WITH_LEN("Field List") }, //4
+ { STRING_WITH_LEN("Create DB") }, //5
+ { STRING_WITH_LEN("Drop DB") }, //6
+ { STRING_WITH_LEN("Refresh") }, //7
+ { STRING_WITH_LEN("Shutdown") }, //8
+ { STRING_WITH_LEN("Statistics") }, //9
+ { STRING_WITH_LEN("Processlist") }, //10
+ { STRING_WITH_LEN("Connect") }, //11
+ { STRING_WITH_LEN("Kill") }, //12
+ { STRING_WITH_LEN("Debug") }, //13
+ { STRING_WITH_LEN("Ping") }, //14
+ { STRING_WITH_LEN("Time") }, //15
+ { STRING_WITH_LEN("Delayed insert") }, //16
+ { STRING_WITH_LEN("Change user") }, //17
+ { STRING_WITH_LEN("Binlog Dump") }, //18
+ { STRING_WITH_LEN("Table Dump") }, //19
+ { STRING_WITH_LEN("Connect Out") }, //20
+ { STRING_WITH_LEN("Register Slave") }, //21
+ { STRING_WITH_LEN("Prepare") }, //22
+ { STRING_WITH_LEN("Execute") }, //23
+ { STRING_WITH_LEN("Long Data") }, //24
+ { STRING_WITH_LEN("Close stmt") }, //25
+ { STRING_WITH_LEN("Reset stmt") }, //26
+ { STRING_WITH_LEN("Set option") }, //27
+ { STRING_WITH_LEN("Fetch") }, //28
+ { STRING_WITH_LEN("Daemon") }, //29
+ { STRING_WITH_LEN("Unimpl get tid") }, //30
+ { STRING_WITH_LEN("Reset connection") },//31
{ 0, 0 }, //32
{ 0, 0 }, //33
{ 0, 0 }, //34
@@ -391,12 +382,12 @@ const LEX_STRING command_name[257]={
{ 0, 0 }, //247
{ 0, 0 }, //248
{ 0, 0 }, //249
- { C_STRING_WITH_LEN("Bulk_execute") }, //250
- { C_STRING_WITH_LEN("Slave_worker") }, //251
- { C_STRING_WITH_LEN("Slave_IO") }, //252
- { C_STRING_WITH_LEN("Slave_SQL") }, //253
- { C_STRING_WITH_LEN("Com_multi") }, //254
- { C_STRING_WITH_LEN("Error") } // Last command number 255
+ { STRING_WITH_LEN("Bulk_execute") }, //250
+ { STRING_WITH_LEN("Slave_worker") }, //251
+ { STRING_WITH_LEN("Slave_IO") }, //252
+ { STRING_WITH_LEN("Slave_SQL") }, //253
+ { STRING_WITH_LEN("Com_multi") }, //254
+ { STRING_WITH_LEN("Error") } // Last command number 255
};
const char *xa_state_names[]={
@@ -411,7 +402,7 @@ inline bool all_tables_not_ok(THD *thd, TABLE_LIST *tables)
{
Rpl_filter *rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter;
return rpl_filter->is_on() && tables && !thd->spcont &&
- !rpl_filter->tables_ok(thd->db, tables);
+ !rpl_filter->tables_ok(thd->db.str, tables);
}
#endif
@@ -420,7 +411,7 @@ static bool some_non_temp_table_to_be_updated(THD *thd, TABLE_LIST *tables)
{
for (TABLE_LIST *table= tables; table; table= table->next_global)
{
- DBUG_ASSERT(table->db && table->table_name);
+ DBUG_ASSERT(table->db.str && table->table_name.str);
if (table->updating && !thd->find_tmp_table_share(table))
return 1;
}
@@ -448,14 +439,17 @@ bool stmt_causes_implicit_commit(THD *thd, uint mask)
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_CREATE_TABLE:
+ case SQLCOM_CREATE_SEQUENCE:
/*
If CREATE TABLE of non-temporary table and the table is not part
if a BEGIN GTID ... COMMIT group, do a implicit commit.
@@ -544,24 +538,36 @@ void init_update_queries(void)
*/
sql_command_flags[SQLCOM_CREATE_TABLE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS |
- CF_CAN_GENERATE_ROW_EVENTS;
+ CF_CAN_GENERATE_ROW_EVENTS |
+ CF_SCHEMA_CHANGE;
+ sql_command_flags[SQLCOM_CREATE_SEQUENCE]= (CF_CHANGES_DATA |
+ CF_REEXECUTION_FRAGILE |
+ CF_AUTO_COMMIT_TRANS |
+ CF_SCHEMA_CHANGE);
sql_command_flags[SQLCOM_CREATE_INDEX]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS |
- CF_REPORT_PROGRESS | CF_ADMIN_COMMAND;
+ CF_ADMIN_COMMAND | CF_REPORT_PROGRESS;
sql_command_flags[SQLCOM_ALTER_TABLE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND |
CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS |
CF_INSERTS_DATA | CF_ADMIN_COMMAND;
+ sql_command_flags[SQLCOM_ALTER_SEQUENCE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND |
+ CF_AUTO_COMMIT_TRANS | CF_SCHEMA_CHANGE |
+ CF_ADMIN_COMMAND;
sql_command_flags[SQLCOM_TRUNCATE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND |
CF_AUTO_COMMIT_TRANS;
- sql_command_flags[SQLCOM_DROP_TABLE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_DROP_TABLE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_SCHEMA_CHANGE;
+ sql_command_flags[SQLCOM_DROP_SEQUENCE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_SCHEMA_CHANGE;
sql_command_flags[SQLCOM_LOAD]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
CF_CAN_GENERATE_ROW_EVENTS | CF_REPORT_PROGRESS |
CF_INSERTS_DATA;
- sql_command_flags[SQLCOM_CREATE_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
- sql_command_flags[SQLCOM_DROP_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_CREATE_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_DB_CHANGE;
+ sql_command_flags[SQLCOM_DROP_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_DB_CHANGE;
+ sql_command_flags[SQLCOM_CREATE_PACKAGE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_DROP_PACKAGE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_CREATE_PACKAGE_BODY]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_DROP_PACKAGE_BODY]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_ALTER_DB_UPGRADE]= CF_AUTO_COMMIT_TRANS;
- sql_command_flags[SQLCOM_ALTER_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
- sql_command_flags[SQLCOM_RENAME_TABLE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS |
- CF_ADMIN_COMMAND;
+ sql_command_flags[SQLCOM_ALTER_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_DB_CHANGE;
+ sql_command_flags[SQLCOM_RENAME_TABLE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_ADMIN_COMMAND;
sql_command_flags[SQLCOM_DROP_INDEX]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS |
CF_REPORT_PROGRESS | CF_ADMIN_COMMAND;
sql_command_flags[SQLCOM_CREATE_VIEW]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
@@ -599,11 +605,12 @@ void init_update_queries(void)
CF_CAN_GENERATE_ROW_EVENTS |
CF_OPTIMIZER_TRACE |
CF_CAN_BE_EXPLAINED |
- CF_SP_BULK_SAFE;
+ CF_SP_BULK_SAFE | CF_DELETES_DATA;
sql_command_flags[SQLCOM_DELETE_MULTI]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
CF_CAN_GENERATE_ROW_EVENTS |
CF_OPTIMIZER_TRACE |
- CF_CAN_BE_EXPLAINED;;
+ CF_CAN_BE_EXPLAINED |
+ CF_DELETES_DATA;
sql_command_flags[SQLCOM_REPLACE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
CF_CAN_GENERATE_ROW_EVENTS |
CF_OPTIMIZER_TRACE |
@@ -635,6 +642,8 @@ void init_update_queries(void)
CF_OPTIMIZER_TRACE; // (1)
sql_command_flags[SQLCOM_SHOW_STATUS_PROC]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
+ sql_command_flags[SQLCOM_SHOW_STATUS_PACKAGE]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
+ sql_command_flags[SQLCOM_SHOW_STATUS_PACKAGE_BODY]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
sql_command_flags[SQLCOM_SHOW_STATUS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
sql_command_flags[SQLCOM_SHOW_DATABASES]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
sql_command_flags[SQLCOM_SHOW_TRIGGERS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
@@ -669,10 +678,13 @@ void init_update_queries(void)
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;
+ sql_command_flags[SQLCOM_SHOW_CREATE_PACKAGE]= CF_STATUS_COMMAND;
+ sql_command_flags[SQLCOM_SHOW_CREATE_PACKAGE_BODY]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_CREATE_TRIGGER]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_STATUS_FUNC]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
sql_command_flags[SQLCOM_SHOW_PROC_CODE]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_FUNC_CODE]= CF_STATUS_COMMAND;
+ sql_command_flags[SQLCOM_SHOW_PACKAGE_BODY_CODE]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_CREATE_EVENT]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_PROFILES]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_PROFILE]= CF_STATUS_COMMAND;
@@ -728,6 +740,7 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_TRUNCATE]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT;
/* We don't want to replicate DROP for temp tables in row format */
sql_command_flags[SQLCOM_DROP_TABLE]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT;
+ sql_command_flags[SQLCOM_DROP_SEQUENCE]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT;
/* We don't want to replicate CREATE/DROP INDEX for temp tables in row format */
sql_command_flags[SQLCOM_CREATE_INDEX]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT;
sql_command_flags[SQLCOM_DROP_INDEX]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT;
@@ -809,7 +822,9 @@ void init_update_queries(void)
have to be closed before temporary tables are pre-opened.
*/
sql_command_flags[SQLCOM_CREATE_TABLE]|= CF_HA_CLOSE;
+ sql_command_flags[SQLCOM_CREATE_SEQUENCE]|= CF_HA_CLOSE;
sql_command_flags[SQLCOM_DROP_TABLE]|= CF_HA_CLOSE;
+ sql_command_flags[SQLCOM_DROP_SEQUENCE]|= CF_HA_CLOSE;
sql_command_flags[SQLCOM_ALTER_TABLE]|= CF_HA_CLOSE;
sql_command_flags[SQLCOM_TRUNCATE]|= CF_HA_CLOSE;
sql_command_flags[SQLCOM_REPAIR]|= CF_HA_CLOSE;
@@ -827,13 +842,19 @@ void init_update_queries(void)
even temporary table DDL should be disallowed.
*/
sql_command_flags[SQLCOM_CREATE_TABLE]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_CREATE_SEQUENCE]|= CF_DISALLOW_IN_RO_TRANS;
sql_command_flags[SQLCOM_ALTER_TABLE]|= CF_DISALLOW_IN_RO_TRANS;
sql_command_flags[SQLCOM_DROP_TABLE]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_DROP_SEQUENCE]|= CF_DISALLOW_IN_RO_TRANS;
sql_command_flags[SQLCOM_RENAME_TABLE]|= CF_DISALLOW_IN_RO_TRANS;
sql_command_flags[SQLCOM_CREATE_INDEX]|= CF_DISALLOW_IN_RO_TRANS;
sql_command_flags[SQLCOM_DROP_INDEX]|= CF_DISALLOW_IN_RO_TRANS;
sql_command_flags[SQLCOM_CREATE_DB]|= CF_DISALLOW_IN_RO_TRANS;
sql_command_flags[SQLCOM_DROP_DB]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_CREATE_PACKAGE]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_DROP_PACKAGE]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_CREATE_PACKAGE_BODY]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_DROP_PACKAGE_BODY]|= CF_DISALLOW_IN_RO_TRANS;
sql_command_flags[SQLCOM_ALTER_DB_UPGRADE]|= CF_DISALLOW_IN_RO_TRANS;
sql_command_flags[SQLCOM_ALTER_DB]|= CF_DISALLOW_IN_RO_TRANS;
sql_command_flags[SQLCOM_CREATE_VIEW]|= CF_DISALLOW_IN_RO_TRANS;
@@ -923,7 +944,7 @@ void execute_init_command(THD *thd, LEX_STRING *init_command,
save_vio= thd->net.vio;
thd->net.vio= 0;
thd->clear_error(1);
- dispatch_command(COM_QUERY, thd, buf, len, FALSE, FALSE);
+ dispatch_command(COM_QUERY, thd, buf, (uint)len, FALSE, FALSE);
thd->client_capabilities= save_client_capabilities;
thd->net.vio= save_vio;
@@ -933,8 +954,8 @@ void execute_init_command(THD *thd, LEX_STRING *init_command,
static char *fgets_fn(char *buffer, size_t size, fgets_input_t input, int *error)
{
MYSQL_FILE *in= static_cast<MYSQL_FILE*> (input);
- char *line= mysql_file_fgets(buffer, size, in);
- if (error)
+ char *line= mysql_file_fgets(buffer, (int)size, in);
+ if (unlikely(error))
*error= (line == NULL) ? ferror(in->m_file) : 0;
return line;
}
@@ -1012,7 +1033,7 @@ static void handle_bootstrap_impl(THD *thd)
}
query= (char *) thd->memdup_w_gap(buffer, length + 1,
- thd->db_length + 1 +
+ thd->db.length + 1 +
QUERY_CACHE_DB_LENGTH_SIZE +
QUERY_CACHE_FLAGS_SIZE);
size_t db_len= 0;
@@ -1048,7 +1069,7 @@ static void handle_bootstrap_impl(THD *thd)
#endif
delete_explain_query(thd->lex);
- if (bootstrap_error)
+ if (unlikely(bootstrap_error))
break;
thd->reset_kill_query(); /* Ensure that killed_errmsg is released */
@@ -1158,15 +1179,10 @@ static bool wsrep_tables_accessible_when_detached(const TABLE_LIST *tables)
for (const TABLE_LIST *table= tables; table; table= table->next_global)
{
TABLE_CATEGORY c;
- LEX_STRING db, tn;
- lex_string_set(&db, table->db);
- lex_string_set(&tn, table->table_name);
+ LEX_CSTRING db= table->db, tn= table->table_name;
c= get_table_category(&db, &tn);
- if (c != TABLE_CATEGORY_INFORMATION &&
- c != TABLE_CATEGORY_PERFORMANCE)
- {
+ if (c != TABLE_CATEGORY_INFORMATION && c != TABLE_CATEGORY_PERFORMANCE)
return false;
- }
}
return true;
}
@@ -1223,8 +1239,8 @@ bool do_command(THD *thd)
the client, the connection is closed or "net_wait_timeout"
number of seconds has passed.
*/
- if(!thd->skip_wait_timeout)
- my_net_set_read_timeout(net, thd->variables.net_wait_timeout);
+ if (!thd->skip_wait_timeout)
+ my_net_set_read_timeout(net, thd->get_net_wait_timeout());
/* Errors and diagnostics are cleared once here before query */
thd->clear_error(1);
@@ -1275,7 +1291,7 @@ bool do_command(THD *thd)
}
#endif /* WITH_WSREP */
- if (packet_length == packet_error)
+ if (unlikely(packet_length == packet_error))
{
DBUG_PRINT("info",("Got error %d reading command from socket %s",
net->error,
@@ -1288,7 +1304,7 @@ bool do_command(THD *thd)
if (thd->wsrep_conflict_state == MUST_ABORT)
{
DBUG_PRINT("wsrep",("aborted for wsrep rollback: %lu",
- (long unsigned int) thd->real_id));
+ (ulong) thd->real_id));
wsrep_client_rollback(thd);
}
mysql_mutex_unlock(&thd->LOCK_thd_data);
@@ -1424,7 +1440,7 @@ out:
This is a helper function to mysql_execute_command.
- @note SQLCOM_MULTI_UPDATE is an exception and delt with elsewhere.
+ @note SQLCOM_MULTI_UPDATE is an exception and dealt with elsewhere.
@see mysql_execute_command
@returns Status code
@@ -1441,9 +1457,12 @@ static bool deny_updates_if_read_only_option(THD *thd, TABLE_LIST *all_tables)
LEX *lex= thd->lex;
- if (thd->security_ctx->master_access & SUPER_ACL)
+ /* Super user is allowed to do changes */
+ if (((ulong)(thd->security_ctx->master_access & SUPER_ACL) ==
+ (ulong)SUPER_ACL))
DBUG_RETURN(FALSE);
+ /* Check if command doesn't update anything */
if (!(sql_command_flags[lex->sql_command] & CF_CHANGES_DATA))
DBUG_RETURN(FALSE);
@@ -1451,11 +1470,6 @@ static bool deny_updates_if_read_only_option(THD *thd, TABLE_LIST *all_tables)
if (lex->sql_command == SQLCOM_UPDATE_MULTI)
DBUG_RETURN(FALSE);
- if (lex->sql_command == SQLCOM_CREATE_DB ||
- lex->sql_command == SQLCOM_ALTER_DB ||
- lex->sql_command == SQLCOM_DROP_DB)
- DBUG_RETURN(TRUE);
-
/*
a table-to-be-created is not in the temp table list yet,
so CREATE TABLE needs a special treatment
@@ -1470,8 +1484,15 @@ static bool deny_updates_if_read_only_option(THD *thd, TABLE_LIST *all_tables)
if (lex->sql_command == SQLCOM_DROP_TABLE && lex->tmp_table())
DBUG_RETURN(FALSE);
- /* Now, check thd->temporary_tables list */
- DBUG_RETURN(some_non_temp_table_to_be_updated(thd, all_tables));
+ /* Check if we created, dropped, or renamed a database */
+ if ((sql_command_flags[lex->sql_command] & CF_DB_CHANGE))
+ DBUG_RETURN(TRUE);
+
+ if (some_non_temp_table_to_be_updated(thd, all_tables))
+ DBUG_RETURN(TRUE);
+
+ /* Assuming that only temporary tables are modified. */
+ DBUG_RETURN(FALSE);
}
@@ -1486,7 +1507,7 @@ static bool deny_updates_if_read_only_option(THD *thd, TABLE_LIST *all_tables)
@retval # - Number of commands in the batch
*/
-uint maria_multi_check(THD *thd, char *packet, uint packet_length)
+uint maria_multi_check(THD *thd, char *packet, size_t packet_length)
{
uint counter= 0;
DBUG_ENTER("maria_multi_check");
@@ -1553,9 +1574,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
"<?>")));
bool drop_more_results= 0;
- if (!is_com_multi)
- inc_thread_running();
-
/* keep it withing 1 byte */
compile_time_assert(COM_END == 255);
@@ -1603,12 +1621,19 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
DBUG_EXECUTE_IF("crash_dispatch_command_before",
{ DBUG_PRINT("crash_dispatch_command_before", ("now"));
- DBUG_ABORT(); });
+ DBUG_SUICIDE(); });
/* Performance Schema Interface instrumentation, begin */
thd->m_statement_psi= MYSQL_REFINE_STATEMENT(thd->m_statement_psi,
com_statement_info[command].
m_key);
+ /*
+ We should always call reset_for_next_command() before a query.
+ mysql_parse() will do this for queries. Ensure it's also done
+ for other commands.
+ */
+ if (command != COM_QUERY)
+ thd->reset_for_next_command();
thd->set_command(command);
thd->enable_slow_log= true;
@@ -1658,14 +1683,14 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
switch (command) {
case COM_INIT_DB:
{
- LEX_STRING tmp;
+ LEX_CSTRING tmp;
status_var_increment(thd->status_var.com_stat[SQLCOM_CHANGE_DB]);
- if (thd->copy_with_error(system_charset_info, &tmp,
- thd->charset(), packet, packet_length))
+ if (unlikely(thd->copy_with_error(system_charset_info, (LEX_STRING*) &tmp,
+ thd->charset(), packet, packet_length)))
break;
if (!mysql_change_db(thd, &tmp, FALSE))
{
- general_log_write(thd, command, thd->db, thd->db_length);
+ general_log_write(thd, command, thd->db.str, thd->db.length);
my_ok(thd);
}
break;
@@ -1701,8 +1726,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
/* acl_authenticate() takes the data from net->read_pos */
net->read_pos= (uchar*)packet;
- uint save_db_length= thd->db_length;
- char *save_db= thd->db;
+ LEX_CSTRING save_db= thd->db;
USER_CONN *save_user_connect= thd->user_connect;
Security_context save_security_ctx= *thd->security_ctx;
CHARSET_INFO *save_character_set_client=
@@ -1733,12 +1757,12 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
if (auth_rc)
{
/* Free user if allocated by acl_authenticate */
- my_free(thd->security_ctx->user);
+ my_free(const_cast<char*>(thd->security_ctx->user));
*thd->security_ctx= save_security_ctx;
if (thd->user_connect)
decrease_user_connections(thd->user_connect);
thd->user_connect= save_user_connect;
- thd->reset_db(save_db, save_db_length);
+ thd->reset_db(&save_db);
thd->update_charset(save_character_set_client, save_collation_connection,
save_character_set_results);
thd->failed_com_change_user++;
@@ -1751,8 +1775,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
if (save_user_connect)
decrease_user_connections(save_user_connect);
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
- my_free(save_db);
- my_free(save_security_ctx.user);
+ my_free((char*) save_db.str);
+ my_free(const_cast<char*>(save_security_ctx.user));
}
break;
}
@@ -1797,10 +1821,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd->m_digest= & thd->m_digest_state;
thd->m_digest->reset(thd->m_token_array, max_digest_length);
- if (alloc_query(thd, packet, packet_length))
+ if (unlikely(alloc_query(thd, packet, packet_length)))
break; // fatal error is set
MYSQL_QUERY_START(thd->query(), thd->thread_id,
- (char *) (thd->db ? thd->db : ""),
+ thd->get_db(),
&thd->security_ctx->priv_user[0],
(char *) thd->security_ctx->host_or_ip);
char *packet_end= thd->query() + thd->query_length();
@@ -1813,7 +1837,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd->query_length());
Parser_state parser_state;
- if (parser_state.init(thd, thd->query(), thd->query_length()))
+ if (unlikely(parser_state.init(thd, thd->query(), thd->query_length())))
break;
if (WSREP(thd))
@@ -1877,7 +1901,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
/* DTRACE begin */
MYSQL_QUERY_START(beginning_of_next_stmt, thd->thread_id,
- (char *) (thd->db ? thd->db : ""),
+ thd->get_db(),
&thd->security_ctx->priv_user[0],
(char *) thd->security_ctx->host_or_ip);
@@ -1886,7 +1910,7 @@ 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, thd->db_length,
+ thd->db.str, thd->db.length,
thd->charset());
THD_STAGE_INFO(thd, stage_init);
MYSQL_SET_STATEMENT_TEXT(thd->m_statement_psi, beginning_of_next_stmt,
@@ -1894,6 +1918,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd->set_query_and_id(beginning_of_next_stmt, length,
thd->charset(), next_query_id());
+
/*
Count each statement from the client.
*/
@@ -1903,7 +1928,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd->set_time(); /* Reset the query start time. */
parser_state.reset(beginning_of_next_stmt, length);
- /* TODO: set thd->lex->sql_command to SQLCOM_END here */
if (WSREP(thd))
wsrep_mysql_parse(thd, beginning_of_next_stmt, length, &parser_state,
@@ -1928,7 +1952,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
/* Locked closure of all tables */
TABLE_LIST table_list;
LEX_STRING table_name;
- LEX_STRING db;
+ LEX_CSTRING db;
/*
SHOW statements should not add the used tables to the list of tables
used in a transaction.
@@ -1936,7 +1960,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
status_var_increment(thd->status_var.com_stat[SQLCOM_SHOW_FIELDS]);
- if (thd->copy_db_to(&db.str, &db.length))
+ if (thd->copy_db_to(&db))
break;
/*
We have name + wildcard in packet, separated by endzero
@@ -1961,7 +1985,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
break;
}
packet= arg_end + 1;
- thd->reset_for_next_command(0); // Don't clear errors
// thd->reset_for_next_command reset state => restore it
if (is_next_command)
{
@@ -1975,10 +1998,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
if (lower_case_table_names)
{
table_name.length= my_casedn_str(files_charset_info, table_name.str);
- db.length= my_casedn_str(files_charset_info, db.str);
+ db.length= my_casedn_str(files_charset_info, (char*) db.str);
}
- table_list.init_one_table(db.str, db.length, table_name.str,
- table_name.length, table_name.str, TL_READ);
+ table_list.init_one_table(&db, (LEX_CSTRING*) &table_name, 0, TL_READ);
/*
Init TABLE_LIST members necessary when the undelrying
table is view.
@@ -1989,9 +2011,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
&table_list.next_local);
thd->lex->add_to_query_tables(&table_list);
- if (is_infoschema_db(table_list.db, table_list.db_length))
+ if (is_infoschema_db(&table_list.db))
{
- ST_SCHEMA_TABLE *schema_table= find_schema_table(thd, table_list.alias);
+ ST_SCHEMA_TABLE *schema_table= find_schema_table(thd, &table_list.alias);
if (schema_table)
table_list.schema_table= schema_table;
}
@@ -2000,7 +2022,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
if (!(fields= (char *) thd->memdup(packet, query_length + 1)))
break;
thd->set_query(fields, query_length);
- general_log_print(thd, command, "%s %s", table_list.table_name, fields);
+ general_log_print(thd, command, "%s %s", table_list.table_name.str,
+ fields);
if (thd->open_temporary_tables(&table_list))
break;
@@ -2121,7 +2144,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
else
#endif
{
- thd->lex->relay_log_connection_name= empty_lex_str;
+ thd->lex->relay_log_connection_name= empty_clex_str;
if (reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, &not_used))
break;
}
@@ -2165,7 +2188,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
{
STATUS_VAR *current_global_status_var; // Big; Don't allocate on stack
ulong uptime;
- uint length __attribute__((unused));
ulonglong queries_per_second1000;
char buff[250];
uint buff_len= sizeof(buff);
@@ -2180,8 +2202,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
queries_per_second1000= 0;
else
queries_per_second1000= thd->query_id * 1000 / uptime;
-
- length= my_snprintf(buff, buff_len - 1,
+#ifndef EMBEDDED_LIBRARY
+ size_t length=
+#endif
+ my_snprintf(buff, buff_len - 1,
"Uptime: %lu Threads: %d Questions: %lu "
"Slow queries: %lu Opens: %lu Flush tables: %lld "
"Open tables: %u Queries per second avg: %u.%03u",
@@ -2308,7 +2332,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
}
if (dispatch_command(subcommand, thd, packet + (1 + length_length),
- subpacket_length - (1 + length_length), TRUE,
+ (uint)(subpacket_length - (1 + length_length)), TRUE,
(current_com != counter)))
{
DBUG_ASSERT(thd->is_error());
@@ -2317,7 +2341,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
DBUG_ASSERT(subpacket_length <= packet_length);
packet+= subpacket_length;
- packet_length-= subpacket_length;
+ packet_length-= (uint)subpacket_length;
}
com_multi_end:
@@ -2372,7 +2396,7 @@ com_multi_end:
(thd->open_tables == NULL ||
(thd->locked_tables_mode == LTM_LOCK_TABLES)));
- thd_proc_info(thd, "updating status");
+ thd_proc_info(thd, "Updating status");
/* Finalize server status flags after executing a command. */
thd->update_server_status();
if (command != COM_MULTI)
@@ -2384,7 +2408,7 @@ com_multi_end:
if (drop_more_results)
thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS;
- if (!thd->is_error() && !thd->killed_errno())
+ if (likely(!thd->is_error() && !thd->killed_errno()))
mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_RESULT, 0, 0);
mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_STATUS,
@@ -2408,10 +2432,8 @@ com_multi_end:
thd->m_digest= NULL;
if (!is_com_multi)
- {
- dec_thread_running();
thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory
- }
+
thd->reset_kill_query(); /* Ensure that killed_errmsg is released */
/*
LEX::m_sql_cmd can point to Sql_cmd allocated on thd->mem_root.
@@ -2441,11 +2463,18 @@ com_multi_end:
DBUG_RETURN(error);
}
+static bool slow_filter_masked(THD *thd, ulonglong mask)
+{
+ return thd->variables.log_slow_filter && !(thd->variables.log_slow_filter & mask);
+}
/*
+ Log query to slow queries, if it passes filtering
+
@note
This function must call delete_explain_query().
*/
+
void log_slow_statement(THD *thd)
{
DBUG_ENTER("log_slow_statement");
@@ -2457,7 +2486,6 @@ void log_slow_statement(THD *thd)
*/
if (unlikely(thd->in_sub_stmt))
goto end; // Don't set time for sub stmt
-
/*
Skip both long_query_count increment and logging if the current
statement forces slow log suppression (e.g. an SP statement).
@@ -2476,21 +2504,27 @@ void log_slow_statement(THD *thd)
thd->server_status|= SERVER_QUERY_WAS_SLOW;
});
- if (((thd->server_status & SERVER_QUERY_WAS_SLOW) ||
- ((thd->server_status &
- (SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED)) &&
- opt_log_queries_not_using_indexes &&
- !(thd->query_plan_flags & QPLAN_STATUS))) &&
+ if ((thd->server_status &
+ (SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED)) &&
+ !(thd->query_plan_flags & QPLAN_STATUS) &&
+ !slow_filter_masked(thd, QPLAN_NOT_USING_INDEX))
+ {
+ thd->query_plan_flags|= QPLAN_NOT_USING_INDEX;
+ /* We are always logging no index queries if enabled in filter */
+ thd->server_status|= SERVER_QUERY_WAS_SLOW;
+ }
+
+ if ((thd->server_status & SERVER_QUERY_WAS_SLOW) &&
thd->get_examined_row_count() >= thd->variables.min_examined_row_limit)
{
thd->status_var.long_query_count++;
/*
- until opt_log_slow_admin_statements is removed, it
+ until log_slow_disabled_statements=admin is removed, it
duplicates slow_log_filter=admin
*/
if ((thd->query_plan_flags & QPLAN_ADMIN) &&
- !opt_log_slow_admin_statements)
+ (thd->variables.log_slow_disabled_statements & LOG_SLOW_DISABLE_ADMIN))
goto end;
if (!global_system_variables.sql_log_slow || !thd->variables.sql_log_slow)
@@ -2508,8 +2542,7 @@ void log_slow_statement(THD *thd)
Follow the slow log filter configuration:
skip logging if the current statement matches the filter.
*/
- if (thd->variables.log_slow_filter &&
- !(thd->variables.log_slow_filter & thd->query_plan_flags))
+ if (slow_filter_masked(thd, thd->query_plan_flags))
goto end;
THD_STAGE_INFO(thd, stage_logging_slow_query);
@@ -2567,6 +2600,7 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
case SCH_TABLE_NAMES:
case SCH_TABLES:
+ case SCH_CHECK_CONSTRAINTS:
case SCH_VIEWS:
case SCH_TRIGGERS:
case SCH_EVENTS:
@@ -2576,21 +2610,23 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
DBUG_RETURN(1);
#else
{
- LEX_STRING db;
- size_t dummy;
- if (lex->select_lex.db == NULL &&
- lex->copy_db_to(&lex->select_lex.db, &dummy))
+ if (lex->select_lex.db.str == NULL &&
+ lex->copy_db_to(&lex->select_lex.db))
{
DBUG_RETURN(1);
}
schema_select_lex= new (thd->mem_root) SELECT_LEX();
- db.str= schema_select_lex->db= lex->select_lex.db;
schema_select_lex->table_list.first= NULL;
- db.length= strlen(db.str);
-
- if (check_db_name(&db))
+ if (lower_case_table_names == 1)
+ lex->select_lex.db.str= thd->strdup(lex->select_lex.db.str);
+ schema_select_lex->db= lex->select_lex.db;
+ /*
+ check_db_name() may change db.str if lower_case_table_names == 1,
+ but that's ok as the db is allocted above in this case.
+ */
+ if (check_db_name((LEX_STRING*) &lex->select_lex.db))
{
- my_error(ER_WRONG_DB_NAME, MYF(0), db.str);
+ my_error(ER_WRONG_DB_NAME, MYF(0), lex->select_lex.db.str);
DBUG_RETURN(1);
}
break;
@@ -2653,7 +2689,7 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
TRUE error; In this case thd->fatal_error is set
*/
-bool alloc_query(THD *thd, const char *packet, uint packet_length)
+bool alloc_query(THD *thd, const char *packet, size_t packet_length)
{
char *query;
/* Remove garbage at start and end of query */
@@ -2681,7 +2717,7 @@ bool alloc_query(THD *thd, const char *packet, uint packet_length)
*/
if (! (query= (char*) thd->memdup_w_gap(packet,
packet_length,
- 1 + thd->db_length +
+ 1 + thd->db.length +
QUERY_CACHE_DB_LENGTH_SIZE +
QUERY_CACHE_FLAGS_SIZE)))
return TRUE;
@@ -2691,7 +2727,7 @@ bool alloc_query(THD *thd, const char *packet, uint packet_length)
also store this length, in case current database is changed during
execution. We might need to reallocate the 'query' buffer
*/
- int2store(query + packet_length + 1, thd->db_length);
+ int2store(query + packet_length + 1, thd->db.length);
thd->set_query(query, packet_length);
@@ -2749,7 +2785,7 @@ bool sp_process_definer(THD *thd)
DBUG_RETURN(TRUE);
if (thd->slave_thread && lex->sphead)
- lex->sphead->m_chistics->suid= SP_IS_NOT_SUID;
+ lex->sphead->set_suid(SP_IS_NOT_SUID);
}
else
{
@@ -2909,12 +2945,13 @@ static bool do_execute_sp(THD *thd, sp_head *sp)
{
/* bits that should be cleared in thd->server_status */
uint bits_to_be_cleared= 0;
+ ulonglong affected_rows;
if (sp->m_flags & sp_head::MULTI_RESULTS)
{
if (!(thd->client_capabilities & CLIENT_MULTI_RESULTS))
{
/* The client does not support multiple result sets being sent back */
- my_error(ER_SP_BADSELECT, MYF(0), sp->m_qname.str);
+ my_error(ER_SP_BADSELECT, MYF(0), ErrConvDQName(sp).ptr());
return 1;
}
}
@@ -2954,11 +2991,249 @@ static bool do_execute_sp(THD *thd, sp_head *sp)
return 1; // Substatement should already have sent error
}
- my_ok(thd, (thd->get_row_count_func() < 0) ? 0 : thd->get_row_count_func());
+ affected_rows= thd->affected_rows; // Affected rows for all sub statements
+ thd->affected_rows= 0; // Reset total, as my_ok() adds to it
+ my_ok(thd, affected_rows);
return 0;
}
+static int 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 */
+ /*
+ Verify that the database name is allowed, optionally
+ lowercase it.
+ */
+ if (check_db_name((LEX_STRING*) &lex->sphead->m_db))
+ {
+ my_error(ER_WRONG_DB_NAME, MYF(0), lex->sphead->m_db.str);
+ return true;
+ }
+
+ if (check_access(thd, CREATE_PROC_ACL, lex->sphead->m_db.str,
+ NULL, NULL, 0, 0))
+ return true;
+
+ /* Checking the drop permissions if CREATE OR REPLACE is used */
+ if (lex->create_info.or_replace())
+ {
+ if (check_routine_access(thd, ALTER_PROC_ACL, &lex->sphead->m_db,
+ &lex->sphead->m_name,
+ Sp_handler::handler(lex->sql_command), 0))
+ return true;
+ }
+
+ const LEX_CSTRING *name= lex->sphead->name();
+#ifdef HAVE_DLOPEN
+ if (lex->sphead->m_handler->type() == TYPE_ENUM_FUNCTION)
+ {
+ udf_func *udf = find_udf(name->str, name->length);
+
+ if (udf)
+ {
+ my_error(ER_UDF_EXISTS, MYF(0), name->str);
+ return true;
+ }
+ }
+#endif
+
+ if (sp_process_definer(thd))
+ 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
+ /* only add privileges if really neccessary */
+
+ Security_context security_context;
+ bool restore_backup_context= false;
+ Security_context *backup= NULL;
+ LEX_USER *definer= thd->lex->definer;
+ /*
+ We're going to issue an implicit GRANT 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 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());
+ close_thread_tables(thd);
+ /*
+ Check if the definer exists on slave,
+ then use definer privilege to insert routine privileges to mysql.procs_priv.
+
+ For current user of SQL thread has GLOBAL_ACL privilege,
+ which doesn't any check routine privileges,
+ so no routine privilege record will insert into mysql.procs_priv.
+ */
+ if (thd->slave_thread && is_acl_user(definer->host.str, definer->user.str))
+ {
+ security_context.change_security_context(thd,
+ &thd->lex->definer->user,
+ &thd->lex->definer->host,
+ &thd->lex->sphead->m_db,
+ &backup);
+ restore_backup_context= true;
+ }
+
+ if (sp_automatic_privileges && !opt_noacl &&
+ check_routine_access(thd, DEFAULT_CREATE_PROC_ACLS,
+ &lex->sphead->m_db, name,
+ Sp_handler::handler(lex->sql_command), 1))
+ {
+ if (sp_grant_privileges(thd, lex->sphead->m_db.str, name->str,
+ Sp_handler::handler(lex->sql_command)))
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_PROC_AUTO_GRANT_FAIL, ER_THD(thd, ER_PROC_AUTO_GRANT_FAIL));
+ thd->clear_error();
+ }
+
+ /*
+ Restore current user with GLOBAL_ACL privilege of SQL thread
+ */
+ if (restore_backup_context)
+ {
+ DBUG_ASSERT(thd->slave_thread == 1);
+ thd->security_ctx->restore_security_context(thd, backup);
+ }
+
+#endif
+ return false;
+ }
+WSREP_ERROR_LABEL:
+ return true;
+}
+
+
+/**
+ Prepare for CREATE DATABASE, ALTER DATABASE, DROP DATABASE.
+
+ @param thd - current THD
+ @param want_access - access needed
+ @param dbname - the database name
+
+ @retval false - Ok to proceed with CREATE/ALTER/DROP
+ @retval true - not OK to proceed (error, or filtered)
+
+ Note, on slave this function returns true if the database
+ is in the ignore filter. The caller must distinguish this case
+ 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)
+{
+ if (check_db_name((LEX_STRING*)dbname))
+ {
+ my_error(ER_WRONG_DB_NAME, MYF(0), dbname->str);
+ return true;
+ }
+ /*
+ If in a slave thread :
+ - CREATE DATABASE DB was certainly not preceded by USE DB.
+ - ALTER DATABASE DB may not be preceded by USE DB.
+ - DROP DATABASE DB may not be preceded by USE DB.
+ For that reason, db_ok() in sql/slave.cc did not check the
+ do_db/ignore_db. And as this query involves no tables, tables_ok()
+ was not called. So we have to check rules again here.
+ */
+#ifdef HAVE_REPLICATION
+ if (thd->slave_thread)
+ {
+ Rpl_filter *rpl_filter;
+ rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter;
+ if (!rpl_filter->db_ok(dbname->str) ||
+ !rpl_filter->db_ok_with_wild_table(dbname->str))
+ {
+ my_message(ER_SLAVE_IGNORED_TABLE,
+ ER_THD(thd, ER_SLAVE_IGNORED_TABLE), MYF(0));
+ return true;
+ }
+ }
+#endif
+ return check_access(thd, want_access, dbname->str, NULL, NULL, 1, 0);
+}
+
+
+bool Sql_cmd_call::execute(THD *thd)
+{
+ TABLE_LIST *all_tables= thd->lex->query_tables;
+ sp_head *sp;
+ /*
+ This will cache all SP and SF and open and lock all tables
+ required for execution.
+ */
+ if (check_table_access(thd, SELECT_ACL, all_tables, FALSE,
+ UINT_MAX, FALSE) ||
+ open_and_lock_tables(thd, all_tables, TRUE, 0))
+ return true;
+
+ /*
+ By this moment all needed SPs should be in cache so no need to look
+ into DB.
+ */
+ if (!(sp= m_handler->sp_find_routine(thd, m_name, true)))
+ {
+ /*
+ If the routine is not found, let's still check EXECUTE_ACL to decide
+ whether to return "Access denied" or "Routine does not exist".
+ */
+ if (check_routine_access(thd, EXECUTE_ACL, &m_name->m_db,
+ &m_name->m_name,
+ &sp_handler_procedure,
+ false))
+ return true;
+ /*
+ sp_find_routine can have issued an ER_SP_RECURSION_LIMIT error.
+ Send message ER_SP_DOES_NOT_EXIST only if procedure is not found in
+ cache.
+ */
+ if (!sp_cache_lookup(&thd->sp_proc_cache, m_name))
+ my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PROCEDURE",
+ ErrConvDQName(m_name).ptr());
+ return true;
+ }
+ else
+ {
+ if (sp->check_execute_access(thd))
+ return true;
+ /*
+ Check that the stored procedure doesn't contain Dynamic SQL
+ and doesn't return result sets: such stored procedures can't
+ be called from a function or trigger.
+ */
+ if (thd->in_sub_stmt)
+ {
+ const char *where= (thd->in_sub_stmt & SUB_STMT_TRIGGER ?
+ "trigger" : "function");
+ if (sp->is_not_allowed_in_function(where))
+ return true;
+ }
+
+ if (do_execute_sp(thd, sp))
+ return true;
+
+ /*
+ Disable slow log for the above call(), if calls are disabled.
+ Instead we will log the executed statements to the slow log.
+ */
+ if (thd->variables.log_slow_disabled_statements & LOG_SLOW_DISABLE_CALL)
+ thd->enable_slow_log= 0;
+ }
+ return false;
+}
+
+
/**
Execute command saved in thd and lex->sql_command.
@@ -2999,6 +3274,10 @@ mysql_execute_command(THD *thd)
#endif
DBUG_ENTER("mysql_execute_command");
+ // check that we correctly marked first table for data insertion
+ DBUG_ASSERT(!(sql_command_flags[lex->sql_command] & CF_INSERTS_DATA) ||
+ first_table->for_insert_data);
+
DBUG_ASSERT(thd->transaction.stmt.is_empty() || thd->in_sub_stmt);
/*
Each statement or replication event which might produce deadlock
@@ -3031,6 +3310,12 @@ mysql_execute_command(THD *thd)
table_list.first);
/*
+ Remember last commmand executed, so that we can use it in functions called by
+ dispatch_command()
+ */
+ thd->last_sql_command= lex->sql_command;
+
+ /*
Reset warning count for each query that uses tables
A better approach would be to reset this for any commands
that is not a SHOW command or a select that only access local
@@ -3131,7 +3416,8 @@ mysql_execute_command(THD *thd)
*/
if (!(lex->sql_command == SQLCOM_UPDATE_MULTI) &&
!(lex->sql_command == SQLCOM_SET_OPTION) &&
- !(lex->sql_command == SQLCOM_DROP_TABLE &&
+ !((lex->sql_command == SQLCOM_DROP_TABLE ||
+ lex->sql_command == SQLCOM_DROP_SEQUENCE) &&
lex->tmp_table() && lex->if_exists()) &&
all_tables_not_ok(thd, all_tables))
{
@@ -3288,6 +3574,7 @@ mysql_execute_command(THD *thd)
case GET_NO_ARG:
case GET_DISABLED:
DBUG_ASSERT(0);
+ /* fall through */
case 0:
case GET_FLAGSET:
case GET_ENUM:
@@ -3466,8 +3753,7 @@ mysql_execute_command(THD *thd)
Item **it= lex->value_list.head_ref();
if (!(*it)->basic_const_item() ||
- (!(*it)->fixed && (*it)->fix_fields(lex->thd, it)) ||
- (*it)->check_cols(1))
+ (*it)->fix_fields_if_needed_for_scalar(lex->thd, it))
{
my_message(ER_SET_CONSTANTS_ONLY, ER_THD(thd, ER_SET_CONSTANTS_ONLY),
MYF(0));
@@ -3477,6 +3763,8 @@ mysql_execute_command(THD *thd)
/* fall through */
case SQLCOM_SHOW_STATUS_PROC:
case SQLCOM_SHOW_STATUS_FUNC:
+ case SQLCOM_SHOW_STATUS_PACKAGE:
+ case SQLCOM_SHOW_STATUS_PACKAGE_BODY:
case SQLCOM_SHOW_DATABASES:
case SQLCOM_SHOW_TABLES:
case SQLCOM_SHOW_TRIGGERS:
@@ -3578,8 +3866,7 @@ mysql_execute_command(THD *thd)
goto error;
/* PURGE MASTER LOGS BEFORE 'data' */
it= (Item *)lex->value_list.head();
- if ((!it->fixed && it->fix_fields(lex->thd, &it)) ||
- it->check_cols(1))
+ if (it->fix_fields_if_needed_for_scalar(lex->thd, &it))
{
my_error(ER_WRONG_ARGUMENTS, MYF(0), "PURGE LOGS BEFORE");
goto error;
@@ -3641,7 +3928,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_ASSIGN_TO_KEYCACHE:
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_access(thd, INDEX_ACL, first_table->db,
+ if (check_access(thd, INDEX_ACL, first_table->db.str,
&first_table->grant.privilege,
&first_table->grant.m_internal,
0, 0))
@@ -3652,7 +3939,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_PRELOAD_KEYS:
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_access(thd, INDEX_ACL, first_table->db,
+ if (check_access(thd, INDEX_ACL, first_table->db.str,
&first_table->grant.privilege,
&first_table->grant.m_internal,
0, 0))
@@ -3689,7 +3976,7 @@ mysql_execute_command(THD *thd)
{
/* New replication created */
mi= new Master_info(&lex_mi->connection_name, relay_log_recovery);
- if (!mi || mi->error())
+ if (unlikely(!mi || mi->error()))
{
delete mi;
res= 1;
@@ -3786,20 +4073,20 @@ mysql_execute_command(THD *thd)
HA_CREATE_INFO create_info;
Alter_info alter_info(lex->alter_info, thd->mem_root);
- if (thd->is_fatal_error) /* out of memory creating a copy of alter_info */
+ if (unlikely(thd->is_fatal_error)) /* out of memory creating alter_info */
goto error;
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, first_table->table_name, NULL)
+ 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;
- res= mysql_alter_table(thd, first_table->db, first_table->table_name,
+ res= mysql_alter_table(thd, &first_table->db, &first_table->table_name,
&create_info, first_table, &alter_info,
0, (ORDER*) 0, 0);
break;
@@ -3817,8 +4104,8 @@ mysql_execute_command(THD *thd)
We don't need to ensure that only one user is using master_info
as start_slave is protected against simultaneous usage
*/
- if ((mi= get_master_info(&lex_mi->connection_name,
- Sql_condition::WARN_LEVEL_ERROR)))
+ if (unlikely((mi= get_master_info(&lex_mi->connection_name,
+ Sql_condition::WARN_LEVEL_ERROR))))
{
if (load_error)
{
@@ -3911,7 +4198,7 @@ mysql_execute_command(THD *thd)
if (check_rename_table(thd, first_table, all_tables))
goto error;
- WSREP_TO_ISOLATION_BEGIN(0, 0, first_table)
+ WSREP_TO_ISOLATION_BEGIN(0, 0, first_table);
if (mysql_rename_tables(thd, first_table, 0))
goto error;
@@ -3953,8 +4240,8 @@ mysql_execute_command(THD *thd)
*/
DBUG_PRINT("debug", ("lex->only_view: %d, table: %s.%s",
- lex->only_view,
- first_table->db, first_table->table_name));
+ lex->table_type == TABLE_TYPE_VIEW,
+ first_table->db.str, first_table->table_name.str));
res= mysqld_show_create(thd, first_table);
break;
#endif
@@ -4002,8 +4289,7 @@ mysql_execute_command(THD *thd)
select_lex->order_list.elements,
select_lex->order_list.first,
unit->select_limit_cnt,
- lex->duplicates, lex->ignore,
- &found, &updated);
+ lex->ignore, &found, &updated);
MYSQL_UPDATE_DONE(res, found, updated);
/* mysql_update return 2 if we need to switch to multi-update */
if (up_result != 2)
@@ -4023,6 +4309,7 @@ mysql_execute_command(THD *thd)
else
res= 0;
+ unit->set_limit(select_lex);
/*
We can not use mysql_explain_union() because of parameters of
mysql_select in mysql_multi_update so just set the option if needed
@@ -4206,7 +4493,7 @@ mysql_execute_command(THD *thd)
if (WSREP(thd) && thd->wsrep_consistency_check == CONSISTENCY_CHECK_DECLARED)
{
thd->wsrep_consistency_check = CONSISTENCY_CHECK_RUNNING;
- WSREP_TO_ISOLATION_BEGIN(first_table->db, first_table->table_name, NULL);
+ WSREP_TO_ISOLATION_BEGIN(first_table->db.str, first_table->table_name.str, NULL);
}
#endif /* WITH_WSREP */
@@ -4335,7 +4622,7 @@ mysql_execute_command(THD *thd)
unit->set_limit(select_lex);
MYSQL_DELETE_START(thd->query());
- Protocol *save_protocol;
+ Protocol * UNINIT_VAR(save_protocol);
bool replaced_protocol= false;
if (!select_lex->item_list.is_empty())
@@ -4404,18 +4691,20 @@ mysql_execute_command(THD *thd)
break;
MYSQL_MULTI_DELETE_START(thd->query());
- if ((res= mysql_multi_delete_prepare(thd)))
+ if (unlikely(res= mysql_multi_delete_prepare(thd)))
{
MYSQL_MULTI_DELETE_DONE(1, 0);
goto error;
}
- if (!thd->is_fatal_error)
+ if (likely(!thd->is_fatal_error))
{
result= new (thd->mem_root) multi_delete(thd, aux_tables,
lex->table_count);
- if (result)
+ if (likely(result))
{
+ if (unlikely(select_lex->vers_setup_conds(thd, aux_tables)))
+ goto multi_delete_error;
res= mysql_select(thd,
select_lex->get_table_list(),
select_lex->with_wild,
@@ -4427,7 +4716,7 @@ mysql_execute_command(THD *thd)
SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK |
OPTION_SETUP_TABLES_DONE) & ~OPTION_BUFFER_RESULT,
result, unit, select_lex);
- res|= thd->is_error();
+ res|= (int)(thd->is_error());
MYSQL_MULTI_DELETE_DONE(res, result->num_deleted());
if (res)
@@ -4437,6 +4726,7 @@ mysql_execute_command(THD *thd)
if (lex->describe || lex->analyze_stmt)
res= thd->lex->explain->send_explain(thd);
}
+ multi_delete_error:
delete result;
}
}
@@ -4447,6 +4737,7 @@ mysql_execute_command(THD *thd)
}
break;
}
+ case SQLCOM_DROP_SEQUENCE:
case SQLCOM_DROP_TABLE:
{
int result;
@@ -4464,7 +4755,7 @@ mysql_execute_command(THD *thd)
}
else
{
- status_var_decrement(thd->status_var.com_stat[SQLCOM_DROP_TABLE]);
+ status_var_decrement(thd->status_var.com_stat[lex->sql_command]);
status_var_increment(thd->status_var.com_drop_tmp_table);
/* So that DROP TEMPORARY TABLE gets to binlog at commit/rollback */
@@ -4495,10 +4786,13 @@ mysql_execute_command(THD *thd)
}
/* DDL and binlog write order are protected by metadata locks. */
- res= mysql_rm_table(thd, first_table, lex->if_exists(), lex->tmp_table());
+ res= mysql_rm_table(thd, first_table, lex->if_exists(), lex->tmp_table(),
+ lex->table_type == TABLE_TYPE_SEQUENCE);
- /* when dropping temporary tables if @@session_track_state_change is ON then
- send the boolean tracker in the OK packet */
+ /*
+ When dropping temporary tables if @@session_track_state_change is ON
+ then send the boolean tracker in the OK packet
+ */
if(!res && (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
{
SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL);
@@ -4539,9 +4833,7 @@ mysql_execute_command(THD *thd)
#endif
case SQLCOM_CHANGE_DB:
{
- LEX_STRING db_str= { (char *) select_lex->db, strlen(select_lex->db) };
-
- if (!mysql_change_db(thd, &db_str, FALSE))
+ if (!mysql_change_db(thd, &select_lex->db, FALSE))
my_ok(thd);
break;
@@ -4580,9 +4872,9 @@ mysql_execute_command(THD *thd)
if ((check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE)
|| open_and_lock_tables(thd, all_tables, TRUE, 0)))
goto error;
- if (!(res= sql_set_variables(thd, lex_var_list, true)))
+ if (likely(!(res= sql_set_variables(thd, lex_var_list, true))))
{
- if (!thd->is_error())
+ if (likely(!thd->is_error()))
my_ok(thd);
}
else
@@ -4663,74 +4955,26 @@ mysql_execute_command(THD *thd)
break;
case SQLCOM_CREATE_DB:
{
- if (check_db_name(&lex->name))
- {
- my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str);
- break;
- }
- /*
- If in a slave thread :
- CREATE DATABASE DB was certainly not preceded by USE DB.
- For that reason, db_ok() in sql/slave.cc did not check the
- do_db/ignore_db. And as this query involves no tables, tables_ok()
- above was not called. So we have to check rules again here.
- */
-#ifdef HAVE_REPLICATION
- if (thd->slave_thread)
- {
- rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter;
- if (!rpl_filter->db_ok(lex->name.str) ||
- !rpl_filter->db_ok_with_wild_table(lex->name.str))
- {
- my_message(ER_SLAVE_IGNORED_TABLE, ER_THD(thd, ER_SLAVE_IGNORED_TABLE), MYF(0));
- break;
- }
- }
-#endif
- if (check_access(thd, lex->create_info.or_replace() ?
+ if (prepare_db_action(thd, lex->create_info.or_replace() ?
(CREATE_ACL | DROP_ACL) : CREATE_ACL,
- lex->name.str, NULL, NULL, 1, 0))
+ &lex->name))
break;
- WSREP_TO_ISOLATION_BEGIN(lex->name.str, NULL, NULL)
- res= mysql_create_db(thd, lex->name.str,
+ 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 (check_db_name(&lex->name))
- {
- my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str);
- break;
- }
- /*
- If in a slave thread :
- DROP DATABASE DB may not be preceded by USE DB.
- For that reason, maybe db_ok() in sql/slave.cc did not check the
- do_db/ignore_db. And as this query involves no tables, tables_ok()
- above was not called. So we have to check rules again here.
- */
-#ifdef HAVE_REPLICATION
- if (thd->slave_thread)
- {
- rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter;
- if (!rpl_filter->db_ok(lex->name.str) ||
- !rpl_filter->db_ok_with_wild_table(lex->name.str))
- {
- my_message(ER_SLAVE_IGNORED_TABLE, ER_THD(thd, ER_SLAVE_IGNORED_TABLE), MYF(0));
- break;
- }
- }
-#endif
- if (check_access(thd, DROP_ACL, lex->name.str, NULL, NULL, 1, 0))
+ 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.str, lex->if_exists());
+ WSREP_TO_ISOLATION_BEGIN(lex->name.str, NULL, NULL);
+ res= mysql_rm_db(thd, &lex->name, lex->if_exists());
break;
}
case SQLCOM_ALTER_DB_UPGRADE:
{
- LEX_STRING *db= & lex->name;
+ LEX_CSTRING *db= &lex->name;
#ifdef HAVE_REPLICATION
if (thd->slave_thread)
{
@@ -4744,7 +4988,7 @@ mysql_execute_command(THD *thd)
}
}
#endif
- if (check_db_name(db))
+ if (check_db_name((LEX_STRING*) db))
{
my_error(ER_WRONG_DB_NAME, MYF(0), db->str);
break;
@@ -4756,7 +5000,7 @@ mysql_execute_command(THD *thd)
res= 1;
break;
}
- WSREP_TO_ISOLATION_BEGIN(db->str, NULL, NULL)
+ WSREP_TO_ISOLATION_BEGIN(db->str, NULL, NULL);
res= mysql_upgrade_db(thd, db);
if (!res)
my_ok(thd);
@@ -4764,51 +5008,27 @@ mysql_execute_command(THD *thd)
}
case SQLCOM_ALTER_DB:
{
- LEX_STRING *db= &lex->name;
- if (check_db_name(db))
- {
- my_error(ER_WRONG_DB_NAME, MYF(0), db->str);
- break;
- }
- /*
- If in a slave thread :
- ALTER DATABASE DB may not be preceded by USE DB.
- For that reason, maybe db_ok() in sql/slave.cc did not check the
- do_db/ignore_db. And as this query involves no tables, tables_ok()
- above was not called. So we have to check rules again here.
- */
-#ifdef HAVE_REPLICATION
- if (thd->slave_thread)
- {
- rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter;
- if (!rpl_filter->db_ok(db->str) ||
- !rpl_filter->db_ok_with_wild_table(db->str))
- {
- my_message(ER_SLAVE_IGNORED_TABLE, ER_THD(thd, ER_SLAVE_IGNORED_TABLE), MYF(0));
- break;
- }
- }
-#endif
- if (check_access(thd, ALTER_ACL, db->str, NULL, NULL, 1, 0))
+ 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->str, &lex->create_info);
+ 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_STRING db_name;
+ 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.str, lex->name.str);
+ strmov(db_name_buff, lex->name.str);
WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_SHOW);
- if (check_db_name(&db_name))
+ if (check_db_name((LEX_STRING*) &db_name))
{
my_error(ER_WRONG_DB_NAME, MYF(0), db_name.str);
break;
@@ -4861,12 +5081,12 @@ mysql_execute_command(THD *thd)
break;
case SQLCOM_SHOW_CREATE_EVENT:
WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_SHOW);
- res= Events::show_create_event(thd, lex->spname->m_db,
- lex->spname->m_name);
+ res= Events::show_create_event(thd, &lex->spname->m_db,
+ &lex->spname->m_name);
break;
case SQLCOM_DROP_EVENT:
if (!(res= Events::drop_event(thd,
- lex->spname->m_db, lex->spname->m_name,
+ &lex->spname->m_db, &lex->spname->m_name,
lex->if_exists())))
my_ok(thd);
break;
@@ -4881,7 +5101,7 @@ mysql_execute_command(THD *thd)
"mysql", NULL, NULL, 1, 0))
break;
#ifdef HAVE_DLOPEN
- WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
if (!(res = mysql_create_function(thd, &lex->udf)))
my_ok(thd);
#else
@@ -4899,7 +5119,7 @@ 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)
+ 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)))
@@ -4913,7 +5133,7 @@ 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)
+ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
if (!(res= mysql_drop_user(thd, lex->users_list,
lex->sql_command == SQLCOM_DROP_ROLE)))
my_ok(thd);
@@ -4926,7 +5146,7 @@ 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)
+ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
if (lex->sql_command == SQLCOM_ALTER_USER)
res= mysql_alter_user(thd, lex->users_list);
else
@@ -4942,7 +5162,7 @@ mysql_execute_command(THD *thd)
break;
/* Conditionally writes to binlog */
- WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
if (!(res = mysql_revoke_all(thd, lex->users_list)))
my_ok(thd);
break;
@@ -4952,7 +5172,7 @@ mysql_execute_command(THD *thd)
{
if (lex->type != TYPE_ENUM_PROXY &&
check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL,
- first_table ? first_table->db : select_lex->db,
+ 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))
@@ -4992,19 +5212,18 @@ mysql_execute_command(THD *thd)
}
if (first_table)
{
- if (lex->type == TYPE_ENUM_PROCEDURE ||
- lex->type == TYPE_ENUM_FUNCTION)
+ 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,
- lex->type == TYPE_ENUM_PROCEDURE, 0))
+ 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,
- lex->type == TYPE_ENUM_PROCEDURE,
+ 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)
@@ -5016,7 +5235,7 @@ mysql_execute_command(THD *thd)
all_tables, FALSE, UINT_MAX, FALSE))
goto error;
/* Conditionally writes to binlog */
- WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+ 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);
@@ -5032,9 +5251,9 @@ mysql_execute_command(THD *thd)
}
else
{
- WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
/* Conditionally writes to binlog */
- res= mysql_grant(thd, select_lex->db, lex->users_list, lex->grant,
+ res= mysql_grant(thd, select_lex->db.str, lex->users_list, lex->grant,
lex->sql_command == SQLCOM_REVOKE,
lex->type == TYPE_ENUM_PROXY);
}
@@ -5058,7 +5277,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_REVOKE_ROLE:
case SQLCOM_GRANT_ROLE:
{
- WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+ 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);
@@ -5116,7 +5335,7 @@ mysql_execute_command(THD *thd)
REFRESH_STATUS |
REFRESH_USER_RESOURCES))
{
- WSREP_TO_ISOLATION_BEGIN_WRTCHK(WSREP_MYSQL_DB, NULL, NULL)
+ WSREP_TO_ISOLATION_BEGIN_WRTCHK(WSREP_MYSQL_DB, NULL, NULL);
}
#endif /* WITH_WSREP*/
@@ -5150,11 +5369,11 @@ mysql_execute_command(THD *thd)
*/
if (first_table)
{
- WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table);
+ WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table);
}
else
{
- WSREP_TO_ISOLATION_BEGIN_WRTCHK(WSREP_MYSQL_DB, NULL, NULL);
+ WSREP_TO_ISOLATION_BEGIN_WRTCHK(WSREP_MYSQL_DB, NULL, NULL);
}
}
#endif /* WITH_WSREP */
@@ -5202,7 +5421,7 @@ mysql_execute_command(THD *thd)
if (lex->kill_type == KILL_TYPE_ID || lex->kill_type == KILL_TYPE_QUERY)
{
Item *it= (Item *)lex->value_list.head();
- if ((!it->fixed && it->fix_fields(lex->thd, &it)) || it->check_cols(1))
+ if (it->fix_fields_if_needed_for_scalar(lex->thd, &it))
{
my_message(ER_SET_CONSTANTS_ONLY, ER_THD(thd, ER_SET_CONSTANTS_ONLY),
MYF(0));
@@ -5400,173 +5619,14 @@ mysql_execute_command(THD *thd)
break;
case SQLCOM_CREATE_PROCEDURE:
case SQLCOM_CREATE_SPFUNCTION:
+ case SQLCOM_CREATE_PACKAGE:
+ case SQLCOM_CREATE_PACKAGE_BODY:
{
- uint namelen;
- char *name;
-
- DBUG_ASSERT(lex->sphead != 0);
- DBUG_ASSERT(lex->sphead->m_db.str); /* Must be initialized in the parser */
- /*
- Verify that the database name is allowed, optionally
- lowercase it.
- */
- if (check_db_name(&lex->sphead->m_db))
- {
- my_error(ER_WRONG_DB_NAME, MYF(0), lex->sphead->m_db.str);
- goto error;
- }
-
- if (check_access(thd, CREATE_PROC_ACL, lex->sphead->m_db.str,
- NULL, NULL, 0, 0))
- goto error;
-
- /* Checking the drop permissions if CREATE OR REPLACE is used */
- if (lex->create_info.or_replace())
- {
- if (check_routine_access(thd, ALTER_PROC_ACL, lex->spname->m_db.str,
- lex->spname->m_name.str,
- lex->sql_command == SQLCOM_CREATE_PROCEDURE, 0))
- goto error;
- }
-
- name= lex->sphead->name(&namelen);
-#ifdef HAVE_DLOPEN
- if (lex->sphead->m_type == TYPE_ENUM_FUNCTION)
- {
- udf_func *udf = find_udf(name, namelen);
-
- if (udf)
- {
- my_error(ER_UDF_EXISTS, MYF(0), name);
- goto error;
- }
- }
-#endif
-
- if (sp_process_definer(thd))
- goto error;
-
- WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
- if (!sp_create_routine(thd, lex->sphead->m_type, lex->sphead))
- {
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- /* only add privileges if really neccessary */
-
- Security_context security_context;
- bool restore_backup_context= false;
- Security_context *backup= NULL;
- LEX_USER *definer= thd->lex->definer;
- /*
- We're going to issue an implicit GRANT 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 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());
- close_thread_tables(thd);
- /*
- Check if the definer exists on slave,
- then use definer privilege to insert routine privileges to mysql.procs_priv.
-
- For current user of SQL thread has GLOBAL_ACL privilege,
- which doesn't any check routine privileges,
- so no routine privilege record will insert into mysql.procs_priv.
- */
- if (thd->slave_thread && is_acl_user(definer->host.str, definer->user.str))
- {
- security_context.change_security_context(thd,
- &thd->lex->definer->user,
- &thd->lex->definer->host,
- &thd->lex->sphead->m_db,
- &backup);
- restore_backup_context= true;
- }
-
- if (sp_automatic_privileges && !opt_noacl &&
- check_routine_access(thd, DEFAULT_CREATE_PROC_ACLS,
- lex->sphead->m_db.str, name,
- lex->sql_command == SQLCOM_CREATE_PROCEDURE, 1))
- {
- if (sp_grant_privileges(thd, lex->sphead->m_db.str, name,
- lex->sql_command == SQLCOM_CREATE_PROCEDURE))
- push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
- ER_PROC_AUTO_GRANT_FAIL, ER_THD(thd, ER_PROC_AUTO_GRANT_FAIL));
- thd->clear_error();
- }
-
- /*
- Restore current user with GLOBAL_ACL privilege of SQL thread
- */
- if (restore_backup_context)
- {
- DBUG_ASSERT(thd->slave_thread == 1);
- thd->security_ctx->restore_security_context(thd, backup);
- }
-
-#endif
- }
- else
+ if (mysql_create_routine(thd, lex))
goto error;
my_ok(thd);
break; /* break super switch */
} /* end case group bracket */
- case SQLCOM_CALL:
- {
- sp_head *sp;
- /*
- This will cache all SP and SF and open and lock all tables
- required for execution.
- */
- if (check_table_access(thd, SELECT_ACL, all_tables, FALSE,
- UINT_MAX, FALSE) ||
- open_and_lock_tables(thd, all_tables, TRUE, 0))
- goto error;
-
- if (check_routine_access(thd, EXECUTE_ACL, lex->spname->m_db.str,
- lex->spname->m_name.str, TRUE, FALSE))
- goto error;
-
- /*
- By this moment all needed SPs should be in cache so no need to look
- into DB.
- */
- if (!(sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
- &thd->sp_proc_cache, TRUE)))
- {
- my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PROCEDURE",
- lex->spname->m_qname.str);
- goto error;
- }
- else
- {
- /*
- Check that the stored procedure doesn't contain Dynamic SQL
- and doesn't return result sets: such stored procedures can't
- be called from a function or trigger.
- */
- if (thd->in_sub_stmt)
- {
- const char *where= (thd->in_sub_stmt & SUB_STMT_TRIGGER ?
- "trigger" : "function");
- if (sp->is_not_allowed_in_function(where))
- goto error;
- }
-
- if (do_execute_sp(thd, sp))
- goto error;
- }
- break;
- }
-
case SQLCOM_COMPOUND:
DBUG_ASSERT(all_tables == 0);
DBUG_ASSERT(thd->in_sub_stmt == 0);
@@ -5579,13 +5639,9 @@ mysql_execute_command(THD *thd)
case SQLCOM_ALTER_FUNCTION:
{
int sp_result;
- enum stored_procedure_type type;
- type= (lex->sql_command == SQLCOM_ALTER_PROCEDURE ?
- TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION);
-
- if (check_routine_access(thd, ALTER_PROC_ACL, lex->spname->m_db.str,
- lex->spname->m_name.str,
- lex->sql_command == SQLCOM_ALTER_PROCEDURE, 0))
+ 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;
/*
@@ -5595,7 +5651,7 @@ mysql_execute_command(THD *thd)
already puts on CREATE FUNCTION.
*/
/* Conditionally writes to binlog */
- sp_result= sp_update_routine(thd, type, lex->spname, &lex->sp_chistics);
+ sp_result= sph->sp_update_routine(thd, lex->spname, &lex->sp_chistics);
switch (sp_result)
{
case SP_OK:
@@ -5603,17 +5659,19 @@ mysql_execute_command(THD *thd)
break;
case SP_KEY_NOT_FOUND:
my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
- SP_COM_STRING(lex), lex->spname->m_qname.str);
+ sph->type_str(), ErrConvDQName(lex->spname).ptr());
goto error;
default:
my_error(ER_SP_CANT_ALTER, MYF(0),
- SP_COM_STRING(lex), lex->spname->m_qname.str);
+ sph->type_str(), ErrConvDQName(lex->spname).ptr());
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 &&
@@ -5657,19 +5715,15 @@ mysql_execute_command(THD *thd)
#endif
int sp_result;
- enum stored_procedure_type type;
- type= (lex->sql_command == SQLCOM_DROP_PROCEDURE ?
- TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION);
- char *db= lex->spname->m_db.str;
- char *name= lex->spname->m_name.str;
-
- if (check_routine_access(thd, ALTER_PROC_ACL, db, name,
- lex->sql_command == SQLCOM_DROP_PROCEDURE, 0))
+ 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)
+ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
/* Conditionally writes to binlog */
- sp_result= sp_drop_routine(thd, type, lex->spname);
+ sp_result= sph->sp_drop_routine(thd, lex->spname);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/*
@@ -5692,8 +5746,8 @@ mysql_execute_command(THD *thd)
if (sp_result != SP_KEY_NOT_FOUND &&
sp_automatic_privileges && !opt_noacl &&
- sp_revoke_privileges(thd, db, name,
- lex->sql_command == SQLCOM_DROP_PROCEDURE))
+ 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,
@@ -5714,51 +5768,52 @@ mysql_execute_command(THD *thd)
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),
- SP_COM_STRING(lex), lex->spname->m_qname.str);
+ sph->type_str(),
+ ErrConvDQName(lex->spname).ptr());
if (!res)
my_ok(thd);
break;
}
my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
- SP_COM_STRING(lex), lex->spname->m_qname.str);
+ sph->type_str(), ErrConvDQName(lex->spname).ptr());
goto error;
default:
my_error(ER_SP_DROP_FAILED, MYF(0),
- SP_COM_STRING(lex), lex->spname->m_qname.str);
+ sph->type_str(), ErrConvDQName(lex->spname).ptr());
goto error;
}
break;
}
case SQLCOM_SHOW_CREATE_PROC:
- {
- WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_SHOW);
- if (sp_show_create_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname))
- goto error;
- break;
- }
case SQLCOM_SHOW_CREATE_FUNC:
+ case SQLCOM_SHOW_CREATE_PACKAGE:
+ case SQLCOM_SHOW_CREATE_PACKAGE_BODY:
{
WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_SHOW);
- if (sp_show_create_routine(thd, TYPE_ENUM_FUNCTION, lex->spname))
- goto error;
+ const Sp_handler *sph= Sp_handler::handler(lex->sql_command);
+ if (sph->sp_show_create_routine(thd, lex->spname))
+ goto error;
break;
}
case SQLCOM_SHOW_PROC_CODE:
case SQLCOM_SHOW_FUNC_CODE:
+ case SQLCOM_SHOW_PACKAGE_BODY_CODE:
{
#ifndef DBUG_OFF
+ Database_qualified_name pkgname(&null_clex_str, &null_clex_str);
sp_head *sp;
- stored_procedure_type type= (lex->sql_command == SQLCOM_SHOW_PROC_CODE ?
- TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION);
-
+ const Sp_handler *sph= Sp_handler::handler(lex->sql_command);
WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_SHOW);
- if (sp_cache_routine(thd, type, lex->spname, FALSE, &sp))
+ if (sph->sp_resolve_package_routine(thd, thd->lex->sphead,
+ lex->spname, &sph, &pkgname))
+ return true;
+ if (sph->sp_cache_routine(thd, lex->spname, false, &sp))
goto error;
if (!sp || sp->show_routine_code(thd))
{
/* We don't distinguish between errors for now */
my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
- SP_COM_STRING(lex), lex->spname->m_name.str);
+ sph->type_str(), lex->spname->m_name.str);
goto error;
}
break;
@@ -5783,9 +5838,9 @@ mysql_execute_command(THD *thd)
{
/*
Note: SQLCOM_CREATE_VIEW also handles 'ALTER VIEW' commands
- as specified through the thd->lex->create_view_mode flag.
+ as specified through the thd->lex->create_view->mode flag.
*/
- res= mysql_create_view(thd, first_table, thd->lex->create_view_mode);
+ res= mysql_create_view(thd, first_table, thd->lex->create_view->mode);
break;
}
case SQLCOM_DROP_VIEW:
@@ -5793,7 +5848,7 @@ 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)
+ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
res= mysql_drop_view(thd, first_table, thd->lex->drop_mode);
break;
}
@@ -5896,7 +5951,7 @@ mysql_execute_command(THD *thd)
if (check_global_access(thd, SUPER_ACL))
break;
- WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
res= create_server(thd, &lex->server_options);
break;
@@ -5909,9 +5964,9 @@ mysql_execute_command(THD *thd)
if (check_global_access(thd, SUPER_ACL))
break;
- WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
- if ((error= alter_server(thd, &lex->server_options)))
+ if (unlikely((error= alter_server(thd, &lex->server_options))))
{
DBUG_PRINT("info", ("problem altering server <%s>",
lex->server_options.server_name.str));
@@ -5929,7 +5984,7 @@ mysql_execute_command(THD *thd)
if (check_global_access(thd, SUPER_ACL))
break;
- WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
if ((err_code= drop_server(thd, &lex->server_options)))
{
@@ -5954,12 +6009,15 @@ mysql_execute_command(THD *thd)
case SQLCOM_REPAIR:
case SQLCOM_TRUNCATE:
case SQLCOM_CREATE_TABLE:
+ case SQLCOM_CREATE_SEQUENCE:
case SQLCOM_ALTER_TABLE:
DBUG_ASSERT(first_table == all_tables && first_table != 0);
/* fall through */
+ case SQLCOM_ALTER_SEQUENCE:
case SQLCOM_SIGNAL:
case SQLCOM_RESIGNAL:
case SQLCOM_GET_DIAGNOSTICS:
+ case SQLCOM_CALL:
DBUG_ASSERT(lex->m_sql_cmd != NULL);
res= lex->m_sql_cmd->execute(thd);
break;
@@ -6007,8 +6065,12 @@ finish:
}
thd->reset_kill_query();
}
- if (thd->is_error() || (thd->variables.option_bits & OPTION_MASTER_SQL_ERROR))
+ if (unlikely(thd->is_error()) ||
+ (thd->variables.option_bits & OPTION_MASTER_SQL_ERROR))
+ {
+ THD_STAGE_INFO(thd, stage_rollback);
trans_rollback_stmt(thd);
+ }
#ifdef WITH_WSREP
else if (thd->spcont &&
(thd->wsrep_conflict_state == MUST_ABORT ||
@@ -6031,6 +6093,7 @@ finish:
else
{
/* If commit fails, we should be able to reset the OK status. */
+ THD_STAGE_INFO(thd, stage_commit);
thd->get_stmt_da()->set_overwrite_status(true);
trans_commit_stmt(thd);
thd->get_stmt_da()->set_overwrite_status(false);
@@ -6040,12 +6103,13 @@ finish:
#endif
}
- /* Free tables */
+ /* Free tables. Set stage 'closing tables' */
close_thread_tables(thd);
#ifdef WITH_WSREP
thd->wsrep_consistency_check= NO_CONSISTENCY_CHECK;
#endif /* WITH_WSREP */
+
#ifndef DBUG_OFF
if (lex->sql_command != SQLCOM_SET_OPTION && ! thd->in_sub_stmt)
DEBUG_SYNC(thd, "execute_command_after_close_tables");
@@ -6063,6 +6127,7 @@ finish:
one of storage engines (e.g. due to deadlock). Rollback transaction in
all storage engines including binary log.
*/
+ THD_STAGE_INFO(thd, stage_rollback_implicit);
trans_rollback_implicit(thd);
thd->mdl_context.release_transactional_locks();
}
@@ -6072,6 +6137,7 @@ finish:
DBUG_ASSERT(! thd->in_sub_stmt);
if (!(thd->variables.option_bits & OPTION_GTID_BEGIN))
{
+ THD_STAGE_INFO(thd, stage_commit_implicit);
/* If commit fails, we should be able to reset the OK status. */
thd->get_stmt_da()->set_overwrite_status(true);
/* Commit the normal transaction if one is active. */
@@ -6099,6 +6165,8 @@ finish:
thd->mdl_context.release_statement_locks();
}
+ THD_STAGE_INFO(thd, stage_starting_cleanup);
+
TRANSACT_TRACKER(add_trx_state_from_thd(thd));
WSREP_TO_ISOLATION_END;
@@ -6146,7 +6214,7 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
to prepend EXPLAIN to any query and receive output for it,
even if the query itself redirects the output.
*/
- if (!(result= new (thd->mem_root) select_send(thd)))
+ if (unlikely(!(result= new (thd->mem_root) select_send(thd))))
return 1; /* purecov: inspected */
thd->send_explain_fields(result, lex->describe, lex->analyze_stmt);
@@ -6157,7 +6225,7 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
res= mysql_explain_union(thd, &lex->unit, result);
/* Print EXPLAIN only if we don't have an error */
- if (!res)
+ if (likely(!res))
{
/*
Do like the original select_describe did: remove OFFSET from the
@@ -6328,11 +6396,11 @@ static bool check_rename_table(THD *thd, TABLE_LIST *first_table,
TABLE_LIST *table;
for (table= first_table; table; table= table->next_local->next_local)
{
- if (check_access(thd, ALTER_ACL | DROP_ACL, table->db,
+ if (check_access(thd, ALTER_ACL | DROP_ACL, table->db.str,
&table->grant.privilege,
&table->grant.m_internal,
0, 0) ||
- check_access(thd, INSERT_ACL | CREATE_ACL, table->next_local->db,
+ check_access(thd, INSERT_ACL | CREATE_ACL, table->next_local->db.str,
&table->next_local->grant.privilege,
&table->next_local->grant.m_internal,
0, 0))
@@ -6425,13 +6493,15 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
dummy= 0;
}
- THD_STAGE_INFO(thd, stage_checking_permissions);
- if ((!db || !db[0]) && !thd->db && !dont_check_global_grants)
+ /* check access may be called twice in a row. Don't change to same stage */
+ if (thd->proc_info != stage_checking_permissions.m_name)
+ THD_STAGE_INFO(thd, stage_checking_permissions);
+ if (unlikely((!db || !db[0]) && !thd->db.str && !dont_check_global_grants))
{
DBUG_RETURN(FALSE); // CTE reference or an error later
}
- if ((db != NULL) && (db != any_db))
+ if (likely((db != NULL) && (db != any_db)))
{
/*
Check if this is reserved database, like information schema or
@@ -6478,7 +6548,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
*/
if (!(sctx->master_access & SELECT_ACL))
{
- if (db && (!thd->db || db_is_pattern || strcmp(db, thd->db)))
+ if (db && (!thd->db.str || db_is_pattern || strcmp(db, thd->db.str)))
{
db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db,
db_is_pattern);
@@ -6501,8 +6571,8 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
*save_priv|= sctx->master_access;
DBUG_RETURN(FALSE);
}
- if (((want_access & ~sctx->master_access) & ~DB_ACLS) ||
- (! db && dont_check_global_grants))
+ if (unlikely(((want_access & ~sctx->master_access) & ~DB_ACLS) ||
+ (! db && dont_check_global_grants)))
{ // We can never grant this
DBUG_PRINT("error",("No possible access"));
if (!no_errors)
@@ -6518,7 +6588,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
DBUG_RETURN(TRUE); /* purecov: tested */
}
- if (db == any_db)
+ if (unlikely(db == any_db))
{
/*
Access granted; Allow select on *any* db.
@@ -6527,7 +6597,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
DBUG_RETURN(FALSE);
}
- if (db && (!thd->db || db_is_pattern || strcmp(db,thd->db)))
+ if (db && (!thd->db.str || db_is_pattern || strcmp(db, thd->db.str)))
{
db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db,
db_is_pattern);
@@ -6582,8 +6652,8 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
status_var_increment(thd->status_var.access_denied_errors);
my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
sctx->priv_user, sctx->priv_host,
- (db ? db : (thd->db ?
- thd->db :
+ (db ? db : (thd->db.str ?
+ thd->db.str :
"unknown")));
}
DBUG_RETURN(TRUE);
@@ -6617,7 +6687,7 @@ bool check_single_table_access(THD *thd, ulong privilege,
!all_tables->schema_table)
db_name= all_tables->view_db.str;
else
- db_name= all_tables->db;
+ db_name= all_tables->db.str;
if (check_access(thd, privilege, db_name,
&all_tables->grant.privilege,
@@ -6701,7 +6771,7 @@ static bool check_show_access(THD *thd, TABLE_LIST *table)
case SCH_TRIGGERS:
case SCH_EVENTS:
{
- const char *dst_db_name= table->schema_select_lex->db;
+ const char *dst_db_name= table->schema_select_lex->db.str;
DBUG_ASSERT(dst_db_name);
@@ -6736,7 +6806,7 @@ static bool check_show_access(THD *thd, TABLE_LIST *table)
if (thd->open_temporary_tables(dst_table))
return TRUE;
- if (check_access(thd, SELECT_ACL, dst_table->db,
+ if (check_access(thd, SELECT_ACL, dst_table->db.str,
&dst_table->grant.privilege,
&dst_table->grant.m_internal,
FALSE, FALSE))
@@ -6840,6 +6910,15 @@ check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables,
if (table_ref->is_anonymous_derived_table())
continue;
+ if (table_ref->sequence)
+ {
+ /* We want to have either SELECT or INSERT rights to sequences depending
+ on how they are accessed
+ */
+ want_access= ((table_ref->lock_type == TL_WRITE_ALLOW_WRITE) ?
+ INSERT_ACL : SELECT_ACL);
+ }
+
if (check_access(thd, want_access, table_ref->get_db_name(),
&table_ref->grant.privilege,
&table_ref->grant.m_internal,
@@ -6853,14 +6932,15 @@ check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables,
bool
-check_routine_access(THD *thd, ulong want_access,char *db, char *name,
- bool is_proc, bool no_errors)
+check_routine_access(THD *thd, ulong want_access, const LEX_CSTRING *db,
+ const LEX_CSTRING *name,
+ const Sp_handler *sph, bool no_errors)
{
TABLE_LIST tables[1];
bzero((char *)tables, sizeof(TABLE_LIST));
- tables->db= db;
- tables->table_name= tables->alias= name;
+ tables->db= *db;
+ tables->table_name= tables->alias= *name;
/*
The following test is just a shortcut for check_access() (to avoid
@@ -6877,13 +6957,13 @@ check_routine_access(THD *thd, ulong want_access,char *db, char *name,
DBUG_ASSERT((want_access & CREATE_PROC_ACL) == 0);
if ((thd->security_ctx->master_access & want_access) == want_access)
tables->grant.privilege= want_access;
- else if (check_access(thd, want_access, db,
+ else if (check_access(thd, want_access, db->str,
&tables->grant.privilege,
&tables->grant.m_internal,
0, no_errors))
return TRUE;
- return check_grant_routine(thd, want_access, tables, is_proc, no_errors);
+ return check_grant_routine(thd, want_access, tables, sph, no_errors);
}
@@ -6901,7 +6981,7 @@ check_routine_access(THD *thd, ulong want_access,char *db, char *name,
*/
bool check_some_routine_access(THD *thd, const char *db, const char *name,
- bool is_proc)
+ const Sp_handler *sph)
{
ulong save_priv;
/*
@@ -6918,7 +6998,7 @@ bool check_some_routine_access(THD *thd, const char *db, const char *name,
if (!check_access(thd, SHOW_PROC_ACLS, db, &save_priv, NULL, 0, 1) ||
(save_priv & SHOW_PROC_ACLS))
return FALSE;
- return check_routine_level_acl(thd, db, name, is_proc);
+ return check_routine_level_acl(thd, db, name, sph);
}
@@ -6944,7 +7024,7 @@ bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table)
{
if (access & want_access)
{
- if (!check_access(thd, access, table->db,
+ if (!check_access(thd, access, table->db.str,
&table->grant.privilege,
&table->grant.m_internal,
0, 1) &&
@@ -6983,7 +7063,7 @@ bool check_global_access(THD *thd, ulong want_access, bool no_errors)
char command[128];
if ((thd->security_ctx->master_access & want_access))
return 0;
- if (!no_errors)
+ if (unlikely(!no_errors))
{
get_privilege_desc(command, sizeof(command), want_access);
my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command);
@@ -7026,8 +7106,8 @@ bool check_fk_parent_table_access(THD *thd,
TABLE_LIST parent_table;
bool is_qualified_table_name;
Foreign_key *fk_key= (Foreign_key *)key;
- LEX_STRING db_name;
- LEX_STRING table_name= { fk_key->ref_table.str,
+ 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);
@@ -7043,12 +7123,13 @@ bool check_fk_parent_table_access(THD *thd,
if (fk_key->ref_db.str)
{
is_qualified_table_name= true;
- db_name.str= (char *) thd->memdup(fk_key->ref_db.str,
- fk_key->ref_db.length+1);
+ if (!(db_name.str= (char *) thd->memdup(fk_key->ref_db.str,
+ fk_key->ref_db.length+1)))
+ return true;
db_name.length= fk_key->ref_db.length;
// Check if database name is valid or not.
- if (fk_key->ref_db.str && check_db_name(&db_name))
+ if (check_db_name((LEX_STRING*) &db_name))
{
my_error(ER_WRONG_DB_NAME, MYF(0), db_name.str);
return true;
@@ -7056,13 +7137,16 @@ bool check_fk_parent_table_access(THD *thd,
}
else
{
- if (!thd->db)
+ if (!thd->db.str)
{
- db_name.str= (char *) thd->memdup(create_db, strlen(create_db)+1);
+ DBUG_ASSERT(create_db);
db_name.length= strlen(create_db);
+ if (!(db_name.str= (char *) thd->memdup(create_db,
+ db_name.length+1)))
+ return true;
is_qualified_table_name= true;
- if(create_db && check_db_name(&db_name))
+ if (check_db_name((LEX_STRING*) &db_name))
{
my_error(ER_WRONG_DB_NAME, MYF(0), db_name.str);
return true;
@@ -7070,7 +7154,7 @@ bool check_fk_parent_table_access(THD *thd,
}
else
{
- if (thd->lex->copy_db_to(&db_name.str, &db_name.length))
+ if (thd->lex->copy_db_to(&db_name))
return true;
else
is_qualified_table_name= false;
@@ -7080,15 +7164,14 @@ bool check_fk_parent_table_access(THD *thd,
// if lower_case_table_names is set then convert tablename to lower case.
if (lower_case_table_names)
{
- table_name.str= (char *) thd->memdup(fk_key->ref_table.str,
- fk_key->ref_table.length+1);
- table_name.length= my_casedn_str(files_charset_info, table_name.str);
- db_name.length = my_casedn_str(files_charset_info, db_name.str);
+ char *name;
+ table_name.str= name= (char *) thd->memdup(fk_key->ref_table.str,
+ fk_key->ref_table.length+1);
+ table_name.length= my_casedn_str(files_charset_info, name);
+ db_name.length= my_casedn_str(files_charset_info, (char*) db_name.str);
}
- parent_table.init_one_table(db_name.str, db_name.length,
- table_name.str, table_name.length,
- table_name.str, TL_IGNORE);
+ parent_table.init_one_table(&db_name, &table_name, 0, TL_IGNORE);
/*
Check if user has any of the "privileges" at table level on
@@ -7173,16 +7256,16 @@ bool check_stack_overrun(THD *thd, long margin,
#define MY_YACC_INIT 1000 // Start with big alloc
#define MY_YACC_MAX 32000 // Because of 'short'
-bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize)
+bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, size_t *yystacksize)
{
Yacc_state *state= & current_thd->m_parser_state->m_yacc;
- ulong old_info=0;
+ size_t old_info=0;
DBUG_ASSERT(state);
- if ((uint) *yystacksize >= MY_YACC_MAX)
+ if ( *yystacksize >= MY_YACC_MAX)
return 1;
if (!state->yacc_yyvs)
old_info= *yystacksize;
- *yystacksize= set_zone((*yystacksize)*2,MY_YACC_INIT,MY_YACC_MAX);
+ *yystacksize= set_zone((int)(*yystacksize)*2,MY_YACC_INIT,MY_YACC_MAX);
if (!(state->yacc_yyvs= (uchar*)
my_realloc(state->yacc_yyvs,
*yystacksize*sizeof(**yyvs),
@@ -7223,17 +7306,16 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize)
void THD::reset_for_next_command(bool do_clear_error)
{
- THD *thd= this;
DBUG_ENTER("THD::reset_for_next_command");
- DBUG_ASSERT(!thd->spcont); /* not for substatements of routines */
- DBUG_ASSERT(! thd->in_sub_stmt);
+ DBUG_ASSERT(!spcont); /* not for substatements of routines */
+ DBUG_ASSERT(!in_sub_stmt);
- if (do_clear_error)
+ if (likely(do_clear_error))
clear_error(1);
- thd->free_list= 0;
+ free_list= 0;
/*
- We also assign thd->stmt_lex in lex_start(), but during bootstrap this
+ We also assign stmt_lex in lex_start(), but during bootstrap this
code is executed first.
*/
DBUG_ASSERT(lex == &main_lex);
@@ -7243,8 +7325,8 @@ void THD::reset_for_next_command(bool do_clear_error)
Those two lines below are theoretically unneeded as
THD::cleanup_after_query() should take care of this already.
*/
- thd->auto_inc_intervals_in_cur_stmt_for_binlog.empty();
- thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0;
+ auto_inc_intervals_in_cur_stmt_for_binlog.empty();
+ stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0;
#ifdef WITH_WSREP
/*
@@ -7255,58 +7337,56 @@ void THD::reset_for_next_command(bool do_clear_error)
use autoinc values passed in binlog events, not the values forced by
the cluster.
*/
- if (WSREP(thd) && thd->wsrep_exec_mode == LOCAL_STATE &&
- !thd->slave_thread && wsrep_auto_increment_control)
+ if (WSREP(this) && wsrep_exec_mode == LOCAL_STATE &&
+ !slave_thread && wsrep_auto_increment_control)
{
- thd->variables.auto_increment_offset=
+ variables.auto_increment_offset=
global_system_variables.auto_increment_offset;
- thd->variables.auto_increment_increment=
+ variables.auto_increment_increment=
global_system_variables.auto_increment_increment;
}
#endif /* WITH_WSREP */
- thd->query_start_used= 0;
- thd->query_start_sec_part_used= 0;
- thd->is_fatal_error= thd->time_zone_used= 0;
- thd->log_current_statement= 0;
+ query_start_sec_part_used= 0;
+ is_fatal_error= time_zone_used= 0;
+ log_current_statement= 0;
/*
Clear the status flag that are expected to be cleared at the
beginning of each SQL statement.
*/
- thd->server_status&= ~SERVER_STATUS_CLEAR_SET;
+ server_status&= ~SERVER_STATUS_CLEAR_SET;
/*
If in autocommit mode and not in a transaction, reset
OPTION_STATUS_NO_TRANS_UPDATE | OPTION_KEEP_LOG to not get warnings
in ha_rollback_trans() about some tables couldn't be rolled back.
*/
- if (!thd->in_multi_stmt_transaction_mode())
+ if (!in_multi_stmt_transaction_mode())
{
- thd->variables.option_bits&= ~OPTION_KEEP_LOG;
- thd->transaction.all.reset();
+ variables.option_bits&= ~OPTION_KEEP_LOG;
+ transaction.all.reset();
}
- DBUG_ASSERT(thd->security_ctx== &thd->main_security_ctx);
- thd->thread_specific_used= FALSE;
+ DBUG_ASSERT(security_ctx== &main_security_ctx);
+ thread_specific_used= FALSE;
if (opt_bin_log)
- reset_dynamic(&thd->user_var_events);
- DBUG_ASSERT(thd->user_var_events_alloc == &thd->main_mem_root);
+ reset_dynamic(&user_var_events);
+ DBUG_ASSERT(user_var_events_alloc == &main_mem_root);
+ enable_slow_log= true;
+ get_stmt_da()->reset_for_next_command();
+ rand_used= 0;
+ m_sent_row_count= m_examined_row_count= 0;
+ accessed_rows_and_keys= 0;
- thd->get_stmt_da()->reset_for_next_command();
- thd->rand_used= 0;
- thd->m_sent_row_count= thd->m_examined_row_count= 0;
- thd->accessed_rows_and_keys= 0;
+ reset_slow_query_state();
- thd->query_plan_flags= QPLAN_INIT;
- thd->query_plan_fsort_passes= 0;
-
- thd->reset_current_stmt_binlog_format_row();
- thd->binlog_unsafe_warning_flags= 0;
+ reset_current_stmt_binlog_format_row();
+ binlog_unsafe_warning_flags= 0;
- thd->save_prep_leaf_list= false;
+ save_prep_leaf_list= false;
DBUG_PRINT("debug",
("is_current_stmt_binlog_format_row(): %d",
- thd->is_current_stmt_binlog_format_row()));
+ is_current_stmt_binlog_format_row()));
DBUG_VOID_RETURN;
}
@@ -7347,18 +7427,21 @@ mysql_init_select(LEX *lex)
*/
bool
-mysql_new_select(LEX *lex, bool move_down)
+mysql_new_select(LEX *lex, bool move_down, SELECT_LEX *select_lex)
{
- SELECT_LEX *select_lex;
THD *thd= lex->thd;
+ bool new_select= select_lex == NULL;
DBUG_ENTER("mysql_new_select");
- if (!(select_lex= new (thd->mem_root) SELECT_LEX()))
- DBUG_RETURN(1);
- select_lex->select_number= ++thd->lex->stmt_lex->current_select_number;
- select_lex->parent_lex= lex; /* Used in init_query. */
- select_lex->init_query();
- select_lex->init_select();
+ if (new_select)
+ {
+ if (!(select_lex= new (thd->mem_root) SELECT_LEX()))
+ DBUG_RETURN(1);
+ select_lex->select_number= ++thd->lex->stmt_lex->current_select_number;
+ select_lex->parent_lex= lex; /* Used in init_query. */
+ select_lex->init_query();
+ select_lex->init_select();
+ }
lex->nest_level++;
if (lex->nest_level > (int) MAX_SELECT_NESTING)
{
@@ -7370,13 +7453,11 @@ mysql_new_select(LEX *lex, bool move_down)
if (move_down)
{
SELECT_LEX_UNIT *unit;
- lex->subqueries= TRUE;
/* first select_lex of subselect or derived table */
if (!(unit= new (thd->mem_root) SELECT_LEX_UNIT()))
DBUG_RETURN(1);
unit->init_query();
- unit->init_select();
unit->thd= thd;
unit->include_down(lex->current_select);
unit->link_next= 0;
@@ -7429,7 +7510,8 @@ mysql_new_select(LEX *lex, bool move_down)
unit->first_select()->context.outer_context;
}
- select_lex->include_global((st_select_lex_node**)&lex->all_selects_list);
+ if (new_select)
+ select_lex->include_global((st_select_lex_node**)&lex->all_selects_list);
lex->current_select= select_lex;
/*
in subquery is SELECT query and we allow resolution of names in SELECT
@@ -7449,28 +7531,23 @@ mysql_new_select(LEX *lex, bool move_down)
@param var_name Variable name
*/
-void create_select_for_variable(const char *var_name)
+void create_select_for_variable(THD *thd, LEX_CSTRING *var_name)
{
- THD *thd;
LEX *lex;
- LEX_STRING tmp;
Item *var;
char buff[MAX_SYS_VAR_LENGTH*2+4+8], *end;
DBUG_ENTER("create_select_for_variable");
- thd= current_thd;
lex= thd->lex;
mysql_init_select(lex);
lex->sql_command= SQLCOM_SELECT;
- tmp.str= (char*) var_name;
- tmp.length=strlen(var_name);
/*
We set the name of Item to @@session.var_name because that then is used
as the column name in the output.
*/
- if ((var= get_system_var(thd, OPT_SESSION, tmp, null_lex_str)))
+ if ((var= get_system_var(thd, OPT_SESSION, var_name, &null_clex_str)))
{
- end= strxmov(buff, "@@session.", var_name, NullS);
+ end= strxmov(buff, "@@session.", var_name->str, NullS);
var->set_name(thd, buff, (uint)(end-buff), system_charset_info);
add_item_to_list(thd, var);
}
@@ -7685,7 +7762,7 @@ void mysql_parse(THD *thd, char *rawbuf, uint length,
bool err= parse_sql(thd, parser_state, NULL, true);
- if (!err)
+ if (likely(!err))
{
thd->m_statement_psi=
MYSQL_REFINE_STATEMENT(thd->m_statement_psi,
@@ -7700,7 +7777,7 @@ void mysql_parse(THD *thd, char *rawbuf, uint length,
else
#endif
{
- if (! thd->is_error())
+ if (likely(! thd->is_error()))
{
const char *found_semicolon= parser_state->m_lip.found_semicolon;
/*
@@ -7725,7 +7802,7 @@ void mysql_parse(THD *thd, char *rawbuf, uint length,
lex->set_trg_event_type_for_tables();
MYSQL_QUERY_EXEC_START(thd->query(),
thd->thread_id,
- (char *) (thd->db ? thd->db : ""),
+ thd->get_db(),
&thd->security_ctx->priv_user[0],
(char *) thd->security_ctx->host_or_ip,
0);
@@ -7750,6 +7827,8 @@ void mysql_parse(THD *thd, char *rawbuf, uint length,
THD_STAGE_INFO(thd, stage_freeing_items);
sp_cache_enforce_limit(thd->sp_proc_cache, stored_program_cache_size);
sp_cache_enforce_limit(thd->sp_func_cache, stored_program_cache_size);
+ sp_cache_enforce_limit(thd->sp_package_spec_cache, stored_program_cache_size);
+ sp_cache_enforce_limit(thd->sp_package_body_cache, stored_program_cache_size);
thd->end_statement();
thd->Item_change_list::rollback_item_tree_changes();
thd->cleanup_after_query();
@@ -7792,7 +7871,7 @@ bool mysql_test_parse_for_slave(THD *thd, char *rawbuf, uint length)
DBUG_ENTER("mysql_test_parse_for_slave");
Parser_state parser_state;
- if (!(error= parser_state.init(thd, rawbuf, length)))
+ if (likely(!(error= parser_state.init(thd, rawbuf, length))))
{
lex_start(thd);
thd->reset_for_next_command();
@@ -7814,7 +7893,7 @@ add_proc_to_list(THD* thd, Item *item)
ORDER *order;
Item **item_ptr;
- if (!(order = (ORDER *) thd->alloc(sizeof(ORDER)+sizeof(Item*))))
+ if (unlikely(!(order = (ORDER *) thd->alloc(sizeof(ORDER)+sizeof(Item*)))))
return 1;
item_ptr = (Item**) (order+1);
*item_ptr= item;
@@ -7832,7 +7911,7 @@ bool add_to_list(THD *thd, SQL_I_List<ORDER> &list, Item *item,bool asc)
{
ORDER *order;
DBUG_ENTER("add_to_list");
- if (!(order = (ORDER *) thd->alloc(sizeof(ORDER))))
+ if (unlikely(!(order = (ORDER *) thd->alloc(sizeof(ORDER)))))
DBUG_RETURN(1);
order->item_ptr= item;
order->item= &order->item_ptr;
@@ -7867,7 +7946,7 @@ bool add_to_list(THD *thd, SQL_I_List<ORDER> &list, Item *item,bool asc)
TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
Table_ident *table,
- LEX_STRING *alias,
+ LEX_CSTRING *alias,
ulong table_options,
thr_lock_type lock_type,
enum_mdl_type mdl_type,
@@ -7877,47 +7956,48 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
{
TABLE_LIST *ptr;
TABLE_LIST *UNINIT_VAR(previous_table_ref); /* The table preceding the current one. */
- char *alias_str;
+ LEX_CSTRING alias_str;
LEX *lex= thd->lex;
DBUG_ENTER("add_table_to_list");
- if (!table)
+ if (unlikely(!table))
DBUG_RETURN(0); // End of memory
- alias_str= alias ? alias->str : table->table.str;
+ alias_str= alias ? *alias : table->table;
+ DBUG_ASSERT(alias_str.str);
if (!MY_TEST(table_options & TL_OPTION_ALIAS) &&
- check_table_name(table->table.str, table->table.length, FALSE))
+ unlikely(check_table_name(table->table.str, table->table.length, FALSE)))
{
my_error(ER_WRONG_TABLE_NAME, MYF(0), table->table.str);
DBUG_RETURN(0);
}
- if (table->is_derived_table() == FALSE && table->db.str &&
- check_db_name(&table->db))
+ if (unlikely(table->is_derived_table() == FALSE && table->db.str &&
+ check_db_name((LEX_STRING*) &table->db)))
{
my_error(ER_WRONG_DB_NAME, MYF(0), table->db.str);
DBUG_RETURN(0);
}
- if (!alias) /* Alias is case sensitive */
+ if (!alias) /* Alias is case sensitive */
{
- if (table->sel)
+ if (unlikely(table->sel))
{
my_message(ER_DERIVED_MUST_HAVE_ALIAS,
ER_THD(thd, ER_DERIVED_MUST_HAVE_ALIAS), MYF(0));
DBUG_RETURN(0);
}
- if (!(alias_str= (char*) thd->memdup(alias_str,table->table.length+1)))
+ /* alias_str points to table->table; Let's make a copy */
+ if (unlikely(!(alias_str.str= (char*) thd->memdup(alias_str.str, alias_str.length+1))))
DBUG_RETURN(0);
}
- if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))))
+ if (unlikely(!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST)))))
DBUG_RETURN(0); /* purecov: inspected */
if (table->db.str)
{
ptr->is_fqtn= TRUE;
- ptr->db= table->db.str;
- ptr->db_length= table->db.length;
+ ptr->db= table->db;
}
- else if (lex->copy_db_to(&ptr->db, &ptr->db_length))
+ else if (lex->copy_db_to(&ptr->db))
DBUG_RETURN(0);
else
ptr->is_fqtn= FALSE;
@@ -7927,20 +8007,21 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
if (lower_case_table_names)
{
if (table->table.length)
- table->table.length= my_casedn_str(files_charset_info, table->table.str);
- if (ptr->db_length && ptr->db != any_db)
- ptr->db_length= my_casedn_str(files_charset_info, ptr->db);
+ table->table.length= my_casedn_str(files_charset_info,
+ (char*) table->table.str);
+ if (ptr->db.length && ptr->db.str != any_db)
+ ptr->db.length= my_casedn_str(files_charset_info, (char*) ptr->db.str);
}
- ptr->table_name=table->table.str;
- ptr->table_name_length=table->table.length;
+ ptr->table_name= table->table;
ptr->lock_type= lock_type;
ptr->updating= MY_TEST(table_options & TL_OPTION_UPDATING);
/* TODO: remove TL_OPTION_FORCE_INDEX as it looks like it's not used */
ptr->force_index= MY_TEST(table_options & TL_OPTION_FORCE_INDEX);
ptr->ignore_leaves= MY_TEST(table_options & TL_OPTION_IGNORE_LEAVES);
+ ptr->sequence= MY_TEST(table_options & TL_OPTION_SEQUENCE);
ptr->derived= table->sel;
- if (!ptr->derived && is_infoschema_db(ptr->db, ptr->db_length))
+ if (!ptr->derived && is_infoschema_db(&ptr->db))
{
if (ptr->updating &&
/* Special cases which are processed by commands itself */
@@ -7954,7 +8035,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
DBUG_RETURN(0);
}
ST_SCHEMA_TABLE *schema_table;
- schema_table= find_schema_table(thd, ptr->table_name);
+ schema_table= find_schema_table(thd, &ptr->table_name);
ptr->schema_table_name= ptr->table_name;
ptr->schema_table= schema_table;
}
@@ -7966,8 +8047,8 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
ptr->cacheable_table= !table->is_derived_table();
ptr->index_hints= index_hints_arg;
ptr->option= option ? option->str : 0;
- /* check that used name is unique */
- if (lock_type != TL_IGNORE)
+ /* check that used name is unique. Sequences are ignored */
+ if (lock_type != TL_IGNORE && !ptr->sequence)
{
TABLE_LIST *first_table= table_list.first;
if (lex->sql_command == SQLCOM_CREATE_VIEW)
@@ -7976,16 +8057,17 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
tables ;
tables=tables->next_local)
{
- if (!my_strcasecmp(table_alias_charset, alias_str, tables->alias) &&
- !strcmp(ptr->db, tables->db))
+ if (unlikely(!my_strcasecmp(table_alias_charset, alias_str.str,
+ tables->alias.str) &&
+ !cmp(&ptr->db, &tables->db) && ! tables->sequence))
{
- my_error(ER_NONUNIQ_TABLE, MYF(0), alias_str); /* purecov: tested */
+ my_error(ER_NONUNIQ_TABLE, MYF(0), alias_str.str); /* purecov: tested */
DBUG_RETURN(0); /* purecov: tested */
}
}
}
/* Store the table reference preceding the current one. */
- if (table_list.elements > 0)
+ if (table_list.elements > 0 && likely(!ptr->sequence))
{
/*
table_list.next points to the last inserted TABLE_LIST->next_local'
@@ -8010,8 +8092,11 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
Notice that as a side effect here we set the next_local field of the
previous table reference to 'ptr'. Here we also add one element to the
list 'table_list'.
+ We don't store sequences into the local list to hide them from INSERT
+ and SELECT.
*/
- table_list.link_in_list(ptr, &ptr->next_local);
+ if (likely(!ptr->sequence))
+ table_list.link_in_list(ptr, &ptr->next_local);
ptr->next_name_resolution_table= NULL;
#ifdef WITH_PARTITION_STORAGE_ENGINE
ptr->partition_names= partition_names;
@@ -8020,9 +8105,10 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
lex->add_to_query_tables(ptr);
// Pure table aliases do not need to be locked:
- if (ptr->db && !(table_options & TL_OPTION_ALIAS))
+ if (ptr->db.str && !(table_options & TL_OPTION_ALIAS))
{
- ptr->mdl_request.init(MDL_key::TABLE, ptr->db, ptr->table_name, mdl_type,
+ ptr->mdl_request.init(MDL_key::TABLE, ptr->db.str, ptr->table_name.str,
+ mdl_type,
MDL_TRANSACTION);
}
DBUG_RETURN(ptr);
@@ -8054,16 +8140,18 @@ bool st_select_lex::init_nested_join(THD *thd)
NESTED_JOIN *nested_join;
DBUG_ENTER("init_nested_join");
- if (!(ptr= (TABLE_LIST*) thd->calloc(ALIGN_SIZE(sizeof(TABLE_LIST))+
- sizeof(NESTED_JOIN))))
+ if (unlikely(!(ptr= (TABLE_LIST*) thd->calloc(ALIGN_SIZE(sizeof(TABLE_LIST))+
+ sizeof(NESTED_JOIN)))))
DBUG_RETURN(1);
nested_join= ptr->nested_join=
((NESTED_JOIN*) ((uchar*) ptr + ALIGN_SIZE(sizeof(TABLE_LIST))));
- join_list->push_front(ptr, thd->mem_root);
+ if (unlikely(join_list->push_front(ptr, thd->mem_root)))
+ DBUG_RETURN(1);
ptr->embedding= embedding;
ptr->join_list= join_list;
- ptr->alias= (char*) "(nested_join)";
+ ptr->alias.str="(nested_join)";
+ ptr->alias.length= sizeof("(nested_join)")-1;
embedding= ptr;
join_list= &nested_join->join_list;
join_list->empty();
@@ -8143,16 +8231,16 @@ TABLE_LIST *st_select_lex::nest_last_join(THD *thd)
{
DBUG_RETURN(head);
}
-
- if (!(ptr= (TABLE_LIST*) thd->calloc(ALIGN_SIZE(sizeof(TABLE_LIST))+
- sizeof(NESTED_JOIN))))
+ if (unlikely(!(ptr= (TABLE_LIST*) thd->calloc(ALIGN_SIZE(sizeof(TABLE_LIST))+
+ sizeof(NESTED_JOIN)))))
DBUG_RETURN(0);
nested_join= ptr->nested_join=
((NESTED_JOIN*) ((uchar*) ptr + ALIGN_SIZE(sizeof(TABLE_LIST))));
ptr->embedding= embedding;
ptr->join_list= join_list;
- ptr->alias= (char*) "(nest_last_join)";
+ ptr->alias.str= "(nest_last_join)";
+ ptr->alias.length= sizeof("(nest_last_join)")-1;
embedded_list= &nested_join->join_list;
embedded_list->empty();
nested_join->nest_type= JOIN_OP_NEST;
@@ -8160,7 +8248,7 @@ TABLE_LIST *st_select_lex::nest_last_join(THD *thd)
for (uint i=0; i < 2; i++)
{
TABLE_LIST *table= join_list->pop();
- if (!table)
+ if (unlikely(!table))
DBUG_RETURN(NULL);
table->join_list= embedded_list;
table->embedding= ptr;
@@ -8434,7 +8522,8 @@ bool st_select_lex::add_cross_joined_table(TABLE_LIST *left_op,
cj_nest->on_expr= tbl->on_expr;
cj_nest->embedding= tbl->embedding;
cj_nest->join_list= jl;
- cj_nest->alias= (char*) "(nest_last_join)";
+ cj_nest->alias.str= "(nest_last_join)";
+ cj_nest->alias.length= sizeof("(nest_last_join)");
li.replace(cj_nest);
/*
@@ -8537,8 +8626,8 @@ void st_select_lex::prepare_add_window_spec(THD *thd)
}
bool st_select_lex::add_window_def(THD *thd,
- LEX_STRING *win_name,
- LEX_STRING *win_ref,
+ LEX_CSTRING *win_name,
+ LEX_CSTRING *win_ref,
SQL_I_List<ORDER> win_partition_list,
SQL_I_List<ORDER> win_order_list,
Window_frame *win_frame)
@@ -8565,7 +8654,7 @@ bool st_select_lex::add_window_def(THD *thd,
}
bool st_select_lex::add_window_spec(THD *thd,
- LEX_STRING *win_ref,
+ LEX_CSTRING *win_ref,
SQL_I_List<ORDER> win_partition_list,
SQL_I_List<ORDER> win_order_list,
Window_frame *win_frame)
@@ -8670,7 +8759,7 @@ bool st_select_lex_unit::add_fake_select_lex(THD *thd_arg)
fake_select_lex->nest_level_base= first_select()->nest_level_base;
fake_select_lex->nest_level=first_select()->nest_level;
- if (!is_union())
+ if (!is_unit_op())
{
/*
This works only for
@@ -8815,18 +8904,20 @@ void add_join_natural(TABLE_LIST *a, TABLE_LIST *b, List<String> *using_fields,
SELECT_LEX *lex)
{
b->natural_join= a;
+ a->part_of_natural_join= TRUE;
+ b->part_of_natural_join= TRUE;
lex->prev_join_using= using_fields;
}
/**
- Find a thread by id and return it, locking it LOCK_thd_data
+ Find a thread by id and return it, locking it LOCK_thd_kill
@param id Identifier of the thread we're looking for
@param query_id If true, search by query_id instead of thread_id
@return NULL - not found
- pointer - thread found, and its LOCK_thd_data is locked.
+ pointer - thread found, and its LOCK_thd_kill is locked.
*/
THD *find_thread_by_id(longlong id, bool query_id)
@@ -8840,7 +8931,7 @@ THD *find_thread_by_id(longlong id, bool query_id)
continue;
if (id == (query_id ? tmp->query_id : (longlong) tmp->thread_id))
{
- mysql_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete
+ mysql_mutex_lock(&tmp->LOCK_thd_kill); // Lock from delete
break;
}
}
@@ -8896,13 +8987,13 @@ kill_one_thread(THD *thd, longlong id, killed_state kill_signal, killed_type typ
thd->security_ctx->user_matches(tmp->security_ctx)) &&
!wsrep_thd_is_BF(tmp, false))
{
- tmp->awake(kill_signal);
+ tmp->awake_no_mutex(kill_signal);
error=0;
}
else
error= (type == KILL_TYPE_QUERY ? ER_KILL_QUERY_DENIED_ERROR :
ER_KILL_DENIED_ERROR);
- mysql_mutex_unlock(&tmp->LOCK_thd_data);
+ mysql_mutex_unlock(&tmp->LOCK_thd_kill);
}
DBUG_PRINT("exit", ("%d", error));
DBUG_RETURN(error);
@@ -8932,7 +9023,7 @@ static uint kill_threads_for_user(THD *thd, LEX_USER *user,
*rows= 0;
- if (thd->is_fatal_error) // If we run out of memory
+ if (unlikely(thd->is_fatal_error)) // If we run out of memory
DBUG_RETURN(ER_OUT_OF_RESOURCES);
DBUG_PRINT("enter", ("user: %s signal: %u", user->user.str,
@@ -8960,7 +9051,7 @@ static uint kill_threads_for_user(THD *thd, LEX_USER *user,
DBUG_RETURN(ER_KILL_DENIED_ERROR);
}
if (!threads_to_kill.push_back(tmp, thd->mem_root))
- mysql_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete
+ mysql_mutex_lock(&tmp->LOCK_thd_kill); // Lock from delete
}
}
mysql_mutex_unlock(&LOCK_thread_count);
@@ -8971,17 +9062,17 @@ static uint kill_threads_for_user(THD *thd, LEX_USER *user,
THD *ptr= it2++;
do
{
- ptr->awake(kill_signal);
+ ptr->awake_no_mutex(kill_signal);
/*
Careful here: The list nodes are allocated on the memroots of the
THDs to be awakened.
But those THDs may be terminated and deleted as soon as we release
- LOCK_thd_data, which will make the list nodes invalid.
+ LOCK_thd_kill, which will make the list nodes invalid.
Since the operation "it++" dereferences the "next" pointer of the
- previous list node, we need to do this while holding LOCK_thd_data.
+ previous list node, we need to do this while holding LOCK_thd_kill.
*/
next_ptr= it2++;
- mysql_mutex_unlock(&ptr->LOCK_thd_data);
+ mysql_mutex_unlock(&ptr->LOCK_thd_kill);
(*rows)++;
} while ((ptr= next_ptr));
}
@@ -9002,7 +9093,7 @@ static
void sql_kill(THD *thd, longlong id, killed_state state, killed_type type)
{
uint error;
- if (!(error= kill_one_thread(thd, id, state, type)))
+ if (likely(!(error= kill_one_thread(thd, id, state, type))))
{
if (!thd->killed)
my_ok(thd);
@@ -9019,7 +9110,7 @@ void sql_kill_user(THD *thd, LEX_USER *user, killed_state state)
{
uint error;
ha_rows rows;
- if (!(error= kill_threads_for_user(thd, user, state, &rows)))
+ if (likely(!(error= kill_threads_for_user(thd, user, state, &rows))))
my_ok(thd, rows);
else
{
@@ -9035,14 +9126,14 @@ void sql_kill_user(THD *thd, LEX_USER *user, killed_state state)
/** If pointer is not a null pointer, append filename to it. */
bool append_file_to_dir(THD *thd, const char **filename_ptr,
- const char *table_name)
+ const LEX_CSTRING *table_name)
{
char buff[FN_REFLEN],*ptr, *end;
if (!*filename_ptr)
return 0; // nothing to do
/* Check that the filename is not too long and it's a hard path */
- if (strlen(*filename_ptr)+strlen(table_name) >= FN_REFLEN-1 ||
+ if (strlen(*filename_ptr)+table_name->length >= FN_REFLEN-1 ||
!test_if_hard_path(*filename_ptr))
{
my_error(ER_WRONG_TABLE_NAME, MYF(0), *filename_ptr);
@@ -9051,36 +9142,11 @@ bool append_file_to_dir(THD *thd, const char **filename_ptr,
/* Fix is using unix filename format on dos */
strmov(buff,*filename_ptr);
end=convert_dirname(buff, *filename_ptr, NullS);
- if (!(ptr= (char*) thd->alloc((size_t) (end-buff) + strlen(table_name)+1)))
+ if (unlikely(!(ptr= (char*) thd->alloc((size_t) (end-buff) +
+ table_name->length + 1))))
return 1; // End of memory
*filename_ptr=ptr;
- strxmov(ptr,buff,table_name,NullS);
- return 0;
-}
-
-
-/**
- Check if the select is a simple select (not an union).
-
- @retval
- 0 ok
- @retval
- 1 error ; In this case the error messege is sent to the client
-*/
-
-bool check_simple_select()
-{
- THD *thd= current_thd;
- LEX *lex= thd->lex;
- if (lex->current_select != &lex->select_lex)
- {
- char command[80];
- Lex_input_stream *lip= & thd->m_parser_state->m_lip;
- strmake(command, lip->yylval->symbol.str,
- MY_MIN(lip->yylval->symbol.length, sizeof(command)-1));
- my_error(ER_CANT_USE_OPTION_HERE, MYF(0), command);
- return 1;
- }
+ strxmov(ptr,buff,table_name->str,NullS);
return 0;
}
@@ -9169,7 +9235,6 @@ Item * all_any_subquery_creator(THD *thd, Item *left_expr,
bool multi_update_precheck(THD *thd, TABLE_LIST *tables)
{
- const char *msg= 0;
TABLE_LIST *table;
LEX *lex= thd->lex;
SELECT_LEX *select_lex= &lex->select_lex;
@@ -9190,12 +9255,12 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables)
continue;
if (table->derived)
table->grant.privilege= SELECT_ACL;
- else if ((check_access(thd, UPDATE_ACL, table->db,
+ else if ((check_access(thd, UPDATE_ACL, table->db.str,
&table->grant.privilege,
&table->grant.m_internal,
0, 1) ||
check_grant(thd, UPDATE_ACL, table, FALSE, 1, TRUE)) &&
- (check_access(thd, SELECT_ACL, table->db,
+ (check_access(thd, SELECT_ACL, table->db.str,
&table->grant.privilege,
&table->grant.m_internal,
0, 0) ||
@@ -9215,7 +9280,7 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables)
{
if (!table->table_in_first_from_clause)
{
- if (check_access(thd, SELECT_ACL, table->db,
+ if (check_access(thd, SELECT_ACL, table->db.str,
&table->grant.privilege,
&table->grant.m_internal,
0, 0) ||
@@ -9225,15 +9290,6 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables)
}
}
- if (select_lex->order_list.elements)
- msg= "ORDER BY";
- else if (select_lex->select_limit)
- msg= "LIMIT";
- if (msg)
- {
- my_error(ER_WRONG_USAGE, MYF(0), "UPDATE", msg);
- DBUG_RETURN(TRUE);
- }
DBUG_RETURN(FALSE);
}
@@ -9322,25 +9378,25 @@ static TABLE_LIST *multi_delete_table_match(LEX *lex, TABLE_LIST *tbl,
for (TABLE_LIST *elem= tables; elem; elem= elem->next_local)
{
- int cmp;
+ int res;
if (tbl->is_fqtn && elem->is_alias)
continue; /* no match */
if (tbl->is_fqtn && elem->is_fqtn)
- cmp= my_strcasecmp(table_alias_charset, tbl->table_name, elem->table_name) ||
- strcmp(tbl->db, elem->db);
+ res= (my_strcasecmp(table_alias_charset, tbl->table_name.str, elem->table_name.str) ||
+ cmp(&tbl->db, &elem->db));
else if (elem->is_alias)
- cmp= my_strcasecmp(table_alias_charset, tbl->alias, elem->alias);
+ res= my_strcasecmp(table_alias_charset, tbl->alias.str, elem->alias.str);
else
- cmp= my_strcasecmp(table_alias_charset, tbl->table_name, elem->table_name) ||
- strcmp(tbl->db, elem->db);
+ res= (my_strcasecmp(table_alias_charset, tbl->table_name.str, elem->table_name.str) ||
+ cmp(&tbl->db, &elem->db));
- if (cmp)
+ if (res)
continue;
if (match)
{
- my_error(ER_NONUNIQ_TABLE, MYF(0), elem->alias);
+ my_error(ER_NONUNIQ_TABLE, MYF(0), elem->alias.str);
DBUG_RETURN(NULL);
}
@@ -9348,7 +9404,7 @@ static TABLE_LIST *multi_delete_table_match(LEX *lex, TABLE_LIST *tbl,
}
if (!match)
- my_error(ER_UNKNOWN_TABLE, MYF(0), tbl->table_name, "MULTI DELETE");
+ my_error(ER_UNKNOWN_TABLE, MYF(0), tbl->table_name.str, "MULTI DELETE");
DBUG_RETURN(match);
}
@@ -9383,10 +9439,7 @@ bool multi_delete_set_locks_and_link_aux_tables(LEX *lex)
if (!walk)
DBUG_RETURN(TRUE);
if (!walk->derived)
- {
target_tbl->table_name= walk->table_name;
- target_tbl->table_name_length= walk->table_name_length;
- }
walk->updating= target_tbl->updating;
walk->lock_type= target_tbl->lock_type;
/* We can assume that tables to be deleted from are locked for write. */
@@ -9437,10 +9490,18 @@ bool update_precheck(THD *thd, TABLE_LIST *tables)
bool delete_precheck(THD *thd, TABLE_LIST *tables)
{
DBUG_ENTER("delete_precheck");
- if (check_one_table_access(thd, DELETE_ACL, tables))
- DBUG_RETURN(TRUE);
- /* Set privilege for the WHERE clause */
- tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege);
+ if (tables->vers_conditions.delete_history)
+ {
+ if (check_one_table_access(thd, DELETE_HISTORY_ACL, tables))
+ DBUG_RETURN(TRUE);
+ }
+ else
+ {
+ if (check_one_table_access(thd, DELETE_ACL, tables))
+ DBUG_RETURN(TRUE);
+ /* Set privilege for the WHERE clause */
+ tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege);
+ }
DBUG_RETURN(FALSE);
}
@@ -9544,7 +9605,7 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables,
if (lex->create_info.or_replace() && !lex->tmp_table())
want_priv|= DROP_ACL;
- if (check_access(thd, want_priv, create_table->db,
+ if (check_access(thd, want_priv, create_table->db.str,
&create_table->grant.privilege,
&create_table->grant.m_internal,
0, 0))
@@ -9610,7 +9671,8 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables,
goto err;
}
- if (check_fk_parent_table_access(thd, &lex->create_info, &lex->alter_info, create_table->db))
+ if (check_fk_parent_table_access(thd, &lex->create_info, &lex->alter_info,
+ create_table->db.str))
goto err;
error= FALSE;
@@ -9697,7 +9759,7 @@ void get_default_definer(THD *thd, LEX_USER *definer, bool role)
if (role)
{
definer->user.str= const_cast<char*>(sctx->priv_role);
- definer->host= empty_lex_str;
+ definer->host= empty_clex_str;
}
else
{
@@ -9726,7 +9788,7 @@ LEX_USER *create_default_definer(THD *thd, bool role)
{
LEX_USER *definer;
- if (! (definer= (LEX_USER*) thd->alloc(sizeof(LEX_USER))))
+ if (unlikely(! (definer= (LEX_USER*) thd->alloc(sizeof(LEX_USER)))))
return 0;
thd->get_definer(definer, role);
@@ -9754,13 +9816,14 @@ LEX_USER *create_default_definer(THD *thd, bool role)
- On error, return 0.
*/
-LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name)
+LEX_USER *create_definer(THD *thd, LEX_CSTRING *user_name,
+ LEX_CSTRING *host_name)
{
LEX_USER *definer;
/* Create and initialize. */
- if (! (definer= (LEX_USER*) thd->alloc(sizeof(LEX_USER))))
+ if (unlikely(!(definer= (LEX_USER*) thd->alloc(sizeof(LEX_USER)))))
return 0;
definer->user= *user_name;
@@ -9788,8 +9851,8 @@ LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name)
The function is not used in existing code but can be useful later?
*/
-bool check_string_byte_length(LEX_STRING *str, uint err_msg,
- uint max_byte_length)
+bool check_string_byte_length(const LEX_CSTRING *str, uint err_msg,
+ size_t max_byte_length)
{
if (str->length <= max_byte_length)
return FALSE;
@@ -9818,12 +9881,13 @@ bool check_string_byte_length(LEX_STRING *str, uint err_msg,
*/
-bool check_string_char_length(LEX_STRING *str, uint err_msg,
- uint max_char_length, CHARSET_INFO *cs,
+bool check_string_char_length(const LEX_CSTRING *str, uint err_msg,
+ size_t max_char_length, CHARSET_INFO *cs,
bool no_error)
{
Well_formed_prefix prefix(cs, str->str, str->length, max_char_length);
- if (!prefix.well_formed_error_pos() && str->length == prefix.length())
+ if (likely(!prefix.well_formed_error_pos() &&
+ str->length == prefix.length()))
return FALSE;
if (!no_error)
@@ -9837,7 +9901,7 @@ bool check_string_char_length(LEX_STRING *str, uint err_msg,
}
-bool check_ident_length(LEX_STRING *ident)
+bool check_ident_length(const LEX_CSTRING *ident)
{
if (check_string_char_length(ident, 0, NAME_CHAR_LEN, system_charset_info, 1))
{
@@ -9863,7 +9927,7 @@ extern "C" {
int path_starts_from_data_home_dir(const char *path)
{
- int dir_len= strlen(path);
+ size_t dir_len= strlen(path);
DBUG_ENTER("path_starts_from_data_home_dir");
if (mysql_unpacked_real_data_home_len<= dir_len)
@@ -9947,7 +10011,7 @@ int error_if_data_home_dir(const char *path, const char *what)
has invalid symbols
*/
-bool check_host_name(LEX_STRING *str)
+bool check_host_name(LEX_CSTRING *str)
{
const char *name= str->str;
const char *end= str->str + str->length;
@@ -9970,6 +10034,7 @@ bool check_host_name(LEX_STRING *str)
extern int MYSQLparse(THD *thd); // from sql_yacc.cc
+extern int ORAparse(THD *thd); // from sql_yacc_ora.cc
/**
@@ -10013,8 +10078,7 @@ bool parse_sql(THD *thd, Parser_state *parser_state,
/* Start Digest */
parser_state->m_digest_psi= MYSQL_DIGEST_START(thd->m_statement_psi);
- if (parser_state->m_input.m_compute_digest ||
- (parser_state->m_digest_psi != NULL))
+ if (parser_state->m_digest_psi != NULL)
{
/*
If either:
@@ -10029,7 +10093,10 @@ bool parse_sql(THD *thd, Parser_state *parser_state,
/* Parse the query. */
- bool mysql_parse_status= MYSQLparse(thd) != 0;
+ bool mysql_parse_status=
+ ((thd->variables.sql_mode & MODE_ORACLE) ?
+ ORAparse(thd) :
+ MYSQLparse(thd)) != 0;
/*
Check that if MYSQLparse() failed either thd->is_error() is set, or an
@@ -10126,3 +10193,14 @@ CHARSET_INFO *find_bin_collation(CHARSET_INFO *cs)
}
return cs;
}
+
+void LEX::mark_first_table_as_inserting()
+{
+ TABLE_LIST *t= select_lex.table_list.first;
+ DBUG_ENTER("Query_tables_list::mark_tables_with_important_flags");
+ DBUG_ASSERT(sql_command_flags[sql_command] & CF_INSERTS_DATA);
+ t->for_insert_data= TRUE;
+ DBUG_PRINT("info", ("table_list: %p name: %s db: %s command: %u",
+ t, t->table_name.str,t->db.str, sql_command));
+ DBUG_VOID_RETURN;
+}
diff --git a/sql/sql_parse.h b/sql/sql_parse.h
index ce5953696a2..6b0fc6718d5 100644
--- a/sql/sql_parse.h
+++ b/sql/sql_parse.h
@@ -16,7 +16,6 @@
#ifndef SQL_PARSE_INCLUDED
#define SQL_PARSE_INCLUDED
-#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_acl.h" /* GLOBAL_ACLS */
class Comp_creator;
@@ -70,32 +69,32 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
enum enum_schema_tables schema_table_idx);
void get_default_definer(THD *thd, LEX_USER *definer, bool role);
LEX_USER *create_default_definer(THD *thd, bool role);
-LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name);
+LEX_USER *create_definer(THD *thd, LEX_CSTRING *user_name, LEX_CSTRING *host_name);
LEX_USER *get_current_user(THD *thd, LEX_USER *user, bool lock=true);
bool sp_process_definer(THD *thd);
-bool check_string_byte_length(LEX_STRING *str, uint err_msg,
- uint max_byte_length);
-bool check_string_char_length(LEX_STRING *str, uint err_msg,
- uint max_char_length, CHARSET_INFO *cs,
+bool check_string_byte_length(const LEX_CSTRING *str, uint err_msg,
+ size_t max_byte_length);
+bool check_string_char_length(const LEX_CSTRING *str, uint err_msg,
+ size_t max_char_length, CHARSET_INFO *cs,
bool no_error);
-bool check_ident_length(LEX_STRING *ident);
+bool check_ident_length(const LEX_CSTRING *ident);
CHARSET_INFO* merge_charset_and_collation(CHARSET_INFO *cs, CHARSET_INFO *cl);
CHARSET_INFO *find_bin_collation(CHARSET_INFO *cs);
-bool check_host_name(LEX_STRING *str);
-bool check_identifier_name(LEX_STRING *str, uint max_char_length,
+bool check_host_name(LEX_CSTRING *str);
+bool check_identifier_name(LEX_CSTRING *str, uint max_char_length,
uint err_code, const char *param_for_err_msg);
bool mysql_test_parse_for_slave(THD *thd,char *inBuf,uint length);
bool sqlcom_can_generate_row_events(const THD *thd);
bool stmt_causes_implicit_commit(THD *thd, uint mask);
bool is_update_query(enum enum_sql_command command);
bool is_log_table_write_query(enum enum_sql_command command);
-bool alloc_query(THD *thd, const char *packet, uint packet_length);
+bool alloc_query(THD *thd, const char *packet, size_t packet_length);
void mysql_init_select(LEX *lex);
void mysql_parse(THD *thd, char *rawbuf, uint length,
Parser_state *parser_state, bool is_com_multi,
bool is_next_command);
-bool mysql_new_select(LEX *lex, bool move_down);
-void create_select_for_variable(const char *var_name);
+bool mysql_new_select(LEX *lex, bool move_down, SELECT_LEX *sel);
+void create_select_for_variable(THD *thd, LEX_CSTRING *var_name);
void create_table_set_open_action_and_adjust_tables(LEX *lex);
void mysql_init_multi_delete(LEX *lex);
bool multi_delete_set_locks_and_link_aux_tables(LEX *lex);
@@ -109,9 +108,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
bool is_com_multi, bool is_next_command);
void log_slow_statement(THD *thd);
bool append_file_to_dir(THD *thd, const char **filename_ptr,
- const char *table_name);
-bool append_file_to_dir(THD *thd, const char **filename_ptr,
- const char *table_name);
+ const LEX_CSTRING *table_name);
void execute_init_command(THD *thd, LEX_STRING *init_command,
mysql_rwlock_t *var_lock);
bool add_to_list(THD *thd, SQL_I_List<ORDER> &list, Item *group, bool asc);
@@ -123,7 +120,6 @@ bool push_new_name_resolution_context(THD *thd,
TABLE_LIST *left_op,
TABLE_LIST *right_op);
void init_update_queries(void);
-bool check_simple_select();
Item *normalize_cond(THD *thd, Item *cond);
Item *negate_expression(THD *thd, Item *expr);
bool check_stack_overrun(THD *thd, long margin, uchar *dummy);
@@ -133,16 +129,16 @@ bool check_stack_overrun(THD *thd, long margin, uchar *dummy);
extern const char* any_db;
extern uint sql_command_flags[];
extern uint server_command_flags[];
-extern const LEX_STRING command_name[];
+extern const LEX_CSTRING command_name[];
extern uint server_command_flags[];
/* Inline functions */
-inline bool check_identifier_name(LEX_STRING *str, uint err_code)
+inline bool check_identifier_name(LEX_CSTRING *str, uint err_code)
{
return check_identifier_name(str, NAME_CHAR_LEN, err_code, "");
}
-inline bool check_identifier_name(LEX_STRING *str)
+inline bool check_identifier_name(LEX_CSTRING *str)
{
return check_identifier_name(str, NAME_CHAR_LEN, 0, "");
}
@@ -151,10 +147,13 @@ inline bool check_identifier_name(LEX_STRING *str)
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,char *db,char *name,
- bool is_proc, bool no_errors);
+bool check_routine_access(THD *thd,ulong 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_routine_access(THD *thd, const char *db, const char *name, bool is_proc);
+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 any_combination_of_privileges_will_do,
uint number,
@@ -165,8 +164,10 @@ inline bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables
inline bool check_single_table_access(THD *thd, ulong privilege,
TABLE_LIST *tables, bool no_errors)
{ return false; }
-inline bool check_routine_access(THD *thd,ulong want_access,char *db,
- char *name, bool is_proc, bool no_errors)
+inline bool check_routine_access(THD *thd,ulong 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)
{
@@ -174,7 +175,8 @@ inline bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table)
return false;
}
inline bool check_some_routine_access(THD *thd, const char *db,
- const char *name, bool is_proc)
+ const char *name,
+ const Sp_handler *sph)
{ return false; }
inline bool
check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables,
diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc
index 2334286b039..4e9c78480b5 100644
--- a/sql/sql_partition.cc
+++ b/sql/sql_partition.cc
@@ -47,7 +47,7 @@
/* Some general useful functions */
#define MYSQL_LEX 1
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "sql_partition.h"
#include "key.h" // key_restore
@@ -67,6 +67,8 @@
#include "opt_range.h" // store_key_image_to_rec
#include "sql_alter.h" // Alter_table_ctx
#include "sql_select.h"
+#include "sql_tablespace.h" // check_tablespace_name
+#include "tztime.h" // my_tz_OFFSET0
#include <algorithm>
using std::max;
@@ -87,6 +89,7 @@ static int get_partition_id_list_col(partition_info *, uint32 *, longlong *);
static int get_partition_id_list(partition_info *, uint32 *, longlong *);
static int get_partition_id_range_col(partition_info *, uint32 *, longlong *);
static int get_partition_id_range(partition_info *, uint32 *, longlong *);
+static int vers_get_partition_id(partition_info *, uint32 *, longlong *);
static int get_part_id_charset_func_part(partition_info *, uint32 *, longlong *);
static int get_part_id_charset_func_subpart(partition_info *, uint32 *);
static int get_partition_id_hash_nosub(partition_info *, uint32 *, longlong *);
@@ -105,29 +108,14 @@ static uint32 get_next_subpartition_via_walking(PARTITION_ITERATOR*);
uint32 get_next_partition_id_range(PARTITION_ITERATOR* part_iter);
uint32 get_next_partition_id_list(PARTITION_ITERATOR* part_iter);
-int get_part_iter_for_interval_via_mapping(partition_info *part_info,
- bool is_subpart,
- uint32 *store_length_array,
- uchar *min_value, uchar *max_value,
- uint min_len, uint max_len,
- uint flags,
- PARTITION_ITERATOR *part_iter);
-int get_part_iter_for_interval_cols_via_map(partition_info *part_info,
- bool is_subpart,
- uint32 *store_length_array,
- uchar *min_value, uchar *max_value,
- uint min_len, uint max_len,
- uint flags,
- PARTITION_ITERATOR *part_iter);
-int get_part_iter_for_interval_via_walking(partition_info *part_info,
- bool is_subpart,
- uint32 *store_length_array,
- uchar *min_value, uchar *max_value,
- uint min_len, uint max_len,
- uint flags,
- PARTITION_ITERATOR *part_iter);
#ifdef WITH_PARTITION_STORAGE_ENGINE
+static int get_part_iter_for_interval_via_mapping(partition_info *, bool,
+ uint32 *, uchar *, uchar *, uint, uint, uint, PARTITION_ITERATOR *);
+static int get_part_iter_for_interval_cols_via_map(partition_info *, bool,
+ uint32 *, uchar *, uchar *, uint, uint, uint, PARTITION_ITERATOR *);
+static int get_part_iter_for_interval_via_walking(partition_info *, bool,
+ uint32 *, uchar *, uchar *, uint, uint, uint, PARTITION_ITERATOR *);
static int cmp_rec_and_tuple(part_column_list_val *val, uint32 nvals_in_rec);
static int cmp_rec_and_tuple_prune(part_column_list_val *val,
uint32 n_vals_in_rec,
@@ -158,7 +146,7 @@ Item* convert_charset_partition_constant(Item *item, CHARSET_INFO *cs)
item= item->safe_charset_converter(thd, cs);
context->table_list= NULL;
thd->where= "convert character set partition constant";
- if (!item || item->fix_fields(thd, (Item**)NULL))
+ if (item && item->fix_fields_if_needed(thd, (Item**)NULL))
item= NULL;
thd->where= save_where;
context->table_list= save_list;
@@ -177,15 +165,15 @@ Item* convert_charset_partition_constant(Item *item, CHARSET_INFO *cs)
@retval false String not found
*/
-static bool is_name_in_list(char *name, List<char> list_names)
+static bool is_name_in_list(const char *name, List<const char> list_names)
{
- List_iterator<char> names_it(list_names);
+ List_iterator<const char> names_it(list_names);
uint num_names= list_names.elements;
uint i= 0;
do
{
- char *list_name= names_it++;
+ const char *list_name= names_it++;
if (!(my_strcasecmp(system_charset_info, name, list_name)))
return TRUE;
} while (++i < num_names);
@@ -244,82 +232,11 @@ bool partition_default_handling(THD *thd, TABLE *table, partition_info *part_inf
/*
- A useful routine used by update_row for partition handlers to calculate
- the partition ids of the old and the new record.
+ A useful routine used by update/delete_row for partition handlers to
+ calculate the partition id.
SYNOPSIS
- get_part_for_update()
- old_data Buffer of old record
- new_data Buffer of new record
- rec0 Reference to table->record[0]
- part_info Reference to partition information
- out:old_part_id The returned partition id of old record
- out:new_part_id The returned partition id of new record
-
- RETURN VALUE
- 0 Success
- > 0 Error code
-*/
-
-int get_parts_for_update(const uchar *old_data, uchar *new_data,
- const uchar *rec0, partition_info *part_info,
- uint32 *old_part_id, uint32 *new_part_id,
- longlong *new_func_value)
-{
- Field **part_field_array= part_info->full_part_field_array;
- int error;
- longlong old_func_value;
- DBUG_ENTER("get_parts_for_update");
-
- DBUG_ASSERT(new_data == rec0); // table->record[0]
- part_info->table->move_fields(part_field_array, old_data, rec0);
- error= part_info->get_partition_id(part_info, old_part_id,
- &old_func_value);
- part_info->table->move_fields(part_field_array, rec0, old_data);
- if (unlikely(error)) // Should never happen
- {
- DBUG_ASSERT(0);
- DBUG_RETURN(error);
- }
-#ifdef NOT_NEEDED
- if (new_data == rec0)
-#endif
- {
- if (unlikely(error= part_info->get_partition_id(part_info,
- new_part_id,
- new_func_value)))
- {
- DBUG_RETURN(error);
- }
- }
-#ifdef NOT_NEEDED
- else
- {
- /*
- This branch should never execute but it is written anyways for
- future use. It will be tested by ensuring that the above
- condition is false in one test situation before pushing the code.
- */
- part_info->table->move_fields(part_field_array, new_data, rec0);
- error= part_info->get_partition_id(part_info, new_part_id,
- new_func_value);
- part_info->table->move_fields(part_field_array, rec0, new_data);
- if (unlikely(error))
- {
- DBUG_RETURN(error);
- }
- }
-#endif
- DBUG_RETURN(0);
-}
-
-
-/*
- A useful routine used by delete_row for partition handlers to calculate
- the partition id.
-
- SYNOPSIS
- get_part_for_delete()
+ get_part_for_buf()
buf Buffer of old record
rec0 Reference to table->record[0]
part_info Reference to partition information
@@ -335,21 +252,19 @@ int get_parts_for_update(const uchar *old_data, uchar *new_data,
calculate the partition id.
*/
-int get_part_for_delete(const uchar *buf, const uchar *rec0,
- partition_info *part_info, uint32 *part_id)
+int get_part_for_buf(const uchar *buf, const uchar *rec0,
+ partition_info *part_info, uint32 *part_id)
{
int error;
longlong func_value;
- DBUG_ENTER("get_part_for_delete");
+ DBUG_ENTER("get_part_for_buf");
- if (likely(buf == rec0))
+ if (buf == rec0)
{
- if (unlikely((error= part_info->get_partition_id(part_info, part_id,
- &func_value))))
- {
- DBUG_RETURN(error);
- }
- DBUG_PRINT("info", ("Delete from partition %d", *part_id));
+ error= part_info->get_partition_id(part_info, part_id, &func_value);
+ if (unlikely((error)))
+ goto err;
+ DBUG_PRINT("info", ("Partition %d", *part_id));
}
else
{
@@ -358,12 +273,13 @@ int get_part_for_delete(const uchar *buf, const uchar *rec0,
error= part_info->get_partition_id(part_info, part_id, &func_value);
part_info->table->move_fields(part_field_array, rec0, buf);
if (unlikely(error))
- {
- DBUG_RETURN(error);
- }
- DBUG_PRINT("info", ("Delete from partition %d (path2)", *part_id));
+ goto err;
+ DBUG_PRINT("info", ("Partition %d (path2)", *part_id));
}
DBUG_RETURN(0);
+err:
+ part_info->err_value= func_value;
+ DBUG_RETURN(error);
}
@@ -425,9 +341,17 @@ static bool set_up_field_array(THD *thd, TABLE *table,
while ((field= *(ptr++)))
{
if (field->flags & GET_FIXED_FIELDS_FLAG)
+ {
+ if (table->versioned(VERS_TRX_ID)
+ && unlikely(field->flags & VERS_SYSTEM_FIELD))
+ {
+ my_error(ER_VERS_TRX_PART_HISTORIC_ROW_NOT_SUPPORTED, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
num_fields++;
+ }
}
- if (num_fields > MAX_REF_PARTS)
+ if (unlikely(num_fields > MAX_REF_PARTS))
{
char *err_str;
if (is_sub_part)
@@ -443,15 +367,13 @@ static bool set_up_field_array(THD *thd, TABLE *table,
We are using hidden key as partitioning field
*/
DBUG_ASSERT(!is_sub_part);
- DBUG_RETURN(result);
+ DBUG_RETURN(FALSE);
}
size_field_array= (num_fields+1)*sizeof(Field*);
field_array= (Field**) thd->calloc(size_field_array);
if (unlikely(!field_array))
- {
- mem_alloc_error(size_field_array);
- result= TRUE;
- }
+ DBUG_RETURN(TRUE);
+
ptr= table->field;
while ((field= *(ptr++)))
{
@@ -463,8 +385,8 @@ static bool set_up_field_array(THD *thd, TABLE *table,
{
if (!is_sub_part && part_info->column_list)
{
- List_iterator<char> it(part_info->part_field_list);
- char *field_name;
+ List_iterator<const char> it(part_info->part_field_list);
+ const char *field_name;
DBUG_ASSERT(num_fields == part_info->part_field_list.elements);
inx= 0;
@@ -473,7 +395,7 @@ static bool set_up_field_array(THD *thd, TABLE *table,
field_name= it++;
if (!my_strcasecmp(system_charset_info,
field_name,
- field->field_name))
+ field->field_name.str))
break;
} while (++inx < num_fields);
if (inx == num_fields)
@@ -574,7 +496,6 @@ static bool create_full_part_field_array(THD *thd, TABLE *table,
field_array= (Field**) thd->calloc(size_field_array);
if (unlikely(!field_array))
{
- mem_alloc_error(size_field_array);
result= TRUE;
goto end;
}
@@ -599,14 +520,12 @@ static bool create_full_part_field_array(THD *thd, TABLE *table,
if (!(bitmap_buf= (my_bitmap_map*)
thd->alloc(bitmap_buffer_size(table->s->fields))))
{
- mem_alloc_error(bitmap_buffer_size(table->s->fields));
result= TRUE;
goto end;
}
- if (my_bitmap_init(&part_info->full_part_field_set, bitmap_buf,
- table->s->fields, FALSE))
+ if (unlikely(my_bitmap_init(&part_info->full_part_field_set, bitmap_buf,
+ table->s->fields, FALSE)))
{
- mem_alloc_error(table->s->fields);
result= TRUE;
goto end;
}
@@ -765,14 +684,14 @@ static void clear_field_flag(TABLE *table)
*/
-static bool handle_list_of_fields(THD *thd, List_iterator<char> it,
+static bool handle_list_of_fields(THD *thd, List_iterator<const char> it,
TABLE *table,
partition_info *part_info,
bool is_sub_part)
{
Field *field;
bool result;
- char *field_name;
+ const char *field_name;
bool is_list_empty= TRUE;
DBUG_ENTER("handle_list_of_fields");
@@ -911,7 +830,7 @@ int check_signed_flag(partition_info *part_info)
*/
static bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table,
- bool is_sub_part, bool is_create_table_ind)
+ bool is_sub_part, bool is_create_table_ind)
{
partition_info *part_info= table->part_info;
bool result= TRUE;
@@ -946,9 +865,9 @@ static bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table,
const bool save_agg_field= thd->lex->current_select->non_agg_field_used();
const bool save_agg_func= thd->lex->current_select->agg_func_used();
const nesting_map saved_allow_sum_func= thd->lex->allow_sum_func;
- thd->lex->allow_sum_func= 0;
+ thd->lex->allow_sum_func.clear_all();
- if (!(error= func_expr->fix_fields(thd, (Item**)&func_expr)))
+ if (likely(!(error= func_expr->fix_fields_if_needed(thd, (Item**)&func_expr))))
func_expr->walk(&Item::post_fix_fields_part_expr_processor, 0, NULL);
/*
@@ -992,7 +911,7 @@ static bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table,
ER_THD(thd, ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR));
}
- if ((!is_sub_part) && (error= check_signed_flag(part_info)))
+ if (unlikely((!is_sub_part) && (error= check_signed_flag(part_info))))
goto end;
result= set_up_field_array(thd, table, is_sub_part);
end:
@@ -1179,12 +1098,11 @@ static bool set_up_partition_bitmaps(THD *thd, partition_info *part_info)
DBUG_ASSERT(!part_info->bitmaps_are_initialized);
/* Allocate for both read and lock_partitions */
- if (!(bitmap_buf= (uint32*) alloc_root(&part_info->table->mem_root,
- bitmap_bytes * 2)))
- {
- mem_alloc_error(bitmap_bytes * 2);
+ if (unlikely(!(bitmap_buf=
+ (uint32*) alloc_root(&part_info->table->mem_root,
+ bitmap_bytes * 2))))
DBUG_RETURN(TRUE);
- }
+
my_bitmap_init(&part_info->read_partitions, bitmap_buf, bitmap_bits, FALSE);
/* Use the second half of the allocated buffer for lock_partitions */
my_bitmap_init(&part_info->lock_partitions, bitmap_buf + (bitmap_bytes / 4),
@@ -1256,6 +1174,423 @@ static void set_up_partition_key_maps(TABLE *table,
DBUG_VOID_RETURN;
}
+static bool check_no_constants(THD *, partition_info*)
+{
+ return FALSE;
+}
+
+/*
+ Support routines for check_list_constants used by qsort to sort the
+ constant list expressions. One routine for integers and one for
+ column lists.
+
+ SYNOPSIS
+ list_part_cmp()
+ a First list constant to compare with
+ b Second list constant to compare with
+
+ RETURN VALUE
+ +1 a > b
+ 0 a == b
+ -1 a < b
+*/
+
+extern "C"
+int partition_info_list_part_cmp(const void* a, const void* b)
+{
+ longlong a1= ((LIST_PART_ENTRY*)a)->list_value;
+ longlong b1= ((LIST_PART_ENTRY*)b)->list_value;
+ if (a1 < b1)
+ return -1;
+ else if (a1 > b1)
+ return +1;
+ else
+ return 0;
+}
+
+
+/*
+ Compare two lists of column values in RANGE/LIST partitioning
+ SYNOPSIS
+ partition_info_compare_column_values()
+ first First column list argument
+ second Second column list argument
+ RETURN VALUES
+ 0 Equal
+ -1 First argument is smaller
+ +1 First argument is larger
+*/
+
+extern "C"
+int partition_info_compare_column_values(const void *first_arg,
+ const void *second_arg)
+{
+ const part_column_list_val *first= (part_column_list_val*)first_arg;
+ const part_column_list_val *second= (part_column_list_val*)second_arg;
+ partition_info *part_info= first->part_info;
+ Field **field;
+
+ for (field= part_info->part_field_array; *field;
+ field++, first++, second++)
+ {
+ if (first->max_value || second->max_value)
+ {
+ if (first->max_value && second->max_value)
+ return 0;
+ if (second->max_value)
+ return -1;
+ else
+ return +1;
+ }
+ if (first->null_value || second->null_value)
+ {
+ if (first->null_value && second->null_value)
+ continue;
+ if (second->null_value)
+ return +1;
+ else
+ return -1;
+ }
+ int res= (*field)->cmp((const uchar*)first->column_value,
+ (const uchar*)second->column_value);
+ if (res)
+ return res;
+ }
+ return 0;
+}
+
+
+/*
+ This routine allocates an array for all range constants to achieve a fast
+ check what partition a certain value belongs to. At the same time it does
+ also check that the range constants are defined in increasing order and
+ that the expressions are constant integer expressions.
+
+ SYNOPSIS
+ check_range_constants()
+ thd Thread object
+
+ RETURN VALUE
+ TRUE An error occurred during creation of range constants
+ FALSE Successful creation of range constant mapping
+
+ DESCRIPTION
+ This routine is called from check_partition_info to get a quick error
+ before we came too far into the CREATE TABLE process. It is also called
+ from fix_partition_func every time we open the .frm file. It is only
+ called for RANGE PARTITIONed tables.
+*/
+
+static bool check_range_constants(THD *thd, partition_info *part_info)
+{
+ partition_element* part_def;
+ bool first= TRUE;
+ uint i;
+ List_iterator<partition_element> it(part_info->partitions);
+ bool result= TRUE;
+ DBUG_ENTER("check_range_constants");
+ DBUG_PRINT("enter", ("RANGE with %d parts, column_list = %u",
+ part_info->num_parts, part_info->column_list));
+
+ if (part_info->column_list)
+ {
+ part_column_list_val *loc_range_col_array;
+ part_column_list_val *UNINIT_VAR(current_largest_col_val);
+ uint num_column_values= part_info->part_field_list.elements;
+ uint size_entries= sizeof(part_column_list_val) * num_column_values;
+ part_info->range_col_array= (part_column_list_val*)
+ thd->calloc(part_info->num_parts * size_entries);
+ if (unlikely(part_info->range_col_array == NULL))
+ goto end;
+
+ loc_range_col_array= part_info->range_col_array;
+ i= 0;
+ do
+ {
+ part_def= it++;
+ {
+ List_iterator<part_elem_value> list_val_it(part_def->list_val_list);
+ part_elem_value *range_val= list_val_it++;
+ part_column_list_val *col_val= range_val->col_val_array;
+
+ if (part_info->fix_column_value_functions(thd, range_val, i))
+ goto end;
+ memcpy(loc_range_col_array, (const void*)col_val, size_entries);
+ loc_range_col_array+= num_column_values;
+ if (!first)
+ {
+ if (partition_info_compare_column_values(current_largest_col_val,
+ col_val) >= 0)
+ goto range_not_increasing_error;
+ }
+ current_largest_col_val= col_val;
+ }
+ first= FALSE;
+ } while (++i < part_info->num_parts);
+ }
+ else
+ {
+ longlong UNINIT_VAR(current_largest);
+ longlong part_range_value;
+ bool signed_flag= !part_info->part_expr->unsigned_flag;
+
+ part_info->range_int_array= (longlong*)
+ thd->alloc(part_info->num_parts * sizeof(longlong));
+ if (unlikely(part_info->range_int_array == NULL))
+ goto end;
+
+ i= 0;
+ do
+ {
+ part_def= it++;
+ if ((i != part_info->num_parts - 1) || !part_info->defined_max_value)
+ {
+ part_range_value= part_def->range_value;
+ if (!signed_flag)
+ part_range_value-= 0x8000000000000000ULL;
+ }
+ else
+ part_range_value= LONGLONG_MAX;
+
+ if (!first)
+ {
+ if (current_largest > part_range_value ||
+ (current_largest == part_range_value &&
+ (part_range_value < LONGLONG_MAX ||
+ i != part_info->num_parts - 1 ||
+ !part_info->defined_max_value)))
+ goto range_not_increasing_error;
+ }
+ part_info->range_int_array[i]= part_range_value;
+ current_largest= part_range_value;
+ first= FALSE;
+ } while (++i < part_info->num_parts);
+ }
+ result= FALSE;
+end:
+ DBUG_RETURN(result);
+
+range_not_increasing_error:
+ my_error(ER_RANGE_NOT_INCREASING_ERROR, MYF(0));
+ goto end;
+}
+
+
+/*
+ This routine allocates an array for all list constants to achieve a fast
+ check what partition a certain value belongs to. At the same time it does
+ also check that there are no duplicates among the list constants and that
+ that the list expressions are constant integer expressions.
+
+ SYNOPSIS
+ check_list_constants()
+ thd Thread object
+
+ RETURN VALUE
+ TRUE An error occurred during creation of list constants
+ FALSE Successful creation of list constant mapping
+
+ DESCRIPTION
+ This routine is called from check_partition_info to get a quick error
+ before we came too far into the CREATE TABLE process. It is also called
+ from fix_partition_func every time we open the .frm file. It is only
+ called for LIST PARTITIONed tables.
+*/
+
+static bool check_list_constants(THD *thd, partition_info *part_info)
+{
+ uint i, size_entries, num_column_values;
+ uint list_index= 0;
+ part_elem_value *list_value;
+ bool result= TRUE;
+ longlong type_add, calc_value;
+ void *curr_value;
+ void *UNINIT_VAR(prev_value);
+ partition_element* part_def;
+ bool found_null= FALSE;
+ qsort_cmp compare_func;
+ void *ptr;
+ List_iterator<partition_element> list_func_it(part_info->partitions);
+ DBUG_ENTER("check_list_constants");
+
+ DBUG_ASSERT(part_info->part_type == LIST_PARTITION);
+
+ part_info->num_list_values= 0;
+ /*
+ We begin by calculating the number of list values that have been
+ defined in the first step.
+
+ We use this number to allocate a properly sized array of structs
+ to keep the partition id and the value to use in that partition.
+ In the second traversal we assign them values in the struct array.
+
+ Finally we sort the array of structs in order of values to enable
+ a quick binary search for the proper value to discover the
+ partition id.
+ After sorting the array we check that there are no duplicates in the
+ list.
+ */
+
+ i= 0;
+ do
+ {
+ part_def= list_func_it++;
+ if (part_def->has_null_value)
+ {
+ if (found_null)
+ {
+ my_error(ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR, MYF(0));
+ goto end;
+ }
+ part_info->has_null_value= TRUE;
+ part_info->has_null_part_id= i;
+ found_null= TRUE;
+ }
+ part_info->num_list_values+= part_def->list_val_list.elements;
+ } while (++i < part_info->num_parts);
+ list_func_it.rewind();
+ num_column_values= part_info->part_field_list.elements;
+ size_entries= part_info->column_list ?
+ (num_column_values * sizeof(part_column_list_val)) :
+ sizeof(LIST_PART_ENTRY);
+ if (!(ptr= thd->calloc((part_info->num_list_values+1) * size_entries)))
+ goto end;
+ if (part_info->column_list)
+ {
+ part_column_list_val *loc_list_col_array;
+ loc_list_col_array= (part_column_list_val*)ptr;
+ part_info->list_col_array= (part_column_list_val*)ptr;
+ compare_func= partition_info_compare_column_values;
+ i= 0;
+ do
+ {
+ part_def= list_func_it++;
+ if (part_def->max_value)
+ {
+ // DEFAULT is not a real value so let's exclude it from sorting.
+ DBUG_ASSERT(part_info->num_list_values);
+ part_info->num_list_values--;
+ continue;
+ }
+ List_iterator<part_elem_value> list_val_it2(part_def->list_val_list);
+ while ((list_value= list_val_it2++))
+ {
+ part_column_list_val *col_val= list_value->col_val_array;
+ if (part_info->fix_column_value_functions(thd, list_value, i))
+ DBUG_RETURN(result);
+ memcpy(loc_list_col_array, (const void*)col_val, size_entries);
+ loc_list_col_array+= num_column_values;
+ }
+ } while (++i < part_info->num_parts);
+ }
+ else
+ {
+ compare_func= partition_info_list_part_cmp;
+ part_info->list_array= (LIST_PART_ENTRY*)ptr;
+ i= 0;
+ /*
+ Fix to be able to reuse signed sort functions also for unsigned
+ partition functions.
+ */
+ type_add= (longlong)(part_info->part_expr->unsigned_flag ?
+ 0x8000000000000000ULL :
+ 0ULL);
+
+ do
+ {
+ part_def= list_func_it++;
+ if (part_def->max_value)
+ {
+ // DEFAULT is not a real value so let's exclude it from sorting.
+ DBUG_ASSERT(part_info->num_list_values);
+ part_info->num_list_values--;
+ continue;
+ }
+ List_iterator<part_elem_value> list_val_it2(part_def->list_val_list);
+ while ((list_value= list_val_it2++))
+ {
+ calc_value= list_value->value - type_add;
+ part_info->list_array[list_index].list_value= calc_value;
+ part_info->list_array[list_index++].partition_id= i;
+ }
+ } while (++i < part_info->num_parts);
+ }
+ DBUG_ASSERT(part_info->fixed);
+ if (part_info->num_list_values)
+ {
+ bool first= TRUE;
+ /*
+ list_array and list_col_array are unions, so this works for both
+ variants of LIST partitioning.
+ */
+ my_qsort(part_info->list_array, part_info->num_list_values, size_entries,
+ compare_func);
+
+ i= 0;
+ do
+ {
+ DBUG_ASSERT(i < part_info->num_list_values);
+ curr_value= part_info->column_list
+ ? (void*)&part_info->list_col_array[num_column_values * i]
+ : (void*)&part_info->list_array[i];
+ if (likely(first || compare_func(curr_value, prev_value)))
+ {
+ prev_value= curr_value;
+ first= FALSE;
+ }
+ else
+ {
+ my_error(ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR, MYF(0));
+ goto end;
+ }
+ } while (++i < part_info->num_list_values);
+ }
+ result= FALSE;
+end:
+ DBUG_RETURN(result);
+}
+
+
+/* Set partition boundaries when rotating by INTERVAL */
+static bool check_vers_constants(THD *thd, partition_info *part_info)
+{
+ uint hist_parts= part_info->num_parts - 1;
+ Vers_part_info *vers_info= part_info->vers_info;
+ vers_info->hist_part= part_info->partitions.head();
+ vers_info->now_part= part_info->partitions.elem(hist_parts);
+
+ if (!vers_info->interval.is_set())
+ return 0;
+
+ part_info->range_int_array=
+ (longlong*) thd->alloc(part_info->num_parts * sizeof(longlong));
+
+ MYSQL_TIME ltime;
+ List_iterator<partition_element> it(part_info->partitions);
+ partition_element *el;
+ my_tz_OFFSET0->gmt_sec_to_TIME(&ltime, vers_info->interval.start);
+ while ((el= it++)->id < hist_parts)
+ {
+ if (date_add_interval(&ltime, vers_info->interval.type,
+ vers_info->interval.step))
+ goto err;
+ uint error= 0;
+ part_info->range_int_array[el->id]= el->range_value=
+ my_tz_OFFSET0->TIME_to_gmt_sec(&ltime, &error);
+ if (error)
+ goto err;
+ if (vers_info->hist_part->range_value <= thd->query_start())
+ vers_info->hist_part= el;
+ }
+ DBUG_ASSERT(el == vers_info->now_part);
+ el->max_value= true;
+ part_info->range_int_array[el->id]= el->range_value= LONGLONG_MAX;
+ return 0;
+err:
+ my_error(ER_DATA_OUT_OF_RANGE, MYF(0), "TIMESTAMP", "INTERVAL");
+ return 1;
+}
+
/*
Set up function pointers for partition function
@@ -1302,6 +1637,24 @@ static void set_up_partition_func_pointers(partition_info *part_info)
part_info->get_subpartition_id= get_partition_id_hash_sub;
}
}
+ else if (part_info->part_type == VERSIONING_PARTITION)
+ {
+ part_info->get_part_partition_id= vers_get_partition_id;
+ if (part_info->list_of_subpart_fields)
+ {
+ if (part_info->linear_hash_ind)
+ part_info->get_subpartition_id= get_partition_id_linear_key_sub;
+ else
+ part_info->get_subpartition_id= get_partition_id_key_sub;
+ }
+ else
+ {
+ if (part_info->linear_hash_ind)
+ part_info->get_subpartition_id= get_partition_id_linear_hash_sub;
+ else
+ part_info->get_subpartition_id= get_partition_id_hash_sub;
+ }
+ }
else /* LIST Partitioning */
{
if (part_info->column_list)
@@ -1342,6 +1695,10 @@ static void set_up_partition_func_pointers(partition_info *part_info)
else
part_info->get_partition_id= get_partition_id_list;
}
+ else if (part_info->part_type == VERSIONING_PARTITION)
+ {
+ part_info->get_partition_id= vers_get_partition_id;
+ }
else /* HASH partitioning */
{
if (part_info->list_of_part_fields)
@@ -1398,6 +1755,14 @@ static void set_up_partition_func_pointers(partition_info *part_info)
part_info->get_subpartition_id;
part_info->get_subpartition_id= get_part_id_charset_func_subpart;
}
+ if (part_info->part_type == RANGE_PARTITION)
+ part_info->check_constants= check_range_constants;
+ else if (part_info->part_type == LIST_PARTITION)
+ part_info->check_constants= check_list_constants;
+ else if (part_info->part_type == VERSIONING_PARTITION)
+ part_info->check_constants= check_vers_constants;
+ else
+ part_info->check_constants= check_no_constants;
DBUG_VOID_RETURN;
}
@@ -1561,20 +1926,19 @@ NOTES
of an error that is not discovered until here.
*/
-bool fix_partition_func(THD *thd, TABLE *table,
- bool is_create_table_ind)
+bool fix_partition_func(THD *thd, TABLE *table, bool is_create_table_ind)
{
bool result= TRUE;
partition_info *part_info= table->part_info;
- enum_mark_columns save_mark_used_columns= thd->mark_used_columns;
+ enum_column_usage saved_column_usage= thd->column_usage;
DBUG_ENTER("fix_partition_func");
if (part_info->fixed)
{
DBUG_RETURN(FALSE);
}
- thd->mark_used_columns= MARK_COLUMNS_NONE;
- DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
+ thd->column_usage= COLUMNS_WRITE;
+ DBUG_PRINT("info", ("thd->column_usage: %d", thd->column_usage));
if (!is_create_table_ind ||
thd->lex->sql_command != SQLCOM_CREATE_TABLE)
@@ -1597,7 +1961,7 @@ bool fix_partition_func(THD *thd, TABLE *table,
set_linear_hash_mask(part_info, part_info->num_subparts);
if (part_info->list_of_subpart_fields)
{
- List_iterator<char> it(part_info->subpart_field_list);
+ List_iterator<const char> it(part_info->subpart_field_list);
if (unlikely(handle_list_of_fields(thd, it, table, part_info, TRUE)))
goto end;
}
@@ -1618,13 +1982,14 @@ bool fix_partition_func(THD *thd, TABLE *table,
Partition is defined. We need to verify that partitioning
function is correct.
*/
+ set_up_partition_func_pointers(part_info);
if (part_info->part_type == HASH_PARTITION)
{
if (part_info->linear_hash_ind)
set_linear_hash_mask(part_info, part_info->num_parts);
if (part_info->list_of_part_fields)
{
- List_iterator<char> it(part_info->part_field_list);
+ List_iterator<const char> it(part_info->part_field_list);
if (unlikely(handle_list_of_fields(thd, it, table, part_info, FALSE)))
goto end;
}
@@ -1643,45 +2008,34 @@ bool fix_partition_func(THD *thd, TABLE *table,
}
else
{
- const char *error_str;
if (part_info->column_list)
{
- List_iterator<char> it(part_info->part_field_list);
+ List_iterator<const char> it(part_info->part_field_list);
if (unlikely(handle_list_of_fields(thd, it, table, part_info, FALSE)))
goto end;
}
else
{
+ if (part_info->part_type == VERSIONING_PARTITION &&
+ part_info->vers_setup_expression(thd))
+ goto end;
if (unlikely(fix_fields_part_func(thd, part_info->part_expr,
table, FALSE, is_create_table_ind)))
goto end;
}
part_info->fixed= TRUE;
- if (part_info->part_type == RANGE_PARTITION)
- {
- error_str= "HASH";
- if (unlikely(part_info->check_range_constants(thd)))
- goto end;
- }
- else if (part_info->part_type == LIST_PARTITION)
- {
- error_str= "LIST";
- if (unlikely(part_info->check_list_constants(thd)))
- goto end;
- }
- else
- {
- DBUG_ASSERT(0);
- my_error(ER_INCONSISTENT_PARTITION_INFO_ERROR, MYF(0));
+ if (part_info->check_constants(thd, part_info))
goto end;
- }
if (unlikely(part_info->num_parts < 1))
{
+ const char *error_str= part_info->part_type == LIST_PARTITION
+ ? "LIST" : "RANGE";
my_error(ER_PARTITIONS_MUST_BE_DEFINED_ERROR, MYF(0), error_str);
goto end;
}
if (unlikely(!part_info->column_list &&
- part_info->part_expr->result_type() != INT_RESULT))
+ part_info->part_expr->result_type() != INT_RESULT &&
+ part_info->part_expr->result_type() != DECIMAL_RESULT))
{
part_info->report_part_expr_error(FALSE);
goto end;
@@ -1724,13 +2078,12 @@ bool fix_partition_func(THD *thd, TABLE *table,
}
check_range_capable_PF(table);
set_up_partition_key_maps(table, part_info);
- set_up_partition_func_pointers(part_info);
set_up_range_analysis_info(part_info);
table->file->set_part_info(part_info);
result= FALSE;
end:
- thd->mark_used_columns= save_mark_used_columns;
- DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
+ thd->column_usage= saved_column_usage;
+ DBUG_PRINT("info", ("thd->column_usage: %d", thd->column_usage));
DBUG_RETURN(result);
}
@@ -1743,11 +2096,11 @@ end:
ALTER TABLE commands. Finally it is used for SHOW CREATE TABLES.
*/
-static int add_part_field_list(THD *thd, String *str, List<char> field_list)
+static int add_part_field_list(THD *thd, String *str, List<const char> field_list)
{
int err= 0;
const char *field_name;
- List_iterator<char> part_it(field_list);
+ List_iterator<const char> part_it(field_list);
err+= str->append('(');
while ((field_name= part_it++))
@@ -1836,7 +2189,7 @@ static int add_keyword_path(String *str, const char *keyword,
#ifdef __WIN__
/* Convert \ to / to be able to create table on unix */
char *pos, *end;
- uint length= strlen(temp_path);
+ size_t length= strlen(temp_path);
for (pos= temp_path, end= pos+length ; pos < end ; pos++)
{
if (*pos == '\\')
@@ -1978,7 +2331,7 @@ error:
NULL No field found
*/
-static Create_field* get_sql_field(char *field_name,
+static Create_field* get_sql_field(const char *field_name,
Alter_info *alter_info)
{
List_iterator<Create_field> it(alter_info->create_list);
@@ -1988,7 +2341,7 @@ static Create_field* get_sql_field(char *field_name,
while ((sql_field= it++))
{
if (!(my_strcasecmp(system_charset_info,
- sql_field->field_name,
+ sql_field->field_name.str,
field_name)))
{
DBUG_RETURN(sql_field);
@@ -2005,7 +2358,7 @@ static int add_column_list_values(String *str, partition_info *part_info,
{
int err= 0;
uint i;
- List_iterator<char> it(part_info->part_field_list);
+ List_iterator<const char> it(part_info->part_field_list);
uint num_elements= part_info->part_field_list.elements;
bool use_parenthesis= (part_info->part_type == LIST_PARTITION &&
part_info->num_columns > 1U);
@@ -2015,7 +2368,7 @@ static int add_column_list_values(String *str, partition_info *part_info,
for (i= 0; i < num_elements; i++)
{
part_column_list_val *col_val= &list_value->col_val_array[i];
- char *field_name= it++;
+ const char *field_name= it++;
if (col_val->max_value)
err+= str->append(STRING_WITH_LEN("MAXVALUE"));
else if (col_val->null_value)
@@ -2047,8 +2400,8 @@ 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->sql_type,
- sql_field->field_name,
+ if (check_part_field(sql_field->real_field_type(),
+ sql_field->field_name.str,
&result_type,
&need_cs_check))
return 1;
@@ -2062,7 +2415,7 @@ static int add_column_list_values(String *str, partition_info *part_info,
Field *field= part_info->part_field_array[i];
result_type= field->result_type();
if (check_part_field(field->real_type(),
- field->field_name,
+ field->field_name.str,
&result_type,
&need_cs_check))
return 1;
@@ -2189,6 +2542,20 @@ static int add_partition_values(String *str, partition_info *part_info,
} while (++i < num_items);
err+= str->append(')');
}
+ else if (part_info->part_type == VERSIONING_PARTITION)
+ {
+ switch (p_elem->type)
+ {
+ case partition_element::CURRENT:
+ err+= str->append(STRING_WITH_LEN(" CURRENT"));
+ break;
+ case partition_element::HISTORY:
+ err+= str->append(STRING_WITH_LEN(" HISTORY"));
+ break;
+ default:
+ DBUG_ASSERT(0 && "wrong p_elem->type");
+ }
+ }
end:
return err;
}
@@ -2294,13 +2661,37 @@ char *generate_partition_syntax(THD *thd, partition_info *part_info,
else
err+= str.append(STRING_WITH_LEN("HASH "));
break;
+ case VERSIONING_PARTITION:
+ err+= str.append(STRING_WITH_LEN("SYSTEM_TIME "));
+ break;
default:
DBUG_ASSERT(0);
/* We really shouldn't get here, no use in continuing from here */
my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
DBUG_RETURN(NULL);
}
- if (part_info->part_expr)
+ if (part_info->part_type == VERSIONING_PARTITION)
+ {
+ Vers_part_info *vers_info= part_info->vers_info;
+ DBUG_ASSERT(vers_info);
+ if (vers_info->interval.is_set())
+ {
+ err+= str.append(STRING_WITH_LEN("INTERVAL "));
+ err+= append_interval(&str, vers_info->interval.type,
+ vers_info->interval.step);
+ if (create_info) // not SHOW CREATE
+ {
+ err+= str.append(STRING_WITH_LEN(" STARTS "));
+ err+= str.append_ulonglong(vers_info->interval.start);
+ }
+ }
+ if (vers_info->limit)
+ {
+ err+= str.append(STRING_WITH_LEN("LIMIT "));
+ err+= str.append_ulonglong(vers_info->limit);
+ }
+ }
+ else if (part_info->part_expr)
{
err+= str.append('(');
part_info->part_expr->print_for_table_def(&str);
@@ -2449,15 +2840,34 @@ bool partition_key_modified(TABLE *table, const MY_BITMAP *fields)
static inline int part_val_int(Item *item_expr, longlong *result)
{
- *result= item_expr->val_int();
+ switch (item_expr->cmp_type())
+ {
+ case DECIMAL_RESULT:
+ {
+ my_decimal buf;
+ my_decimal *val= item_expr->val_decimal(&buf);
+ if (val && my_decimal2int(E_DEC_FATAL_ERROR, val, item_expr->unsigned_flag,
+ result, FLOOR) != E_DEC_OK)
+ return true;
+ break;
+ }
+ case INT_RESULT:
+ *result= item_expr->val_int();
+ break;
+ case STRING_RESULT:
+ case REAL_RESULT:
+ case ROW_RESULT:
+ case TIME_RESULT:
+ DBUG_ASSERT(0);
+ break;
+ }
if (item_expr->null_value)
{
- if (current_thd->is_error())
- return TRUE;
- else
- *result= LONGLONG_MIN;
+ if (unlikely(current_thd->is_error()))
+ return true;
+ *result= LONGLONG_MIN;
}
- return FALSE;
+ return false;
}
@@ -3107,6 +3517,48 @@ int get_partition_id_range_col(partition_info *part_info,
}
+int vers_get_partition_id(partition_info *part_info, uint32 *part_id,
+ longlong *func_value)
+{
+ DBUG_ENTER("vers_get_partition_id");
+ Field *row_end= part_info->part_field_array[STAT_TRX_END];
+ Vers_part_info *vers_info= part_info->vers_info;
+
+ if (row_end->is_max() || row_end->is_null())
+ *part_id= vers_info->now_part->id;
+ else // row is historical
+ {
+ longlong *range_value= part_info->range_int_array;
+ uint max_hist_id= part_info->num_parts - 2;
+ uint min_hist_id= 0, loc_hist_id= vers_info->hist_part->id;
+ ulong unused;
+ my_time_t ts;
+
+ if (!range_value)
+ goto done; // fastpath
+
+ ts= row_end->get_timestamp(&unused);
+ if ((loc_hist_id == 0 || range_value[loc_hist_id - 1] < ts) &&
+ (loc_hist_id == max_hist_id || range_value[loc_hist_id] >= ts))
+ goto done; // fastpath
+
+ while (max_hist_id > min_hist_id)
+ {
+ loc_hist_id= (max_hist_id + min_hist_id) / 2;
+ if (range_value[loc_hist_id] <= ts)
+ min_hist_id= loc_hist_id + 1;
+ else
+ max_hist_id= loc_hist_id;
+ }
+ loc_hist_id= max_hist_id;
+done:
+ *part_id= (uint32)loc_hist_id;
+ }
+ DBUG_PRINT("exit",("partition: %d", *part_id));
+ DBUG_RETURN(0);
+}
+
+
int get_partition_id_range(partition_info *part_info,
uint32 *part_id,
longlong *func_value)
@@ -3121,7 +3573,7 @@ int get_partition_id_range(partition_info *part_info,
bool unsigned_flag= part_info->part_expr->unsigned_flag;
DBUG_ENTER("get_partition_id_range");
- if (error)
+ if (unlikely(error))
DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);
if (part_info->part_expr->null_value)
@@ -3694,26 +4146,21 @@ bool verify_data_with_partition(TABLE *table, TABLE *part_table,
old_rec= part_table->record[0];
part_table->record[0]= table->record[0];
part_info->table->move_fields(part_info->full_part_field_array, table->record[0], old_rec);
- if ((error= file->ha_rnd_init(TRUE)))
- {
- file->print_error(error, MYF(0));
+ if (unlikely(error= file->ha_rnd_init_with_error(TRUE)))
goto err;
- }
do
{
- if ((error= file->ha_rnd_next(table->record[0])))
+ if (unlikely((error= file->ha_rnd_next(table->record[0]))))
{
- if (error == HA_ERR_RECORD_DELETED)
- continue;
if (error == HA_ERR_END_OF_FILE)
error= 0;
else
file->print_error(error, MYF(0));
break;
}
- if ((error= part_info->get_partition_id(part_info, &found_part_id,
- &func_value)))
+ if (unlikely((error= part_info->get_partition_id(part_info, &found_part_id,
+ &func_value))))
{
part_table->file->print_error(error, MYF(0));
break;
@@ -3731,9 +4178,7 @@ err:
part_info->table->move_fields(part_info->full_part_field_array, old_rec,
table->record[0]);
part_table->record[0]= old_rec;
- if (error)
- DBUG_RETURN(TRUE);
- DBUG_RETURN(FALSE);
+ DBUG_RETURN(unlikely(error) ? TRUE : FALSE);
}
@@ -4039,8 +4484,7 @@ bool mysql_unpack_partition(THD *thd,
{
bool result= TRUE;
partition_info *part_info;
- CHARSET_INFO *old_character_set_client=
- thd->variables.character_set_client;
+ CHARSET_INFO *old_character_set_client= thd->variables.character_set_client;
LEX *old_lex= thd->lex;
LEX lex;
PSI_statement_locker *parent_locker= thd->m_statement_psi;
@@ -4049,34 +4493,24 @@ bool mysql_unpack_partition(THD *thd,
thd->variables.character_set_client= system_charset_info;
Parser_state parser_state;
- if (parser_state.init(thd, part_buf, part_info_len))
+ if (unlikely(parser_state.init(thd, part_buf, part_info_len)))
goto end;
- if (init_lex_with_single_table(thd, table, &lex))
+ if (unlikely(init_lex_with_single_table(thd, table, &lex)))
goto end;
- /*
- All Items created is put into a free list on the THD object. This list
- is used to free all Item objects after completing a query. We don't
- want that to happen with the Item tree created as part of the partition
- info. This should be attached to the table object and remain so until
- the table object is released.
- Thus we move away the current list temporarily and start a new list that
- we then save in the partition info structure.
- */
*work_part_info_used= FALSE;
- lex.part_info= new partition_info();/* Indicates MYSQLparse from this place */
- if (!lex.part_info)
- {
- mem_alloc_error(sizeof(partition_info));
+
+ if (unlikely(!(lex.part_info= new partition_info())))
goto end;
- }
+
+ lex.part_info->table= table; /* Indicates MYSQLparse from this place */
part_info= lex.part_info;
DBUG_PRINT("info", ("Parse: %s", part_buf));
thd->m_statement_psi= NULL;
- if (parse_sql(thd, & parser_state, NULL) ||
- part_info->fix_parser_data(thd))
+ if (unlikely(parse_sql(thd, & parser_state, NULL)) ||
+ unlikely(part_info->fix_parser_data(thd)))
{
thd->free_items();
thd->m_statement_psi= parent_locker;
@@ -4307,7 +4741,7 @@ bool set_part_state(Alter_info *alter_info, partition_info *tab_part_info,
do
{
partition_element *part_elem= part_it++;
- if ((alter_info->flags & Alter_info::ALTER_ALL_PARTITION) ||
+ if ((alter_info->partition_flags & ALTER_PARTITION_ALL) ||
(is_name_in_list(part_elem->partition_name,
alter_info->partition_names)))
{
@@ -4326,7 +4760,7 @@ bool set_part_state(Alter_info *alter_info, partition_info *tab_part_info,
} while (++part_count < tab_part_info->num_parts);
if (num_parts_found != alter_info->partition_names.elements &&
- !(alter_info->flags & Alter_info::ALTER_ALL_PARTITION))
+ !(alter_info->partition_flags & ALTER_PARTITION_ALL))
{
/* Not all given partitions found, revert and return failure */
part_it.rewind();
@@ -4361,8 +4795,6 @@ bool compare_partition_options(HA_CREATE_INFO *table_create_info,
const char *option_diffs[MAX_COMPARE_PARTITION_OPTION_ERRORS + 1];
int i, errors= 0;
DBUG_ENTER("compare_partition_options");
- DBUG_ASSERT(!part_elem->tablespace_name &&
- !table_create_info->tablespace);
/*
Note that there are not yet any engine supporting tablespace together
@@ -4480,15 +4912,15 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
DBUG_ENTER("prep_alter_part_table");
/* Foreign keys on partitioned tables are not supported, waits for WL#148 */
- if (table->part_info && (alter_info->flags & Alter_info::ADD_FOREIGN_KEY ||
- alter_info->flags & Alter_info::DROP_FOREIGN_KEY))
+ if (table->part_info && (alter_info->flags & (ALTER_ADD_FOREIGN_KEY |
+ ALTER_DROP_FOREIGN_KEY)))
{
my_error(ER_FOREIGN_KEY_ON_PARTITIONED, MYF(0));
DBUG_RETURN(TRUE);
}
/* Remove partitioning on a not partitioned table is not possible */
- if (!table->part_info && (alter_info->flags &
- Alter_info::ALTER_REMOVE_PARTITIONING))
+ if (!table->part_info && (alter_info->partition_flags &
+ ALTER_PARTITION_REMOVE))
{
my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
DBUG_RETURN(TRUE);
@@ -4508,7 +4940,7 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
alt_part_info->current_partition->list_val_list.head()->
col_val_array[0].max_value) &&
alt_part_info->part_type == LIST_PARTITION &&
- (alter_info->flags & Alter_info::ALTER_ADD_PARTITION);
+ (alter_info->partition_flags & ALTER_PARTITION_ADD);
if (only_default_value_added &&
!thd->lex->part_info->num_columns)
thd->lex->part_info->num_columns= 1; // to make correct clone
@@ -4523,19 +4955,32 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
!(thd->work_part_info= thd->work_part_info->get_clone(thd)))
DBUG_RETURN(TRUE);
- /* ALTER_ADMIN_PARTITION is handled in mysql_admin_table */
- DBUG_ASSERT(!(alter_info->flags & Alter_info::ALTER_ADMIN_PARTITION));
+ /* ALTER_PARTITION_ADMIN is handled in mysql_admin_table */
+ DBUG_ASSERT(!(alter_info->partition_flags & ALTER_PARTITION_ADMIN));
- if (alter_info->flags &
- (Alter_info::ALTER_ADD_PARTITION |
- Alter_info::ALTER_DROP_PARTITION |
- Alter_info::ALTER_COALESCE_PARTITION |
- Alter_info::ALTER_REORGANIZE_PARTITION |
- Alter_info::ALTER_TABLE_REORG |
- Alter_info::ALTER_REBUILD_PARTITION))
+ partition_info *saved_part_info= NULL;
+
+ if (alter_info->partition_flags &
+ (ALTER_PARTITION_ADD |
+ ALTER_PARTITION_DROP |
+ ALTER_PARTITION_COALESCE |
+ ALTER_PARTITION_REORGANIZE |
+ ALTER_PARTITION_TABLE_REORG |
+ ALTER_PARTITION_REBUILD))
{
+ /*
+ You can't add column when we are doing alter related to partition
+ */
+ DBUG_EXECUTE_IF("test_pseudo_invisible", {
+ my_error(ER_INTERNAL_ERROR, MYF(0), "Don't to it with test_pseudo_invisible");
+ DBUG_RETURN(1);
+ });
+ DBUG_EXECUTE_IF("test_completely_invisible", {
+ my_error(ER_INTERNAL_ERROR, MYF(0), "Don't to it with test_completely_invisible");
+ DBUG_RETURN(1);
+ });
partition_info *tab_part_info;
- uint flags= 0;
+ ulonglong flags= 0;
bool is_last_partition_reorged= FALSE;
part_elem_value *tab_max_elem_val= NULL;
part_elem_value *alt_max_elem_val= NULL;
@@ -4555,13 +5000,13 @@ 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,
- alter_ctx->table_name,
+ alter_ctx->db.str,
+ alter_ctx->table_name.str,
MDL_INTENTION_EXCLUSIVE));
tab_part_info= table->part_info;
- if (alter_info->flags & Alter_info::ALTER_TABLE_REORG)
+ if (alter_info->partition_flags & ALTER_PARTITION_TABLE_REORG)
{
uint new_part_no, curr_part_no;
/*
@@ -4613,7 +5058,7 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
We will add more partitions, we use the ADD PARTITION without
setting the flag for no default number of partitions
*/
- alter_info->flags|= Alter_info::ALTER_ADD_PARTITION;
+ alter_info->partition_flags|= ALTER_PARTITION_ADD;
thd->work_part_info->num_parts= new_part_no - curr_part_no;
}
else
@@ -4622,7 +5067,7 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
We will remove hash partitions, we use the COALESCE PARTITION
without setting the flag for no default number of partitions
*/
- alter_info->flags|= Alter_info::ALTER_COALESCE_PARTITION;
+ alter_info->partition_flags|= ALTER_PARTITION_COALESCE;
alter_info->num_parts= curr_part_no - new_part_no;
}
}
@@ -4654,9 +5099,9 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
if (!(tab_part_info= tab_part_info->get_clone(thd)))
DBUG_RETURN(TRUE);
}
- DBUG_PRINT("info", ("*fast_alter_table flags: 0x%x", flags));
- if ((alter_info->flags & Alter_info::ALTER_ADD_PARTITION) ||
- (alter_info->flags & Alter_info::ALTER_REORGANIZE_PARTITION))
+ DBUG_PRINT("info", ("*fast_alter_table flags: 0x%llx", flags));
+ if ((alter_info->partition_flags & ALTER_PARTITION_ADD) ||
+ (alter_info->partition_flags & ALTER_PARTITION_REORGANIZE))
{
if (thd->work_part_info->part_type != tab_part_info->part_type)
{
@@ -4690,6 +5135,11 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
my_error(ER_PARTITION_WRONG_VALUES_ERROR, MYF(0),
"LIST", "IN");
}
+ else if (thd->work_part_info->part_type == VERSIONING_PARTITION ||
+ tab_part_info->part_type == VERSIONING_PARTITION)
+ {
+ my_error(ER_PARTITION_WRONG_TYPE, MYF(0), "SYSTEM_TIME");
+ }
else
{
DBUG_ASSERT(tab_part_info->part_type == RANGE_PARTITION ||
@@ -4720,8 +5170,18 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
goto err;
}
}
- if (alter_info->flags & Alter_info::ALTER_ADD_PARTITION)
+ if (alter_info->partition_flags & ALTER_PARTITION_ADD)
{
+ if (*fast_alter_table && thd->locked_tables_mode)
+ {
+ MEM_ROOT *old_root= thd->mem_root;
+ thd->mem_root= &thd->locked_tables_list.m_locked_tables_root;
+ saved_part_info= tab_part_info->get_clone(thd);
+ thd->mem_root= old_root;
+ saved_part_info->read_partitions= tab_part_info->read_partitions;
+ saved_part_info->lock_partitions= tab_part_info->lock_partitions;
+ saved_part_info->bitmaps_are_initialized= tab_part_info->bitmaps_are_initialized;
+ }
/*
We start by moving the new partitions to the list of temporary
partitions. We will then check that the new partitions fit in the
@@ -4737,7 +5197,8 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
must know the number of new partitions in this case.
*/
if (thd->lex->no_write_to_binlog &&
- tab_part_info->part_type != HASH_PARTITION)
+ tab_part_info->part_type != HASH_PARTITION &&
+ tab_part_info->part_type != VERSIONING_PARTITION)
{
my_error(ER_NO_BINLOG_ERROR, MYF(0));
goto err;
@@ -4842,16 +5303,14 @@ adding and copying partitions, the second after completing the adding
and copying and finally the third line after also dropping the partitions
that are reorganised.
*/
- if (*fast_alter_table &&
- tab_part_info->part_type == HASH_PARTITION)
+ if (*fast_alter_table && tab_part_info->part_type == HASH_PARTITION)
{
uint part_no= 0, start_part= 1, start_sec_part= 1;
uint end_part= 0, end_sec_part= 0;
uint upper_2n= tab_part_info->linear_hash_mask + 1;
uint lower_2n= upper_2n >> 1;
bool all_parts= TRUE;
- if (tab_part_info->linear_hash_ind &&
- num_new_partitions < upper_2n)
+ if (tab_part_info->linear_hash_ind && num_new_partitions < upper_2n)
{
/*
An analysis of which parts needs reorganisation shows that it is
@@ -4942,6 +5401,26 @@ that are reorganised.
partition configuration is made.
*/
{
+ partition_element *now_part= NULL;
+ if (tab_part_info->part_type == VERSIONING_PARTITION)
+ {
+ List_iterator<partition_element> it(tab_part_info->partitions);
+ partition_element *el;
+ while ((el= it++))
+ {
+ if (el->type == partition_element::CURRENT)
+ {
+ it.remove();
+ now_part= el;
+ }
+ }
+ if (*fast_alter_table && tab_part_info->vers_info->interval.is_set())
+ {
+ partition_element *hist_part= tab_part_info->vers_info->hist_part;
+ if (hist_part->range_value <= thd->query_start())
+ hist_part->part_state= PART_CHANGED;
+ }
+ }
List_iterator<partition_element> alt_it(alt_part_info->partitions);
uint part_count= 0;
do
@@ -4949,13 +5428,18 @@ that are reorganised.
partition_element *part_elem= alt_it++;
if (*fast_alter_table)
part_elem->part_state= PART_TO_BE_ADDED;
- if (tab_part_info->partitions.push_back(part_elem, thd->mem_root))
- {
- mem_alloc_error(1);
+ if (unlikely(tab_part_info->partitions.push_back(part_elem,
+ thd->mem_root)))
goto err;
- }
} while (++part_count < num_new_partitions);
tab_part_info->num_parts+= num_new_partitions;
+ if (tab_part_info->part_type == VERSIONING_PARTITION)
+ {
+ DBUG_ASSERT(now_part);
+ if (unlikely(tab_part_info->partitions.push_back(now_part,
+ thd->mem_root)))
+ goto err;
+ }
}
/*
If we specify partitions explicitly we don't use defaults anymore.
@@ -4963,7 +5447,7 @@ that are reorganised.
of partitions anymore. We use this code also for Table reorganisations
and here we don't set any default flags to FALSE.
*/
- if (!(alter_info->flags & Alter_info::ALTER_TABLE_REORG))
+ if (!(alter_info->partition_flags & ALTER_PARTITION_TABLE_REORG))
{
if (!alt_part_info->use_default_partitions)
{
@@ -4974,7 +5458,7 @@ that are reorganised.
tab_part_info->is_auto_partitioned= FALSE;
}
}
- else if (alter_info->flags & Alter_info::ALTER_DROP_PARTITION)
+ else if (alter_info->partition_flags & ALTER_PARTITION_DROP)
{
/*
Drop a partition from a range partition and list partitioning is
@@ -4989,16 +5473,28 @@ that are reorganised.
List_iterator<partition_element> part_it(tab_part_info->partitions);
tab_part_info->is_auto_partitioned= FALSE;
- if (!(tab_part_info->part_type == RANGE_PARTITION ||
- tab_part_info->part_type == LIST_PARTITION))
+ if (tab_part_info->part_type == VERSIONING_PARTITION)
{
- my_error(ER_ONLY_ON_RANGE_LIST_PARTITION, MYF(0), "DROP");
- goto err;
+ if (num_parts_dropped >= tab_part_info->num_parts - 1)
+ {
+ DBUG_ASSERT(table && table->s && table->s->table_name.str);
+ my_error(ER_VERS_WRONG_PARTS, MYF(0), table->s->table_name.str);
+ goto err;
+ }
}
- if (num_parts_dropped >= tab_part_info->num_parts)
+ else
{
- my_error(ER_DROP_LAST_PARTITION, MYF(0));
- goto err;
+ if (!(tab_part_info->part_type == RANGE_PARTITION ||
+ tab_part_info->part_type == LIST_PARTITION))
+ {
+ my_error(ER_ONLY_ON_RANGE_LIST_PARTITION, MYF(0), "DROP");
+ goto err;
+ }
+ if (num_parts_dropped >= tab_part_info->num_parts)
+ {
+ my_error(ER_DROP_LAST_PARTITION, MYF(0));
+ goto err;
+ }
}
do
{
@@ -5006,6 +5502,24 @@ that are reorganised.
if (is_name_in_list(part_elem->partition_name,
alter_info->partition_names))
{
+ if (tab_part_info->part_type == VERSIONING_PARTITION)
+ {
+ if (part_elem->type == partition_element::CURRENT)
+ {
+ my_error(ER_VERS_WRONG_PARTS, MYF(0), table->s->table_name.str);
+ goto err;
+ }
+ if (tab_part_info->vers_info->interval.is_set())
+ {
+ if (num_parts_found < part_count)
+ {
+ my_error(ER_VERS_DROP_PARTITION_INTERVAL, MYF(0));
+ goto err;
+ }
+ tab_part_info->vers_info->interval.start=
+ (my_time_t)part_elem->range_value;
+ }
+ }
/*
Set state to indicate that the partition is to be dropped.
*/
@@ -5025,7 +5539,7 @@ that are reorganised.
}
tab_part_info->num_parts-= num_parts_dropped;
}
- else if (alter_info->flags & Alter_info::ALTER_REBUILD_PARTITION)
+ else if (alter_info->partition_flags & ALTER_PARTITION_REBUILD)
{
set_engine_all_partitions(tab_part_info,
tab_part_info->default_engine_type);
@@ -5040,7 +5554,7 @@ that are reorganised.
goto err;
}
}
- else if (alter_info->flags & Alter_info::ALTER_COALESCE_PARTITION)
+ else if (alter_info->partition_flags & ALTER_PARTITION_COALESCE)
{
uint num_parts_coalesced= alter_info->num_parts;
uint num_parts_remain= tab_part_info->num_parts - num_parts_coalesced;
@@ -5138,13 +5652,13 @@ state of p1.
} while (part_count < tab_part_info->num_parts);
tab_part_info->num_parts= num_parts_remain;
}
- if (!(alter_info->flags & Alter_info::ALTER_TABLE_REORG))
+ if (!(alter_info->partition_flags & ALTER_PARTITION_TABLE_REORG))
{
tab_part_info->use_default_num_partitions= FALSE;
tab_part_info->is_auto_partitioned= FALSE;
}
}
- else if (alter_info->flags & Alter_info::ALTER_REORGANIZE_PARTITION)
+ else if (alter_info->partition_flags & ALTER_PARTITION_REORGANIZE)
{
/*
Reorganise partitions takes a number of partitions that are next
@@ -5259,12 +5773,10 @@ the generated partition syntax in a correct manner.
else
tab_max_range= part_elem->range_value;
if (*fast_alter_table &&
- tab_part_info->temp_partitions.push_back(part_elem,
- thd->mem_root))
- {
- mem_alloc_error(1);
+ unlikely(tab_part_info->temp_partitions.
+ push_back(part_elem, thd->mem_root)))
goto err;
- }
+
if (*fast_alter_table)
part_elem->part_state= PART_TO_BE_REORGED;
if (!found_first)
@@ -5321,8 +5833,8 @@ the generated partition syntax in a correct manner.
}
*partition_changed= TRUE;
thd->work_part_info= tab_part_info;
- if (alter_info->flags & Alter_info::ALTER_ADD_PARTITION ||
- alter_info->flags & Alter_info::ALTER_REORGANIZE_PARTITION)
+ if (alter_info->partition_flags & (ALTER_PARTITION_ADD |
+ ALTER_PARTITION_REORGANIZE))
{
if (tab_part_info->use_default_subpartitions &&
!alt_part_info->use_default_subpartitions)
@@ -5330,8 +5842,9 @@ the generated partition syntax in a correct manner.
tab_part_info->use_default_subpartitions= FALSE;
tab_part_info->use_default_num_subpartitions= FALSE;
}
+
if (tab_part_info->check_partition_info(thd, (handlerton**)NULL,
- table->file, 0, TRUE))
+ table->file, 0, alt_part_info))
{
goto err;
}
@@ -5340,17 +5853,17 @@ the generated partition syntax in a correct manner.
since this function "fixes" the item trees of the new partitions
to reorganize into
*/
- if (alter_info->flags == Alter_info::ALTER_REORGANIZE_PARTITION &&
+ if (alter_info->partition_flags == ALTER_PARTITION_REORGANIZE &&
tab_part_info->part_type == RANGE_PARTITION &&
((is_last_partition_reorged &&
(tab_part_info->column_list ?
- (tab_part_info->compare_column_values(
+ (partition_info_compare_column_values(
alt_max_elem_val->col_val_array,
tab_max_elem_val->col_val_array) < 0) :
alt_max_range < tab_max_range)) ||
(!is_last_partition_reorged &&
(tab_part_info->column_list ?
- (tab_part_info->compare_column_values(
+ (partition_info_compare_column_values(
alt_max_elem_val->col_val_array,
tab_max_elem_val->col_val_array) != 0) :
alt_max_range != tab_max_range))))
@@ -5368,7 +5881,7 @@ the generated partition syntax in a correct manner.
goto err;
}
}
- }
+ } // ADD, DROP, COALESCE, REORGANIZE, TABLE_REORG, REBUILD
else
{
/*
@@ -5423,7 +5936,7 @@ the generated partition syntax in a correct manner.
if (tab_part_info)
{
- if (alter_info->flags & Alter_info::ALTER_REMOVE_PARTITIONING)
+ if (alter_info->partition_flags & ALTER_PARTITION_REMOVE)
{
DBUG_PRINT("info", ("Remove partitioning"));
if (!(create_info->used_fields & HA_CREATE_USED_ENGINE))
@@ -5474,26 +5987,35 @@ the generated partition syntax in a correct manner.
}
}
/*
- Prohibit inplace when partitioned by primary key and the primary key is dropped.
+ Prohibit inplace when partitioned by primary key and the primary key is changed.
*/
if (!*partition_changed &&
tab_part_info->part_field_array &&
!tab_part_info->part_field_list.elements &&
table->s->primary_key != MAX_KEY)
{
- KEY *primary_key= table->key_info + table->s->primary_key;
- List_iterator_fast<Alter_drop> drop_it(alter_info->drop_list);
- const char *primary_name= primary_key->name;
- const Alter_drop *drop;
- drop_it.rewind();
- while ((drop= drop_it++))
+
+ if (alter_info->flags & (ALTER_DROP_SYSTEM_VERSIONING |
+ ALTER_ADD_SYSTEM_VERSIONING))
{
- if (drop->type == Alter_drop::KEY &&
- 0 == my_strcasecmp(system_charset_info, primary_name, drop->name))
- break;
+ *partition_changed= true;
+ }
+ else
+ {
+ KEY *primary_key= table->key_info + table->s->primary_key;
+ List_iterator_fast<Alter_drop> drop_it(alter_info->drop_list);
+ const char *primary_name= primary_key->name.str;
+ const Alter_drop *drop;
+ drop_it.rewind();
+ while ((drop= drop_it++))
+ {
+ if (drop->type == Alter_drop::KEY &&
+ 0 == my_strcasecmp(system_charset_info, primary_name, drop->name))
+ break;
+ }
+ if (drop)
+ *partition_changed= TRUE;
}
- if (drop)
- *partition_changed= TRUE;
}
}
if (thd->work_part_info)
@@ -5516,9 +6038,9 @@ the generated partition syntax in a correct manner.
rebuild). This is to handle KEY (numeric_cols) partitioned tables
created in 5.1. For more info, see bug#14521864.
*/
- if (alter_info->flags != Alter_info::ALTER_PARTITION ||
+ if (alter_info->partition_flags != ALTER_PARTITION_INFO ||
!table->part_info ||
- alter_info->requested_algorithm !=
+ alter_info->algorithm(thd) !=
Alter_info::ALTER_TABLE_ALGORITHM_INPLACE ||
!table->part_info->has_same_partitioning(part_info))
{
@@ -5526,6 +6048,7 @@ the generated partition syntax in a correct manner.
*partition_changed= true;
}
}
+
/*
Set up partition default_engine_type either from the create_info
or from the previus table
@@ -5556,6 +6079,8 @@ the generated partition syntax in a correct manner.
DBUG_RETURN(FALSE);
err:
*fast_alter_table= false;
+ if (saved_part_info)
+ table->part_info= saved_part_info;
DBUG_RETURN(TRUE);
}
@@ -5594,16 +6119,18 @@ static bool mysql_change_partitions(ALTER_PARTITION_PARAM_TYPE *lpt)
THD *thd= lpt->thd;
DBUG_ENTER("mysql_change_partitions");
- build_table_filename(path, sizeof(path) - 1, lpt->db, lpt->table_name, "", 0);
+ build_table_filename(path, sizeof(path) - 1, lpt->db.str, lpt->table_name.str, "", 0);
if(mysql_trans_prepare_alter_copy_data(thd))
DBUG_RETURN(TRUE);
/* TODO: test if bulk_insert would increase the performance */
- if ((error= file->ha_change_partitions(lpt->create_info, path, &lpt->copied,
- &lpt->deleted, lpt->pack_frm_data,
- lpt->pack_frm_len)))
+ if (unlikely((error= file->ha_change_partitions(lpt->create_info, path,
+ &lpt->copied,
+ &lpt->deleted,
+ lpt->pack_frm_data,
+ lpt->pack_frm_len))))
{
file->print_error(error, MYF(error != ER_OUTOFMEMORY ? 0 : ME_FATALERROR));
}
@@ -5640,8 +6167,8 @@ static bool mysql_rename_partitions(ALTER_PARTITION_PARAM_TYPE *lpt)
int error;
DBUG_ENTER("mysql_rename_partitions");
- build_table_filename(path, sizeof(path) - 1, lpt->db, lpt->table_name, "", 0);
- if ((error= lpt->table->file->ha_rename_partitions(path)))
+ build_table_filename(path, sizeof(path) - 1, lpt->db.str, lpt->table_name.str, "", 0);
+ if (unlikely((error= lpt->table->file->ha_rename_partitions(path))))
{
if (error != 1)
lpt->table->file->print_error(error, MYF(0));
@@ -5686,7 +6213,7 @@ static bool mysql_drop_partitions(ALTER_PARTITION_PARAM_TYPE *lpt)
lpt->table->s->table_name.str,
MDL_EXCLUSIVE));
- build_table_filename(path, sizeof(path) - 1, lpt->db, lpt->table_name, "", 0);
+ build_table_filename(path, sizeof(path) - 1, lpt->db.str, lpt->table_name.str, "", 0);
if ((error= lpt->table->file->ha_drop_partitions(path)))
{
lpt->table->file->print_error(error, MYF(0));
@@ -6083,8 +6610,7 @@ static bool write_log_rename_frm(ALTER_PARTITION_PARAM_TYPE *lpt)
DBUG_ENTER("write_log_rename_frm");
part_info->first_log_entry= NULL;
- build_table_filename(path, sizeof(path) - 1, lpt->db,
- lpt->table_name, "", 0);
+ build_table_filename(path, sizeof(path) - 1, lpt->db.str, lpt->table_name.str, "", 0);
build_table_shadow_filename(shadow_path, sizeof(shadow_path) - 1, lpt);
mysql_mutex_lock(&LOCK_gdl);
if (write_log_replace_delete_frm(lpt, 0UL, shadow_path, path, TRUE))
@@ -6135,8 +6661,7 @@ static bool write_log_drop_partition(ALTER_PARTITION_PARAM_TYPE *lpt)
DBUG_ENTER("write_log_drop_partition");
part_info->first_log_entry= NULL;
- build_table_filename(path, sizeof(path) - 1, lpt->db,
- lpt->table_name, "", 0);
+ build_table_filename(path, sizeof(path) - 1, lpt->db.str, lpt->table_name.str, "", 0);
build_table_shadow_filename(tmp_path, sizeof(tmp_path) - 1, lpt);
mysql_mutex_lock(&LOCK_gdl);
if (write_log_dropped_partitions(lpt, &next_entry, (const char*)path,
@@ -6194,8 +6719,7 @@ static bool write_log_add_change_partition(ALTER_PARTITION_PARAM_TYPE *lpt)
DBUG_ASSERT(old_first_log_entry);
DBUG_ENTER("write_log_add_change_partition");
- build_table_filename(path, sizeof(path) - 1, lpt->db,
- lpt->table_name, "", 0);
+ build_table_filename(path, sizeof(path) - 1, lpt->db.str, lpt->table_name.str, "", 0);
build_table_shadow_filename(tmp_path, sizeof(tmp_path) - 1, lpt);
mysql_mutex_lock(&LOCK_gdl);
@@ -6263,15 +6787,14 @@ static bool write_log_final_change_partition(ALTER_PARTITION_PARAM_TYPE *lpt)
Replace the revert operations with forced retry operations.
*/
part_info->first_log_entry= NULL;
- build_table_filename(path, sizeof(path) - 1, lpt->db,
- lpt->table_name, "", 0);
+ build_table_filename(path, sizeof(path) - 1, lpt->db.str, lpt->table_name.str, "", 0);
build_table_shadow_filename(shadow_path, sizeof(shadow_path) - 1, lpt);
mysql_mutex_lock(&LOCK_gdl);
if (write_log_changed_partitions(lpt, &next_entry, (const char*)path))
goto error;
if (write_log_dropped_partitions(lpt, &next_entry, (const char*)path,
- lpt->alter_info->flags &
- Alter_info::ALTER_REORGANIZE_PARTITION))
+ lpt->alter_info->partition_flags &
+ ALTER_PARTITION_REORGANIZE))
goto error;
if (write_log_replace_delete_frm(lpt, next_entry, shadow_path, path, TRUE))
goto error;
@@ -6382,14 +6905,14 @@ static void alter_partition_lock_handling(ALTER_PARTITION_PARAM_TYPE *lpt)
Diagnostics_area *stmt_da= NULL;
Diagnostics_area tmp_stmt_da(true);
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
{
/* reopen might fail if we have a previous error, use a temporary da. */
stmt_da= thd->get_stmt_da();
thd->set_stmt_da(&tmp_stmt_da);
}
- if (thd->locked_tables_list.reopen_tables(thd, false))
+ if (unlikely(thd->locked_tables_list.reopen_tables(thd, false)))
sql_print_warning("We failed to reacquire LOCKs in ALTER TABLE");
if (stmt_da)
@@ -6449,8 +6972,8 @@ void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt,
Better to do that here, than leave the cleaning up to others.
Aquire EXCLUSIVE mdl lock if not already aquired.
*/
- if (!thd->mdl_context.is_lock_owner(MDL_key::TABLE, lpt->db,
- lpt->table_name,
+ 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))
@@ -6588,14 +7111,14 @@ err_exclusive_lock:
Diagnostics_area *stmt_da= NULL;
Diagnostics_area tmp_stmt_da(true);
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
{
/* reopen might fail if we have a previous error, use a temporary da. */
stmt_da= thd->get_stmt_da();
thd->set_stmt_da(&tmp_stmt_da);
}
- if (thd->locked_tables_list.reopen_tables(thd, false))
+ if (unlikely(thd->locked_tables_list.reopen_tables(thd, false)))
sql_print_warning("We failed to reacquire LOCKs in ALTER TABLE");
if (stmt_da)
@@ -6647,8 +7170,8 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
Alter_info *alter_info,
HA_CREATE_INFO *create_info,
TABLE_LIST *table_list,
- char *db,
- const char *table_name)
+ const LEX_CSTRING *db,
+ const LEX_CSTRING *table_name)
{
/* Set-up struct used to write frm files */
partition_info *part_info;
@@ -6671,8 +7194,8 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
lpt->table= table;
lpt->key_info_buffer= 0;
lpt->key_count= 0;
- lpt->db= db;
- lpt->table_name= table_name;
+ lpt->db= *db;
+ lpt->table_name= *table_name;
lpt->copied= 0;
lpt->deleted= 0;
lpt->pack_frm_data= NULL;
@@ -6726,7 +7249,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
goto err;
}
}
- else if (alter_info->flags & Alter_info::ALTER_DROP_PARTITION)
+ else if (alter_info->partition_flags & ALTER_PARTITION_DROP)
{
/*
Now after all checks and setting state on dropped partitions we can
@@ -6826,7 +7349,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
goto err;
}
}
- else if ((alter_info->flags & Alter_info::ALTER_ADD_PARTITION) &&
+ else if ((alter_info->partition_flags & ALTER_PARTITION_ADD) &&
(part_info->part_type == RANGE_PARTITION ||
part_info->part_type == LIST_PARTITION))
{
@@ -7020,6 +7543,39 @@ err:
}
#endif
+
+/*
+ Prepare for calling val_int on partition function by setting fields to
+ point to the record where the values of the PF-fields are stored.
+
+ SYNOPSIS
+ set_field_ptr()
+ ptr Array of fields to change ptr
+ new_buf New record pointer
+ old_buf Old record pointer
+
+ DESCRIPTION
+ Set ptr in field objects of field array to refer to new_buf record
+ instead of previously old_buf. Used before calling val_int and after
+ it is used to restore pointers to table->record[0].
+ This routine is placed outside of partition code since it can be useful
+ also for other programs.
+*/
+
+void set_field_ptr(Field **ptr, const uchar *new_buf,
+ const uchar *old_buf)
+{
+ my_ptrdiff_t diff= (new_buf - old_buf);
+ DBUG_ENTER("set_field_ptr");
+
+ do
+ {
+ (*ptr)->move_field_offset(diff);
+ } while (*(++ptr));
+ DBUG_VOID_RETURN;
+}
+
+
/*
Prepare for calling val_int on partition function by setting fields to
point to the record where the values of the PF-fields are stored.
@@ -7058,27 +7614,61 @@ void set_key_field_ptr(KEY *key_info, const uchar *new_buf,
}
-/*
- SYNOPSIS
- mem_alloc_error()
- size Size of memory attempted to allocate
- None
-
- RETURN VALUES
- None
+/**
+ Append all fields in read_set to string
- DESCRIPTION
- A routine to use for all the many places in the code where memory
- allocation error can happen, a tremendous amount of them, needs
- simple routine that signals this error.
+ @param[in,out] str String to append to.
+ @param[in] row Row to append.
+ @param[in] table Table containing read_set and fields for the row.
*/
-
-void mem_alloc_error(size_t size)
+void append_row_to_str(String &str, const uchar *row, TABLE *table)
{
- my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR),
- static_cast<int>(size));
+ Field **fields, **field_ptr;
+ const uchar *rec;
+ uint num_fields= bitmap_bits_set(table->read_set);
+ uint curr_field_index= 0;
+ bool is_rec0= !row || row == table->record[0];
+ if (!row)
+ rec= table->record[0];
+ else
+ rec= row;
+
+ /* Create a new array of all read fields. */
+ fields= (Field**) my_malloc(sizeof(void*) * (num_fields + 1),
+ MYF(0));
+ if (!fields)
+ return;
+ fields[num_fields]= NULL;
+ for (field_ptr= table->field;
+ *field_ptr;
+ field_ptr++)
+ {
+ if (!bitmap_is_set(table->read_set, (*field_ptr)->field_index))
+ continue;
+ fields[curr_field_index++]= *field_ptr;
+ }
+
+
+ if (!is_rec0)
+ set_field_ptr(fields, rec, table->record[0]);
+
+ for (field_ptr= fields;
+ *field_ptr;
+ field_ptr++)
+ {
+ Field *field= *field_ptr;
+ str.append(" ");
+ str.append(&field->field_name);
+ str.append(":");
+ field_unpack(&str, field, rec, 0, false);
+ }
+
+ if (!is_rec0)
+ set_field_ptr(fields, table->record[0], rec);
+ my_free(fields);
}
+
#ifdef WITH_PARTITION_STORAGE_ENGINE
/**
Return comma-separated list of used partitions in the provided given string.
@@ -7202,6 +7792,10 @@ static void set_up_range_analysis_info(partition_info *part_info)
partitioning
*/
switch (part_info->part_type) {
+ case VERSIONING_PARTITION:
+ if (!part_info->vers_info->interval.is_set())
+ break;
+ /* Fall through */
case RANGE_PARTITION:
case LIST_PARTITION:
if (!part_info->column_list)
@@ -7490,13 +8084,11 @@ uint32 get_partition_id_cols_range_for_endpoint(partition_info *part_info,
}
-int get_part_iter_for_interval_cols_via_map(partition_info *part_info,
- bool is_subpart,
- uint32 *store_length_array,
- uchar *min_value, uchar *max_value,
- uint min_len, uint max_len,
- uint flags,
- PARTITION_ITERATOR *part_iter)
+static int get_part_iter_for_interval_cols_via_map(partition_info *part_info,
+ bool is_subpart, uint32 *store_length_array,
+ uchar *min_value, uchar *max_value,
+ uint min_len, uint max_len,
+ uint flags, PARTITION_ITERATOR *part_iter)
{
bool can_match_multiple_values;
uint32 nparts;
@@ -7504,7 +8096,7 @@ int get_part_iter_for_interval_cols_via_map(partition_info *part_info,
uint full_length= 0;
DBUG_ENTER("get_part_iter_for_interval_cols_via_map");
- if (part_info->part_type == RANGE_PARTITION)
+ if (part_info->part_type == RANGE_PARTITION || part_info->part_type == VERSIONING_PARTITION)
{
get_col_endpoint= get_partition_id_cols_range_for_endpoint;
part_iter->get_next= get_next_partition_id_range;
@@ -7550,7 +8142,7 @@ int get_part_iter_for_interval_cols_via_map(partition_info *part_info,
}
if (flags & NO_MAX_RANGE)
{
- if (part_info->part_type == RANGE_PARTITION)
+ if (part_info->part_type == RANGE_PARTITION || part_info->part_type == VERSIONING_PARTITION)
part_iter->part_nums.end= part_info->num_parts;
else /* LIST_PARTITION */
{
@@ -7617,13 +8209,12 @@ int get_part_iter_for_interval_cols_via_map(partition_info *part_info,
@retval -1 All partitions would match (iterator not initialized)
*/
-int get_part_iter_for_interval_via_mapping(partition_info *part_info,
- bool is_subpart,
- uint32 *store_length_array, /* ignored */
- uchar *min_value, uchar *max_value,
- uint min_len, uint max_len, /* ignored */
- uint flags,
- PARTITION_ITERATOR *part_iter)
+static int get_part_iter_for_interval_via_mapping(partition_info *part_info,
+ bool is_subpart,
+ uint32 *store_length_array, /* ignored */
+ uchar *min_value, uchar *max_value,
+ uint min_len, uint max_len, /* ignored */
+ uint flags, PARTITION_ITERATOR *part_iter)
{
Field *field= part_info->part_field_array[0];
uint32 UNINIT_VAR(max_endpoint_val);
@@ -7641,7 +8232,8 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info,
part_iter->ret_null_part= part_iter->ret_null_part_orig= FALSE;
part_iter->ret_default_part= part_iter->ret_default_part_orig= FALSE;
- if (part_info->part_type == RANGE_PARTITION)
+ if (part_info->part_type == RANGE_PARTITION ||
+ part_info->part_type == VERSIONING_PARTITION)
{
if (part_info->part_charset_field_array)
get_endpoint= get_partition_id_range_for_endpoint_charset;
@@ -7864,13 +8456,12 @@ not_found:
-1 - All partitions would match, iterator not initialized
*/
-int get_part_iter_for_interval_via_walking(partition_info *part_info,
- bool is_subpart,
- uint32 *store_length_array, /* ignored */
- uchar *min_value, uchar *max_value,
- uint min_len, uint max_len, /* ignored */
- uint flags,
- PARTITION_ITERATOR *part_iter)
+static int get_part_iter_for_interval_via_walking(partition_info *part_info,
+ bool is_subpart,
+ uint32 *store_length_array, /* ignored */
+ uchar *min_value, uchar *max_value,
+ uint min_len, uint max_len, /* ignored */
+ uint flags, PARTITION_ITERATOR *part_iter)
{
Field *field;
uint total_parts;
@@ -8253,4 +8844,5 @@ uint get_partition_field_store_length(Field *field)
store_length+= HA_KEY_BLOB_LENGTH;
return store_length;
}
+
#endif
diff --git a/sql/sql_partition.h b/sql/sql_partition.h
index c66f670d0b0..ea197a6fc2c 100644
--- a/sql/sql_partition.h
+++ b/sql/sql_partition.h
@@ -41,6 +41,7 @@ typedef struct st_key_range key_range;
#define HA_CAN_UPDATE_PARTITION_KEY (1 << 1)
#define HA_CAN_PARTITION_UNIQUE (1 << 2)
#define HA_USE_AUTO_PARTITION (1 << 3)
+#define HA_ONLY_VERS_PARTITION (1 << 4)
#define NORMAL_PART_NAME 0
#define TEMP_PART_NAME 1
@@ -56,8 +57,8 @@ typedef struct st_lock_param_type
Alter_info *alter_info;
TABLE *table;
KEY *key_info_buffer;
- const char *db;
- const char *table_name;
+ LEX_CSTRING db;
+ LEX_CSTRING table_name;
uchar *pack_frm_data;
uint key_count;
uint db_options;
@@ -86,12 +87,8 @@ bool check_reorganise_list(partition_info *new_part_info,
partition_info *old_part_info,
List<char> list_part_names);
handler *get_ha_partition(partition_info *part_info);
-int get_parts_for_update(const uchar *old_data, uchar *new_data,
- const uchar *rec0, partition_info *part_info,
- uint32 *old_part_id, uint32 *new_part_id,
- longlong *func_value);
-int get_part_for_delete(const uchar *buf, const uchar *rec0,
- partition_info *part_info, uint32 *part_id);
+int get_part_for_buf(const uchar *buf, const uchar *rec0,
+ partition_info *part_info, uint32 *part_id);
void prune_partition_set(const TABLE *table, part_id_range *part_spec);
bool check_partition_info(partition_info *part_info,handlerton **eng_type,
TABLE *table, handler *file, HA_CREATE_INFO *info);
@@ -128,7 +125,14 @@ uint32 get_partition_id_range_for_endpoint(partition_info *part_info,
bool check_part_func_fields(Field **ptr, bool ok_with_charsets);
bool field_is_partition_charset(Field *field);
Item* convert_charset_partition_constant(Item *item, CHARSET_INFO *cs);
-void mem_alloc_error(size_t size);
+/**
+ Append all fields in read_set to string
+
+ @param[in,out] str String to append to.
+ @param[in] row Row to append.
+ @param[in] table Table containing read_set and fields for the row.
+*/
+void append_row_to_str(String &str, const uchar *row, TABLE *table);
void truncate_partition_filename(char *path);
/*
@@ -258,8 +262,8 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
Alter_info *alter_info,
HA_CREATE_INFO *create_info,
TABLE_LIST *table_list,
- char *db,
- const char *table_name);
+ const LEX_CSTRING *db,
+ const LEX_CSTRING *table_name);
bool set_part_state(Alter_info *alter_info, partition_info *tab_part_info,
enum partition_state part_state);
uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
@@ -295,4 +299,30 @@ int __attribute__((warn_unused_result))
void set_key_field_ptr(KEY *key_info, const uchar *new_buf,
const uchar *old_buf);
+/** Set up table for creating a partition.
+Copy info from partition to the table share so the created partition
+has the correct info.
+ @param thd THD object
+ @param share Table share to be updated.
+ @param info Create info to be updated.
+ @param part_elem partition_element containing the info.
+
+ @return status
+ @retval TRUE Error
+ @retval FALSE Success
+
+ @details
+ Set up
+ 1) Comment on partition
+ 2) MAX_ROWS, MIN_ROWS on partition
+ 3) Index file name on partition
+ 4) Data file name on partition
+*/
+bool set_up_table_before_create(THD *thd,
+ TABLE_SHARE *share,
+ const char *partition_name_with_path,
+ HA_CREATE_INFO *info,
+ partition_element *part_elem);
+
#endif /* SQL_PARTITION_INCLUDED */
+
diff --git a/sql/sql_partition_admin.cc b/sql/sql_partition_admin.cc
index da0d0cdf2a8..90156a76dec 100644
--- a/sql/sql_partition_admin.cc
+++ b/sql/sql_partition_admin.cc
@@ -15,6 +15,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
+#include "mariadb.h"
#include "sql_parse.h" // check_one_table_access
// check_merge_table_access
// check_one_table_access
@@ -66,25 +67,29 @@ bool Sql_cmd_alter_table_exchange_partition::execute(THD *thd)
DBUG_ENTER("Sql_cmd_alter_table_exchange_partition::execute");
- if (thd->is_fatal_error) /* out of memory creating a copy of alter_info */
+ if (unlikely(thd->is_fatal_error))
+ {
+ /* out of memory creating a copy of alter_info */
DBUG_RETURN(TRUE);
+ }
/* Must be set in the parser */
- DBUG_ASSERT(select_lex->db);
+ DBUG_ASSERT(select_lex->db.str);
/* also check the table to be exchanged with the partition */
- DBUG_ASSERT(alter_info.flags & Alter_info::ALTER_EXCHANGE_PARTITION);
-
- if (check_access(thd, priv_needed, first_table->db,
- &first_table->grant.privilege,
- &first_table->grant.m_internal,
- 0, 0) ||
- check_access(thd, priv_needed, first_table->next_local->db,
- &first_table->next_local->grant.privilege,
- &first_table->next_local->grant.m_internal,
- 0, 0))
+ DBUG_ASSERT(alter_info.partition_flags & ALTER_PARTITION_EXCHANGE);
+
+ if (unlikely(check_access(thd, priv_needed, first_table->db.str,
+ &first_table->grant.privilege,
+ &first_table->grant.m_internal,
+ 0, 0)) ||
+ unlikely(check_access(thd, priv_needed, first_table->next_local->db.str,
+ &first_table->next_local->grant.privilege,
+ &first_table->next_local->grant.m_internal,
+ 0, 0)))
DBUG_RETURN(TRUE);
- if (check_grant(thd, priv_needed, first_table, FALSE, UINT_MAX, FALSE))
+ if (unlikely(check_grant(thd, priv_needed, first_table, FALSE, UINT_MAX,
+ FALSE)))
DBUG_RETURN(TRUE);
/* Not allowed with EXCHANGE PARTITION */
@@ -107,31 +112,32 @@ bool Sql_cmd_alter_table_exchange_partition::execute(THD *thd)
@retval FALSE if OK, otherwise error is reported and TRUE is returned.
*/
+
static bool check_exchange_partition(TABLE *table, TABLE *part_table)
{
DBUG_ENTER("check_exchange_partition");
/* Both tables must exist */
- if (!part_table || !table)
+ if (unlikely(!part_table || !table))
{
my_error(ER_CHECK_NO_SUCH_TABLE, MYF(0));
DBUG_RETURN(TRUE);
}
/* The first table must be partitioned, and the second must not */
- if (!part_table->part_info)
+ if (unlikely(!part_table->part_info))
{
my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
DBUG_RETURN(TRUE);
}
- if (table->part_info)
+ if (unlikely(table->part_info))
{
my_error(ER_PARTITION_EXCHANGE_PART_TABLE, MYF(0),
table->s->table_name.str);
DBUG_RETURN(TRUE);
}
- if (part_table->file->ht != partition_hton)
+ if (unlikely(part_table->file->ht != partition_hton))
{
/*
Only allowed on partitioned tables throught the generic ha_partition
@@ -141,14 +147,14 @@ static bool check_exchange_partition(TABLE *table, TABLE *part_table)
DBUG_RETURN(TRUE);
}
- if (table->file->ht != part_table->part_info->default_engine_type)
+ if (unlikely(table->file->ht != part_table->part_info->default_engine_type))
{
my_error(ER_MIX_HANDLER_ERROR, MYF(0));
DBUG_RETURN(TRUE);
}
/* Verify that table is not tmp table, partitioned tables cannot be tmp. */
- if (table->s->tmp_table != NO_TMP_TABLE)
+ if (unlikely(table->s->tmp_table != NO_TMP_TABLE))
{
my_error(ER_PARTITION_EXCHANGE_TEMP_TABLE, MYF(0),
table->s->table_name.str);
@@ -156,7 +162,7 @@ static bool check_exchange_partition(TABLE *table, TABLE *part_table)
}
/* The table cannot have foreign keys constraints or be referenced */
- if(!table->file->can_switch_engines())
+ if (unlikely(!table->file->can_switch_engines()))
{
my_error(ER_PARTITION_EXCHANGE_FOREIGN_KEY, MYF(0),
table->s->table_name.str);
@@ -186,8 +192,8 @@ static bool compare_table_with_partition(THD *thd, TABLE *table,
DBUG_ENTER("compare_table_with_partition");
bool metadata_equal= false;
- memset(&part_create_info, 0, sizeof(HA_CREATE_INFO));
- memset(&table_create_info, 0, sizeof(HA_CREATE_INFO));
+ part_create_info.init();
+ table_create_info.init();
update_create_info_from_table(&table_create_info, table);
/* get the current auto_increment value */
@@ -195,8 +201,8 @@ static bool compare_table_with_partition(THD *thd, TABLE *table,
/* mark all columns used, since they are used when preparing the new table */
part_table->use_all_columns();
table->use_all_columns();
- if (mysql_prepare_alter_table(thd, part_table, &part_create_info,
- &part_alter_info, &part_alter_ctx))
+ if (unlikely(mysql_prepare_alter_table(thd, part_table, &part_create_info,
+ &part_alter_info, &part_alter_ctx)))
{
my_error(ER_TABLES_DIFFERENT_METADATA, MYF(0));
DBUG_RETURN(TRUE);
@@ -334,11 +340,8 @@ static bool exchange_name_with_ddl_log(THD *thd,
handler *file= NULL;
DBUG_ENTER("exchange_name_with_ddl_log");
- if (!(file= get_new_handler(NULL, thd->mem_root, ht)))
- {
- mem_alloc_error(sizeof(handler));
+ if (unlikely(!(file= get_new_handler(NULL, thd->mem_root, ht))))
DBUG_RETURN(TRUE);
- }
/* prepare the action entry */
exchange_entry.entry_type= DDL_LOG_ENTRY_CODE;
@@ -358,12 +361,13 @@ static bool exchange_name_with_ddl_log(THD *thd,
*/
DBUG_EXECUTE_IF("exchange_partition_fail_1", goto err_no_action_written;);
DBUG_EXECUTE_IF("exchange_partition_abort_1", DBUG_SUICIDE(););
- if (write_ddl_log_entry(&exchange_entry, &log_entry))
+ if (unlikely(write_ddl_log_entry(&exchange_entry, &log_entry)))
goto err_no_action_written;
DBUG_EXECUTE_IF("exchange_partition_fail_2", goto err_no_execute_written;);
DBUG_EXECUTE_IF("exchange_partition_abort_2", DBUG_SUICIDE(););
- if (write_execute_ddl_log_entry(log_entry->entry_pos, FALSE, &exec_log_entry))
+ if (unlikely(write_execute_ddl_log_entry(log_entry->entry_pos, FALSE,
+ &exec_log_entry)))
goto err_no_execute_written;
/* ddl_log is written and synced */
@@ -381,7 +385,7 @@ static bool exchange_name_with_ddl_log(THD *thd,
error_set= TRUE;
goto err_rename;);
DBUG_EXECUTE_IF("exchange_partition_abort_3", DBUG_SUICIDE(););
- if (file->ha_rename_table(name, tmp_name))
+ if (unlikely(file->ha_rename_table(name, tmp_name)))
{
my_error(ER_ERROR_ON_RENAME, MYF(0), name, tmp_name, my_errno);
error_set= TRUE;
@@ -389,7 +393,7 @@ static bool exchange_name_with_ddl_log(THD *thd,
}
DBUG_EXECUTE_IF("exchange_partition_fail_4", goto err_rename;);
DBUG_EXECUTE_IF("exchange_partition_abort_4", DBUG_SUICIDE(););
- if (deactivate_ddl_log_entry(log_entry->entry_pos))
+ if (unlikely(deactivate_ddl_log_entry(log_entry->entry_pos)))
goto err_rename;
/* call rename table from partition to table */
@@ -398,7 +402,7 @@ static bool exchange_name_with_ddl_log(THD *thd,
error_set= TRUE;
goto err_rename;);
DBUG_EXECUTE_IF("exchange_partition_abort_5", DBUG_SUICIDE(););
- if (file->ha_rename_table(from_name, name))
+ if (unlikely(file->ha_rename_table(from_name, name)))
{
my_error(ER_ERROR_ON_RENAME, MYF(0), from_name, name, my_errno);
error_set= TRUE;
@@ -406,7 +410,7 @@ static bool exchange_name_with_ddl_log(THD *thd,
}
DBUG_EXECUTE_IF("exchange_partition_fail_6", goto err_rename;);
DBUG_EXECUTE_IF("exchange_partition_abort_6", DBUG_SUICIDE(););
- if (deactivate_ddl_log_entry(log_entry->entry_pos))
+ if (unlikely(deactivate_ddl_log_entry(log_entry->entry_pos)))
goto err_rename;
/* call rename table from tmp-nam to partition */
@@ -415,7 +419,7 @@ static bool exchange_name_with_ddl_log(THD *thd,
error_set= TRUE;
goto err_rename;);
DBUG_EXECUTE_IF("exchange_partition_abort_7", DBUG_SUICIDE(););
- if (file->ha_rename_table(tmp_name, from_name))
+ if (unlikely(file->ha_rename_table(tmp_name, from_name)))
{
my_error(ER_ERROR_ON_RENAME, MYF(0), tmp_name, from_name, my_errno);
error_set= TRUE;
@@ -423,7 +427,7 @@ static bool exchange_name_with_ddl_log(THD *thd,
}
DBUG_EXECUTE_IF("exchange_partition_fail_8", goto err_rename;);
DBUG_EXECUTE_IF("exchange_partition_abort_8", DBUG_SUICIDE(););
- if (deactivate_ddl_log_entry(log_entry->entry_pos))
+ if (unlikely(deactivate_ddl_log_entry(log_entry->entry_pos)))
goto err_rename;
/* The exchange is complete and ddl_log is deactivated */
@@ -486,7 +490,7 @@ bool Sql_cmd_alter_table_exchange_partition::
TABLE_LIST *swap_table_list;
handlerton *table_hton;
partition_element *part_elem;
- char *partition_name;
+ const char *partition_name;
char temp_name[FN_REFLEN+1];
char part_file_name[2*FN_REFLEN+1];
char swap_file_name[FN_REFLEN+1];
@@ -499,7 +503,7 @@ bool Sql_cmd_alter_table_exchange_partition::
uint table_counter;
bool error= TRUE;
DBUG_ENTER("mysql_exchange_partition");
- DBUG_ASSERT(alter_info->flags & Alter_info::ALTER_EXCHANGE_PARTITION);
+ DBUG_ASSERT(alter_info->partition_flags & ALTER_PARTITION_EXCHANGE);
/* Don't allow to exchange with log table */
swap_table_list= table_list->next_local;
@@ -523,23 +527,24 @@ bool Sql_cmd_alter_table_exchange_partition::
to be able to verify the structure/metadata.
*/
table_list->mdl_request.set_type(MDL_SHARED_NO_WRITE);
- if (open_tables(thd, &table_list, &table_counter, 0,
- &alter_prelocking_strategy))
+ if (unlikely(open_tables(thd, &table_list, &table_counter, 0,
+ &alter_prelocking_strategy)))
DBUG_RETURN(true);
part_table= table_list->table;
swap_table= swap_table_list->table;
- if (check_exchange_partition(swap_table, part_table))
+ if (unlikely(check_exchange_partition(swap_table, part_table)))
DBUG_RETURN(TRUE);
/* set lock pruning on first table */
partition_name= alter_info->partition_names.head();
- if (table_list->table->part_info->
- set_named_partition_bitmap(partition_name, strlen(partition_name)))
+ if (unlikely(table_list->table->part_info->
+ set_named_partition_bitmap(partition_name,
+ strlen(partition_name))))
DBUG_RETURN(true);
- if (lock_tables(thd, table_list, table_counter, 0))
+ if (unlikely(lock_tables(thd, table_list, table_counter, 0)))
DBUG_RETURN(true);
@@ -550,49 +555,52 @@ bool Sql_cmd_alter_table_exchange_partition::
/* Will append the partition name later in part_info->get_part_elem() */
part_file_name_len= build_table_filename(part_file_name,
sizeof(part_file_name),
- table_list->db,
- table_list->table_name,
+ table_list->db.str,
+ table_list->table_name.str,
"", 0);
build_table_filename(swap_file_name,
sizeof(swap_file_name),
- swap_table_list->db,
- swap_table_list->table_name,
+ 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_%lx",
+ my_snprintf(temp_name, sizeof(temp_name), "%sx-%lx_%llx",
tmp_file_prefix, current_pid, thd->thread_id);
if (lower_case_table_names)
my_casedn_str(files_charset_info, temp_name);
build_table_filename(temp_file_name, sizeof(temp_file_name),
- table_list->next_local->db,
+ table_list->next_local->db.str,
temp_name, "", FN_IS_TMP);
- if (!(part_elem= part_table->part_info->get_part_elem(partition_name,
- part_file_name + part_file_name_len,
- sizeof(part_file_name) - part_file_name_len,
- &swap_part_id)))
+ if (unlikely(!(part_elem=
+ part_table->part_info->get_part_elem(partition_name,
+ part_file_name +
+ part_file_name_len,
+ sizeof(part_file_name) -
+ part_file_name_len,
+ &swap_part_id))))
{
- // my_error(ER_UNKNOWN_PARTITION, MYF(0), partition_name,
- // part_table->alias);
DBUG_RETURN(TRUE);
}
- if (swap_part_id == NOT_A_PARTITION_ID)
+ if (unlikely(swap_part_id == NOT_A_PARTITION_ID))
{
DBUG_ASSERT(part_table->part_info->is_sub_partitioned());
my_error(ER_PARTITION_INSTEAD_OF_SUBPARTITION, MYF(0));
DBUG_RETURN(TRUE);
}
- if (compare_table_with_partition(thd, swap_table, part_table, part_elem,
- swap_part_id))
+ if (unlikely(compare_table_with_partition(thd, swap_table, part_table,
+ part_elem,
+ swap_part_id)))
DBUG_RETURN(TRUE);
/* Table and partition has same structure/options, OK to exchange */
- thd_proc_info(thd, "verifying data with partition");
+ thd_proc_info(thd, "Verifying data with partition");
- if (verify_data_with_partition(swap_table, part_table, swap_part_id))
+ if (unlikely(verify_data_with_partition(swap_table, part_table,
+ swap_part_id)))
DBUG_RETURN(TRUE);
/*
@@ -619,8 +627,8 @@ bool Sql_cmd_alter_table_exchange_partition::
DEBUG_SYNC(thd, "swap_partition_before_rename");
- if (exchange_name_with_ddl_log(thd, swap_file_name, part_file_name,
- temp_file_name, table_hton))
+ if (unlikely(exchange_name_with_ddl_log(thd, swap_file_name, part_file_name,
+ temp_file_name, table_hton)))
goto err;
/*
@@ -630,7 +638,8 @@ bool Sql_cmd_alter_table_exchange_partition::
*/
(void) thd->locked_tables_list.reopen_tables(thd, false);
- if ((error= write_bin_log(thd, TRUE, thd->query(), thd->query_length())))
+ if (unlikely((error= write_bin_log(thd, TRUE, thd->query(),
+ thd->query_length()))))
{
/*
The error is reported in write_bin_log().
@@ -649,7 +658,7 @@ err:
part_table_mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE);
}
- if (!error)
+ if (unlikely(!error))
my_ok(thd);
// For query cache
@@ -669,7 +678,7 @@ bool Sql_cmd_alter_table_analyze_partition::execute(THD *thd)
Flag that it is an ALTER command which administrates partitions, used
by ha_partition
*/
- thd->lex->alter_info.flags|= Alter_info::ALTER_ADMIN_PARTITION;
+ thd->lex->alter_info.partition_flags|= ALTER_PARTITION_ADMIN;
res= Sql_cmd_analyze_table::execute(thd);
@@ -686,7 +695,7 @@ bool Sql_cmd_alter_table_check_partition::execute(THD *thd)
Flag that it is an ALTER command which administrates partitions, used
by ha_partition
*/
- thd->lex->alter_info.flags|= Alter_info::ALTER_ADMIN_PARTITION;
+ thd->lex->alter_info.partition_flags|= ALTER_PARTITION_ADMIN;
res= Sql_cmd_check_table::execute(thd);
@@ -703,7 +712,7 @@ bool Sql_cmd_alter_table_optimize_partition::execute(THD *thd)
Flag that it is an ALTER command which administrates partitions, used
by ha_partition
*/
- thd->lex->alter_info.flags|= Alter_info::ALTER_ADMIN_PARTITION;
+ thd->lex->alter_info.partition_flags|= ALTER_PARTITION_ADMIN;
res= Sql_cmd_optimize_table::execute(thd);
@@ -720,7 +729,7 @@ bool Sql_cmd_alter_table_repair_partition::execute(THD *thd)
Flag that it is an ALTER command which administrates partitions, used
by ha_partition
*/
- thd->lex->alter_info.flags|= Alter_info::ALTER_ADMIN_PARTITION;
+ thd->lex->alter_info.partition_flags|= ALTER_PARTITION_ADMIN;
res= Sql_cmd_repair_table::execute(thd);
@@ -744,8 +753,8 @@ bool Sql_cmd_alter_table_truncate_partition::execute(THD *thd)
Flag that it is an ALTER command which administrates partitions, used
by ha_partition.
*/
- thd->lex->alter_info.flags|= Alter_info::ALTER_ADMIN_PARTITION |
- Alter_info::ALTER_TRUNCATE_PARTITION;
+ thd->lex->alter_info.partition_flags|= (ALTER_PARTITION_ADMIN |
+ ALTER_PARTITION_TRUNCATE);
/* Fix the lock types (not the same as ordinary ALTER TABLE). */
first_table->lock_type= TL_WRITE;
@@ -766,7 +775,7 @@ bool Sql_cmd_alter_table_truncate_partition::execute(THD *thd)
(!thd->is_current_stmt_binlog_format_row() ||
!thd->find_temporary_table(first_table)) &&
wsrep_to_isolation_begin(
- thd, first_table->db, first_table->table_name, NULL)
+ thd, first_table->db.str, first_table->table_name.str, NULL)
)
{
WSREP_WARN("ALTER TABLE TRUNCATE PARTITION isolation failure");
@@ -789,19 +798,19 @@ bool Sql_cmd_alter_table_truncate_partition::execute(THD *thd)
Prune all, but named partitions,
to avoid excessive calls to external_lock().
*/
- List_iterator<char> partition_names_it(alter_info->partition_names);
+ List_iterator<const char> partition_names_it(alter_info->partition_names);
uint num_names= alter_info->partition_names.elements;
for (i= 0; i < num_names; i++)
{
- char *partition_name= partition_names_it++;
+ const char *partition_name= partition_names_it++;
String *str_partition_name= new (thd->mem_root)
String(partition_name, system_charset_info);
if (!str_partition_name)
DBUG_RETURN(true);
partition_names_list.push_back(str_partition_name, thd->mem_root);
}
- first_table->partition_names= &partition_names_list;
- if (first_table->table->part_info->set_partition_bitmaps(first_table))
+ if (first_table->table->
+ part_info->set_partition_bitmaps(&partition_names_list))
DBUG_RETURN(true);
if (lock_tables(thd, first_table, table_counter, 0))
@@ -816,12 +825,13 @@ 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,
- first_table->table_name, FALSE);
+ tdc_remove_table(thd, TDC_RT_REMOVE_NOT_OWN, first_table->db.str,
+ first_table->table_name.str, FALSE);
partition= (ha_partition*) first_table->table->file;
/* Invoke the handler method responsible for truncating the partition. */
- if ((error= partition->truncate_partition(alter_info, &binlog_stmt)))
+ if (unlikely(error= partition->truncate_partition(alter_info,
+ &binlog_stmt)))
partition->print_error(error, MYF(0));
/*
@@ -834,7 +844,7 @@ bool Sql_cmd_alter_table_truncate_partition::execute(THD *thd)
Since we've changed data within the table, we also have to invalidate
the query cache for it.
*/
- if (error != HA_ERR_WRONG_COMMAND)
+ if (likely(error != HA_ERR_WRONG_COMMAND))
{
query_cache_invalidate3(thd, first_table, FALSE);
if (binlog_stmt)
@@ -849,7 +859,7 @@ bool Sql_cmd_alter_table_truncate_partition::execute(THD *thd)
if (thd->locked_tables_mode)
ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE);
- if (! error)
+ if (likely(!error))
my_ok(thd);
// Invalidate query cache
diff --git a/sql/sql_plist.h b/sql/sql_plist.h
index 574a151f956..7f75208ca09 100644
--- a/sql/sql_plist.h
+++ b/sql/sql_plist.h
@@ -16,8 +16,6 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
-
template <typename T, typename L>
class I_P_List_iterator;
class I_P_List_null_counter;
@@ -237,7 +235,7 @@ protected:
void reset() {}
void inc() {}
void dec() {}
- void swap(I_P_List_null_counter &rhs) {}
+ void swap(I_P_List_null_counter &) {}
};
@@ -269,14 +267,14 @@ public:
template <typename T> class I_P_List_no_push_back
{
protected:
- I_P_List_no_push_back(T **a) {};
- void set_last(T **a) {}
+ I_P_List_no_push_back(T **) {}
+ void set_last(T **) {}
/*
T** get_last() const method is intentionally left unimplemented
in order to prohibit usage of push_back() method in lists which
use this policy.
*/
- void swap(I_P_List_no_push_back<T> &rhs) {}
+ void swap(I_P_List_no_push_back<T> &) {}
};
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
index 968ec6ac7d4..ffb7d747537 100644
--- a/sql/sql_plugin.cc
+++ b/sql/sql_plugin.cc
@@ -15,8 +15,8 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include "sql_plugin.h" // Includes my_global.h
-#include "sql_priv.h" // SHOW_MY_BOOL
+#include "sql_plugin.h" // SHOW_MY_BOOL
+#include "sql_priv.h"
#include "unireg.h"
#include "sql_class.h" // set_var.h: THD
#include "sys_vars_shared.h"
@@ -40,9 +40,6 @@
#include <mysql/plugin_encryption.h>
#include "sql_plugin_compat.h"
-#define REPORT_TO_LOG 1
-#define REPORT_TO_USER 2
-
#ifdef HAVE_LINK_H
#include <link.h>
#endif
@@ -66,6 +63,8 @@ char *opt_plugin_dir_ptr;
char opt_plugin_dir[FN_REFLEN];
ulong plugin_maturity;
+static LEX_CSTRING MYSQL_PLUGIN_NAME= {STRING_WITH_LEN("plugin") };
+
/*
not really needed now, this map will become essential when we add more
maturity levels. We cannot change existing maturity constants,
@@ -80,18 +79,18 @@ uint plugin_maturity_map[]=
When you add a new plugin type, add both a string and make sure that the
init and deinit array are correctly updated.
*/
-const LEX_STRING plugin_type_names[MYSQL_MAX_PLUGIN_TYPE_NUM]=
+const LEX_CSTRING plugin_type_names[MYSQL_MAX_PLUGIN_TYPE_NUM]=
{
- { C_STRING_WITH_LEN("UDF") },
- { C_STRING_WITH_LEN("STORAGE ENGINE") },
- { C_STRING_WITH_LEN("FTPARSER") },
- { C_STRING_WITH_LEN("DAEMON") },
- { C_STRING_WITH_LEN("INFORMATION SCHEMA") },
- { C_STRING_WITH_LEN("AUDIT") },
- { C_STRING_WITH_LEN("REPLICATION") },
- { C_STRING_WITH_LEN("AUTHENTICATION") },
- { C_STRING_WITH_LEN("PASSWORD VALIDATION") },
- { C_STRING_WITH_LEN("ENCRYPTION") }
+ { STRING_WITH_LEN("UDF") },
+ { STRING_WITH_LEN("STORAGE ENGINE") },
+ { STRING_WITH_LEN("FTPARSER") },
+ { STRING_WITH_LEN("DAEMON") },
+ { STRING_WITH_LEN("INFORMATION SCHEMA") },
+ { STRING_WITH_LEN("AUDIT") },
+ { STRING_WITH_LEN("REPLICATION") },
+ { STRING_WITH_LEN("AUTHENTICATION") },
+ { STRING_WITH_LEN("PASSWORD VALIDATION") },
+ { STRING_WITH_LEN("ENCRYPTION") }
};
extern int initialize_schema_table(st_plugin_int *plugin);
@@ -239,7 +238,7 @@ ulong dlopen_count;
the following variables/structures
*/
static MEM_ROOT plugin_vars_mem_root;
-static uint global_variables_dynamic_size= 0;
+static size_t global_variables_dynamic_size= 0;
static HASH bookmark_hash;
@@ -296,10 +295,10 @@ public:
sys_var_pluginvar *cast_pluginvar() { return this; }
uchar* real_value_ptr(THD *thd, enum_var_type type);
TYPELIB* plugin_var_typelib(void);
- uchar* do_value_ptr(THD *thd, enum_var_type type, const LEX_STRING *base);
- uchar* session_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar* do_value_ptr(THD *thd, enum_var_type type, const LEX_CSTRING *base);
+ uchar* session_value_ptr(THD *thd, const LEX_CSTRING *base)
{ return do_value_ptr(thd, OPT_SESSION, base); }
- uchar* global_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar* global_value_ptr(THD *thd, const LEX_CSTRING *base)
{ return do_value_ptr(thd, OPT_GLOBAL, base); }
uchar *default_value_ptr(THD *thd)
{ return do_value_ptr(thd, OPT_DEFAULT, 0); }
@@ -332,24 +331,6 @@ bool plugin_is_forced(struct st_plugin_int *p)
p->load_option == PLUGIN_FORCE_PLUS_PERMANENT;
}
-static void report_error(int where_to, uint error, ...)
-{
- va_list args;
- DBUG_ASSERT(where_to & (REPORT_TO_USER | REPORT_TO_LOG));
- if (where_to & REPORT_TO_USER)
- {
- va_start(args, error);
- my_printv_error(error, ER(error), MYF(0), args);
- va_end(args);
- }
- if (where_to & REPORT_TO_LOG)
- {
- va_start(args, error);
- error_log_print(ERROR_LEVEL, ER_DEFAULT(error), args);
- va_end(args);
- }
-}
-
/**
Check if the provided path is valid in the sense that it does cause
a relative reference outside the directory.
@@ -369,7 +350,7 @@ bool check_valid_path(const char *path, size_t len)
return prefix < len;
}
-static void fix_dl_name(MEM_ROOT *root, LEX_STRING *dl)
+static void fix_dl_name(MEM_ROOT *root, LEX_CSTRING *dl)
{
const size_t so_ext_len= sizeof(SO_EXT) - 1;
if (my_strcasecmp(&my_charset_latin1, dl->str + dl->length - so_ext_len,
@@ -449,7 +430,7 @@ static int item_val_real(struct st_mysql_value *value, double *buf)
#ifdef HAVE_DLOPEN
-static struct st_plugin_dl *plugin_dl_find(const LEX_STRING *dl)
+static struct st_plugin_dl *plugin_dl_find(const LEX_CSTRING *dl)
{
uint i;
struct st_plugin_dl *tmp;
@@ -510,7 +491,7 @@ static void free_plugin_mem(struct st_plugin_dl *p)
if (p->handle)
dlclose(p->handle);
#endif
- my_free(p->dl.str);
+ my_free(const_cast<char*>(p->dl.str));
if (p->allocated)
my_free(p->plugins);
}
@@ -522,7 +503,7 @@ static void free_plugin_mem(struct st_plugin_dl *p)
@param plugin_dl Structure where the data should be put
@param sym Reverence on version info
@param dlpath Path to the module
- @param report What errors should be reported
+ @param MyFlags Where errors should be reported (0 or ME_ERROR_LOG)
@retval FALSE OK
@retval TRUE ERROR
@@ -530,14 +511,13 @@ static void free_plugin_mem(struct st_plugin_dl *p)
#ifdef HAVE_DLOPEN
static my_bool read_mysql_plugin_info(struct st_plugin_dl *plugin_dl,
- void *sym, char *dlpath,
- int report)
+ void *sym, char *dlpath, myf MyFlags)
{
DBUG_ENTER("read_maria_plugin_info");
/* Determine interface version */
if (!sym)
{
- report_error(report, ER_CANT_FIND_DL_ENTRY, plugin_interface_version_sym);
+ my_error(ER_CANT_FIND_DL_ENTRY, MyFlags, plugin_interface_version_sym);
DBUG_RETURN(TRUE);
}
plugin_dl->mariaversion= 0;
@@ -546,14 +526,14 @@ static my_bool read_mysql_plugin_info(struct st_plugin_dl *plugin_dl,
if (plugin_dl->mysqlversion < min_plugin_interface_version ||
(plugin_dl->mysqlversion >> 8) > (MYSQL_PLUGIN_INTERFACE_VERSION >> 8))
{
- report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, ENOEXEC,
+ my_error(ER_CANT_OPEN_LIBRARY, MyFlags, dlpath, ENOEXEC,
"plugin interface version mismatch");
DBUG_RETURN(TRUE);
}
/* Find plugin declarations */
if (!(sym= dlsym(plugin_dl->handle, plugin_declarations_sym)))
{
- report_error(report, ER_CANT_FIND_DL_ENTRY, plugin_declarations_sym);
+ my_error(ER_CANT_FIND_DL_ENTRY, MyFlags, plugin_declarations_sym);
DBUG_RETURN(TRUE);
}
@@ -583,7 +563,7 @@ static my_bool read_mysql_plugin_info(struct st_plugin_dl *plugin_dl,
MYF(MY_ZEROFILL|MY_WME));
if (!cur)
{
- report_error(report, ER_OUTOFMEMORY,
+ my_error(ER_OUTOFMEMORY, MyFlags,
static_cast<int>(plugin_dl->dl.length));
DBUG_RETURN(TRUE);
}
@@ -638,15 +618,14 @@ static my_bool read_mysql_plugin_info(struct st_plugin_dl *plugin_dl,
@param plugin_dl Structure where the data should be put
@param sym Reverence on version info
@param dlpath Path to the module
- @param report what errors should be reported
+ @param MyFlags Where errors should be reported (0 or ME_ERROR_LOG)
@retval FALSE OK
@retval TRUE ERROR
*/
static my_bool read_maria_plugin_info(struct st_plugin_dl *plugin_dl,
- void *sym, char *dlpath,
- int report)
+ void *sym, char *dlpath, myf MyFlags)
{
DBUG_ENTER("read_maria_plugin_info");
@@ -657,7 +636,7 @@ static my_bool read_maria_plugin_info(struct st_plugin_dl *plugin_dl,
Actually this branch impossible because in case of absence of maria
version we try mysql version.
*/
- report_error(report, ER_CANT_FIND_DL_ENTRY,
+ my_error(ER_CANT_FIND_DL_ENTRY, MyFlags,
maria_plugin_interface_version_sym);
DBUG_RETURN(TRUE);
}
@@ -667,14 +646,14 @@ static my_bool read_maria_plugin_info(struct st_plugin_dl *plugin_dl,
if (plugin_dl->mariaversion < min_maria_plugin_interface_version ||
(plugin_dl->mariaversion >> 8) > (MARIA_PLUGIN_INTERFACE_VERSION >> 8))
{
- report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, ENOEXEC,
+ my_error(ER_CANT_OPEN_LIBRARY, MyFlags, dlpath, ENOEXEC,
"plugin interface version mismatch");
DBUG_RETURN(TRUE);
}
/* Find plugin declarations */
if (!(sym= dlsym(plugin_dl->handle, maria_plugin_declarations_sym)))
{
- report_error(report, ER_CANT_FIND_DL_ENTRY, maria_plugin_declarations_sym);
+ my_error(ER_CANT_FIND_DL_ENTRY, MyFlags, maria_plugin_declarations_sym);
DBUG_RETURN(TRUE);
}
if (plugin_dl->mariaversion != MARIA_PLUGIN_INTERFACE_VERSION)
@@ -687,7 +666,7 @@ static my_bool read_maria_plugin_info(struct st_plugin_dl *plugin_dl,
sizeof_st_plugin= *(int *)sym;
else
{
- report_error(report, ER_CANT_FIND_DL_ENTRY, maria_sizeof_st_plugin_sym);
+ my_error(ER_CANT_FIND_DL_ENTRY, MyFlags, maria_sizeof_st_plugin_sym);
DBUG_RETURN(TRUE);
}
@@ -704,7 +683,7 @@ static my_bool read_maria_plugin_info(struct st_plugin_dl *plugin_dl,
MYF(MY_ZEROFILL|MY_WME));
if (!cur)
{
- report_error(report, ER_OUTOFMEMORY,
+ my_error(ER_OUTOFMEMORY, MyFlags,
static_cast<int>(plugin_dl->dl.length));
DBUG_RETURN(TRUE);
}
@@ -730,11 +709,12 @@ static my_bool read_maria_plugin_info(struct st_plugin_dl *plugin_dl,
}
#endif /* HAVE_DLOPEN */
-static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report)
+static st_plugin_dl *plugin_dl_add(const LEX_CSTRING *dl, myf MyFlags)
{
#ifdef HAVE_DLOPEN
char dlpath[FN_REFLEN];
- uint plugin_dir_len, dummy_errors, i;
+ size_t plugin_dir_len,i;
+ uint dummy_errors;
struct st_plugin_dl *tmp= 0, plugin_dl;
void *sym;
st_ptr_backup tmp_backup[array_elements(list_of_services)];
@@ -748,12 +728,12 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report)
This is done to ensure that only approved libraries from the
plugin directory are used (to make this even remotely secure).
*/
- if (check_string_char_length((LEX_STRING *) dl, 0, NAME_CHAR_LEN,
+ if (check_string_char_length((LEX_CSTRING *) dl, 0, NAME_CHAR_LEN,
system_charset_info, 1) ||
check_valid_path(dl->str, dl->length) ||
plugin_dir_len + dl->length + 1 >= FN_REFLEN)
{
- report_error(report, ER_UDF_NO_PATHS);
+ my_error(ER_UDF_NO_PATHS, MyFlags);
DBUG_RETURN(0);
}
/* If this dll is already loaded just increase ref_count. */
@@ -770,7 +750,7 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report)
/* Open new dll handle */
if (!(plugin_dl.handle= dlopen(dlpath, RTLD_NOW)))
{
- report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, errno, my_dlerror(dlpath));
+ my_error(ER_CANT_OPEN_LIBRARY, MyFlags, dlpath, errno, my_dlerror(dlpath));
goto ret;
}
dlopen_count++;
@@ -790,12 +770,12 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report)
dlsym(plugin_dl.handle,
plugin_interface_version_sym),
dlpath,
- report))
+ MyFlags))
goto ret;
}
else
{
- if (read_maria_plugin_info(&plugin_dl, sym, dlpath, report))
+ if (read_maria_plugin_info(&plugin_dl, sym, dlpath, MyFlags))
goto ret;
}
@@ -813,7 +793,7 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report)
my_snprintf(buf, sizeof(buf),
"service '%s' interface version mismatch",
list_of_services[i].name);
- report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, ENOEXEC, buf);
+ my_error(ER_CANT_OPEN_LIBRARY, MyFlags, dlpath, ENOEXEC, buf);
goto ret;
}
tmp_backup[plugin_dl.nbackups++].save(ptr);
@@ -828,7 +808,7 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report)
if (!plugin_dl.ptr_backup)
{
restore_ptr_backup(plugin_dl.nbackups, tmp_backup);
- report_error(report, ER_OUTOFMEMORY, bytes);
+ my_error(ER_OUTOFMEMORY, MyFlags, bytes);
goto ret;
}
memcpy(plugin_dl.ptr_backup, tmp_backup, bytes);
@@ -838,18 +818,20 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report)
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))))
{
- report_error(report, ER_OUTOFMEMORY,
+ my_error(ER_OUTOFMEMORY, MyFlags,
static_cast<int>(plugin_dl.dl.length));
goto ret;
}
- plugin_dl.dl.length= copy_and_convert(plugin_dl.dl.str, plugin_dl.dl.length,
- files_charset_info, dl->str, dl->length, system_charset_info,
- &dummy_errors);
- plugin_dl.dl.str[plugin_dl.dl.length]= 0;
+ plugin_dl.dl.length= copy_and_convert((char*) plugin_dl.dl.str,
+ plugin_dl.dl.length,
+ files_charset_info, dl->str,
+ dl->length, system_charset_info,
+ &dummy_errors);
+ ((char*) plugin_dl.dl.str)[plugin_dl.dl.length]= 0;
/* Add this dll to array */
if (! (tmp= plugin_dl_insert_or_reuse(&plugin_dl)))
{
- report_error(report, ER_OUTOFMEMORY,
+ my_error(ER_OUTOFMEMORY, MyFlags,
static_cast<int>(sizeof(struct st_plugin_dl)));
goto ret;
}
@@ -862,7 +844,7 @@ ret:
#else
DBUG_ENTER("plugin_dl_add");
- report_error(report, ER_FEATURE_DISABLED, "plugin", "HAVE_DLOPEN");
+ my_error(ER_FEATURE_DISABLED, MyFlags, "plugin", "HAVE_DLOPEN");
DBUG_RETURN(0);
#endif
}
@@ -888,7 +870,8 @@ static void plugin_dl_del(struct st_plugin_dl *plugin_dl)
}
-static struct st_plugin_int *plugin_find_internal(const LEX_STRING *name, int type)
+static struct st_plugin_int *plugin_find_internal(const LEX_CSTRING *name,
+ int type)
{
uint i;
DBUG_ENTER("plugin_find_internal");
@@ -915,7 +898,7 @@ static struct st_plugin_int *plugin_find_internal(const LEX_STRING *name, int ty
}
-static SHOW_COMP_OPTION plugin_status(const LEX_STRING *name, int type)
+static SHOW_COMP_OPTION plugin_status(const LEX_CSTRING *name, int type)
{
SHOW_COMP_OPTION rc= SHOW_OPTION_NO;
struct st_plugin_int *plugin;
@@ -932,7 +915,7 @@ static SHOW_COMP_OPTION plugin_status(const LEX_STRING *name, int type)
}
-bool plugin_is_ready(const LEX_STRING *name, int type)
+bool plugin_is_ready(const LEX_CSTRING *name, int type)
{
bool rc= FALSE;
if (plugin_status(name, type) == SHOW_OPTION_YES)
@@ -943,11 +926,15 @@ bool plugin_is_ready(const LEX_STRING *name, int type)
SHOW_COMP_OPTION plugin_status(const char *name, size_t len, int type)
{
- LEX_STRING plugin_name= { (char *) name, len };
+ LEX_CSTRING plugin_name= { name, len };
return plugin_status(&plugin_name, type);
}
+/*
+ If LEX is passed non-NULL, an automatic unlock of the plugin will happen
+ in the LEX destructor.
+*/
static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc,
uint state_mask= PLUGIN_IS_READY |
PLUGIN_IS_UNINITIALIZED |
@@ -993,6 +980,16 @@ static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc,
}
+/*
+ Notes on lifetime:
+
+ If THD is passed as non-NULL (and with a non-NULL thd->lex), an entry is made
+ in the thd->lex which will cause an automatic unlock of the plugin in the LEX
+ destructor. In this case, no manual unlock must be done.
+
+ Otherwise, when passing a NULL THD, the caller must arrange that plugin
+ unlock happens later.
+*/
plugin_ref plugin_lock(THD *thd, plugin_ref ptr)
{
LEX *lex= thd ? thd->lex : 0;
@@ -1029,7 +1026,17 @@ plugin_ref plugin_lock(THD *thd, plugin_ref ptr)
}
-plugin_ref plugin_lock_by_name(THD *thd, const LEX_STRING *name, int type)
+/*
+ Notes on lifetime:
+
+ If THD is passed as non-NULL (and with a non-NULL thd->lex), an entry is made
+ in the thd->lex which will cause an automatic unlock of the plugin in the LEX
+ destructor. In this case, no manual unlock must be done.
+
+ Otherwise, when passing a NULL THD, the caller must arrange that plugin
+ unlock happens later.
+*/
+plugin_ref plugin_lock_by_name(THD *thd, const LEX_CSTRING *name, int type)
{
LEX *lex= thd ? thd->lex : 0;
plugin_ref rc= NULL;
@@ -1072,7 +1079,7 @@ static st_plugin_int *plugin_insert_or_reuse(struct st_plugin_int *plugin)
Requires that a write-lock is held on LOCK_system_variables_hash
*/
static bool plugin_add(MEM_ROOT *tmp_root,
- const LEX_STRING *name, LEX_STRING *dl, int report)
+ const LEX_CSTRING *name, LEX_CSTRING *dl, myf MyFlags)
{
struct st_plugin_int tmp, *maybe_dupe;
struct st_maria_plugin *plugin;
@@ -1082,13 +1089,13 @@ static bool plugin_add(MEM_ROOT *tmp_root,
if (name->str && plugin_find_internal(name, MYSQL_ANY_PLUGIN))
{
- report_error(report, ER_PLUGIN_INSTALLED, name->str);
+ my_error(ER_PLUGIN_INSTALLED, MyFlags, name->str);
DBUG_RETURN(TRUE);
}
/* Clear the whole struct to catch future extensions. */
bzero((char*) &tmp, sizeof(tmp));
fix_dl_name(tmp_root, dl);
- if (! (tmp.plugin_dl= plugin_dl_add(dl, report)))
+ if (! (tmp.plugin_dl= plugin_dl_add(dl, MyFlags)))
DBUG_RETURN(TRUE);
/* Find plugin by name */
for (plugin= tmp.plugin_dl->plugins; plugin->info; plugin++)
@@ -1114,7 +1121,7 @@ static bool plugin_add(MEM_ROOT *tmp_root,
{
if (plugin->name != maybe_dupe->plugin->name)
{
- report_error(report, ER_UDF_EXISTS, plugin->name);
+ my_error(ER_UDF_EXISTS, MyFlags, plugin->name);
DBUG_RETURN(TRUE);
}
dupes++;
@@ -1131,9 +1138,10 @@ static bool plugin_add(MEM_ROOT *tmp_root,
plugin_type_names[plugin->type].str,
" plugin ", tmp.name.str,
" not supported by this version of the server", NullS);
- report_error(report, ER_CANT_OPEN_LIBRARY, dl->str, ENOEXEC, buf);
+ my_error(ER_CANT_OPEN_LIBRARY, MyFlags, dl->str, ENOEXEC, buf);
goto err;
}
+
if (plugin_maturity_map[plugin->maturity] < plugin_maturity)
{
char buf[256];
@@ -1143,9 +1151,17 @@ static bool plugin_add(MEM_ROOT *tmp_root,
" is prohibited by --plugin-maturity=",
plugin_maturity_names[plugin_maturity],
NullS);
- report_error(report, ER_CANT_OPEN_LIBRARY, dl->str, EPERM, buf);
+ my_error(ER_CANT_OPEN_LIBRARY, MyFlags, dl->str, EPERM, buf);
goto err;
}
+ else if (plugin_maturity_map[plugin->maturity] < SERVER_MATURITY_LEVEL)
+ {
+ sql_print_warning("Plugin '%s' is of maturity level %s while the server is %s",
+ tmp.name.str,
+ plugin_maturity_names[plugin->maturity],
+ plugin_maturity_names[SERVER_MATURITY_LEVEL]);
+ }
+
tmp.plugin= plugin;
tmp.ref_count= 0;
tmp.state= PLUGIN_IS_UNINITIALIZED;
@@ -1155,7 +1171,7 @@ static bool plugin_add(MEM_ROOT *tmp_root,
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, 4096, 4096, MYF(0));
+ init_alloc_root(&tmp_plugin_ptr->mem_root, "plugin", 4096, 4096, MYF(0));
if (name->str)
DBUG_RETURN(FALSE); // all done
@@ -1173,7 +1189,7 @@ err:
DBUG_ASSERT(!name->str || !dupes); // dupes is ONLY for name->str == 0
if (errs == 0 && oks == 0 && !dupes) // no plugin was found
- report_error(report, ER_CANT_FIND_DL_ENTRY, name->str);
+ my_error(ER_CANT_FIND_DL_ENTRY, MyFlags, name->str);
plugin_dl_del(tmp.plugin_dl);
DBUG_RETURN(errs > 0 || oks + dupes == 0);
@@ -1486,14 +1502,14 @@ uchar *get_bookmark_hash_key(const uchar *buff, size_t *length,
return (uchar*) var->key;
}
-static inline void convert_dash_to_underscore(char *str, int len)
+static inline void convert_dash_to_underscore(char *str, size_t len)
{
for (char *p= str; p <= str+len; p++)
if (*p == '-')
*p= '_';
}
-static inline void convert_underscore_to_dash(char *str, int len)
+static inline void convert_underscore_to_dash(char *str, size_t len)
{
for (char *p= str; p <= str+len; p++)
if (*p == '_')
@@ -1537,7 +1553,7 @@ int plugin_init(int *argc, char **argv, int flags)
MEM_ROOT tmp_root;
bool reaped_mandatory_plugin= false;
bool mandatory= true;
- LEX_STRING MyISAM= { C_STRING_WITH_LEN("MyISAM") };
+ LEX_CSTRING MyISAM= { STRING_WITH_LEN("MyISAM") };
DBUG_ENTER("plugin_init");
if (initialized)
@@ -1545,9 +1561,9 @@ int plugin_init(int *argc, char **argv, int flags)
dlopen_count =0;
- init_alloc_root(&plugin_mem_root, 4096, 4096, MYF(0));
- init_alloc_root(&plugin_vars_mem_root, 4096, 4096, MYF(0));
- init_alloc_root(&tmp_root, 4096, 4096, MYF(0));
+ 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));
if (my_hash_init(&bookmark_hash, &my_charset_bin, 32, 0, 0,
get_bookmark_hash_key, NULL, HASH_UNIQUE))
@@ -1646,7 +1662,7 @@ 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_ASSERT(plugin_ptr->ref_count == 1);
+ DBUG_SLOW_ASSERT(plugin_ptr->ref_count == 1);
}
mysql_mutex_unlock(&LOCK_plugin);
@@ -1666,10 +1682,11 @@ int plugin_init(int *argc, char **argv, int flags)
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_STRING maybe_myisam= { engine_name_buf, 0 };
- frm_type_enum frm_type= dd_frm_type(NULL, path, &maybe_myisam);
+ 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 == FRMTYPE_TABLE && !strcasecmp(maybe_myisam.str, "MyISAM"))
+ if (frm_type == TABLE_TYPE_NORMAL && !strcasecmp(maybe_myisam.str, "MyISAM"))
{
plugin_load(&tmp_root);
flags|= PLUGIN_INIT_SKIP_PLUGIN_TABLE;
@@ -1786,11 +1803,9 @@ static void plugin_load(MEM_ROOT *tmp_root)
new_thd->thread_stack= (char*) &tables;
new_thd->store_globals();
- new_thd->db= my_strdup("mysql", MYF(0));
- new_thd->db_length= 5;
+ new_thd->db= MYSQL_SCHEMA_NAME;
bzero((char*) &new_thd->net, sizeof(new_thd->net));
- tables.init_one_table(STRING_WITH_LEN("mysql"), STRING_WITH_LEN("plugin"),
- "plugin", TL_READ);
+ tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PLUGIN_NAME, 0, TL_READ);
tables.open_strategy= TABLE_LIST::OPEN_NORMAL;
result= open_and_lock_tables(new_thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT);
@@ -1816,15 +1831,15 @@ static void plugin_load(MEM_ROOT *tmp_root)
goto end;
}
table->use_all_columns();
- while (!(error= read_record_info.read_record(&read_record_info)))
+ while (!(error= read_record_info.read_record()))
{
DBUG_PRINT("info", ("init plugin record"));
String str_name, str_dl;
get_field(tmp_root, table->field[0], &str_name);
get_field(tmp_root, table->field[1], &str_dl);
- LEX_STRING name= {(char *)str_name.ptr(), str_name.length()};
- LEX_STRING dl= {(char *)str_dl.ptr(), str_dl.length()};
+ LEX_CSTRING name= {str_name.ptr(), str_name.length()};
+ LEX_CSTRING dl= {str_dl.ptr(), str_dl.length()};
if (!name.length || !dl.length)
continue;
@@ -1836,17 +1851,18 @@ static void plugin_load(MEM_ROOT *tmp_root)
the mutex here to satisfy the assert
*/
mysql_mutex_lock(&LOCK_plugin);
- plugin_add(tmp_root, &name, &dl, REPORT_TO_LOG);
+ plugin_add(tmp_root, &name, &dl, MYF(ME_ERROR_LOG));
free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE));
mysql_mutex_unlock(&LOCK_plugin);
}
- if (error > 0)
+ if (unlikely(error > 0))
sql_print_error(ER_THD(new_thd, ER_GET_ERRNO), my_errno,
table->file->table_type());
end_read_record(&read_record_info);
table->mark_table_for_reopen();
close_mysql_tables(new_thd);
end:
+ new_thd->db= null_clex_str; // Avoid free on thd->db
delete new_thd;
DBUG_VOID_RETURN;
}
@@ -1890,14 +1906,16 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, const char *list)
mysql_mutex_lock(&LOCK_plugin);
free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE));
name.str= 0; // load everything
- if (plugin_add(tmp_root, &name, &dl, REPORT_TO_LOG))
+ if (plugin_add(tmp_root, (LEX_CSTRING*) &name, (LEX_CSTRING*) &dl,
+ MYF(ME_ERROR_LOG)))
goto error;
}
else
{
free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE));
mysql_mutex_lock(&LOCK_plugin);
- if (plugin_add(tmp_root, &name, &dl, REPORT_TO_LOG))
+ if (plugin_add(tmp_root, (LEX_CSTRING*) &name, (LEX_CSTRING*) &dl,
+ MYF(ME_ERROR_LOG)))
goto error;
}
mysql_mutex_unlock(&LOCK_plugin);
@@ -1941,6 +1959,12 @@ void plugin_shutdown(void)
if (initialized)
{
+ if (opt_gtid_pos_auto_plugins)
+ {
+ free_engine_list(opt_gtid_pos_auto_plugins);
+ opt_gtid_pos_auto_plugins= NULL;
+ }
+
mysql_mutex_lock(&LOCK_plugin);
reap_needed= true;
@@ -2066,7 +2090,7 @@ void plugin_shutdown(void)
That is, initialize it, and update mysql.plugin table
*/
-static bool finalize_install(THD *thd, TABLE *table, const LEX_STRING *name,
+static bool finalize_install(THD *thd, TABLE *table, const LEX_CSTRING *name,
int *argc, char **argv)
{
struct st_plugin_int *tmp= plugin_find_internal(name, MYSQL_ANY_PLUGIN);
@@ -2083,7 +2107,7 @@ static bool finalize_install(THD *thd, TABLE *table, const LEX_STRING *name,
{
if (plugin_initialize(thd->mem_root, tmp, argc, argv, false))
{
- report_error(REPORT_TO_USER, ER_CANT_INITIALIZE_UDF, name->str,
+ my_error(ER_CANT_INITIALIZE_UDF, MYF(0), name->str,
"Plugin initialization function failed.");
tmp->state= PLUGIN_IS_DELETED;
return 1;
@@ -2111,7 +2135,7 @@ static bool finalize_install(THD *thd, TABLE *table, const LEX_STRING *name,
files_charset_info);
error= table->file->ha_write_row(table->record[0]);
reenable_binlog(thd);
- if (error)
+ if (unlikely(error))
{
table->file->print_error(error, MYF(0));
tmp->state= PLUGIN_IS_DELETED;
@@ -2120,12 +2144,12 @@ static bool finalize_install(THD *thd, TABLE *table, const LEX_STRING *name,
return 0;
}
-bool mysql_install_plugin(THD *thd, const LEX_STRING *name,
- const LEX_STRING *dl_arg)
+bool mysql_install_plugin(THD *thd, const LEX_CSTRING *name,
+ const LEX_CSTRING *dl_arg)
{
TABLE_LIST tables;
TABLE *table;
- LEX_STRING dl= *dl_arg;
+ LEX_CSTRING dl= *dl_arg;
bool error;
int argc=orig_argc;
char **argv=orig_argv;
@@ -2133,7 +2157,7 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name,
{ MYSQL_AUDIT_GENERAL_CLASSMASK };
DBUG_ENTER("mysql_install_plugin");
- tables.init_one_table("mysql", 5, "plugin", 6, "plugin", TL_WRITE);
+ tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PLUGIN_NAME, 0, TL_WRITE);
if (!opt_noacl && check_table_access(thd, INSERT_ACL, &tables, FALSE, 1, FALSE))
DBUG_RETURN(TRUE);
WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
@@ -2145,7 +2169,7 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name,
if (my_load_defaults(MYSQL_CONFIG_NAME, load_default_groups, &argc, &argv, NULL))
{
- report_error(REPORT_TO_USER, ER_PLUGIN_IS_NOT_LOADED, name->str);
+ my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), name->str);
DBUG_RETURN(TRUE);
}
@@ -2174,8 +2198,8 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name,
mysql_audit_acquire_plugins(thd, event_class_mask);
mysql_mutex_lock(&LOCK_plugin);
- error= plugin_add(thd->mem_root, name, &dl, REPORT_TO_USER);
- if (error)
+ error= plugin_add(thd->mem_root, name, &dl, MYF(0));
+ if (unlikely(error))
goto err;
if (name->str)
@@ -2186,12 +2210,12 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name,
struct st_maria_plugin *plugin;
for (plugin= plugin_dl->plugins; plugin->info; plugin++)
{
- LEX_STRING str= { const_cast<char*>(plugin->name), strlen(plugin->name) };
+ LEX_CSTRING str= { plugin->name, strlen(plugin->name) };
error|= finalize_install(thd, table, &str, &argc, argv);
}
}
- if (error)
+ if (unlikely(error))
{
reap_needed= true;
reap_plugins();
@@ -2208,7 +2232,7 @@ WSREP_ERROR_LABEL:
}
-static bool do_uninstall(THD *thd, TABLE *table, const LEX_STRING *name)
+static bool do_uninstall(THD *thd, TABLE *table, const LEX_CSTRING *name)
{
struct st_plugin_int *plugin;
mysql_mutex_assert_owner(&LOCK_plugin);
@@ -2258,7 +2282,7 @@ static bool do_uninstall(THD *thd, TABLE *table, const LEX_STRING *name)
tmp_disable_binlog(thd);
error= table->file->ha_delete_row(table->record[0]);
reenable_binlog(thd);
- if (error)
+ if (unlikely(error))
{
table->file->print_error(error, MYF(0));
return 1;
@@ -2273,18 +2297,18 @@ static bool do_uninstall(THD *thd, TABLE *table, const LEX_STRING *name)
}
-bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name,
- const LEX_STRING *dl_arg)
+bool mysql_uninstall_plugin(THD *thd, const LEX_CSTRING *name,
+ const LEX_CSTRING *dl_arg)
{
TABLE *table;
TABLE_LIST tables;
- LEX_STRING dl= *dl_arg;
+ LEX_CSTRING dl= *dl_arg;
bool error= false;
unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE] =
{ MYSQL_AUDIT_GENERAL_CLASSMASK };
DBUG_ENTER("mysql_uninstall_plugin");
- tables.init_one_table("mysql", 5, "plugin", 6, "plugin", TL_WRITE);
+ tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PLUGIN_NAME, 0, TL_WRITE);
if (!opt_noacl && check_table_access(thd, DELETE_ACL, &tables, FALSE, 1, FALSE))
DBUG_RETURN(TRUE);
@@ -2342,7 +2366,7 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name,
for (struct st_maria_plugin *plugin= plugin_dl->plugins;
plugin->info; plugin++)
{
- LEX_STRING str= { const_cast<char*>(plugin->name), strlen(plugin->name) };
+ LEX_CSTRING str= { plugin->name, strlen(plugin->name) };
error|= do_uninstall(thd, table, &str);
}
}
@@ -2453,7 +2477,7 @@ static bool plugin_dl_foreach_internal(THD *thd, st_plugin_dl *plugin_dl,
return 0;
}
-bool plugin_dl_foreach(THD *thd, const LEX_STRING *dl,
+bool plugin_dl_foreach(THD *thd, const LEX_CSTRING *dl,
plugin_foreach_func *func, void *arg)
{
bool err= 0;
@@ -2461,7 +2485,7 @@ bool plugin_dl_foreach(THD *thd, const LEX_STRING *dl,
if (dl)
{
mysql_mutex_lock(&LOCK_plugin);
- st_plugin_dl *plugin_dl= plugin_dl_add(dl, REPORT_TO_USER);
+ st_plugin_dl *plugin_dl= plugin_dl_add(dl, MYF(0));
mysql_mutex_unlock(&LOCK_plugin);
if (!plugin_dl)
@@ -2726,7 +2750,7 @@ static int check_func_set(THD *thd, struct st_mysql_sys_var *var,
goto err;
result= find_set(typelib, str, length, NULL,
&error, &error_len, &not_used);
- if (error_len)
+ if (unlikely(error_len))
goto err;
}
else
@@ -2813,49 +2837,33 @@ static void update_func_double(THD *thd, struct st_mysql_sys_var *var,
System Variables support
****************************************************************************/
-sys_var *find_sys_var_ex(THD *thd, const char *str, size_t length,
- bool throw_error, bool locked)
+sys_var *find_sys_var(THD *thd, const char *str, size_t length,
+ bool throw_error)
{
sys_var *var;
- sys_var_pluginvar *pi= NULL;
- plugin_ref plugin;
- DBUG_ENTER("find_sys_var_ex");
+ sys_var_pluginvar *pi;
+ DBUG_ENTER("find_sys_var");
DBUG_PRINT("enter", ("var '%.*s'", (int)length, str));
- if (!locked)
- mysql_mutex_lock(&LOCK_plugin);
mysql_prlock_rdlock(&LOCK_system_variables_hash);
if ((var= intern_find_sys_var(str, length)) &&
(pi= var->cast_pluginvar()))
{
- mysql_prlock_unlock(&LOCK_system_variables_hash);
- LEX *lex= thd ? thd->lex : 0;
- if (!(plugin= intern_plugin_lock(lex, plugin_int_to_ref(pi->plugin))))
+ mysql_mutex_lock(&LOCK_plugin);
+ if (!intern_plugin_lock(thd ? thd->lex : 0, plugin_int_to_ref(pi->plugin),
+ PLUGIN_IS_READY))
var= NULL; /* failed to lock it, it must be uninstalling */
- else
- if (!(plugin_state(plugin) & PLUGIN_IS_READY))
- {
- /* initialization not completed */
- var= NULL;
- intern_plugin_unlock(lex, plugin);
- }
- }
- else
- mysql_prlock_unlock(&LOCK_system_variables_hash);
- if (!locked)
mysql_mutex_unlock(&LOCK_plugin);
+ }
+ mysql_prlock_unlock(&LOCK_system_variables_hash);
- if (!throw_error && !var)
- my_error(ER_UNKNOWN_SYSTEM_VARIABLE, MYF(0), (int)length, (char*) str);
+ if (unlikely(!throw_error && !var))
+ my_error(ER_UNKNOWN_SYSTEM_VARIABLE, MYF(0),
+ (int) (length ? length : strlen(str)), (char*) str);
DBUG_RETURN(var);
}
-sys_var *find_sys_var(THD *thd, const char *str, size_t length)
-{
- return find_sys_var_ex(thd, str, length, false, false);
-}
-
/*
called by register_var, construct_options and test_plugin_options.
Returns the 'bookmark' for the named variable.
@@ -2865,7 +2873,7 @@ static st_bookmark *find_bookmark(const char *plugin, const char *name,
int flags)
{
st_bookmark *result= NULL;
- uint namelen, length, pluginlen= 0;
+ size_t namelen, length, pluginlen= 0;
char *varname, *p;
if (!(flags & PLUGIN_VAR_THDLOCAL))
@@ -2921,7 +2929,7 @@ static size_t var_storage_size(int flags)
static st_bookmark *register_var(const char *plugin, const char *name,
int flags)
{
- uint length= strlen(plugin) + strlen(name) + 3, size, offset, new_size;
+ size_t length= strlen(plugin) + strlen(name) + 3, size, offset, new_size;
st_bookmark *result;
char *varname, *p;
@@ -2940,7 +2948,7 @@ static st_bookmark *register_var(const char *plugin, const char *name,
sizeof(struct st_bookmark) + length-1);
varname[0]= plugin_var_bookmark_key(flags);
memcpy(result->key, varname, length);
- result->name_len= length - 2;
+ result->name_len= (uint)(length - 2);
result->offset= -1;
DBUG_ASSERT(size && !(size & (size-1))); /* must be power of 2 */
@@ -2973,10 +2981,10 @@ static st_bookmark *register_var(const char *plugin, const char *name,
global_variables_dynamic_size= new_size;
}
- global_system_variables.dynamic_variables_head= offset;
- max_system_variables.dynamic_variables_head= offset;
- global_system_variables.dynamic_variables_size= offset + size;
- max_system_variables.dynamic_variables_size= offset + size;
+ global_system_variables.dynamic_variables_head= (uint)offset;
+ max_system_variables.dynamic_variables_head= (uint)offset;
+ global_system_variables.dynamic_variables_size= (uint)(offset + size);
+ max_system_variables.dynamic_variables_size= (uint)(offset + size);
global_system_variables.dynamic_variables_version++;
max_system_variables.dynamic_variables_version++;
@@ -3139,6 +3147,11 @@ void plugin_thdvar_init(THD *thd)
thd->variables.enforced_table_plugin= NULL;
cleanup_variables(&thd->variables);
+ /* This and all other variable cleanups are here for COM_CHANGE_USER :( */
+#ifndef EMBEDDED_LIBRARY
+ thd->session_tracker.sysvars.deinit(thd);
+#endif
+
thd->variables= global_system_variables;
/* we are going to allocate these lazily */
@@ -3160,6 +3173,9 @@ void plugin_thdvar_init(THD *thd)
intern_plugin_unlock(NULL, old_enforced_table_plugin);
mysql_mutex_unlock(&LOCK_plugin);
+#ifndef EMBEDDED_LIBRARY
+ thd->session_tracker.sysvars.init(thd);
+#endif
DBUG_VOID_RETURN;
}
@@ -3225,6 +3241,10 @@ void plugin_thdvar_cleanup(THD *thd)
plugin_ref *list;
DBUG_ENTER("plugin_thdvar_cleanup");
+#ifndef EMBEDDED_LIBRARY
+ thd->session_tracker.sysvars.deinit(thd);
+#endif
+
mysql_mutex_lock(&LOCK_plugin);
unlock_variables(thd, &thd->variables);
@@ -3422,7 +3442,7 @@ TYPELIB* sys_var_pluginvar::plugin_var_typelib(void)
uchar* sys_var_pluginvar::do_value_ptr(THD *thd, enum_var_type type,
- const LEX_STRING *base)
+ const LEX_CSTRING *base)
{
uchar* result;
@@ -3673,9 +3693,9 @@ static int construct_options(MEM_ROOT *mem_root, struct st_plugin_int *tmp,
my_option *options)
{
const char *plugin_name= tmp->plugin->name;
- const LEX_STRING plugin_dash = { C_STRING_WITH_LEN("plugin-") };
- uint plugin_name_len= strlen(plugin_name);
- uint optnamelen;
+ const LEX_CSTRING plugin_dash = { STRING_WITH_LEN("plugin-") };
+ size_t plugin_name_len= strlen(plugin_name);
+ size_t optnamelen;
const int max_comment_len= 255;
char *comment= (char *) alloc_root(mem_root, max_comment_len + 1);
char *optname;
@@ -4001,7 +4021,7 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp,
my_option *opts= NULL;
int error= 1;
struct st_bookmark *var;
- uint len=0, count= EXTRA_OPTIONS;
+ size_t len=0, count= EXTRA_OPTIONS;
st_ptr_backup *tmp_backup= 0;
DBUG_ENTER("test_plugin_options");
DBUG_ASSERT(tmp->plugin && tmp->name.str);
@@ -4096,7 +4116,7 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp,
error= handle_options(argc, &argv, opts, mark_changed);
(*argc)++; /* add back one for the program name */
- if (error)
+ if (unlikely(error))
{
sql_print_error("Parsing options for plugin '%s' failed.",
tmp->name.str);
@@ -4338,13 +4358,19 @@ void wsrep_plugins_post_init()
THD *thd;
I_List_iterator<THD> it(threads);
+ DBUG_ASSERT(!current_thd);
+
+ mysql_mutex_lock(&LOCK_global_system_variables);
+
while ((thd= it++))
{
- if (IF_WSREP(thd->wsrep_applier,1))
+ if (thd->wsrep_applier)
{
- // Save options_bits as it will get overwritten in plugin_thdvar_init()
+ // Save options_bits as it will get overwritten in
+ // plugin_thdvar_init() (verified)
ulonglong option_bits_saved= thd->variables.option_bits;
+ set_current_thd(thd);
plugin_thdvar_init(thd);
// Restore option_bits
@@ -4352,6 +4378,8 @@ void wsrep_plugins_post_init()
}
}
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ set_current_thd(0);
return;
}
#endif /* WITH_WSREP */
diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h
index 64194b5a1b5..352d1e22ac4 100644
--- a/sql/sql_plugin.h
+++ b/sql/sql_plugin.h
@@ -24,8 +24,9 @@
#define SHOW_always_last SHOW_KEY_CACHE_LONG, \
SHOW_LONG_STATUS, SHOW_DOUBLE_STATUS, \
SHOW_HAVE, SHOW_MY_BOOL, SHOW_HA_ROWS, SHOW_SYS, \
- SHOW_LONG_NOFLUSH, SHOW_LONGLONG_STATUS, SHOW_LEX_STRING
-#include <my_global.h>
+ SHOW_LONG_NOFLUSH, SHOW_LONGLONG_STATUS, SHOW_UINT32_STATUS, \
+ SHOW_LEX_STRING
+#include "mariadb.h"
#undef SHOW_always_last
#include "m_string.h" /* LEX_STRING */
@@ -91,7 +92,7 @@ struct st_ptr_backup {
struct st_plugin_dl
{
- LEX_STRING dl;
+ LEX_CSTRING dl;
void *handle;
struct st_maria_plugin *plugins;
st_ptr_backup *ptr_backup;
@@ -106,7 +107,7 @@ struct st_plugin_dl
struct st_plugin_int
{
- LEX_STRING name;
+ LEX_CSTRING name;
struct st_maria_plugin *plugin;
struct st_plugin_dl *plugin_dl;
st_ptr_backup *ptr_backup;
@@ -156,7 +157,7 @@ typedef int (*plugin_type_init)(struct st_plugin_int *);
extern I_List<i_string> *opt_plugin_load_list_ptr;
extern char *opt_plugin_dir_ptr;
extern MYSQL_PLUGIN_IMPORT char opt_plugin_dir[FN_REFLEN];
-extern const LEX_STRING plugin_type_names[];
+extern const LEX_CSTRING plugin_type_names[];
extern ulong plugin_maturity;
extern TYPELIB plugin_maturity_values;
extern const char *plugin_maturity_names[];
@@ -164,18 +165,18 @@ extern const char *plugin_maturity_names[];
extern int plugin_init(int *argc, char **argv, int init_flags);
extern void plugin_shutdown(void);
void add_plugin_options(DYNAMIC_ARRAY *options, MEM_ROOT *mem_root);
-extern bool plugin_is_ready(const LEX_STRING *name, int type);
+extern bool plugin_is_ready(const LEX_CSTRING *name, int type);
#define my_plugin_lock_by_name(A,B,C) plugin_lock_by_name(A,B,C)
#define my_plugin_lock(A,B) plugin_lock(A,B)
extern plugin_ref plugin_lock(THD *thd, plugin_ref ptr);
-extern plugin_ref plugin_lock_by_name(THD *thd, const LEX_STRING *name,
+extern plugin_ref plugin_lock_by_name(THD *thd, const LEX_CSTRING *name,
int type);
extern void plugin_unlock(THD *thd, plugin_ref plugin);
extern void plugin_unlock_list(THD *thd, plugin_ref *list, uint count);
-extern bool mysql_install_plugin(THD *thd, const LEX_STRING *name,
- const LEX_STRING *dl);
-extern bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name,
- const LEX_STRING *dl);
+extern bool mysql_install_plugin(THD *thd, const LEX_CSTRING *name,
+ const LEX_CSTRING *dl);
+extern bool mysql_uninstall_plugin(THD *thd, const LEX_CSTRING *name,
+ const LEX_CSTRING *dl);
extern bool plugin_register_builtin(struct st_mysql_plugin *plugin);
extern void plugin_thdvar_init(THD *thd);
extern void plugin_thdvar_cleanup(THD *thd);
@@ -193,12 +194,9 @@ extern bool plugin_foreach_with_mask(THD *thd, plugin_foreach_func *func,
int type, uint state_mask, void *arg);
extern void sync_dynamic_session_variables(THD* thd, bool global_lock);
-extern bool plugin_dl_foreach(THD *thd, const LEX_STRING *dl,
+extern bool plugin_dl_foreach(THD *thd, const LEX_CSTRING *dl,
plugin_foreach_func *func, void *arg);
-sys_var *find_sys_var_ex(THD *thd, const char *str, size_t length,
- bool throw_error, bool locked);
-
extern void sync_dynamic_session_variables(THD* thd, bool global_lock);
#endif
diff --git a/sql/sql_plugin_compat.h b/sql/sql_plugin_compat.h
index 876b18ee2d7..01b79c5eb61 100644
--- a/sql/sql_plugin_compat.h
+++ b/sql/sql_plugin_compat.h
@@ -25,7 +25,7 @@
#define MIN_AUTHENTICATION_INTERFACE_VERSION 0x0100
struct MYSQL_SERVER_AUTH_INFO_0x0100 {
- char *user_name;
+ const char *user_name;
unsigned int user_name_length;
const char *auth_string;
unsigned long auth_string_length;
diff --git a/sql/sql_plugin_services.ic b/sql/sql_plugin_services.ic
index 20113444b64..7c394b57c3e 100644
--- a/sql/sql_plugin_services.ic
+++ b/sql/sql_plugin_services.ic
@@ -154,6 +154,8 @@ static struct wsrep_service_st wsrep_handler = {
wsrep_aborting_thd_enqueue,
wsrep_consistency_check,
wsrep_is_wsrep_xid,
+ wsrep_xid_seqno,
+ wsrep_xid_uuid,
wsrep_lock_rollback,
wsrep_on,
wsrep_post_commit,
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index f0c9f818f87..a1ca7cc7af2 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -84,7 +84,7 @@ When one supplies long data for a placeholder:
at statement execute.
*/
-#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
#include "unireg.h"
#include "sql_class.h" // set_var.h: THD
@@ -189,7 +189,7 @@ public:
void setup_set_params();
virtual Query_arena::Type type() const;
virtual void cleanup_stmt();
- bool set_name(LEX_STRING *name);
+ bool set_name(LEX_CSTRING *name);
inline void close_cursor() { delete cursor; cursor= 0; }
inline bool is_in_use() { return flags & (uint) IS_IN_USE; }
inline bool is_sql_prepare() const { return flags & (uint) IS_SQL_PREPARE; }
@@ -215,7 +215,7 @@ private:
MEM_ROOT main_mem_root;
sql_mode_t m_sql_mode;
private:
- bool set_db(const char *db, uint db_length);
+ bool set_db(const LEX_CSTRING *db);
bool set_parameters(String *expanded_query,
uchar *packet, uchar *packet_end);
bool execute(String *expanded_query, bool open_cursor);
@@ -380,16 +380,18 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns)
XXX: fix this nasty upcast from List<Item_param> to List<Item>
*/
error= my_net_write(net, buff, sizeof(buff));
- if (stmt->param_count && ! error)
+ if (stmt->param_count && likely(!error))
{
error= thd->protocol_text.send_result_set_metadata((List<Item> *)
&stmt->lex->param_list,
Protocol::SEND_EOF);
}
- if (!error)
+ if (likely(!error))
+ {
/* Flag that a response has already been sent */
thd->get_stmt_da()->disable_status();
+ }
DBUG_RETURN(error);
}
@@ -475,24 +477,23 @@ static ulong get_param_length(uchar **packet, ulong len)
(i.e. when input types altered) and for all subsequent executions
we don't read any values for this.
- @param param parameter item
@param pos input data buffer
@param len length of data in the buffer
*/
-static void set_param_tiny(Item_param *param, uchar **pos, ulong len)
+void Item_param::set_param_tiny(uchar **pos, ulong len)
{
#ifndef EMBEDDED_LIBRARY
if (len < 1)
return;
#endif
int8 value= (int8) **pos;
- param->set_int(param->unsigned_flag ? (longlong) ((uint8) value) :
- (longlong) value, 4);
+ set_int(unsigned_flag ? (longlong) ((uint8) value) :
+ (longlong) value, 4);
*pos+= 1;
}
-static void set_param_short(Item_param *param, uchar **pos, ulong len)
+void Item_param::set_param_short(uchar **pos, ulong len)
{
int16 value;
#ifndef EMBEDDED_LIBRARY
@@ -502,12 +503,12 @@ static void set_param_short(Item_param *param, uchar **pos, ulong len)
#else
shortget(value, *pos);
#endif
- param->set_int(param->unsigned_flag ? (longlong) ((uint16) value) :
- (longlong) value, 6);
+ set_int(unsigned_flag ? (longlong) ((uint16) value) :
+ (longlong) value, 6);
*pos+= 2;
}
-static void set_param_int32(Item_param *param, uchar **pos, ulong len)
+void Item_param::set_param_int32(uchar **pos, ulong len)
{
int32 value;
#ifndef EMBEDDED_LIBRARY
@@ -517,12 +518,12 @@ static void set_param_int32(Item_param *param, uchar **pos, ulong len)
#else
longget(value, *pos);
#endif
- param->set_int(param->unsigned_flag ? (longlong) ((uint32) value) :
- (longlong) value, 11);
+ set_int(unsigned_flag ? (longlong) ((uint32) value) :
+ (longlong) value, 11);
*pos+= 4;
}
-static void set_param_int64(Item_param *param, uchar **pos, ulong len)
+void Item_param::set_param_int64(uchar **pos, ulong len)
{
longlong value;
#ifndef EMBEDDED_LIBRARY
@@ -532,11 +533,11 @@ static void set_param_int64(Item_param *param, uchar **pos, ulong len)
#else
longlongget(value, *pos);
#endif
- param->set_int(value, 21);
+ set_int(value, 21);
*pos+= 8;
}
-static void set_param_float(Item_param *param, uchar **pos, ulong len)
+void Item_param::set_param_float(uchar **pos, ulong len)
{
float data;
#ifndef EMBEDDED_LIBRARY
@@ -546,11 +547,11 @@ static void set_param_float(Item_param *param, uchar **pos, ulong len)
#else
floatget(data, *pos);
#endif
- param->set_double((double) data);
+ set_double((double) data);
*pos+= 4;
}
-static void set_param_double(Item_param *param, uchar **pos, ulong len)
+void Item_param::set_param_double(uchar **pos, ulong len)
{
double data;
#ifndef EMBEDDED_LIBRARY
@@ -560,14 +561,14 @@ static void set_param_double(Item_param *param, uchar **pos, ulong len)
#else
doubleget(data, *pos);
#endif
- param->set_double((double) data);
+ set_double((double) data);
*pos+= 8;
}
-static void set_param_decimal(Item_param *param, uchar **pos, ulong len)
+void Item_param::set_param_decimal(uchar **pos, ulong len)
{
ulong length= get_param_length(pos, len);
- param->set_decimal((char*)*pos, length);
+ set_decimal((char*)*pos, length);
*pos+= length;
}
@@ -583,7 +584,7 @@ static void set_param_decimal(Item_param *param, uchar **pos, ulong len)
@todo
Add warning 'Data truncated' here
*/
-static void set_param_time(Item_param *param, uchar **pos, ulong len)
+void Item_param::set_param_time(uchar **pos, ulong len)
{
MYSQL_TIME tm;
ulong length= get_param_length(pos, len);
@@ -610,11 +611,11 @@ static void set_param_time(Item_param *param, uchar **pos, ulong len)
}
else
set_zero_time(&tm, MYSQL_TIMESTAMP_TIME);
- param->set_time(&tm, MYSQL_TIMESTAMP_TIME, MAX_TIME_FULL_WIDTH);
+ set_time(&tm, MYSQL_TIMESTAMP_TIME, MAX_TIME_FULL_WIDTH);
*pos+= length;
}
-static void set_param_datetime(Item_param *param, uchar **pos, ulong len)
+void Item_param::set_param_datetime(uchar **pos, ulong len)
{
MYSQL_TIME tm;
ulong length= get_param_length(pos, len);
@@ -640,13 +641,12 @@ static void set_param_datetime(Item_param *param, uchar **pos, ulong len)
}
else
set_zero_time(&tm, MYSQL_TIMESTAMP_DATETIME);
- param->set_time(&tm, MYSQL_TIMESTAMP_DATETIME,
- MAX_DATETIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
+ set_time(&tm, MYSQL_TIMESTAMP_DATETIME, MAX_DATETIME_WIDTH);
*pos+= length;
}
-static void set_param_date(Item_param *param, uchar **pos, ulong len)
+void Item_param::set_param_date(uchar **pos, ulong len)
{
MYSQL_TIME tm;
ulong length= get_param_length(pos, len);
@@ -665,8 +665,7 @@ static void set_param_date(Item_param *param, uchar **pos, ulong len)
}
else
set_zero_time(&tm, MYSQL_TIMESTAMP_DATE);
- param->set_time(&tm, MYSQL_TIMESTAMP_DATE,
- MAX_DATE_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
+ set_time(&tm, MYSQL_TIMESTAMP_DATE, MAX_DATE_WIDTH);
*pos+= length;
}
@@ -675,7 +674,7 @@ static void set_param_date(Item_param *param, uchar **pos, ulong len)
@todo
Add warning 'Data truncated' here
*/
-void set_param_time(Item_param *param, uchar **pos, ulong len)
+void Item_param::set_param_time(uchar **pos, ulong len)
{
MYSQL_TIME tm= *((MYSQL_TIME*)*pos);
tm.hour+= tm.day * 24;
@@ -687,136 +686,84 @@ void set_param_time(Item_param *param, uchar **pos, ulong len)
tm.minute= 59;
tm.second= 59;
}
- param->set_time(&tm, MYSQL_TIMESTAMP_TIME,
- MAX_TIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
-
+ set_time(&tm, MYSQL_TIMESTAMP_TIME, MAX_TIME_WIDTH);
}
-void set_param_datetime(Item_param *param, uchar **pos, ulong len)
+void Item_param::set_param_datetime(uchar **pos, ulong len)
{
MYSQL_TIME tm= *((MYSQL_TIME*)*pos);
tm.neg= 0;
-
- param->set_time(&tm, MYSQL_TIMESTAMP_DATETIME,
- MAX_DATETIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
+ set_time(&tm, MYSQL_TIMESTAMP_DATETIME, MAX_DATETIME_WIDTH);
}
-void set_param_date(Item_param *param, uchar **pos, ulong len)
+void Item_param::set_param_date(uchar **pos, ulong len)
{
MYSQL_TIME *to= (MYSQL_TIME*)*pos;
-
- param->set_time(to, MYSQL_TIMESTAMP_DATE,
- MAX_DATE_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
+ set_time(to, MYSQL_TIMESTAMP_DATE, MAX_DATE_WIDTH);
}
#endif /*!EMBEDDED_LIBRARY*/
-static void set_param_str(Item_param *param, uchar **pos, ulong len)
+void Item_param::set_param_str(uchar **pos, ulong len)
{
ulong length= get_param_length(pos, len);
- if (length > len)
- length= len;
- param->set_str((const char *)*pos, length);
- *pos+= length;
+ if (length == 0 && m_empty_string_is_null)
+ set_null();
+ else
+ {
+ if (length > len)
+ length= len;
+ /*
+ We use &my_charset_bin here. Conversion and setting real character
+ sets will be done in Item_param::convert_str_value(), after the
+ original value is appended to the query used for logging.
+ */
+ set_str((const char *) *pos, length, &my_charset_bin, &my_charset_bin);
+ *pos+= length;
+ }
}
#undef get_param_length
-static void setup_one_conversion_function(THD *thd, Item_param *param,
- uchar param_type)
+
+void Item_param::setup_conversion(THD *thd, uchar param_type)
{
- switch (param_type) {
- case MYSQL_TYPE_TINY:
- param->set_param_func= set_param_tiny;
- break;
- case MYSQL_TYPE_SHORT:
- param->set_param_func= set_param_short;
- break;
- case MYSQL_TYPE_LONG:
- param->set_param_func= set_param_int32;
- break;
- case MYSQL_TYPE_LONGLONG:
- param->set_param_func= set_param_int64;
- break;
- case MYSQL_TYPE_FLOAT:
- param->set_param_func= set_param_float;
- break;
- case MYSQL_TYPE_DOUBLE:
- param->set_param_func= set_param_double;
- break;
- case MYSQL_TYPE_DECIMAL:
- case MYSQL_TYPE_NEWDECIMAL:
- param->set_param_func= set_param_decimal;
- break;
- case MYSQL_TYPE_TIME:
- param->set_param_func= set_param_time;
- break;
- case MYSQL_TYPE_DATE:
- param->set_param_func= set_param_date;
- break;
- case MYSQL_TYPE_DATETIME:
- case MYSQL_TYPE_TIMESTAMP:
- param->set_param_func= set_param_datetime;
- break;
- case MYSQL_TYPE_TINY_BLOB:
- case MYSQL_TYPE_MEDIUM_BLOB:
- case MYSQL_TYPE_LONG_BLOB:
- case MYSQL_TYPE_BLOB:
- param->set_param_func= set_param_str;
- param->value.cs_info.character_set_of_placeholder= &my_charset_bin;
- param->value.cs_info.character_set_client=
- thd->variables.character_set_client;
- DBUG_ASSERT(thd->variables.character_set_client);
- param->value.cs_info.final_character_set_of_str_value= &my_charset_bin;
- break;
- default:
- /*
- The client library ensures that we won't get any other typecodes
- except typecodes above and typecodes for string types. Marking
- label as 'default' lets us to handle malformed packets as well.
- */
- {
- CHARSET_INFO *fromcs= thd->variables.character_set_client;
- CHARSET_INFO *tocs= thd->variables.collation_connection;
- uint32 dummy_offset;
+ const Type_handler *h=
+ Type_handler::get_handler_by_field_type((enum_field_types) param_type);
+ /*
+ The client library ensures that we won't get any unexpected typecodes
+ in the bound parameter. Translating unknown typecodes to
+ &type_handler_string lets us to handle malformed packets as well.
+ */
+ if (!h)
+ h= &type_handler_string;
+ set_handler(h);
+ h->Item_param_setup_conversion(thd, this);
+}
- param->value.cs_info.character_set_of_placeholder= fromcs;
- param->value.cs_info.character_set_client= fromcs;
- /*
- Setup source and destination character sets so that they
- are different only if conversion is necessary: this will
- make later checks easier.
- */
- param->value.cs_info.final_character_set_of_str_value=
- String::needs_conversion(0, fromcs, tocs, &dummy_offset) ?
- tocs : fromcs;
- param->set_param_func= set_param_str;
- /*
- Exact value of max_length is not known unless data is converted to
- charset of connection, so we have to set it later.
- */
- }
- }
- param->set_handler_by_field_type((enum enum_field_types) param_type);
+void Item_param::setup_conversion_blob(THD *thd)
+{
+ value.cs_info.character_set_of_placeholder= &my_charset_bin;
+ value.cs_info.character_set_client= thd->variables.character_set_client;
+ DBUG_ASSERT(thd->variables.character_set_client);
+ value.cs_info.final_character_set_of_str_value= &my_charset_bin;
+ m_empty_string_is_null= thd->variables.sql_mode & MODE_EMPTY_STRING_IS_NULL;
}
-#ifndef EMBEDDED_LIBRARY
-/**
- Check whether this parameter data type is compatible with long data.
- Used to detect whether a long data stream has been supplied to a
- incompatible data type.
-*/
-inline bool is_param_long_data_type(Item_param *param)
+void Item_param::setup_conversion_string(THD *thd, CHARSET_INFO *fromcs)
{
- enum_field_types field_type= param->field_type();
- return (((field_type >= MYSQL_TYPE_TINY_BLOB) &&
- (field_type <= MYSQL_TYPE_STRING)) ||
- field_type == MYSQL_TYPE_VARCHAR);
+ value.cs_info.set(thd, fromcs);
+ m_empty_string_is_null= thd->variables.sql_mode & MODE_EMPTY_STRING_IS_NULL;
+ /*
+ Exact value of max_length is not known unless data is converted to
+ charset of connection, so we have to set it later.
+ */
}
+#ifndef EMBEDDED_LIBRARY
/**
Routines to assign parameters from data supplied by the client.
@@ -877,14 +824,13 @@ static bool insert_params_with_log(Prepared_statement *stmt, uchar *null_array,
{
if (read_pos >= data_end)
DBUG_RETURN(1);
- param->set_param_func(param, &read_pos, (uint) (data_end - read_pos));
+ param->set_param_func(&read_pos, (uint) (data_end - read_pos));
if (param->has_no_value())
DBUG_RETURN(1);
if (param->limit_clause_param && !param->has_int_value())
{
- param->set_int(param->val_int(), MY_INT64_NUM_DECIMAL_DIGITS);
- if (!param->unsigned_flag && param->value.integer < 0)
+ if (param->set_limit_clause_param(param->val_int()))
DBUG_RETURN(1);
}
}
@@ -895,7 +841,7 @@ static bool insert_params_with_log(Prepared_statement *stmt, uchar *null_array,
type (the types are supplied at execute). Check that the
supplied type of placeholder can accept a data stream.
*/
- else if (! is_param_long_data_type(param))
+ else if (!param->type_handler()->is_param_long_data_type())
DBUG_RETURN(1);
if (acc.append(param))
@@ -934,7 +880,7 @@ static bool insert_params(Prepared_statement *stmt, uchar *null_array,
{
if (read_pos >= data_end)
DBUG_RETURN(1);
- param->set_param_func(param, &read_pos, (uint) (data_end - read_pos));
+ param->set_param_func(&read_pos, (uint) (data_end - read_pos));
if (param->has_no_value())
DBUG_RETURN(1);
}
@@ -945,7 +891,7 @@ static bool insert_params(Prepared_statement *stmt, uchar *null_array,
type (the types are supplied at execute). Check that the
supplied type of placeholder can accept a data stream.
*/
- else if (! is_param_long_data_type(param))
+ else if (!param->type_handler()->is_param_long_data_type())
DBUG_RETURN(1);
if (param->convert_str_value(stmt->thd))
DBUG_RETURN(1); /* out of memory */
@@ -979,7 +925,7 @@ static bool insert_bulk_params(Prepared_statement *stmt,
case STMT_INDICATOR_NONE:
if ((*read_pos) >= data_end)
DBUG_RETURN(1);
- param->set_param_func(param, read_pos, (uint) (data_end - (*read_pos)));
+ param->set_param_func(read_pos, (uint) (data_end - (*read_pos)));
if (param->has_no_value())
DBUG_RETURN(1);
if (param->convert_str_value(stmt->thd))
@@ -1026,7 +972,7 @@ static bool set_conversion_functions(Prepared_statement *stmt,
typecode= sint2korr(read_pos);
read_pos+= 2;
(**it).unsigned_flag= MY_TEST(typecode & signed_bit);
- setup_one_conversion_function(thd, *it, (uchar) (typecode & 0xff));
+ (*it)->setup_conversion(thd, (uchar) (typecode & 0xff));
(*it)->sync_clones();
}
*data= read_pos;
@@ -1082,7 +1028,7 @@ static bool emb_insert_params(Prepared_statement *stmt, String *expanded_query)
for (; it < end; ++it, ++client_param)
{
Item_param *param= *it;
- setup_one_conversion_function(thd, param, client_param->buffer_type);
+ param->setup_conversion(thd, client_param->buffer_type);
if (!param->has_long_data_value())
{
if (*client_param->is_null)
@@ -1091,7 +1037,7 @@ static bool emb_insert_params(Prepared_statement *stmt, String *expanded_query)
{
uchar *buff= (uchar*) client_param->buffer;
param->unsigned_flag= client_param->is_unsigned;
- param->set_param_func(param, &buff,
+ param->set_param_func(&buff,
client_param->length ?
*client_param->length :
client_param->buffer_length);
@@ -1119,7 +1065,7 @@ static bool emb_insert_params_with_log(Prepared_statement *stmt, String *query)
for (; it < end; ++it, ++client_param)
{
Item_param *param= *it;
- setup_one_conversion_function(thd, param, client_param->buffer_type);
+ param->setup_conversion(thd, client_param->buffer_type);
if (!param->has_long_data_value())
{
if (*client_param->is_null)
@@ -1128,7 +1074,7 @@ static bool emb_insert_params_with_log(Prepared_statement *stmt, String *query)
{
uchar *buff= (uchar*)client_param->buffer;
param->unsigned_flag= client_param->is_unsigned;
- param->set_param_func(param, &buff,
+ param->set_param_func(&buff,
client_param->length ?
*client_param->length :
client_param->buffer_length);
@@ -1267,12 +1213,6 @@ insert_params_from_actual_params_with_log(Prepared_statement *stmt,
{
Item_param *param= *it;
Item *ps_param= param_it++;
- /*
- We have to call the setup_one_conversion_function() here to set
- the parameter's members that might be needed further
- (e.g. value.cs_info.character_set_client is used in the query_val_str()).
- */
- setup_one_conversion_function(thd, param, param->field_type());
if (ps_param->save_in_param(thd, param))
DBUG_RETURN(1);
@@ -1368,7 +1308,7 @@ static bool mysql_test_insert(Prepared_statement *stmt,
{
my_error(ER_DELAYED_NOT_SUPPORTED, MYF(0), (table_list->view ?
table_list->view_name.str :
- table_list->table_name));
+ table_list->table_name.str));
goto error;
}
while ((values= its++))
@@ -1380,7 +1320,7 @@ static bool mysql_test_insert(Prepared_statement *stmt,
goto error;
}
if (setup_fields(thd, Ref_ptr_array(),
- *values, MARK_COLUMNS_NONE, 0, NULL, 0))
+ *values, COLUMNS_READ, 0, NULL, 0))
goto error;
}
}
@@ -1415,6 +1355,7 @@ static int mysql_test_update(Prepared_statement *stmt,
int res;
THD *thd= stmt->thd;
uint table_count= 0;
+ TABLE_LIST *update_source_table;
SELECT_LEX *select= &stmt->lex->select_lex;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
uint want_privilege;
@@ -1428,9 +1369,11 @@ static int mysql_test_update(Prepared_statement *stmt,
if (mysql_handle_derived(thd->lex, DT_INIT))
goto error;
- if (table_list->is_multitable())
+ if (((update_source_table= unique_table(thd, table_list,
+ table_list->next_global, 0)) ||
+ table_list->is_multitable()))
{
- DBUG_ASSERT(table_list->view != 0);
+ DBUG_ASSERT(update_source_table || table_list->view != 0);
DBUG_PRINT("info", ("Switch to multi-update"));
/* pass counter value */
thd->lex->table_count= table_count;
@@ -1449,7 +1392,7 @@ static int mysql_test_update(Prepared_statement *stmt,
if (!table_list->single_table_updatable())
{
- my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "UPDATE");
+ my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "UPDATE");
goto error;
}
@@ -1483,7 +1426,7 @@ static int mysql_test_update(Prepared_statement *stmt,
table_list->register_want_access(SELECT_ACL);
#endif
if (setup_fields(thd, Ref_ptr_array(),
- stmt->lex->value_list, MARK_COLUMNS_NONE, 0, NULL, 0) ||
+ stmt->lex->value_list, COLUMNS_READ, 0, NULL, 0) ||
check_unique_table(thd, table_list))
goto error;
/* TODO: here we should send types of placeholders to the client. */
@@ -1511,6 +1454,7 @@ static bool mysql_test_delete(Prepared_statement *stmt,
uint table_count= 0;
THD *thd= stmt->thd;
LEX *lex= stmt->lex;
+ bool delete_while_scanning;
DBUG_ENTER("mysql_test_delete");
if (delete_precheck(thd, table_list) ||
@@ -1526,7 +1470,7 @@ static bool mysql_test_delete(Prepared_statement *stmt,
if (!table_list->single_table_updatable())
{
- my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "DELETE");
+ my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "DELETE");
goto error;
}
if (!table_list->table || !table_list->table->is_created())
@@ -1539,7 +1483,8 @@ static bool mysql_test_delete(Prepared_statement *stmt,
DBUG_RETURN(mysql_prepare_delete(thd, table_list,
lex->select_lex.with_wild,
lex->select_lex.item_list,
- &lex->select_lex.where));
+ &lex->select_lex.where,
+ &delete_while_scanning));
error:
DBUG_RETURN(TRUE);
}
@@ -1599,7 +1544,7 @@ static int mysql_test_select(Prepared_statement *stmt,
It is not SELECT COMMAND for sure, so setup_tables will be called as
usual, and we pass 0 as setup_tables_done_option
*/
- if (unit->prepare(thd, 0, 0))
+ if (unit->prepare(unit->derived, 0, 0))
goto error;
if (!lex->describe && !thd->lex->analyze_stmt && !stmt->is_sql_prepare())
{
@@ -1654,7 +1599,7 @@ static bool mysql_test_do_fields(Prepared_statement *stmt,
DT_INIT | DT_PREPARE | DT_CREATE))
DBUG_RETURN(TRUE);
DBUG_RETURN(setup_fields(thd, Ref_ptr_array(),
- *values, MARK_COLUMNS_NONE, 0, NULL, 0));
+ *values, COLUMNS_READ, 0, NULL, 0));
}
@@ -1726,8 +1671,7 @@ static bool mysql_test_call_fields(Prepared_statement *stmt,
while ((item= it++))
{
- if ((!item->fixed && item->fix_fields(thd, it.ref())) ||
- item->check_cols(1))
+ if (item->fix_fields_if_needed_for_scalar(thd, it.ref()))
goto err;
}
DBUG_RETURN(FALSE);
@@ -1771,7 +1715,7 @@ static bool select_like_stmt_test(Prepared_statement *stmt,
thd->lex->used_tables= 0; // Updated by setup_fields
/* Calls JOIN::prepare */
- DBUG_RETURN(lex->unit.prepare(thd, 0, setup_tables_done_option));
+ DBUG_RETURN(lex->unit.prepare(lex->unit.derived, 0, setup_tables_done_option));
}
/**
@@ -1960,19 +1904,19 @@ static int mysql_test_show_grants(Prepared_statement *stmt)
THD *thd= stmt->thd;
List<Item> fields;
char buff[1024];
- const char *username= NULL, *hostname= NULL, *rolename= NULL;
+ const char *username= NULL, *hostname= NULL, *rolename= NULL, *end;
if (get_show_user(thd, thd->lex->grant_user, &username, &hostname, &rolename))
DBUG_RETURN(1);
if (username)
- strxmov(buff,"Grants for ",username,"@",hostname, NullS);
+ end= strxmov(buff,"Grants for ",username,"@",hostname, NullS);
else if (rolename)
- strxmov(buff,"Grants for ",rolename, NullS);
+ end= strxmov(buff,"Grants for ",rolename, NullS);
else
DBUG_RETURN(1);
- mysql_show_grants_get_fields(thd, &fields, buff);
+ mysql_show_grants_get_fields(thd, &fields, buff, (uint)(end - buff));
DBUG_RETURN(send_stmt_metadata(thd, stmt, &fields));
}
#endif /*NO_EMBEDDED_ACCESS_CHECKS*/
@@ -2061,13 +2005,14 @@ static int mysql_test_show_binlogs(Prepared_statement *stmt)
TRUE error, error message is set in THD
*/
-static int mysql_test_show_create_routine(Prepared_statement *stmt, int type)
+static int mysql_test_show_create_routine(Prepared_statement *stmt,
+ const Sp_handler *sph)
{
DBUG_ENTER("mysql_test_show_binlogs");
THD *thd= stmt->thd;
List<Item> fields;
- sp_head::show_create_routine_get_fields(thd, type, &fields);
+ sp_head::show_create_routine_get_fields(thd, sph, &fields);
DBUG_RETURN(send_stmt_metadata(thd, stmt, &fields));
}
@@ -2095,7 +2040,7 @@ static bool mysql_test_create_view(Prepared_statement *stmt)
TABLE_LIST *view= lex->unlink_first_table(&link_to_local);
TABLE_LIST *tables= lex->query_tables;
- if (create_view_precheck(thd, tables, view, lex->create_view_mode))
+ if (create_view_precheck(thd, tables, view, lex->create_view->mode))
goto err;
/*
@@ -2285,6 +2230,7 @@ static int mysql_test_handler_read(Prepared_statement *stmt,
if (!(ha_table= mysql_ha_read_prepare(thd, tables, lex->ha_read_mode,
lex->ident.str,
lex->insert_list,
+ lex->ha_rkey_mode,
lex->select_lex.where)))
DBUG_RETURN(1);
@@ -2396,6 +2342,8 @@ static bool check_prepared_statement(Prepared_statement *stmt)
case SQLCOM_SHOW_TABLE_STATUS:
case SQLCOM_SHOW_STATUS_PROC:
case SQLCOM_SHOW_STATUS_FUNC:
+ case SQLCOM_SHOW_STATUS_PACKAGE:
+ case SQLCOM_SHOW_STATUS_PACKAGE_BODY:
case SQLCOM_SELECT:
res= mysql_test_select(stmt, tables);
if (res == 2)
@@ -2405,6 +2353,7 @@ static bool check_prepared_statement(Prepared_statement *stmt)
}
break;
case SQLCOM_CREATE_TABLE:
+ case SQLCOM_CREATE_SEQUENCE:
res= mysql_test_create_table(stmt);
break;
case SQLCOM_SHOW_CREATE:
@@ -2454,21 +2403,36 @@ static bool check_prepared_statement(Prepared_statement *stmt)
break;
#endif /* EMBEDDED_LIBRARY */
case SQLCOM_SHOW_CREATE_PROC:
- if ((res= mysql_test_show_create_routine(stmt, TYPE_ENUM_PROCEDURE)) == 2)
+ if ((res= mysql_test_show_create_routine(stmt, &sp_handler_procedure)) == 2)
{
/* Statement and field info has already been sent */
DBUG_RETURN(FALSE);
}
break;
case SQLCOM_SHOW_CREATE_FUNC:
- if ((res= mysql_test_show_create_routine(stmt, TYPE_ENUM_FUNCTION)) == 2)
+ if ((res= mysql_test_show_create_routine(stmt, &sp_handler_function)) == 2)
+ {
+ /* Statement and field info has already been sent */
+ DBUG_RETURN(FALSE);
+ }
+ break;
+ case SQLCOM_SHOW_CREATE_PACKAGE:
+ if ((res= mysql_test_show_create_routine(stmt, &sp_handler_package_spec)) == 2)
+ {
+ /* Statement and field info has already been sent */
+ DBUG_RETURN(FALSE);
+ }
+ break;
+ case SQLCOM_SHOW_CREATE_PACKAGE_BODY:
+ if ((res= mysql_test_show_create_routine(stmt,
+ &sp_handler_package_body)) == 2)
{
/* Statement and field info has already been sent */
DBUG_RETURN(FALSE);
}
break;
case SQLCOM_CREATE_VIEW:
- if (lex->create_view_mode == VIEW_ALTER)
+ if (lex->create_view->mode == VIEW_ALTER)
{
my_message(ER_UNSUPPORTED_PS, ER_THD(thd, ER_UNSUPPORTED_PS), MYF(0));
goto error;
@@ -2506,8 +2470,10 @@ static bool check_prepared_statement(Prepared_statement *stmt)
*/
case SQLCOM_SHOW_EXPLAIN:
case SQLCOM_DROP_TABLE:
+ case SQLCOM_DROP_SEQUENCE:
case SQLCOM_RENAME_TABLE:
case SQLCOM_ALTER_TABLE:
+ case SQLCOM_ALTER_SEQUENCE:
case SQLCOM_COMMIT:
case SQLCOM_CREATE_INDEX:
case SQLCOM_DROP_INDEX:
@@ -2690,6 +2656,8 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length)
sp_cache_enforce_limit(thd->sp_proc_cache, stored_program_cache_size);
sp_cache_enforce_limit(thd->sp_func_cache, stored_program_cache_size);
+ sp_cache_enforce_limit(thd->sp_package_spec_cache, stored_program_cache_size);
+ sp_cache_enforce_limit(thd->sp_package_body_cache, stored_program_cache_size);
/* check_prepared_statemnt sends the metadata packet in case of success */
end:
@@ -2724,8 +2692,7 @@ end:
bool LEX::get_dynamic_sql_string(LEX_CSTRING *dst, String *buffer)
{
- if (prepared_stmt_code->fix_fields(thd, NULL) ||
- prepared_stmt_code->check_cols(1))
+ if (prepared_stmt_code->fix_fields_if_needed_for_scalar(thd, NULL))
return true;
const String *str= prepared_stmt_code->val_str(buffer);
@@ -2810,7 +2777,7 @@ bool LEX::get_dynamic_sql_string(LEX_CSTRING *dst, String *buffer)
void mysql_sql_stmt_prepare(THD *thd)
{
LEX *lex= thd->lex;
- LEX_STRING *name= &lex->prepared_stmt_name;
+ LEX_CSTRING *name= &lex->prepared_stmt_name;
Prepared_statement *stmt;
LEX_CSTRING query;
DBUG_ENTER("mysql_sql_stmt_prepare");
@@ -3048,7 +3015,7 @@ void reinit_stmt_before_use(THD *thd, LEX *lex)
}
if (sl->changed_elements & TOUCHED_SEL_DERIVED)
{
-#ifndef DBUG_OFF
+#ifdef DBUG_ASSERT_EXISTS
bool res=
#endif
sl->handle_derived(lex, DT_REINIT);
@@ -3107,7 +3074,7 @@ void reinit_stmt_before_use(THD *thd, LEX *lex)
lex->result->cleanup();
lex->result->set_thd(thd);
}
- lex->allow_sum_func= 0;
+ lex->allow_sum_func.clear_all();
lex->in_sum_func= NULL;
DBUG_VOID_RETURN;
}
@@ -3277,6 +3244,8 @@ static void mysql_stmt_execute_common(THD *thd,
sp_cache_enforce_limit(thd->sp_proc_cache, stored_program_cache_size);
sp_cache_enforce_limit(thd->sp_func_cache, stored_program_cache_size);
+ sp_cache_enforce_limit(thd->sp_package_spec_cache, stored_program_cache_size);
+ sp_cache_enforce_limit(thd->sp_package_body_cache, stored_program_cache_size);
/* Close connection socket; for use with client testing (Bug#43560). */
DBUG_EXECUTE_IF("close_conn_after_stmt_execute", vio_shutdown(thd->net.vio,SHUT_RD););
@@ -3306,11 +3275,11 @@ void mysql_sql_stmt_execute(THD *thd)
{
LEX *lex= thd->lex;
Prepared_statement *stmt;
- LEX_STRING *name= &lex->prepared_stmt_name;
+ LEX_CSTRING *name= &lex->prepared_stmt_name;
/* Query text for binary, general or slow log, if any of them is open */
String expanded_query;
DBUG_ENTER("mysql_sql_stmt_execute");
- DBUG_PRINT("info", ("EXECUTE: %.*s\n", (int) name->length, name->str));
+ DBUG_PRINT("info", ("EXECUTE: %.*s", (int) name->length, name->str));
if (!(stmt= (Prepared_statement*) thd->stmt_map.find_by_name(name)))
{
@@ -3547,8 +3516,8 @@ void mysqld_stmt_close(THD *thd, char *packet)
void mysql_sql_stmt_close(THD *thd)
{
Prepared_statement* stmt;
- LEX_STRING *name= &thd->lex->prepared_stmt_name;
- DBUG_PRINT("info", ("DEALLOCATE PREPARE: %.*s\n", (int) name->length,
+ LEX_CSTRING *name= &thd->lex->prepared_stmt_name;
+ DBUG_PRINT("info", ("DEALLOCATE PREPARE: %.*s", (int) name->length,
name->str));
if (! (stmt= (Prepared_statement*) thd->stmt_map.find_by_name(name)))
@@ -3631,7 +3600,7 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length)
#else
param->set_longdata(thd->extra_data, thd->extra_length);
#endif
- if (thd->get_stmt_da()->is_error())
+ if (unlikely(thd->get_stmt_da()->is_error()))
{
stmt->state= Query_arena::STMT_ERROR;
stmt->last_errno= thd->get_stmt_da()->sql_errno();
@@ -3677,7 +3646,7 @@ bool Select_fetch_protocol_binary::send_eof()
Don't send EOF if we're in error condition (which implies we've already
sent or are sending an error)
*/
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
return true;
::my_eof(thd);
@@ -3763,7 +3732,7 @@ Execute_sql_statement::execute_server_code(THD *thd)
error= parse_sql(thd, &parser_state, NULL) || thd->is_error();
- if (error)
+ if (unlikely(error))
goto end;
thd->lex->set_trg_event_type_for_tables();
@@ -3774,7 +3743,7 @@ Execute_sql_statement::execute_server_code(THD *thd)
thd->m_statement_psi= parent_locker;
/* report error issued during command execution */
- if (error == 0 && thd->spcont == NULL)
+ if (likely(error == 0) && thd->spcont == NULL)
general_log_write(thd, COM_STMT_EXECUTE,
thd->query(), thd->query_length());
@@ -3807,8 +3776,10 @@ Prepared_statement::Prepared_statement(THD *thd_arg)
read_types(0),
m_sql_mode(thd->variables.sql_mode)
{
- init_sql_alloc(&main_mem_root, thd_arg->variables.query_alloc_block_size,
- thd_arg->variables.query_prealloc_size, MYF(MY_THREAD_SPECIFIC));
+ 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));
*last_error= '\0';
}
@@ -3901,7 +3872,7 @@ void Prepared_statement::cleanup_stmt()
{
DBUG_ENTER("Prepared_statement::cleanup_stmt");
DBUG_PRINT("enter",("stmt: %p", this));
- thd->restore_set_statement_var();
+ lex->restore_set_statement_var();
thd->rollback_item_tree_changes();
cleanup_items(free_list);
thd->cleanup_after_query();
@@ -3910,7 +3881,7 @@ void Prepared_statement::cleanup_stmt()
}
-bool Prepared_statement::set_name(LEX_STRING *name_arg)
+bool Prepared_statement::set_name(LEX_CSTRING *name_arg)
{
name.length= name_arg->length;
name.str= (char*) memdup_root(mem_root, name_arg->str, name_arg->length);
@@ -3925,24 +3896,22 @@ bool Prepared_statement::set_name(LEX_STRING *name_arg)
a prepared statement since it affects execution environment:
privileges, @@character_set_database, and other.
- @return Returns an error if out of memory.
+ @return 1 if out of memory.
*/
bool
-Prepared_statement::set_db(const char *db_arg, uint db_length_arg)
+Prepared_statement::set_db(const LEX_CSTRING *db_arg)
{
/* Remember the current database. */
- if (db_arg && db_length_arg)
+ if (db_arg->length)
{
- db= this->strmake(db_arg, db_length_arg);
- db_length= db_length_arg;
+ if (!(db.str= this->strmake(db_arg->str, db_arg->length)))
+ return 1;
+ db.length= db_arg->length;
}
else
- {
- db= NULL;
- db_length= 0;
- }
- return db_arg != NULL && db == NULL;
+ db= null_clex_str;
+ return 0;
}
/**************************************************************************
@@ -3991,7 +3960,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
DBUG_RETURN(TRUE);
lex->stmt_lex= lex;
- if (set_db(thd->db, thd->db_length))
+ if (set_db(&thd->db))
DBUG_RETURN(TRUE);
/*
@@ -4026,9 +3995,9 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
lex_start(thd);
lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_PREPARE;
- error= parse_sql(thd, & parser_state, NULL) ||
- thd->is_error() ||
- init_param_array(this);
+ error= (parse_sql(thd, & parser_state, NULL) ||
+ thd->is_error() ||
+ init_param_array(this));
lex->set_trg_event_type_for_tables();
@@ -4060,10 +4029,10 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
Item_null objects.
*/
- if (error == 0)
+ if (likely(error == 0))
error= check_prepared_statement(this);
- if (error)
+ if (unlikely(error))
{
/*
let the following code know we're not in PS anymore,
@@ -4102,7 +4071,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
thd->restore_backup_statement(this, &stmt_backup);
thd->stmt_arena= old_stmt_arena;
- if (error == 0)
+ if (likely(error == 0))
{
setup_set_params();
lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_PREPARE;
@@ -4232,7 +4201,7 @@ Prepared_statement::execute_loop(String *expanded_query,
DBUG_ASSERT(thd->free_list == NULL);
/* Check if we got an error when sending long data */
- if (state == Query_arena::STMT_ERROR)
+ if (unlikely(state == Query_arena::STMT_ERROR))
{
my_message(last_errno, last_error, MYF(0));
return TRUE;
@@ -4296,8 +4265,9 @@ reexecute:
}
#endif /* WITH_WSREP */
- if ((sql_command_flags[lex->sql_command] & CF_REEXECUTION_FRAGILE) &&
- error && !thd->is_fatal_error && !thd->killed &&
+ if (unlikely(error) &&
+ (sql_command_flags[lex->sql_command] & CF_REEXECUTION_FRAGILE) &&
+ !thd->is_fatal_error && !thd->killed &&
reprepare_observer.is_invalidated() &&
reprepare_attempt++ < MAX_REPREPARE_ATTEMPTS)
{
@@ -4306,7 +4276,7 @@ reexecute:
error= reprepare();
- if (! error) /* Success */
+ if (likely(!error)) /* Success */
goto reexecute;
}
reset_stmt_params(this);
@@ -4319,7 +4289,7 @@ my_bool bulk_parameters_set(THD *thd)
DBUG_ENTER("bulk_parameters_set");
Prepared_statement *stmt= (Prepared_statement *) thd->bulk_param;
- if (stmt && stmt->set_bulk_parameters(FALSE))
+ if (stmt && unlikely(stmt->set_bulk_parameters(FALSE)))
DBUG_RETURN(TRUE);
DBUG_RETURN(FALSE);
}
@@ -4370,7 +4340,7 @@ Prepared_statement::execute_bulk_loop(String *expanded_query,
packet_end= packet_end_arg;
iterations= TRUE;
start_param= true;
-#ifndef DBUG_OFF
+#ifdef DBUG_ASSERT_EXISTS
Item *free_list_state= thd->free_list;
#endif
thd->set_bulk_execution((void *)this);
@@ -4491,8 +4461,9 @@ reexecute:
}
#endif /* WITH_WSREP */
- if ((sql_command_flags[lex->sql_command] & CF_REEXECUTION_FRAGILE) &&
- error && !thd->is_fatal_error && !thd->killed &&
+ if (unlikely(error) &&
+ (sql_command_flags[lex->sql_command] & CF_REEXECUTION_FRAGILE) &&
+ !thd->is_fatal_error && !thd->killed &&
reprepare_observer.is_invalidated() &&
reprepare_attempt++ < MAX_REPREPARE_ATTEMPTS)
{
@@ -4501,7 +4472,7 @@ reexecute:
error= reprepare();
- if (! error) /* Success */
+ if (likely(!error)) /* Success */
goto reexecute;
}
}
@@ -4565,7 +4536,7 @@ Prepared_statement::reprepare()
char saved_cur_db_name_buf[SAFE_NAME_LEN+1];
LEX_STRING saved_cur_db_name=
{ saved_cur_db_name_buf, sizeof(saved_cur_db_name_buf) };
- LEX_STRING stmt_db_name= { db, db_length };
+ LEX_CSTRING stmt_db_name= db;
bool cur_db_changed;
bool error;
@@ -4576,8 +4547,8 @@ Prepared_statement::reprepare()
status_var_increment(thd->status_var.com_stmt_reprepare);
- if (mysql_opt_change_db(thd, &stmt_db_name, &saved_cur_db_name, TRUE,
- &cur_db_changed))
+ if (unlikely(mysql_opt_change_db(thd, &stmt_db_name, &saved_cur_db_name,
+ TRUE, &cur_db_changed)))
return TRUE;
sql_mode_t save_sql_mode= thd->variables.sql_mode;
@@ -4588,13 +4559,13 @@ Prepared_statement::reprepare()
thd->variables.sql_mode= save_sql_mode;
if (cur_db_changed)
- mysql_change_db(thd, &saved_cur_db_name, TRUE);
+ mysql_change_db(thd, (LEX_CSTRING*) &saved_cur_db_name, TRUE);
- if (! error)
+ if (likely(!error))
{
swap_prepared_statement(&copy);
swap_parameter_array(param_array, copy.param_array, param_count);
-#ifndef DBUG_OFF
+#ifdef DBUG_ASSERT_EXISTS
is_reprepared= TRUE;
#endif
/*
@@ -4686,10 +4657,9 @@ Prepared_statement::swap_prepared_statement(Prepared_statement *copy)
/* Don't swap flags: the copy has IS_SQL_PREPARE always set. */
/* swap_variables(uint, flags, copy->flags); */
/* Swap names, the old name is allocated in the wrong memory root */
- swap_variables(LEX_STRING, name, copy->name);
+ swap_variables(LEX_CSTRING, name, copy->name);
/* Ditto */
- swap_variables(char *, db, copy->db);
- swap_variables(size_t, db_length, copy->db_length);
+ swap_variables(LEX_CSTRING, db, copy->db);
DBUG_ASSERT(param_count == copy->param_count);
DBUG_ASSERT(thd == copy->thd);
@@ -4732,7 +4702,7 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
{ saved_cur_db_name_buf, sizeof(saved_cur_db_name_buf) };
bool cur_db_changed;
- LEX_STRING stmt_db_name= { db, db_length };
+ LEX_CSTRING stmt_db_name= db;
status_var_increment(thd->status_var.com_stmt_execute);
@@ -4833,7 +4803,7 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
PSI_statement_locker *parent_locker;
MYSQL_QUERY_EXEC_START(thd->query(),
thd->thread_id,
- (char *) (thd->db ? thd->db : ""),
+ thd->get_db(),
&thd->security_ctx->priv_user[0],
(char *) thd->security_ctx->host_or_ip,
1);
@@ -4861,7 +4831,7 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
*/
if (cur_db_changed)
- mysql_change_db(thd, &saved_cur_db_name, TRUE);
+ mysql_change_db(thd, (LEX_CSTRING*) &saved_cur_db_name, TRUE);
/* Assert that if an error, no cursor is open */
DBUG_ASSERT(! (error && cursor));
@@ -4890,7 +4860,7 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
if (state == Query_arena::STMT_PREPARED && !qc_executed)
state= Query_arena::STMT_EXECUTED;
- if (error == 0 && this->lex->sql_command == SQLCOM_CALL)
+ if (likely(error == 0) && this->lex->sql_command == SQLCOM_CALL)
{
if (is_sql_prepare())
{
@@ -4925,7 +4895,7 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
sub-statements inside stored procedures are not logged into
the general log.
*/
- if (error == 0 && thd->spcont == NULL)
+ if (likely(error == 0 && thd->spcont == NULL))
general_log_write(thd, COM_STMT_EXECUTE, thd->query(), thd->query_length());
error:
@@ -4948,12 +4918,12 @@ bool Prepared_statement::execute_immediate(const char *query, uint query_len)
{
DBUG_ENTER("Prepared_statement::execute_immediate");
String expanded_query;
- static LEX_STRING execute_immediate_stmt_name=
- {(char*) STRING_WITH_LEN("(immediate)") };
+ static LEX_CSTRING execute_immediate_stmt_name=
+ {STRING_WITH_LEN("(immediate)") };
set_sql_prepare();
name= execute_immediate_stmt_name; // for DBUG_PRINT etc
- if (prepare(query, query_len))
+ if (unlikely(prepare(query, query_len)))
DBUG_RETURN(true);
if (param_count != thd->lex->prepared_stmt_params.elements)
@@ -5326,7 +5296,7 @@ Protocol_local::store_string(const char *str, size_t length,
src_cs != &my_charset_bin &&
dst_cs != &my_charset_bin)
{
- if (convert->copy(str, length, src_cs, dst_cs, &error_unused))
+ if (unlikely(convert->copy(str, length, src_cs, dst_cs, &error_unused)))
return TRUE;
str= convert->ptr();
length= convert->length();
@@ -5469,7 +5439,8 @@ bool Protocol_local::send_result_set_metadata(List<Item> *columns, uint)
{
DBUG_ASSERT(m_rset == 0 && !alloc_root_inited(&m_rset_root));
- init_sql_alloc(&m_rset_root, MEM_ROOT_BLOCK_SIZE, 0, MYF(MY_THREAD_SPECIFIC));
+ init_sql_alloc(&m_rset_root, "send_result_set_metadata",
+ MEM_ROOT_BLOCK_SIZE, 0, MYF(MY_THREAD_SPECIFIC));
if (! (m_rset= new (&m_rset_root) List<Ed_row>))
return TRUE;
diff --git a/sql/sql_prepare.h b/sql/sql_prepare.h
index ebadafd2d2e..f1c4e5e4be9 100644
--- a/sql/sql_prepare.h
+++ b/sql/sql_prepare.h
@@ -130,6 +130,7 @@ public:
size_t get_field_count() const { return m_column_count; }
static void operator delete(void *ptr, size_t size) throw ();
+ static void operator delete(void *, MEM_ROOT *){}
private:
Ed_result_set(const Ed_result_set &); /* not implemented */
Ed_result_set &operator=(Ed_result_set &); /* not implemented */
@@ -215,21 +216,6 @@ public:
bool execute_direct(Server_runnable *server_runnable);
/**
- Get the number of result set fields.
-
- This method is valid only if we have a result:
- execute_direct() has been called. Otherwise
- the returned value is undefined.
-
- @sa Documentation for C API function
- mysql_field_count()
- */
- ulong get_field_count() const
- {
- return m_current_rset ? m_current_rset->get_field_count() : 0;
- }
-
- /**
Get the number of affected (deleted, updated)
rows for the current statement. Can be
used for statements with get_field_count() == 0.
diff --git a/sql/sql_priv.h b/sql/sql_priv.h
index e0571723330..2f37e1106f0 100644
--- a/sql/sql_priv.h
+++ b/sql/sql_priv.h
@@ -221,6 +221,7 @@
#define OPTIMIZER_SWITCH_EXISTS_TO_IN (1ULL << 28)
#define OPTIMIZER_SWITCH_ORDERBY_EQ_PROP (1ULL << 29)
#define OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_DERIVED (1ULL << 30)
+#define OPTIMIZER_SWITCH_SPLIT_MATERIALIZED (1ULL << 31)
#define OPTIMIZER_SWITCH_DEFAULT (OPTIMIZER_SWITCH_INDEX_MERGE | \
OPTIMIZER_SWITCH_INDEX_MERGE_UNION | \
@@ -246,7 +247,9 @@
OPTIMIZER_SWITCH_LOOSE_SCAN | \
OPTIMIZER_SWITCH_EXISTS_TO_IN | \
OPTIMIZER_SWITCH_ORDERBY_EQ_PROP | \
- OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_DERIVED)
+ OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_DERIVED | \
+ OPTIMIZER_SWITCH_SPLIT_MATERIALIZED)
+
/*
Replication uses 8 bytes to store SQL_MODE in the binary log. The day you
use strictly more than 64 bits by adding one more define above, you should
@@ -318,6 +321,8 @@
/* Used to check GROUP BY list in the MODE_ONLY_FULL_GROUP_BY mode */
#define UNDEF_POS (-1)
+#define IN_SUBQUERY_CONVERSION_THRESHOLD 1000
+
#endif /* !MYSQL_CLIENT */
/* BINLOG_DUMP options */
@@ -343,6 +348,8 @@ enum enum_parsing_place
IN_ON,
IN_GROUP_BY,
IN_ORDER_BY,
+ IN_UPDATE_ON_DUP_KEY,
+ IN_PART_FUNC,
PARSING_PLACE_SIZE /* always should be the last */
};
diff --git a/sql/sql_profile.cc b/sql/sql_profile.cc
index 20d8e8102cb..4d54ab32691 100644
--- a/sql/sql_profile.cc
+++ b/sql/sql_profile.cc
@@ -29,10 +29,9 @@
- "profiling_history_size", integer, session + global, "Num queries stored?"
*/
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "sql_profile.h"
-#include <my_sys.h>
#include "sql_show.h" // schema_table_store_record
#include "sql_class.h" // THD
@@ -120,8 +119,10 @@ int make_profile_table_for_show(THD *thd, ST_SCHEMA_TABLE *schema_table)
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_info->field_name);
+ NullS, NullS, &field_name);
if (field)
{
field->set_name(thd, field_info->old_name,
@@ -283,11 +284,10 @@ QUERY_PROFILE::~QUERY_PROFILE()
/**
@todo Provide a way to include the full text, as in SHOW PROCESSLIST.
*/
-void QUERY_PROFILE::set_query_source(char *query_source_arg,
- uint query_length_arg)
+void QUERY_PROFILE::set_query_source(char *query_source_arg, size_t query_length_arg)
{
/* Truncate to avoid DoS attacks. */
- uint length= MY_MIN(MAX_QUERY_LENGTH, query_length_arg);
+ size_t length= MY_MIN(MAX_QUERY_LENGTH, query_length_arg);
DBUG_ASSERT(query_source == NULL); /* we don't leak memory */
if (query_source_arg != NULL)
diff --git a/sql/sql_profile.h b/sql/sql_profile.h
index bbbdb2f01a0..5b03acf59c0 100644
--- a/sql/sql_profile.h
+++ b/sql/sql_profile.h
@@ -227,7 +227,7 @@ private:
QUERY_PROFILE(PROFILING *profiling_arg, const char *status_arg);
~QUERY_PROFILE();
- void set_query_source(char *query_source_arg, uint query_length_arg);
+ void set_query_source(char *query_source_arg, size_t query_length_arg);
/* Add a profile status change to the current profile. */
void new_status(const char *status_arg,
@@ -275,7 +275,7 @@ public:
This must be called exactly once per descrete statement.
*/
- void set_query_source(char *query_source_arg, uint query_length_arg)
+ void set_query_source(char *query_source_arg, size_t query_length_arg)
{
if (unlikely(current))
current->set_query_source(query_source_arg, query_length_arg);
@@ -287,7 +287,7 @@ public:
@param initial_state (optional) name of period before first state change
*/
- void start_new_query(const char *initial_state= "starting")
+ void start_new_query(const char *initial_state= "Starting")
{
DBUG_ASSERT(!current);
if (unlikely(enabled))
diff --git a/sql/sql_reload.cc b/sql/sql_reload.cc
index 3665b18eee2..930c00fac35 100644
--- a/sql/sql_reload.cc
+++ b/sql/sql_reload.cc
@@ -14,7 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_reload.h"
#include "sql_priv.h"
#include "mysqld.h" // select_errors
@@ -74,13 +74,13 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options,
If reload_acl_and_cache() is called from SIGHUP handler we have to
allocate temporary THD for execution of acl_reload()/grant_reload().
*/
- if (!thd && (thd= (tmp_thd= new THD(0))))
+ if (unlikely(!thd) && (thd= (tmp_thd= new THD(0))))
{
thd->thread_stack= (char*) &tmp_thd;
thd->store_globals();
}
- if (thd)
+ if (likely(thd))
{
bool reload_acl_failed= acl_reload(thd);
bool reload_grants_failed= grant_reload(thd);
@@ -98,7 +98,7 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options,
}
opt_noacl= 0;
- if (tmp_thd)
+ if (unlikely(tmp_thd))
{
delete tmp_thd;
thd= 0;
@@ -123,15 +123,8 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options,
}
if (options & REFRESH_ERROR_LOG)
- if (flush_error_log())
- {
- /*
- When flush_error_log() failed, my_error() has not been called.
- So, we have to do it here to keep the protocol.
- */
- my_error(ER_UNKNOWN_ERROR, MYF(0));
+ if (unlikely(flush_error_log()))
result= 1;
- }
if ((options & REFRESH_SLOW_LOG) && global_system_variables.sql_log_slow)
logger.flush_slow_log();
@@ -170,7 +163,7 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options,
if (options & REFRESH_RELAY_LOG)
{
#ifdef HAVE_REPLICATION
- LEX_STRING connection_name;
+ LEX_CSTRING connection_name;
Master_info *mi;
if (thd)
connection_name= thd->lex->relay_log_connection_name;
@@ -291,13 +284,13 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options,
{
int err;
for (TABLE_LIST *t= tables; t; t= t->next_local)
- if (!find_table_for_mdl_upgrade(thd, t->db, t->table_name, &err))
+ if (!find_table_for_mdl_upgrade(thd, t->db.str, t->table_name.str, &err))
{
if (is_locked_view(thd, t))
t->next_local= t->next_global;
else
{
- my_error(err, MYF(0), t->table_name);
+ my_error(err, MYF(0), t->table_name.str);
return 1;
}
}
@@ -422,11 +415,11 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options,
reset_mqh((LEX_USER *) NULL, 0); /* purecov: inspected */
if (options & REFRESH_GENERIC)
{
- List_iterator_fast<LEX_STRING> li(thd->lex->view_list);
- LEX_STRING *ls;
+ List_iterator_fast<LEX_CSTRING> li(thd->lex->view_list);
+ LEX_CSTRING *ls;
while ((ls= li++))
{
- ST_SCHEMA_TABLE *table= find_schema_table(thd, ls->str);
+ ST_SCHEMA_TABLE *table= find_schema_table(thd, ls);
if (table->reset_table())
result= 1;
}
@@ -550,8 +543,8 @@ bool flush_tables_with_read_lock(THD *thd, TABLE_LIST *all_tables)
{
/* Request removal of table from cache. */
tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED,
- table_list->db,
- table_list->table_name, FALSE);
+ table_list->db.str,
+ table_list->table_name.str, FALSE);
/* Reset ticket to satisfy asserts in open_tables(). */
table_list->mdl_request.ticket= NULL;
}
@@ -583,7 +576,7 @@ bool flush_tables_with_read_lock(THD *thd, TABLE_LIST *all_tables)
if (!(table_list->table->file->ha_table_flags() & HA_CAN_EXPORT))
{
my_error(ER_ILLEGAL_HA, MYF(0),table_list->table->file->table_type(),
- table_list->db, table_list->table_name);
+ table_list->db.str, table_list->table_name.str);
goto error_reset_bits;
}
}
@@ -625,4 +618,3 @@ static void disable_checkpoints(THD *thd)
ha_checkpoint_state(1); // Disable checkpoints
}
}
-
diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc
index af4a6ca3ce1..ada373546be 100644
--- a/sql/sql_rename.cc
+++ b/sql/sql_rename.cc
@@ -19,7 +19,7 @@
Atomic rename of table; RENAME TABLE t1 to t2, tmp to t1 [,...]
*/
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#include "sql_rename.h"
@@ -33,8 +33,8 @@
static TABLE_LIST *rename_tables(THD *thd, TABLE_LIST *table_list,
bool skip_error);
-static bool do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db,
- char *new_table_name, char *new_table_alias,
+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);
static TABLE_LIST *reverse_table_list(TABLE_LIST *table_list);
@@ -50,7 +50,7 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
bool binlog_error= 0;
TABLE_LIST *ren_table= 0;
int to_table;
- char *rename_log_table[2]= {NULL, NULL};
+ const char *rename_log_table[2]= {NULL, NULL};
DBUG_ENTER("mysql_rename_tables");
/*
@@ -103,8 +103,9 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
Two renames of "log_table TO" w/o rename "TO log_table" in
between.
*/
- my_error(ER_CANT_RENAME_LOG_TABLE, MYF(0), ren_table->table_name,
- ren_table->table_name);
+ my_error(ER_CANT_RENAME_LOG_TABLE, MYF(0),
+ ren_table->table_name.str,
+ ren_table->table_name.str);
goto err;
}
}
@@ -116,14 +117,15 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
Attempt to rename a table TO log_table w/o renaming
log_table TO some table.
*/
- my_error(ER_CANT_RENAME_LOG_TABLE, MYF(0), ren_table->table_name,
- ren_table->table_name);
+ my_error(ER_CANT_RENAME_LOG_TABLE, MYF(0),
+ ren_table->table_name.str,
+ ren_table->table_name.str);
goto err;
}
else
{
/* save the name of the log table to report an error */
- rename_log_table[log_table_rename]= ren_table->table_name;
+ rename_log_table[log_table_rename]= ren_table->table_name.str;
}
}
}
@@ -171,14 +173,14 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
error= 1;
}
- if (!silent && !error)
+ if (likely(!silent && !error))
{
binlog_error= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
- if (!binlog_error)
+ if (likely(!binlog_error))
my_ok(thd);
}
- if (!error)
+ if (likely(!error))
query_cache_invalidate3(thd, table_list, 0);
err:
@@ -215,21 +217,21 @@ static bool
do_rename_temporary(THD *thd, TABLE_LIST *ren_table, TABLE_LIST *new_table,
bool skip_error)
{
- const char *new_alias;
+ LEX_CSTRING *new_alias;
DBUG_ENTER("do_rename_temporary");
- new_alias= (lower_case_table_names == 2) ? new_table->alias :
- new_table->table_name;
+ new_alias= (lower_case_table_names == 2) ? &new_table->alias :
+ &new_table->table_name;
if (thd->find_temporary_table(new_table, THD::TMP_TABLE_ANY))
{
- my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
+ my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias->str);
DBUG_RETURN(1); // This can't be skipped
}
DBUG_RETURN(thd->rename_temporary_table(ren_table->table,
- new_table->db, new_alias));
+ &new_table->db, new_alias));
}
@@ -254,55 +256,52 @@ do_rename_temporary(THD *thd, TABLE_LIST *ren_table, TABLE_LIST *new_table,
*/
static bool
-do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name,
- char *new_table_alias, bool skip_error)
+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)
{
int rc= 1;
handlerton *hton;
- const char *new_alias, *old_alias;
+ LEX_CSTRING old_alias, new_alias;
DBUG_ENTER("do_rename");
if (lower_case_table_names == 2)
{
old_alias= ren_table->alias;
- new_alias= new_table_alias;
+ new_alias= *new_table_alias;
}
else
{
old_alias= ren_table->table_name;
- new_alias= new_table_name;
+ new_alias= *new_table_name;
}
- DBUG_ASSERT(new_alias);
+ DBUG_ASSERT(new_alias.str);
- if (ha_table_exists(thd, new_db, new_alias))
+ if (ha_table_exists(thd, new_db, &new_alias))
{
- my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
+ 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)
+ if (ha_table_exists(thd, &ren_table->db, &old_alias, &hton) && hton)
{
DBUG_ASSERT(!thd->locked_tables_mode);
tdc_remove_table(thd, TDC_RT_REMOVE_ALL,
- ren_table->db, ren_table->table_name, false);
+ ren_table->db.str, ren_table->table_name.str, false);
if (hton != view_pseudo_hton)
{
- if (!(rc= mysql_rename_table(hton, ren_table->db, old_alias,
- new_db, new_alias, 0)))
+ if (!(rc= mysql_rename_table(hton, &ren_table->db, &old_alias,
+ new_db, &new_alias, 0)))
{
- LEX_STRING db_name= { ren_table->db, ren_table->db_length };
- LEX_STRING table_name= { ren_table->table_name,
- ren_table->table_name_length };
- LEX_STRING new_table= { (char *) new_alias, strlen(new_alias) };
- LEX_STRING new_db_name= { (char*)new_db, strlen(new_db)};
- (void) rename_table_in_stat_tables(thd, &db_name, &table_name,
- &new_db_name, &new_table);
- if ((rc= Table_triggers_list::change_table_name(thd, ren_table->db,
- old_alias,
- ren_table->table_name,
+ (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)))
+ &new_alias)))
{
/*
We've succeeded in renaming table's .frm and in updating
@@ -310,8 +309,8 @@ do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name,
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);
+ (void) mysql_rename_table(hton, new_db, &new_alias,
+ &ren_table->db, &old_alias, NO_FK_CHECKS);
}
}
}
@@ -323,17 +322,17 @@ do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name,
because a view has valid internal db&table names in this case.
*/
if (thd->lex->sql_command != SQLCOM_ALTER_DB_UPGRADE &&
- strcmp(ren_table->db, new_db))
- my_error(ER_FORBID_SCHEMA_CHANGE, MYF(0), ren_table->db, new_db);
+ 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);
+ rc= mysql_rename_view(thd, new_db, &new_alias, ren_table);
}
}
else
{
- my_error(ER_NO_SUCH_TABLE, MYF(0), ren_table->db, old_alias);
+ my_error(ER_NO_SUCH_TABLE, MYF(0), ren_table->db.str, old_alias.str);
}
- if (rc && !skip_error)
+ if (unlikely(rc && !skip_error))
DBUG_RETURN(1);
DBUG_RETURN(0);
@@ -377,8 +376,8 @@ 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))
+ do_rename(thd, ren_table, &new_table->db, &new_table->table_name,
+ &new_table->alias, skip_error))
DBUG_RETURN(ren_table);
}
DBUG_RETURN(0);
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index 721b6799ed3..cd8a2129410 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -14,7 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#include "sql_base.h"
@@ -28,9 +28,9 @@
#include "log_event.h"
#include "rpl_filter.h"
#include <my_dir.h>
-#include "rpl_handler.h"
#include "debug_sync.h"
-#include "log.h" // get_gtid_list_event
+#include "semisync_master.h"
+#include "semisync_slave.h"
enum enum_gtid_until_state {
GTID_UNTIL_NOT_DONE,
@@ -160,6 +160,7 @@ struct binlog_send_info {
bool clear_initial_log_pos;
bool should_stop;
+ size_t dirlen;
binlog_send_info(THD *thd_arg, String *packet_arg, ushort flags_arg,
char *lfn)
@@ -313,16 +314,43 @@ static int reset_transmit_packet(binlog_send_info *info, ushort flags,
packet->length(0);
packet->set("\0", 1, &my_charset_bin);
- if (RUN_HOOK(binlog_transmit, reserve_header, (info->thd, flags, packet)))
+ if (info->thd->semi_sync_slave)
{
- info->error= ER_UNKNOWN_ERROR;
- *errmsg= "Failed to run hook 'reserve_header'";
- ret= 1;
+ if (repl_semisync_master.reserve_sync_header(packet))
+ {
+ info->error= ER_UNKNOWN_ERROR;
+ *errmsg= "Failed to run hook 'reserve_header'";
+ ret= 1;
+ }
}
+
*ev_offset= packet->length();
return ret;
}
+int get_user_var_int(const char *name,
+ long long int *value, int *null_value)
+{
+ bool null_val;
+ user_var_entry *entry=
+ (user_var_entry*) my_hash_search(&current_thd->user_vars,
+ (uchar*) name, strlen(name));
+ if (!entry)
+ return 1;
+ *value= entry->val_int(&null_val);
+ if (null_value)
+ *null_value= null_val;
+ return 0;
+}
+
+inline bool is_semi_sync_slave()
+{
+ int null_value;
+ long long val= 0;
+ get_user_var_int("rpl_semi_sync_slave", &val, &null_value);
+ return val;
+}
+
static int send_file(THD *thd)
{
NET* net = &thd->net;
@@ -346,7 +374,7 @@ static int send_file(THD *thd)
We need net_flush here because the client will not know it needs to send
us the file name until it has processed the load event entry
*/
- if (net_flush(net) || (packet_len = my_net_read(net)) == packet_error)
+ if (unlikely(net_flush(net) || (packet_len = my_net_read(net)) == packet_error))
{
errmsg = "while reading file name";
goto err;
@@ -428,7 +456,7 @@ inline void fix_checksum(enum_binlog_checksum_alg checksum_alg, String *packet,
static user_var_entry * get_binlog_checksum_uservar(THD * thd)
{
- LEX_STRING name= { C_STRING_WITH_LEN("master_binlog_checksum")};
+ LEX_CSTRING name= { STRING_WITH_LEN("master_binlog_checksum")};
user_var_entry *entry=
(user_var_entry*) my_hash_search(&thd->user_vars, (uchar*) name.str,
name.length);
@@ -678,7 +706,7 @@ void set_read_error(binlog_send_info *info, int error)
static ulonglong get_heartbeat_period(THD * thd)
{
bool null_value;
- LEX_STRING name= { C_STRING_WITH_LEN("master_heartbeat_period")};
+ LEX_CSTRING name= { STRING_WITH_LEN("master_heartbeat_period")};
user_var_entry *entry=
(user_var_entry*) my_hash_search(&thd->user_vars, (uchar*) name.str,
name.length);
@@ -697,7 +725,7 @@ static int
get_mariadb_slave_capability(THD *thd)
{
bool null_value;
- const LEX_STRING name= { C_STRING_WITH_LEN("mariadb_slave_capability") };
+ const LEX_CSTRING name= { STRING_WITH_LEN("mariadb_slave_capability") };
const user_var_entry *entry=
(user_var_entry*) my_hash_search(&thd->user_vars, (uchar*) name.str,
name.length);
@@ -718,7 +746,7 @@ get_slave_connect_state(THD *thd, String *out_str)
{
bool null_value;
- const LEX_STRING name= { C_STRING_WITH_LEN("slave_connect_state") };
+ const LEX_CSTRING name= { STRING_WITH_LEN("slave_connect_state") };
user_var_entry *entry=
(user_var_entry*) my_hash_search(&thd->user_vars, (uchar*) name.str,
name.length);
@@ -731,7 +759,7 @@ get_slave_gtid_strict_mode(THD *thd)
{
bool null_value;
- const LEX_STRING name= { C_STRING_WITH_LEN("slave_gtid_strict_mode") };
+ const LEX_CSTRING name= { STRING_WITH_LEN("slave_gtid_strict_mode") };
user_var_entry *entry=
(user_var_entry*) my_hash_search(&thd->user_vars, (uchar*) name.str,
name.length);
@@ -744,7 +772,7 @@ get_slave_gtid_ignore_duplicates(THD *thd)
{
bool null_value;
- const LEX_STRING name= { C_STRING_WITH_LEN("slave_gtid_ignore_duplicates") };
+ const LEX_CSTRING name= { STRING_WITH_LEN("slave_gtid_ignore_duplicates") };
user_var_entry *entry=
(user_var_entry*) my_hash_search(&thd->user_vars, (uchar*) name.str,
name.length);
@@ -765,7 +793,7 @@ get_slave_until_gtid(THD *thd, String *out_str)
{
bool null_value;
- const LEX_STRING name= { C_STRING_WITH_LEN("slave_until_gtid") };
+ const LEX_CSTRING name= { STRING_WITH_LEN("slave_until_gtid") };
user_var_entry *entry=
(user_var_entry*) my_hash_search(&thd->user_vars, (uchar*) name.str,
name.length);
@@ -811,8 +839,8 @@ static int send_heartbeat_event(binlog_send_info *info,
char* p= coord->file_name + dirname_length(coord->file_name);
- uint ident_len = strlen(p);
- ulong event_len = ident_len + LOG_EVENT_HEADER_LEN +
+ size_t ident_len = strlen(p);
+ size_t event_len = ident_len + LOG_EVENT_HEADER_LEN +
(do_checksum ? BINLOG_CHECKSUM_LEN : 0);
int4store(header + SERVER_ID_OFFSET, global_system_variables.server_id);
int4store(header + EVENT_LEN_OFFSET, event_len);
@@ -1207,8 +1235,9 @@ gtid_find_binlog_file(slave_connection_state *state, char *out_name,
const char *errormsg= NULL;
char buf[FN_REFLEN];
- init_alloc_root(&memroot, 10*(FN_REFLEN+sizeof(binlog_file_entry)), 0,
- MYF(MY_THREAD_SPECIFIC));
+ init_alloc_root(&memroot, "gtid_find_binlog_file",
+ 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";
@@ -1241,12 +1270,12 @@ gtid_find_binlog_file(slave_connection_state *state, char *out_name,
goto end;
}
bzero((char*) &cache, sizeof(cache));
- if ((file= open_binlog(&cache, buf, &errormsg)) == (File)-1)
+ if (unlikely((file= open_binlog(&cache, buf, &errormsg)) == (File)-1))
goto end;
errormsg= get_gtid_list_event(&cache, &glev);
end_io_cache(&cache);
mysql_file_close(file, MYF(MY_WME));
- if (errormsg)
+ if (unlikely(errormsg))
goto end;
if (!glev || contains_all_slave_gtid(state, glev))
@@ -1353,14 +1382,14 @@ gtid_state_from_pos(const char *name, uint32 offset,
String packet;
Format_description_log_event *fdev= NULL;
- if (gtid_state->load((const rpl_gtid *)NULL, 0))
+ if (unlikely(gtid_state->load((const rpl_gtid *)NULL, 0)))
{
errormsg= "Internal error (out of memory?) initializing slave state "
"while scanning binlog to find start position";
return errormsg;
}
- if ((file= open_binlog(&cache, name, &errormsg)) == (File)-1)
+ if (unlikely((file= open_binlog(&cache, name, &errormsg)) == (File)-1))
return errormsg;
if (!(fdev= new Format_description_log_event(3)))
@@ -1393,7 +1422,7 @@ gtid_state_from_pos(const char *name, uint32 offset,
err= Log_event::read_log_event(&cache, &packet, fdev,
opt_master_verify_checksum ? current_checksum_alg
: BINLOG_CHECKSUM_ALG_OFF);
- if (err)
+ if (unlikely(err))
{
errormsg= "Could not read binlog while searching for slave start "
"position on master";
@@ -1408,7 +1437,7 @@ gtid_state_from_pos(const char *name, uint32 offset,
{
Format_description_log_event *tmp;
- if (found_format_description_event)
+ if (unlikely(found_format_description_event))
{
errormsg= "Duplicate format description log event found while "
"searching for old-style position in binlog";
@@ -1417,8 +1446,9 @@ gtid_state_from_pos(const char *name, uint32 offset,
current_checksum_alg= get_checksum_alg(packet.ptr(), packet.length());
found_format_description_event= true;
- if (!(tmp= new Format_description_log_event(packet.ptr(), packet.length(),
- fdev)))
+ if (unlikely(!(tmp= new Format_description_log_event(packet.ptr(),
+ packet.length(),
+ fdev))))
{
errormsg= "Corrupt Format_description event found or out-of-memory "
"while searching for old-style position in binlog";
@@ -1441,7 +1471,8 @@ gtid_state_from_pos(const char *name, uint32 offset,
goto end;
}
}
- else if (typ != FORMAT_DESCRIPTION_EVENT && !found_format_description_event)
+ else if (unlikely(typ != FORMAT_DESCRIPTION_EVENT &&
+ !found_format_description_event))
{
errormsg= "Did not find format description log event while searching "
"for old-style position in binlog";
@@ -1456,7 +1487,7 @@ gtid_state_from_pos(const char *name, uint32 offset,
bool status;
uint32 list_len;
- if (found_gtid_list_event)
+ if (unlikely(found_gtid_list_event))
{
errormsg= "Found duplicate Gtid_list_log_event while scanning binlog "
"to find slave start position";
@@ -1465,7 +1496,7 @@ gtid_state_from_pos(const char *name, uint32 offset,
status= Gtid_list_log_event::peek(packet.ptr(), packet.length(),
current_checksum_alg,
&gtid_list, &list_len, fdev);
- if (status)
+ if (unlikely(status))
{
errormsg= "Error reading Gtid_list_log_event while searching "
"for old-style position in binlog";
@@ -1473,7 +1504,7 @@ gtid_state_from_pos(const char *name, uint32 offset,
}
err= gtid_state->load(gtid_list, list_len);
my_free(gtid_list);
- if (err)
+ if (unlikely(err))
{
errormsg= "Internal error (out of memory?) initialising slave state "
"while scanning binlog to find start position";
@@ -1481,7 +1512,7 @@ gtid_state_from_pos(const char *name, uint32 offset,
}
found_gtid_list_event= true;
}
- else if (!found_gtid_list_event)
+ else if (unlikely(!found_gtid_list_event))
{
/* We did not find any Gtid_list_log_event, must be old binlog. */
goto end;
@@ -1490,15 +1521,16 @@ gtid_state_from_pos(const char *name, uint32 offset,
{
rpl_gtid gtid;
uchar flags2;
- if (Gtid_log_event::peek(packet.ptr(), packet.length(),
- current_checksum_alg, &gtid.domain_id,
- &gtid.server_id, &gtid.seq_no, &flags2, fdev))
+ if (unlikely(Gtid_log_event::peek(packet.ptr(), packet.length(),
+ current_checksum_alg, &gtid.domain_id,
+ &gtid.server_id, &gtid.seq_no, &flags2,
+ fdev)))
{
errormsg= "Corrupt gtid_log_event found while scanning binlog to find "
"initial slave position";
goto end;
}
- if (gtid_state->update(&gtid))
+ if (unlikely(gtid_state->update(&gtid)))
{
errormsg= "Internal error (out of memory?) updating slave state while "
"scanning binlog to find start position";
@@ -1507,7 +1539,7 @@ gtid_state_from_pos(const char *name, uint32 offset,
}
}
- if (!valid_pos)
+ if (unlikely(!valid_pos))
{
errormsg= "Slave requested incorrect position in master binlog. "
"Requested position %u in file '%s', but this position does not "
@@ -1617,6 +1649,7 @@ send_event_to_slave(binlog_send_info *info, Log_event_type event_type,
enum enum_binlog_checksum_alg current_checksum_alg= info->current_checksum_alg;
slave_connection_state *gtid_state= &info->gtid_state;
slave_connection_state *until_gtid_state= info->until_gtid_state;
+ bool need_sync= false;
if (event_type == GTID_LIST_EVENT &&
info->using_gtid_state && until_gtid_state)
@@ -1924,8 +1957,10 @@ send_event_to_slave(binlog_send_info *info, Log_event_type event_type,
THD_STAGE_INFO(info->thd, stage_sending_binlog_event_to_slave);
pos= my_b_tell(log);
- if (RUN_HOOK(binlog_transmit, before_send_event,
- (info->thd, info->flags, packet, info->log_file_name, pos)))
+ if (repl_semisync_master.update_sync_header(info->thd,
+ (uchar*) packet->c_ptr_safe(),
+ info->log_file_name + info->dirlen,
+ pos, &need_sync))
{
info->error= ER_UNKNOWN_ERROR;
return "run 'before_send_event' hook failed";
@@ -1947,8 +1982,8 @@ send_event_to_slave(binlog_send_info *info, Log_event_type event_type,
}
}
- if (RUN_HOOK(binlog_transmit, after_send_event,
- (info->thd, info->flags, packet)))
+ if (need_sync && repl_semisync_master.flush_net(info->thd,
+ packet->c_ptr_safe()))
{
info->error= ER_UNKNOWN_ERROR;
return "Failed to run hook 'after_send_event'";
@@ -2083,8 +2118,8 @@ static int init_binlog_sender(binlog_send_info *info,
info->error= ER_UNKNOWN_ERROR;
return 1;
}
- if ((error= check_slave_start_position(info, &info->errmsg,
- &info->error_gtid)))
+ if (unlikely((error= check_slave_start_position(info, &info->errmsg,
+ &info->error_gtid))))
{
info->error= error;
return 1;
@@ -2184,7 +2219,7 @@ static int send_format_descriptor_event(binlog_send_info *info, IO_CACHE *log,
: BINLOG_CHECKSUM_ALG_OFF);
linfo->pos= my_b_tell(log);
- if (error)
+ if (unlikely(error))
{
set_read_error(info, error);
DBUG_RETURN(1);
@@ -2318,7 +2353,7 @@ static int send_format_descriptor_event(binlog_send_info *info, IO_CACHE *log,
: BINLOG_CHECKSUM_ALG_OFF);
linfo->pos= my_b_tell(log);
- if (error)
+ if (unlikely(error))
{
set_read_error(info, error);
DBUG_RETURN(1);
@@ -2396,7 +2431,7 @@ static int wait_new_events(binlog_send_info *info, /* in */
PSI_stage_info old_stage;
mysql_bin_log.lock_binlog_end_pos();
- info->thd->ENTER_COND(mysql_bin_log.get_log_cond(),
+ info->thd->ENTER_COND(mysql_bin_log.get_bin_log_cond(),
mysql_bin_log.get_binlog_end_pos_lock(),
&stage_master_has_sent_all_binlog_to_slave,
&old_stage);
@@ -2584,7 +2619,7 @@ static int send_events(binlog_send_info *info, IO_CACHE* log, LOG_INFO* linfo,
: BINLOG_CHECKSUM_ALG_OFF);
linfo->pos= my_b_tell(log);
- if (error)
+ if (unlikely(error))
{
set_read_error(info, error);
return 1;
@@ -2708,7 +2743,7 @@ static int send_one_binlog_file(binlog_send_info *info,
/** end of file or error */
return (int)end_pos;
}
-
+ info->dirlen= dirname_length(info->log_file_name);
/**
* send events from current position up to end_pos
*/
@@ -2730,6 +2765,7 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos,
binlog_send_info infoobj(thd, packet, flags, linfo.log_file_name);
binlog_send_info *info= &infoobj;
+ bool has_transmit_started= false;
int old_max_allowed_packet= thd->variables.max_allowed_packet;
thd->variables.max_allowed_packet= MAX_MAX_ALLOWED_PACKET;
@@ -2742,15 +2778,14 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos,
if (init_binlog_sender(info, &linfo, log_ident, &pos))
goto err;
- /*
- run hook first when all check has been made that slave seems to
- be requesting a reasonable position. i.e when transmit actually starts
- */
+ has_transmit_started= true;
+
+ /* Check if the dump thread is created by a slave with semisync enabled. */
+ thd->semi_sync_slave = is_semi_sync_slave();
DBUG_ASSERT(pos == linfo.pos);
- if (RUN_HOOK(binlog_transmit, transmit_start,
- (thd, flags, linfo.log_file_name, linfo.pos)))
+ if (repl_semisync_master.dump_start(thd, linfo.log_file_name, linfo.pos))
{
info->errmsg= "Failed to run hook 'transmit_start'";
info->error= ER_UNKNOWN_ERROR;
@@ -2872,7 +2907,10 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos,
err:
THD_STAGE_INFO(thd, stage_waiting_to_finalize_termination);
- RUN_HOOK(binlog_transmit, transmit_stop, (thd, flags));
+ if (has_transmit_started)
+ {
+ repl_semisync_master.dump_end(thd);
+ }
if (info->thd->killed == KILL_SLAVE_SAME_ID)
{
@@ -2892,6 +2930,12 @@ err:
thd->variables.max_allowed_packet= old_max_allowed_packet;
delete info->fdev;
+ if (likely(info->error == 0))
+ {
+ my_eof(thd);
+ DBUG_VOID_RETURN;
+ }
+
if ((info->error == ER_MASTER_FATAL_ERROR_READING_BINLOG ||
info->error == ER_SLAVE_SAME_ID) && binlog_open)
{
@@ -2953,17 +2997,10 @@ err:
"mysql", rpl_gtid_slave_state_table_name.str);
info->error= ER_MASTER_FATAL_ERROR_READING_BINLOG;
}
- else if (info->error != 0 && info->errmsg != NULL)
+ else if (info->errmsg != NULL)
strcpy(info->error_text, info->errmsg);
- if (info->error == 0)
- {
- my_eof(thd);
- }
- else
- {
- my_message(info->error, info->error_text, MYF(0));
- }
+ my_message(info->error, info->error_text, MYF(0));
DBUG_VOID_RETURN;
}
@@ -3257,7 +3294,7 @@ int reset_slave(THD *thd, Master_info* mi)
char fname[FN_REFLEN];
int thread_mask= 0, error= 0;
uint sql_errno=ER_UNKNOWN_ERROR;
- const char* errmsg= "Unknown error occurred while reseting slave";
+ const char* errmsg= "Unknown error occurred while resetting slave";
char master_info_file_tmp[FN_REFLEN];
char relay_log_info_file_tmp[FN_REFLEN];
DBUG_ENTER("reset_slave");
@@ -3282,9 +3319,9 @@ int reset_slave(THD *thd, Master_info* mi)
}
// delete relay logs, clear relay log coordinates
- if ((error= purge_relay_logs(&mi->rli, thd,
+ if (unlikely((error= purge_relay_logs(&mi->rli, thd,
1 /* just reset */,
- &errmsg)))
+ &errmsg))))
{
sql_errno= ER_RELAY_LOG_FAIL;
goto err;
@@ -3338,10 +3375,11 @@ int reset_slave(THD *thd, Master_info* mi)
else if (global_system_variables.log_warnings > 1)
sql_print_information("Deleted Master_info file '%s'.", fname);
- RUN_HOOK(binlog_relay_io, after_reset_slave, (thd, mi));
+ if (rpl_semi_sync_slave_enabled)
+ repl_semisync_slave.reset_slave(mi);
err:
mi->unlock_slave_threads();
- if (error)
+ if (unlikely(error))
my_error(sql_errno, MYF(0), errmsg);
DBUG_RETURN(error);
}
@@ -3375,7 +3413,7 @@ void kill_zombie_dump_threads(uint32 slave_server_id)
if (tmp->get_command() == COM_BINLOG_DUMP &&
tmp->variables.server_id == slave_server_id)
{
- mysql_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete
+ mysql_mutex_lock(&tmp->LOCK_thd_kill); // Lock from delete
break;
}
}
@@ -3387,8 +3425,8 @@ void kill_zombie_dump_threads(uint32 slave_server_id)
it will be slow because it will iterate through the list
again. We just to do kill the thread ourselves.
*/
- tmp->awake(KILL_SLAVE_SAME_ID);
- mysql_mutex_unlock(&tmp->LOCK_thd_data);
+ tmp->awake_no_mutex(KILL_SLAVE_SAME_ID);
+ mysql_mutex_unlock(&tmp->LOCK_thd_kill);
}
}
@@ -3407,7 +3445,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);
- uint from_numchars= cs->cset->numchars(cs, from, from + from_length);
+ size_t from_numchars= cs->cset->numchars(cs, from, from + from_length);
if (from_numchars > length / cs->mbmaxlen)
{
my_error(ER_WRONG_STRING_LENGTH, MYF(0), from, name,
@@ -3841,11 +3879,13 @@ int reset_master(THD* thd, rpl_gtid *init_state, uint32 init_state_len,
return 1;
}
- if (mysql_bin_log.reset_logs(thd, 1, init_state, init_state_len,
- next_log_number))
- return 1;
- RUN_HOOK(binlog_transmit, after_reset_master, (thd, 0 /* flags */));
- return 0;
+ bool ret= 0;
+ /* Temporarily disable master semisync before resetting master. */
+ repl_semisync_master.before_reset_master();
+ ret= mysql_bin_log.reset_logs(thd, 1, init_state, init_state_len,
+ next_log_number);
+ repl_semisync_master.after_reset_master();
+ return ret;
}
@@ -3971,7 +4011,7 @@ bool mysql_show_binlog_events(THD* thd)
my_off_t scan_pos = BIN_LOG_HEADER_SIZE;
while (scan_pos < pos)
{
- ev= Log_event::read_log_event(&log, (mysql_mutex_t*)0, description_event,
+ ev= Log_event::read_log_event(&log, description_event,
opt_master_verify_checksum);
scan_pos = my_b_tell(&log);
if (ev == NULL || !ev->is_valid())
@@ -4005,7 +4045,7 @@ bool mysql_show_binlog_events(THD* thd)
my_b_seek(&log, pos);
for (event_count = 0;
- (ev = Log_event::read_log_event(&log, (mysql_mutex_t*) 0,
+ (ev = Log_event::read_log_event(&log,
description_event,
(opt_master_verify_checksum ||
verify_checksum_once))); )
@@ -4049,7 +4089,7 @@ bool mysql_show_binlog_events(THD* thd)
break;
}
- if (event_count < limit_end && log.error)
+ if (unlikely(event_count < limit_end && log.error))
{
errmsg = "Wrong offset or I/O error";
mysql_mutex_unlock(log_lock);
@@ -4131,7 +4171,7 @@ bool show_binlog_info(THD* thd)
{
LOG_INFO li;
mysql_bin_log.get_current_log(&li);
- int dir_len = dirname_length(li.log_file_name);
+ size_t dir_len = dirname_length(li.log_file_name);
protocol->store(li.log_file_name + dir_len, &my_charset_bin);
protocol->store((ulonglong) li.pos);
protocol->store(binlog_filter->get_do_db());
@@ -4173,8 +4213,8 @@ bool show_binlogs(THD* thd)
File file;
char fname[FN_REFLEN];
List<Item> field_list;
- uint length;
- int cur_dir_len;
+ size_t length;
+ size_t cur_dir_len;
Protocol *protocol= thd->protocol;
DBUG_ENTER("show_binlogs");
@@ -4204,7 +4244,7 @@ bool show_binlogs(THD* thd)
/* The file ends with EOF or empty line */
while ((length=my_b_gets(index_file, fname, sizeof(fname))) > 1)
{
- int dir_len;
+ size_t dir_len;
ulonglong file_length= 0; // Length if open fails
fname[--length] = '\0'; // remove the newline
@@ -4230,7 +4270,7 @@ bool show_binlogs(THD* thd)
if (protocol->write())
goto err;
}
- if(index_file->error == -1)
+ if (unlikely(index_file->error == -1))
goto err;
mysql_bin_log.unlock_index();
my_eof(thd);
@@ -4273,7 +4313,7 @@ int log_loaded_block(IO_CACHE* file, uchar *Buffer, size_t Count)
lf_info->last_pos_in_file= my_b_get_pos_in_file(file);
if (lf_info->wrote_create_file)
{
- Append_block_log_event a(lf_info->thd, lf_info->thd->db, buffer,
+ Append_block_log_event a(lf_info->thd, lf_info->thd->db.str, buffer,
MY_MIN(block_len, max_event_size),
lf_info->log_delayed);
if (mysql_bin_log.write(&a))
@@ -4281,7 +4321,7 @@ int log_loaded_block(IO_CACHE* file, uchar *Buffer, size_t Count)
}
else
{
- Begin_load_query_log_event b(lf_info->thd, lf_info->thd->db,
+ Begin_load_query_log_event b(lf_info->thd, lf_info->thd->db.str,
buffer,
MY_MIN(block_len, max_event_size),
lf_info->log_delayed);
diff --git a/sql/sql_schema.cc b/sql/sql_schema.cc
new file mode 100644
index 00000000000..0bf4a63c2f8
--- /dev/null
+++ b/sql/sql_schema.cc
@@ -0,0 +1,80 @@
+/*
+ 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 "mariadb.h"
+#include "sql_type.h"
+#include "sql_schema.h"
+#include "sql_class.h"
+
+class Schema_oracle: public Schema
+{
+public:
+ Schema_oracle(const LEX_CSTRING &name)
+ :Schema(name)
+ { }
+ const Type_handler *map_data_type(THD *thd, const Type_handler *src)
+ const
+ {
+ if (src == &type_handler_newdate)
+ return thd->type_handler_for_datetime();
+ return src;
+ }
+};
+
+
+class Schema_maxdb: public Schema
+{
+public:
+ Schema_maxdb(const LEX_CSTRING &name)
+ :Schema(name)
+ { }
+ const Type_handler *map_data_type(THD *thd, const Type_handler *src)
+ const
+ {
+ if (src == &type_handler_timestamp ||
+ src == &type_handler_timestamp2)
+ return thd->type_handler_for_datetime();
+ return src;
+ }
+};
+
+
+Schema mariadb_schema(Lex_cstring(STRING_WITH_LEN("mariadb_schema")));
+Schema_oracle oracle_schema(Lex_cstring(STRING_WITH_LEN("oracle_schema")));
+Schema_maxdb maxdb_schema(Lex_cstring(STRING_WITH_LEN("maxdb_schema")));
+
+
+Schema *Schema::find_by_name(const LEX_CSTRING &name)
+{
+ DBUG_ASSERT(name.str);
+ if (mariadb_schema.eq_name(name))
+ return &mariadb_schema;
+ if (oracle_schema.eq_name(name))
+ return &oracle_schema;
+ if (maxdb_schema.eq_name(name))
+ return &maxdb_schema;
+ return NULL;
+}
+
+
+Schema *Schema::find_implied(THD *thd)
+{
+ if (thd->variables.sql_mode & MODE_ORACLE)
+ return &oracle_schema;
+ if (thd->variables.sql_mode & MODE_MAXDB)
+ return &maxdb_schema;
+ return &mariadb_schema;
+}
diff --git a/sql/sql_schema.h b/sql/sql_schema.h
new file mode 100644
index 00000000000..7c8f284d526
--- /dev/null
+++ b/sql/sql_schema.h
@@ -0,0 +1,70 @@
+#ifndef SQL_SCHEMA_H_INCLUDED
+#define SQL_SCHEMA_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 "mysqld.h"
+#include "lex_string.h"
+
+class Schema
+{
+ LEX_CSTRING m_name;
+public:
+ Schema(const LEX_CSTRING &name)
+ :m_name(name)
+ { }
+ virtual ~Schema() { }
+ const LEX_CSTRING &name() const { return m_name; }
+ virtual const Type_handler *map_data_type(THD *thd, const Type_handler *src)
+ const
+ {
+ return src;
+ }
+ /*
+ For now we have *hard-coded* compatibility schemas:
+ schema_mariadb, schema_oracle, schema_maxdb.
+ But eventually we'll turn then into real databases on disk.
+ So the code below compares names according to the filesystem
+ case sensitivity, like it is done for regular databases.
+
+ Note, this is different to information_schema, whose name
+ is always case insensitive. This is intentional!
+ The assymetry will be gone when we'll implement SQL standard
+ regular and delimited identifiers.
+ */
+ 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);
+};
+
+
+extern Schema mariadb_schema;
+
+#endif // SQL_SCHEMA_H_INCLUDED
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 96e9602d77d..34c137c4732 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -1,5 +1,5 @@
/* 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
@@ -29,7 +29,7 @@
#pragma implementation // gcc: Class implementation
#endif
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#include "sql_select.h"
@@ -50,17 +50,20 @@
#include "filesort.h" // filesort_free_buffers
#include "sql_union.h" // mysql_union
#include "opt_subselect.h"
-#include "log_slow.h"
#include "sql_derived.h"
#include "sql_statistics.h"
#include "sql_cte.h"
#include "sql_window.h"
+#include "tztime.h"
#include "debug_sync.h" // DEBUG_SYNC
#include <m_ctype.h>
#include <my_bit.h>
#include <hash.h>
#include <ft_global.h>
+#include "sys_vars_shared.h"
+#include "sp_head.h"
+#include "sp_rcontext.h"
/*
A key part number that means we're using a fulltext scan.
@@ -81,9 +84,11 @@ const char *join_type_str[]={ "UNKNOWN","system","const","eq_ref","ref",
"index_merge", "hash_ALL", "hash_range",
"hash_index", "hash_index_merge" };
+LEX_CSTRING group_key= {STRING_WITH_LEN("group_key")};
+LEX_CSTRING distinct_key= {STRING_WITH_LEN("distinct_key")};
+
struct st_sargable_param;
-static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array);
static bool make_join_statistics(JOIN *join, List<TABLE_LIST> &leaves,
DYNAMIC_ARRAY *keyuse);
static bool update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,
@@ -91,8 +96,6 @@ static bool update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,
uint tables, COND *conds,
table_map table_map, SELECT_LEX *select_lex,
SARGABLE_PARAM **sargables);
-static bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse,
- bool skip_unprefixed_keyparts);
static int sort_keyuse(KEYUSE *a,KEYUSE *b);
static bool are_tables_local(JOIN_TAB *jtab, table_map used_tables);
static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse,
@@ -220,6 +223,9 @@ static bool test_if_cheaper_ordering(const JOIN_TAB *tab,
ha_rows *new_select_limit,
uint *new_used_key_parts= NULL,
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);
static bool test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,
ha_rows select_limit, bool no_changes,
const key_map *map);
@@ -283,6 +289,9 @@ static bool find_order_in_list(THD *, Ref_ptr_array, TABLE_LIST *, ORDER *,
static double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s,
table_map rem_tables);
void set_postjoin_aggr_write_func(JOIN_TAB *tab);
+
+static Item **get_sargable_cond(JOIN *join, TABLE *table);
+
#ifndef DBUG_OFF
/*
@@ -300,7 +309,7 @@ void dbug_serve_apcs(THD *thd, int n_calls)
thd_proc_info(thd, "show_explain_trap");
my_sleep(30000);
thd_proc_info(thd, save_proc_info);
- if (thd->check_killed())
+ if (unlikely(thd->check_killed(1)))
break;
}
}
@@ -321,8 +330,8 @@ void dbug_serve_apcs(THD *thd, int n_calls)
bool dbug_user_var_equals_int(THD *thd, const char *name, int value)
{
user_var_entry *var;
- LEX_STRING varname= {(char*)name, strlen(name)};
- if ((var= get_variable(&thd->user_vars, varname, FALSE)))
+ LEX_CSTRING varname= { name, strlen(name)};
+ if ((var= get_variable(&thd->user_vars, &varname, FALSE)))
{
bool null_value;
longlong var_value= var->val_int(&null_value);
@@ -346,7 +355,7 @@ bool handle_select(THD *thd, LEX *lex, select_result *result,
DBUG_ENTER("handle_select");
MYSQL_SELECT_START(thd->query());
- if (select_lex->master_unit()->is_union() ||
+ if (select_lex->master_unit()->is_unit_op() ||
select_lex->master_unit()->fake_select_lex)
res= mysql_union(thd, lex, result, &lex->unit, setup_tables_done_option);
else
@@ -377,7 +386,7 @@ bool handle_select(THD *thd, LEX *lex, select_result *result,
res|= thd->is_error();
if (unlikely(res))
result->abort_result_set();
- if (thd->killed == ABORT_QUERY)
+ if (unlikely(thd->killed == ABORT_QUERY && !thd->no_errors))
{
/*
If LIMIT ROWS EXAMINED interrupted query execution, issue a warning,
@@ -514,15 +523,15 @@ 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;
ref->ref= &ref->outer_ref;
- if (!ref->fixed && ref->fix_fields(thd, 0))
+ if (ref->fix_fields_if_needed(thd, 0))
return TRUE;
thd->lex->used_tables|= item->used_tables();
thd->lex->current_select->select_list_tables|= item->used_tables();
@@ -633,7 +642,7 @@ setup_without_group(THD *thd, Ref_ptr_array ref_pointer_array,
const bool saved_non_agg_field_used= select->non_agg_field_used();
DBUG_ENTER("setup_without_group");
- thd->lex->allow_sum_func&= ~((nesting_map)1 << select->nest_level);
+ thd->lex->allow_sum_func.clear_bit(select->nest_level);
res= setup_conds(thd, tables, leaves, conds);
if (thd->lex->current_select->first_cond_optimization)
{
@@ -646,24 +655,347 @@ setup_without_group(THD *thd, Ref_ptr_array ref_pointer_array,
/* it's not wrong to have non-aggregated columns in a WHERE */
select->set_non_agg_field_used(saved_non_agg_field_used);
- thd->lex->allow_sum_func|= (nesting_map)1 << select->nest_level;
+ thd->lex->allow_sum_func.set_bit(select->nest_level);
save_place= thd->lex->current_select->context_analysis_place;
thd->lex->current_select->context_analysis_place= IN_ORDER_BY;
res= res || setup_order(thd, ref_pointer_array, tables, fields, all_fields,
order);
- thd->lex->allow_sum_func&= ~((nesting_map)1 << select->nest_level);
+ thd->lex->allow_sum_func.clear_bit(select->nest_level);
thd->lex->current_select->context_analysis_place= IN_GROUP_BY;
res= res || setup_group(thd, ref_pointer_array, tables, fields, all_fields,
group, hidden_group_fields);
thd->lex->current_select->context_analysis_place= save_place;
- thd->lex->allow_sum_func|= (nesting_map)1 << select->nest_level;
+ thd->lex->allow_sum_func.set_bit(select->nest_level);
res= res || setup_windows(thd, ref_pointer_array, tables, fields, all_fields,
win_specs, win_funcs);
thd->lex->allow_sum_func= save_allow_sum_func;
DBUG_RETURN(res);
}
+bool vers_select_conds_t::init_from_sysvar(THD *thd)
+{
+ vers_asof_timestamp_t &in= thd->variables.vers_asof_timestamp;
+ type= (vers_system_time_t) in.type;
+ delete_history= false;
+ start.unit= VERS_TIMESTAMP;
+ if (type != SYSTEM_TIME_UNSPECIFIED && type != SYSTEM_TIME_ALL)
+ {
+ DBUG_ASSERT(type == SYSTEM_TIME_AS_OF);
+ start.item= new (thd->mem_root)
+ Item_datetime_literal(thd, &in.ltime, TIME_SECOND_PART_DIGITS);
+ if (!start.item)
+ return true;
+ }
+ else
+ start.item= NULL;
+ end.empty();
+ return false;
+}
+
+void vers_select_conds_t::print(String *str, enum_query_type query_type) const
+{
+ switch (type) {
+ case SYSTEM_TIME_UNSPECIFIED:
+ break;
+ case SYSTEM_TIME_AS_OF:
+ start.print(str, query_type, STRING_WITH_LEN(" FOR SYSTEM_TIME AS OF "));
+ break;
+ case SYSTEM_TIME_FROM_TO:
+ start.print(str, query_type, STRING_WITH_LEN(" FOR SYSTEM_TIME FROM "));
+ end.print(str, query_type, STRING_WITH_LEN(" TO "));
+ break;
+ case SYSTEM_TIME_BETWEEN:
+ start.print(str, query_type, STRING_WITH_LEN(" FOR SYSTEM_TIME BETWEEN "));
+ end.print(str, query_type, STRING_WITH_LEN(" AND "));
+ break;
+ case SYSTEM_TIME_BEFORE:
+ case SYSTEM_TIME_HISTORY:
+ DBUG_ASSERT(0);
+ break;
+ case SYSTEM_TIME_ALL:
+ str->append(" FOR SYSTEM_TIME ALL");
+ break;
+ }
+}
+
+static
+bool skip_setup_conds(THD *thd)
+{
+ return (!thd->stmt_arena->is_conventional()
+ && !thd->stmt_arena->is_stmt_prepare_or_first_sp_execute())
+ || thd->lex->is_view_context_analysis();
+}
+
+int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables)
+{
+ DBUG_ENTER("SELECT_LEX::vers_setup_cond");
+#define newx new (thd->mem_root)
+
+ const bool update_conds= !skip_setup_conds(thd);
+ TABLE_LIST *table;
+
+ if (!versioned_tables)
+ {
+ for (table= tables; table; table= table->next_local)
+ {
+ if (table->table && table->table->versioned())
+ versioned_tables++;
+ else if (table->vers_conditions.is_set() &&
+ (table->is_non_derived() || !table->vers_conditions.used))
+ {
+ my_error(ER_VERS_NOT_VERSIONED, MYF(0), table->alias.str);
+ DBUG_RETURN(-1);
+ }
+ }
+ }
+
+ if (versioned_tables == 0)
+ DBUG_RETURN(0);
+
+ /* For prepared statements we create items on statement arena,
+ because they must outlive execution phase for multiple executions. */
+ Query_arena_stmt on_stmt_arena(thd);
+
+ // find outer system_time
+ SELECT_LEX *outer_slex= outer_select();
+ TABLE_LIST* outer_table= NULL;
+
+ if (outer_slex)
+ {
+ TABLE_LIST* derived= master_unit()->derived;
+ // inner SELECT may not be a derived table (derived == NULL)
+ while (derived && outer_slex && !derived->vers_conditions.is_set())
+ {
+ derived= outer_slex->master_unit()->derived;
+ outer_slex= outer_slex->outer_select();
+ }
+ if (derived && outer_slex)
+ {
+ DBUG_ASSERT(derived->vers_conditions.is_set());
+ outer_table= derived;
+ }
+ }
+
+ bool is_select= false;
+ bool use_sysvar= false;
+ switch (thd->lex->sql_command)
+ {
+ case SQLCOM_SELECT:
+ use_sysvar= true;
+ /* fall through */
+ case SQLCOM_INSERT_SELECT:
+ case SQLCOM_REPLACE_SELECT:
+ case SQLCOM_DELETE_MULTI:
+ case SQLCOM_UPDATE_MULTI:
+ is_select= true;
+ default:
+ break;
+ }
+
+ for (table= tables; table; table= table->next_local)
+ {
+ if (!table->table || table->is_view() || !table->table->versioned())
+ continue;
+
+ vers_select_conds_t &vers_conditions= table->vers_conditions;
+
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ /*
+ if the history is stored in partitions, then partitions
+ themselves are not versioned
+ */
+ if (table->partition_names && table->table->part_info->vers_info)
+ {
+ /* If the history is stored in partitions, then partitions
+ themselves are not versioned. */
+ if (vers_conditions.was_set())
+ {
+ my_error(ER_VERS_QUERY_IN_PARTITION, MYF(0), table->alias.str);
+ DBUG_RETURN(-1);
+ }
+ else if (!vers_conditions.is_set())
+ vers_conditions.type= SYSTEM_TIME_ALL;
+ }
+#endif
+
+ if (outer_table && !vers_conditions.is_set())
+ {
+ // propagate system_time from nearest outer SELECT_LEX
+ vers_conditions= outer_table->vers_conditions;
+ outer_table->vers_conditions.used= true;
+ }
+
+ // propagate system_time from sysvar
+ if (!vers_conditions.is_set() && use_sysvar)
+ {
+ if (vers_conditions.init_from_sysvar(thd))
+ DBUG_RETURN(-1);
+ }
+
+ if (vers_conditions.is_set())
+ {
+ if (vers_conditions.was_set() &&
+ table->lock_type > TL_READ_NO_INSERT &&
+ !vers_conditions.delete_history)
+ {
+ my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), table->alias.str);
+ DBUG_RETURN(-1);
+ }
+
+ if (vers_conditions.type == SYSTEM_TIME_ALL)
+ continue;
+ }
+
+ const LEX_CSTRING *fstart=
+ thd->make_clex_string(table->table->vers_start_field()->field_name);
+ const LEX_CSTRING *fend=
+ thd->make_clex_string(table->table->vers_end_field()->field_name);
+
+ Item *row_start=
+ newx Item_field(thd, &this->context, table->db.str, table->alias.str, fstart);
+ Item *row_end=
+ newx Item_field(thd, &this->context, table->db.str, table->alias.str, fend);
+
+ bool timestamps_only= table->table->versioned(VERS_TIMESTAMP);
+
+ if (vers_conditions.is_set() && vers_conditions.type != SYSTEM_TIME_HISTORY)
+ {
+ thd->where= "FOR SYSTEM_TIME";
+ /* TODO: do resolve fix_length_and_dec(), fix_fields(). This requires
+ 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))
+ DBUG_RETURN(-1);
+ if (timestamps_only && (vers_conditions.start.unit == VERS_TRX_ID ||
+ vers_conditions.end.unit == VERS_TRX_ID))
+ {
+ my_error(ER_VERS_ENGINE_UNSUPPORTED, MYF(0), table->table_name.str);
+ DBUG_RETURN(-1);
+ }
+ }
+
+ if (!update_conds)
+ continue;
+
+ Item *cond1= NULL, *cond2= NULL, *cond3= NULL, *curr= NULL;
+ Item *point_in_time1= vers_conditions.start.item;
+ Item *point_in_time2= vers_conditions.end.item;
+ TABLE *t= table->table;
+ if (t->versioned(VERS_TIMESTAMP))
+ {
+ MYSQL_TIME max_time;
+ switch (vers_conditions.type)
+ {
+ case SYSTEM_TIME_UNSPECIFIED:
+ case SYSTEM_TIME_HISTORY:
+ thd->variables.time_zone->gmt_sec_to_TIME(&max_time, TIMESTAMP_MAX_VALUE);
+ max_time.second_part= TIME_MAX_SECOND_PART;
+ curr= newx Item_datetime_literal(thd, &max_time, TIME_SECOND_PART_DIGITS);
+ if (vers_conditions.type == SYSTEM_TIME_UNSPECIFIED)
+ cond1= newx Item_func_eq(thd, row_end, curr);
+ else
+ cond1= newx Item_func_lt(thd, row_end, curr);
+ break;
+ case SYSTEM_TIME_AS_OF:
+ cond1= newx Item_func_le(thd, row_start, point_in_time1);
+ cond2= newx Item_func_gt(thd, row_end, point_in_time1);
+ break;
+ case SYSTEM_TIME_FROM_TO:
+ cond1= newx Item_func_lt(thd, row_start, point_in_time2);
+ cond2= newx Item_func_gt(thd, row_end, point_in_time1);
+ cond3= newx Item_func_lt(thd, point_in_time1, point_in_time2);
+ break;
+ case SYSTEM_TIME_BETWEEN:
+ cond1= newx Item_func_le(thd, row_start, point_in_time2);
+ cond2= newx Item_func_gt(thd, row_end, point_in_time1);
+ cond3= newx Item_func_le(thd, point_in_time1, point_in_time2);
+ break;
+ case SYSTEM_TIME_BEFORE:
+ cond1= newx Item_func_lt(thd, row_end, point_in_time1);
+ break;
+ default:
+ DBUG_ASSERT(0);
+ }
+ }
+ else
+ {
+ DBUG_ASSERT(table->table->s && table->table->s->db_plugin);
+
+ Item *trx_id0, *trx_id1;
+
+ switch (vers_conditions.type)
+ {
+ case SYSTEM_TIME_UNSPECIFIED:
+ case SYSTEM_TIME_HISTORY:
+ curr= newx Item_int(thd, ULONGLONG_MAX);
+ if (vers_conditions.type == SYSTEM_TIME_UNSPECIFIED)
+ cond1= newx Item_func_eq(thd, row_end, curr);
+ else
+ cond1= newx Item_func_lt(thd, row_end, curr);
+ break;
+ case SYSTEM_TIME_AS_OF:
+ trx_id0= vers_conditions.start.unit == VERS_TIMESTAMP
+ ? newx Item_func_trt_id(thd, point_in_time1, TR_table::FLD_TRX_ID)
+ : point_in_time1;
+ cond1= newx Item_func_trt_trx_sees_eq(thd, trx_id0, row_start);
+ cond2= newx Item_func_trt_trx_sees(thd, row_end, trx_id0);
+ break;
+ case SYSTEM_TIME_FROM_TO:
+ cond3= newx Item_func_lt(thd, point_in_time1, point_in_time2);
+ /* fall through */
+ case SYSTEM_TIME_BETWEEN:
+ trx_id0= vers_conditions.start.unit == VERS_TIMESTAMP
+ ? newx Item_func_trt_id(thd, point_in_time1, TR_table::FLD_TRX_ID, true)
+ : point_in_time1;
+ trx_id1= vers_conditions.end.unit == VERS_TIMESTAMP
+ ? newx Item_func_trt_id(thd, point_in_time2, TR_table::FLD_TRX_ID, false)
+ : point_in_time2;
+ cond1= vers_conditions.type == SYSTEM_TIME_FROM_TO
+ ? newx Item_func_trt_trx_sees(thd, trx_id1, row_start)
+ : newx Item_func_trt_trx_sees_eq(thd, trx_id1, row_start);
+ cond2= newx Item_func_trt_trx_sees_eq(thd, row_end, trx_id0);
+ if (!cond3)
+ cond3= newx Item_func_le(thd, point_in_time1, point_in_time2);
+ break;
+ case SYSTEM_TIME_BEFORE:
+ trx_id0= vers_conditions.start.unit == VERS_TIMESTAMP
+ ? newx Item_func_trt_id(thd, point_in_time1, TR_table::FLD_TRX_ID, true)
+ : point_in_time1;
+ cond1= newx Item_func_trt_trx_sees(thd, trx_id0, row_end);
+ break;
+ default:
+ DBUG_ASSERT(0);
+ }
+ }
+
+ if (cond1)
+ {
+ cond1= and_items(thd, cond2, cond1);
+ cond1= and_items(thd, cond3, cond1);
+ if (is_select)
+ table->on_expr= and_items(thd, table->on_expr, cond1);
+ else
+ {
+ if (join)
+ {
+ where= and_items(thd, join->conds, cond1);
+ join->conds= where;
+ }
+ else
+ where= and_items(thd, where, cond1);
+ table->where= and_items(thd, table->where, cond1);
+ }
+ }
+
+ table->vers_conditions.type= SYSTEM_TIME_ALL;
+ } // for (table= tables; ...)
+
+ DBUG_RETURN(0);
+#undef newx
+}
+
/*****************************************************************************
Check fields, find best join, do the select and output fields.
mysql_select assumes that all tables are already opened
@@ -705,13 +1037,13 @@ JOIN::prepare(TABLE_LIST *tables_init,
select_lex= select_lex_arg;
select_lex->join= this;
join_list= &select_lex->top_join_list;
- union_part= unit_arg->is_union();
+ union_part= unit_arg->is_unit_op();
// simple check that we got usable conds
dbug_print_item(conds);
if (select_lex->handle_derived(thd->lex, DT_PREPARE))
- DBUG_RETURN(1);
+ DBUG_RETURN(-1);
thd->lex->current_select->context_analysis_place= NO_MATTER;
thd->lex->current_select->is_item_list_lookup= 1;
@@ -742,11 +1074,15 @@ JOIN::prepare(TABLE_LIST *tables_init,
{
remove_redundant_subquery_clauses(select_lex);
}
-
+
+ /* System Versioning: handle FOR SYSTEM_TIME clause. */
+ if (select_lex->vers_setup_conds(thd, tables_list) < 0)
+ DBUG_RETURN(-1);
+
/*
TRUE if the SELECT list mixes elements with and without grouping,
and there is no GROUP BY clause. Mixing non-aggregated fields with
- aggregate functions in the SELECT list is a MySQL exptenstion that
+ aggregate functions in the SELECT list is a MySQL extenstion that
is allowed only if the ONLY_FULL_GROUP_BY sql mode is not set.
*/
mixed_implicit_grouping= false;
@@ -826,7 +1162,7 @@ JOIN::prepare(TABLE_LIST *tables_init,
select_lex->master_unit()->global_parameters())
{
nesting_map save_allow_sum_func= thd->lex->allow_sum_func;
- thd->lex->allow_sum_func|= (nesting_map)1 << select_lex->nest_level;
+ thd->lex->allow_sum_func.set_bit(select_lex->nest_level);
thd->where= "order clause";
for (ORDER *order= select_lex->order_list.first; order; order= order->next)
{
@@ -844,7 +1180,7 @@ JOIN::prepare(TABLE_LIST *tables_init,
{
nesting_map save_allow_sum_func= thd->lex->allow_sum_func;
thd->where="having clause";
- thd->lex->allow_sum_func|= (nesting_map)1 << select_lex_arg->nest_level;
+ thd->lex->allow_sum_func.set_bit(select_lex_arg->nest_level);
select_lex->having_fix_field= 1;
/*
Wrap alone field in HAVING clause in case it will be outer field
@@ -854,12 +1190,10 @@ JOIN::prepare(TABLE_LIST *tables_init,
if (having->type() == Item::REF_ITEM &&
((Item_ref *)having)->ref_type() == Item_ref::REF)
wrap_ident(thd, &having);
- bool having_fix_rc= (!having->fixed &&
- (having->fix_fields(thd, &having) ||
- having->check_cols(1)));
+ bool having_fix_rc= having->fix_fields_if_needed_for_bool(thd, &having);
select_lex->having_fix_field= 0;
- if (having_fix_rc || thd->is_error())
+ if (unlikely(having_fix_rc || thd->is_error()))
DBUG_RETURN(-1); /* purecov: inspected */
thd->lex->allow_sum_func= save_allow_sum_func;
@@ -971,6 +1305,8 @@ JOIN::prepare(TABLE_LIST *tables_init,
(*ord->item)->field_type() == MYSQL_TYPE_BIT)
{
Item_field *field= new (thd->mem_root) Item_field(thd, *(Item_field**)ord->item);
+ if (!field)
+ DBUG_RETURN(-1);
int el= all_fields.elements;
ref_ptrs[el]= field;
all_fields.push_front(field, thd->mem_root);
@@ -1003,7 +1339,7 @@ JOIN::prepare(TABLE_LIST *tables_init,
}
procedure= setup_procedure(thd, proc_param, result, fields_list, &error);
- if (error)
+ if (unlikely(error))
goto err; /* purecov: inspected */
if (procedure)
{
@@ -1103,53 +1439,70 @@ err:
DBUG_RETURN(res); /* purecov: inspected */
}
-int JOIN::optimize()
+
+bool JOIN::build_explain()
{
- // to prevent double initialization on EXPLAIN
- if (optimization_state != JOIN::NOT_OPTIMIZED)
- return FALSE;
- optimization_state= JOIN::OPTIMIZATION_IN_PROGRESS;
- create_explain_query_if_not_exists(thd->lex, thd->mem_root);
+ have_query_plan= QEP_AVAILABLE;
- int res= optimize_inner();
- if (!res && have_query_plan != QEP_DELETED)
- {
- have_query_plan= QEP_AVAILABLE;
+ /*
+ explain data must be created on the Explain_query::mem_root. Because it's
+ just a memroot, not an arena, explain data must not contain any Items
+ */
+ MEM_ROOT *old_mem_root= thd->mem_root;
+ Item *old_free_list __attribute__((unused))= thd->free_list;
+ thd->mem_root= thd->lex->explain->mem_root;
+ bool res= save_explain_data(thd->lex->explain, false /* can overwrite */,
+ need_tmp,
+ !skip_sort_order && !no_order && (order || group_list),
+ select_distinct);
+ thd->mem_root= old_mem_root;
+ DBUG_ASSERT(thd->free_list == old_free_list); // no Items were created
+ if (res)
+ return 1;
- /*
- explain data must be created on the Explain_query::mem_root. Because it's
- just a memroot, not an arena, explain data must not contain any Items
- */
- MEM_ROOT *old_mem_root= thd->mem_root;
- Item *old_free_list __attribute__((unused))= thd->free_list;
- thd->mem_root= thd->lex->explain->mem_root;
- save_explain_data(thd->lex->explain, false /* can overwrite */,
- need_tmp,
- !skip_sort_order && !no_order && (order || group_list),
- select_distinct);
- thd->mem_root= old_mem_root;
- DBUG_ASSERT(thd->free_list == old_free_list); // no Items were created
-
- uint select_nr= select_lex->select_number;
- JOIN_TAB *curr_tab= join_tab + exec_join_tab_cnt();
- for (uint i= 0; i < aggr_tables; i++, curr_tab++)
+ uint select_nr= select_lex->select_number;
+ JOIN_TAB *curr_tab= join_tab + exec_join_tab_cnt();
+ for (uint i= 0; i < aggr_tables; i++, curr_tab++)
+ {
+ if (select_nr == INT_MAX)
{
- if (select_nr == INT_MAX)
- {
- /* this is a fake_select_lex of a union */
- select_nr= select_lex->master_unit()->first_select()->select_number;
- curr_tab->tracker= thd->lex->explain->get_union(select_nr)->
- get_tmptable_read_tracker();
- }
- else
- {
- curr_tab->tracker= thd->lex->explain->get_select(select_nr)->
- get_using_temporary_read_tracker();
- }
+ /* this is a fake_select_lex of a union */
+ select_nr= select_lex->master_unit()->first_select()->select_number;
+ curr_tab->tracker= thd->lex->explain->get_union(select_nr)->
+ get_tmptable_read_tracker();
+ }
+ else
+ {
+ curr_tab->tracker= thd->lex->explain->get_select(select_nr)->
+ get_using_temporary_read_tracker();
}
-
}
- optimization_state= JOIN::OPTIMIZATION_DONE;
+ return 0;
+}
+
+
+int JOIN::optimize()
+{
+ int res= 0;
+ create_explain_query_if_not_exists(thd->lex, thd->mem_root);
+ join_optimization_state init_state= optimization_state;
+ if (optimization_state == JOIN::OPTIMIZATION_PHASE_1_DONE)
+ res= optimize_stage2();
+ else
+ {
+ // to prevent double initialization on EXPLAIN
+ if (optimization_state != JOIN::NOT_OPTIMIZED)
+ return FALSE;
+ optimization_state= JOIN::OPTIMIZATION_IN_PROGRESS;
+ res= optimize_inner();
+ }
+ if (!with_two_phase_optimization ||
+ init_state == JOIN::OPTIMIZATION_PHASE_1_DONE)
+ {
+ if (!res && have_query_plan != QEP_DELETED)
+ res= build_explain();
+ optimization_state= JOIN::OPTIMIZATION_DONE;
+ }
return res;
}
@@ -1199,10 +1552,8 @@ int JOIN::init_join_caches()
int
JOIN::optimize_inner()
{
- ulonglong select_opts_for_readinfo;
- uint no_jbuf_after;
- JOIN_TAB *tab;
DBUG_ENTER("JOIN::optimize");
+ subq_exit_fl= false;
do_send_rows = (unit->select_limit_cnt) ? 1 : 0;
DEBUG_SYNC(thd, "before_join_optimize");
@@ -1226,6 +1577,11 @@ JOIN::optimize_inner()
DBUG_RETURN(TRUE);
table_count= select_lex->leaf_tables.elements;
}
+
+ if (select_lex->first_cond_optimization &&
+ transform_in_predicates_into_in_subq(thd))
+ DBUG_RETURN(1);
+
// Update used tables after all handling derived table procedures
select_lex->update_used_tables();
@@ -1336,10 +1692,10 @@ JOIN::optimize_inner()
if (optimize_constant_subqueries())
DBUG_RETURN(1);
- if (conds && conds->has_subquery())
+ if (conds && conds->with_subquery())
(void) conds->walk(&Item::cleanup_is_expensive_cache_processor,
0, (void *) 0);
- if (having && having->has_subquery())
+ if (having && having->with_subquery())
(void) having->walk(&Item::cleanup_is_expensive_cache_processor,
0, (void *) 0);
@@ -1366,7 +1722,20 @@ JOIN::optimize_inner()
}
}
- conds= optimize_cond(this, conds, join_list, FALSE,
+ bool ignore_on_expr= false;
+ /*
+ PS/SP note: on_expr of versioned table can not be reallocated
+ (see build_equal_items() below) because it can be not rebuilt
+ at second invocation.
+ */
+ if (!thd->stmt_arena->is_conventional() && thd->mem_root != thd->stmt_arena->mem_root)
+ for (TABLE_LIST *tbl= tables_list; tbl; tbl= tbl->next_local)
+ if (tbl->table && tbl->on_expr && tbl->table->versioned())
+ {
+ ignore_on_expr= true;
+ break;
+ }
+ conds= optimize_cond(this, conds, join_list, ignore_on_expr,
&cond_value, &cond_equal, OPT_LINK_EQUAL_FIELDS);
if (thd->is_error())
@@ -1388,6 +1757,11 @@ JOIN::optimize_inner()
*/
if (tbl->is_materialized_derived())
{
+ JOIN *join= tbl->get_unit()->first_select()->join;
+ if (join &&
+ join->optimization_state == JOIN::OPTIMIZATION_PHASE_1_DONE &&
+ join->with_two_phase_optimization)
+ continue;
/*
Do not push conditions from where into materialized inner tables
of outer joins: this is not valid.
@@ -1408,12 +1782,12 @@ JOIN::optimize_inner()
if (select_lex->handle_derived(thd->lex, DT_OPTIMIZE))
DBUG_RETURN(1);
}
-
+
{
having= optimize_cond(this, having, join_list, TRUE,
&having_value, &having_equal);
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
{
error= 1;
DBUG_PRINT("error",("Error from optimize_cond"));
@@ -1449,6 +1823,7 @@ JOIN::optimize_inner()
table_count= top_join_tab_count= 0;
handle_implicit_grouping_with_window_funcs();
error= 0;
+ subq_exit_fl= true;
goto setup_subq_exit;
}
}
@@ -1459,19 +1834,9 @@ JOIN::optimize_inner()
List_iterator_fast<TABLE_LIST> li(select_lex->leaf_tables);
while ((tbl= li++))
{
- /*
- If tbl->embedding!=NULL that means that this table is in the inner
- part of the nested outer join, and we can't do partition pruning
- (TODO: check if this limitation can be lifted)
- */
- if (!tbl->embedding ||
- (tbl->embedding && tbl->embedding->sj_on_expr))
- {
- Item *prune_cond= tbl->on_expr? tbl->on_expr : conds;
- tbl->table->all_partitions_pruned_away= prune_partitions(thd,
- tbl->table,
- prune_cond);
- }
+ Item **prune_cond= get_sargable_cond(this, tbl->table);
+ tbl->table->all_partitions_pruned_away=
+ prune_partitions(thd, tbl->table, *prune_cond);
}
}
#endif
@@ -1503,6 +1868,7 @@ JOIN::optimize_inner()
zero_result_cause= "No matching min/max row";
table_count= top_join_tab_count= 0;
error=0;
+ subq_exit_fl= true;
handle_implicit_grouping_with_window_funcs();
goto setup_subq_exit;
}
@@ -1547,6 +1913,7 @@ JOIN::optimize_inner()
{
DBUG_PRINT("info",("No tables"));
error= 0;
+ subq_exit_fl= true;
goto setup_subq_exit;
}
error= -1; // Error is sent to client
@@ -1563,7 +1930,7 @@ JOIN::optimize_inner()
group_list= remove_const(this, group_list, conds,
rollup.state == ROLLUP::STATE_NONE,
&simple_group);
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
{
error= 1;
DBUG_RETURN(1);
@@ -1581,13 +1948,59 @@ JOIN::optimize_inner()
/* Calculate how to do the join */
THD_STAGE_INFO(thd, stage_statistics);
result->prepare_to_read_rows();
- if (make_join_statistics(this, select_lex->leaf_tables, &keyuse) ||
- thd->is_fatal_error)
+ if (unlikely(make_join_statistics(this, select_lex->leaf_tables,
+ &keyuse)) ||
+ unlikely(thd->is_fatal_error))
{
DBUG_PRINT("error",("Error: make_join_statistics() failed"));
DBUG_RETURN(1);
}
+ /*
+ If a splittable materialized derived/view dt_i is embedded into
+ into another splittable materialized derived/view dt_o then
+ splitting plans for dt_i and dt_o are evaluated independently.
+ First the optimizer looks for the best splitting plan sp_i for dt_i.
+ It happens when non-splitting plans for dt_o are evaluated.
+ The cost of sp_i is considered as the cost of materialization of dt_i
+ when evaluating any splitting plan for dt_o.
+ */
+ if (fix_all_splittings_in_plan())
+ DBUG_RETURN(1);
+
+setup_subq_exit:
+ with_two_phase_optimization= check_two_phase_optimization(thd);
+ if (with_two_phase_optimization)
+ optimization_state= JOIN::OPTIMIZATION_PHASE_1_DONE;
+ else
+ {
+ if (optimize_stage2())
+ DBUG_RETURN(1);
+ }
+ DBUG_RETURN(0);
+}
+
+
+int JOIN::optimize_stage2()
+{
+ ulonglong select_opts_for_readinfo;
+ uint no_jbuf_after;
+ JOIN_TAB *tab;
+ DBUG_ENTER("JOIN::optimize_stage2");
+
+ if (subq_exit_fl)
+ goto setup_subq_exit;
+
+ if (unlikely(thd->check_killed()))
+ DBUG_RETURN(1);
+
+ /* Generate an execution plan from the found optimal join order. */
+ if (get_best_combination())
+ DBUG_RETURN(1);
+
+ if (select_lex->handle_derived(thd->lex, DT_OPTIMIZE))
+ DBUG_RETURN(1);
+
if (optimizer_flag(thd, OPTIMIZER_SWITCH_DERIVED_WITH_KEYS))
drop_unused_derived_keys();
@@ -1631,7 +2044,14 @@ JOIN::optimize_inner()
}
if (const_tables && !thd->locked_tables_mode &&
!(select_options & SELECT_NO_UNLOCK))
- mysql_unlock_some_tables(thd, table, const_tables);
+ {
+ /*
+ Unlock all tables, except sequences, as accessing these may still
+ require table updates
+ */
+ mysql_unlock_some_tables(thd, table, const_tables,
+ GET_LOCK_SKIP_SEQUENCES);
+ }
if (!conds && outer_join)
{
/* Handle the case where we have an OUTER JOIN without a WHERE */
@@ -1649,7 +2069,7 @@ JOIN::optimize_inner()
select= make_select(*table, const_table_map,
const_table_map, conds, (SORT_INFO*) 0, 1, &error);
- if (error)
+ if (unlikely(error))
{ /* purecov: inspected */
error= -1; /* purecov: inspected */
DBUG_PRINT("error",("Error: make_select() failed"));
@@ -1672,7 +2092,7 @@ JOIN::optimize_inner()
{
conds= substitute_for_best_equal_field(thd, NO_PARTICULAR_TAB, conds,
cond_equal, map2table);
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
{
error= 1;
DBUG_PRINT("error",("Error from substitute_for_best_equal"));
@@ -1698,7 +2118,7 @@ JOIN::optimize_inner()
*tab->on_expr_ref,
tab->cond_equal,
map2table);
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
{
error= 1;
DBUG_PRINT("error",("Error from substitute_for_best_equal"));
@@ -1728,6 +2148,9 @@ JOIN::optimize_inner()
{
ref_item= substitute_for_best_equal_field(thd, tab, ref_item,
equals, map2table);
+ if (unlikely(thd->is_fatal_error))
+ DBUG_RETURN(1);
+
if (first_inner)
{
equals= first_inner->cond_equal;
@@ -1796,7 +2219,7 @@ JOIN::optimize_inner()
{
ORDER *org_order= order;
order=remove_const(this, order,conds,1, &simple_order);
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
{
error= 1;
DBUG_RETURN(1);
@@ -1958,7 +2381,7 @@ JOIN::optimize_inner()
group_list= remove_const(this, group_list, conds,
rollup.state == ROLLUP::STATE_NONE,
&simple_group);
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
{
error= 1;
DBUG_RETURN(1);
@@ -1979,7 +2402,7 @@ JOIN::optimize_inner()
{
group_list= procedure->group= remove_const(this, procedure->group, conds,
1, &simple_group);
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
{
error= 1;
DBUG_RETURN(1);
@@ -2004,7 +2427,8 @@ JOIN::optimize_inner()
FORCE INDEX FOR ORDER BY can be used to prevent join buffering when
sorting on the first table.
*/
- if (!stable || !stable->force_index_order)
+ if (!stable || (!stable->force_index_order &&
+ !map2table[stable->tablenr]->keep_current_rowid))
{
if (group_list)
simple_group= 0;
@@ -2045,7 +2469,8 @@ JOIN::optimize_inner()
/* Perform FULLTEXT search before all regular searches */
if (!(select_options & SELECT_DESCRIBE))
- init_ftfuncs(thd, select_lex, MY_TEST(order));
+ if (init_ftfuncs(thd, select_lex, MY_TEST(order)))
+ DBUG_RETURN(1);
/*
It's necessary to check const part of HAVING cond as
@@ -2209,7 +2634,7 @@ JOIN::optimize_inner()
ordered_index_usage= ordered_index_order_by;
}
}
- }
+ }
if (having)
having_is_correlated= MY_TEST(having->used_tables() & OUTER_REF_TABLE_BIT);
@@ -2346,10 +2771,10 @@ bool JOIN::add_having_as_table_cond(JOIN_TAB *tab)
sort_table_cond)))
DBUG_RETURN(true);
}
- if (tab->select->cond && !tab->select->cond->fixed)
- tab->select->cond->fix_fields(thd, 0);
- if (tab->pre_idx_push_select_cond && !tab->pre_idx_push_select_cond->fixed)
- tab->pre_idx_push_select_cond->fix_fields(thd, 0);
+ if (tab->select->cond)
+ tab->select->cond->fix_fields_if_needed(thd, 0);
+ if (tab->pre_idx_push_select_cond)
+ tab->pre_idx_push_select_cond->fix_fields_if_needed(thd, 0);
tab->select->pre_idx_push_select_cond= tab->pre_idx_push_select_cond;
tab->set_select_cond(tab->select->cond, __LINE__);
tab->select_cond->top_level_item();
@@ -2367,6 +2792,25 @@ bool JOIN::add_having_as_table_cond(JOIN_TAB *tab)
}
+bool JOIN::add_fields_for_current_rowid(JOIN_TAB *cur, List<Item> *table_fields)
+{
+ /*
+ this will not walk into semi-join materialization nests but this is ok
+ because we will never need to save current rowids for those.
+ */
+ for (JOIN_TAB *tab=join_tab; tab < cur; tab++)
+ {
+ if (!tab->keep_current_rowid)
+ continue;
+ Item *item= new (thd->mem_root) Item_temptable_rowid(tab->table);
+ item->fix_fields(thd, 0);
+ table_fields->push_back(item, thd->mem_root);
+ cur->tmp_table_param->func_count++;
+ }
+ return 0;
+}
+
+
/**
Set info for aggregation tables
@@ -2422,20 +2866,22 @@ bool JOIN::make_aggr_tables_info()
/*
All optimization is done. Check if we can use the storage engines
- group by handler to evaluate the group by
+ group by handler to evaluate the group by.
+ Some storage engines, like spider can also do joins, group by and
+ distinct in the engine, so we do this for all queries, not only
+ GROUP BY queries.
*/
- if (tables_list && (tmp_table_param.sum_func_count || group_list) &&
- !procedure)
+ if (tables_list && top_join_tab_count && !procedure)
{
/*
At the moment we only support push down for queries where
all tables are in the same storage engine
*/
TABLE_LIST *tbl= tables_list;
- handlerton *ht= tbl && tbl->table ? tbl->table->file->ht : 0;
+ handlerton *ht= tbl && tbl->table ? tbl->table->file->partition_ht() : 0;
for (tbl= tbl->next_local; ht && tbl; tbl= tbl->next_local)
{
- if (!tbl->table || tbl->table->file->ht != ht)
+ if (!tbl->table || tbl->table->file->partition_ht() != ht)
ht= 0;
}
@@ -2448,7 +2894,8 @@ bool JOIN::make_aggr_tables_info()
if (gbh)
{
- pushdown_query= new (thd->mem_root) Pushdown_query(select_lex, gbh);
+ if (!(pushdown_query= new (thd->mem_root) Pushdown_query(select_lex, gbh)))
+ DBUG_RETURN(1);
/*
We must store rows in the tmp table if we need to do an ORDER BY
or DISTINCT and the storage handler can't handle it.
@@ -2465,17 +2912,19 @@ bool JOIN::make_aggr_tables_info()
curr_tab->ref.key= -1;
curr_tab->join= this;
- curr_tab->tmp_table_param= new TMP_TABLE_PARAM(tmp_table_param);
+ if (!(curr_tab->tmp_table_param= new TMP_TABLE_PARAM(tmp_table_param)))
+ DBUG_RETURN(1);
TABLE* table= create_tmp_table(thd, curr_tab->tmp_table_param,
all_fields,
NULL, query.distinct,
TRUE, select_options, HA_POS_ERROR,
- "", !need_tmp,
+ &empty_clex_str, !need_tmp,
query.order_by || query.group_by);
if (!table)
DBUG_RETURN(1);
- curr_tab->aggr= new (thd->mem_root) AGGR_OP(curr_tab);
+ if (!(curr_tab->aggr= new (thd->mem_root) AGGR_OP(curr_tab)))
+ DBUG_RETURN(1);
curr_tab->aggr->set_write_func(::end_send);
curr_tab->table= table;
/*
@@ -2668,13 +3117,13 @@ bool JOIN::make_aggr_tables_info()
(select_distinct && tmp_table_param.using_outer_summary_function))
{ /* Must copy to another table */
DBUG_PRINT("info",("Creating group table"));
-
+
calc_group_buffer(this, group_list);
count_field_types(select_lex, &tmp_table_param, tmp_all_fields1,
select_distinct && !group_list);
- tmp_table_param.hidden_field_count=
+ tmp_table_param.hidden_field_count=
tmp_all_fields1.elements - tmp_fields_list1.elements;
-
+
curr_tab++;
aggr_tables++;
bzero((void*)curr_tab, sizeof(JOIN_TAB));
@@ -2689,12 +3138,11 @@ bool JOIN::make_aggr_tables_info()
if (join_tab->is_using_loose_index_scan())
tmp_table_param.precomputed_group_by= TRUE;
- tmp_table_param.hidden_field_count=
+ tmp_table_param.hidden_field_count=
curr_all_fields->elements - curr_fields_list->elements;
ORDER *dummy= NULL; //TODO can use table->group here also
- if (create_postjoin_aggr_table(curr_tab,
- curr_all_fields, dummy, true,
+ if (create_postjoin_aggr_table(curr_tab, curr_all_fields, dummy, true,
distinct, keep_row_order))
DBUG_RETURN(true);
@@ -2830,7 +3278,7 @@ bool JOIN::make_aggr_tables_info()
!join_tab ||
!join_tab-> is_using_agg_loose_index_scan()))
DBUG_RETURN(true);
- if (setup_sum_funcs(thd, sum_funcs) || thd->is_fatal_error)
+ if (unlikely(setup_sum_funcs(thd, sum_funcs) || thd->is_fatal_error))
DBUG_RETURN(true);
}
if (group_list || order)
@@ -2923,13 +3371,16 @@ bool JOIN::make_aggr_tables_info()
curr_tab= join_tab + total_join_tab_cnt();
if (select_lex->window_funcs.elements)
{
- curr_tab->window_funcs_step= new Window_funcs_computation;
+ if (!(curr_tab->window_funcs_step= new Window_funcs_computation))
+ DBUG_RETURN(true);
if (curr_tab->window_funcs_step->setup(thd, &select_lex->window_funcs,
curr_tab))
DBUG_RETURN(true);
/* Count that we're using window functions. */
status_var_increment(thd->status_var.feature_window_functions);
}
+ if (select_lex->custom_agg_func_used())
+ status_var_increment(thd->status_var.feature_custom_aggregate_functions);
fields= curr_fields_list;
// Reset before execution
@@ -2962,15 +3413,18 @@ JOIN::create_postjoin_aggr_table(JOIN_TAB *tab, List<Item> *table_fields,
*/
ha_rows table_rows_limit= ((order == NULL || skip_sort_order) &&
!table_group &&
- !select_lex->with_sum_func) ?
- select_limit : HA_POS_ERROR;
+ !select_lex->with_sum_func) ? select_limit
+ : HA_POS_ERROR;
- tab->tmp_table_param= new TMP_TABLE_PARAM(tmp_table_param);
+ if (!(tab->tmp_table_param= new TMP_TABLE_PARAM(tmp_table_param)))
+ DBUG_RETURN(true);
+ if (tmp_table_keep_current_rowid)
+ add_fields_for_current_rowid(tab, table_fields);
tab->tmp_table_param->skip_create_table= true;
TABLE* table= create_tmp_table(thd, tab->tmp_table_param, *table_fields,
table_group, distinct,
save_sum_fields, select_options, table_rows_limit,
- "", true, keep_row_order);
+ &empty_clex_str, true, keep_row_order);
if (!table)
DBUG_RETURN(true);
tmp_table_param.using_outer_summary_function=
@@ -2979,8 +3433,7 @@ JOIN::create_postjoin_aggr_table(JOIN_TAB *tab, List<Item> *table_fields,
DBUG_ASSERT(tab > tab->join->join_tab || !top_join_tab_count || !tables_list);
if (tab > join_tab)
(tab - 1)->next_select= sub_select_postjoin_aggr;
- tab->aggr= new (thd->mem_root) AGGR_OP(tab);
- if (!tab->aggr)
+ if (!(tab->aggr= new (thd->mem_root) AGGR_OP(tab)))
goto err;
tab->table= table;
table->reginfo.join_tab= tab;
@@ -3133,33 +3586,42 @@ bool JOIN::setup_subquery_caches()
select_lex->expr_cache_may_be_used[IN_ON] ||
select_lex->expr_cache_may_be_used[NO_MATTER])
{
- if (conds)
- conds= conds->transform(thd, &Item::expr_cache_insert_transformer,
- NULL);
JOIN_TAB *tab;
+ if (conds &&
+ !(conds= conds->transform(thd, &Item::expr_cache_insert_transformer,
+ NULL)))
+ DBUG_RETURN(TRUE);
for (tab= first_linear_tab(this, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES);
tab; tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS))
{
- if (tab->select_cond)
- tab->select_cond=
- tab->select_cond->transform(thd, &Item::expr_cache_insert_transformer,
- NULL);
+ if (tab->select_cond &&
+ !(tab->select_cond=
+ tab->select_cond->transform(thd,
+ &Item::expr_cache_insert_transformer,
+ NULL)))
+ DBUG_RETURN(TRUE);
if (tab->cache_select && tab->cache_select->cond)
- tab->cache_select->cond=
- tab->cache_select->
- cond->transform(thd, &Item::expr_cache_insert_transformer,
- NULL);
-
+ if (!(tab->cache_select->cond=
+ tab->cache_select->
+ cond->transform(thd, &Item::expr_cache_insert_transformer,
+ NULL)))
+ DBUG_RETURN(TRUE);
}
- if (having)
- having= having->transform(thd, &Item::expr_cache_insert_transformer,
- NULL);
+ if (having &&
+ !(having= having->transform(thd,
+ &Item::expr_cache_insert_transformer,
+ NULL)))
+ DBUG_RETURN(TRUE);
+
if (tmp_having)
{
DBUG_ASSERT(having == NULL);
- tmp_having= tmp_having->transform(thd, &Item::expr_cache_insert_transformer,
- NULL);
+ if (!(tmp_having=
+ tmp_having->transform(thd,
+ &Item::expr_cache_insert_transformer,
+ NULL)))
+ DBUG_RETURN(TRUE);
}
}
if (select_lex->expr_cache_may_be_used[SELECT_LIST] ||
@@ -3170,9 +3632,11 @@ bool JOIN::setup_subquery_caches()
Item *item;
while ((item= li++))
{
- Item *new_item=
- item->transform(thd, &Item::expr_cache_insert_transformer,
- NULL);
+ Item *new_item;
+ if (!(new_item=
+ item->transform(thd, &Item::expr_cache_insert_transformer,
+ NULL)))
+ DBUG_RETURN(TRUE);
if (new_item != item)
{
thd->change_item_tree(li.ref(), new_item);
@@ -3180,18 +3644,22 @@ bool JOIN::setup_subquery_caches()
}
for (ORDER *tmp_group= group_list; tmp_group ; tmp_group= tmp_group->next)
{
- *tmp_group->item=
- (*tmp_group->item)->transform(thd, &Item::expr_cache_insert_transformer,
- NULL);
+ if (!(*tmp_group->item=
+ (*tmp_group->item)->transform(thd,
+ &Item::expr_cache_insert_transformer,
+ NULL)))
+ DBUG_RETURN(TRUE);
}
}
if (select_lex->expr_cache_may_be_used[NO_MATTER])
{
for (ORDER *ord= order; ord; ord= ord->next)
{
- *ord->item=
- (*ord->item)->transform(thd, &Item::expr_cache_insert_transformer,
- NULL);
+ if (!(*ord->item=
+ (*ord->item)->transform(thd,
+ &Item::expr_cache_insert_transformer,
+ NULL)))
+ DBUG_RETURN(TRUE);
}
}
DBUG_RETURN(FALSE);
@@ -3319,7 +3787,8 @@ JOIN::reinit()
}
if (!(select_options & SELECT_DESCRIBE))
- init_ftfuncs(thd, select_lex, MY_TEST(order));
+ if (init_ftfuncs(thd, select_lex, MY_TEST(order)))
+ DBUG_RETURN(1);
DBUG_RETURN(0);
}
@@ -3346,7 +3815,7 @@ bool JOIN::prepare_result(List<Item> **columns_list)
select_lex->handle_derived(thd->lex, DT_CREATE))
goto err;
- if (result->prepare2())
+ if (result->prepare2(this))
goto err;
if ((select_lex->options & OPTION_SCHEMA_TABLE) &&
@@ -3361,7 +3830,14 @@ err:
}
-void JOIN::save_explain_data(Explain_query *output, bool can_overwrite,
+/**
+ @retval
+ 0 ok
+ 1 error
+*/
+
+
+bool JOIN::save_explain_data(Explain_query *output, bool can_overwrite,
bool need_tmp_table, bool need_order,
bool distinct)
{
@@ -3369,7 +3845,7 @@ void 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_ASSERT(select_lex->select_number == UINT_MAX ||
+ DBUG_SLOW_ASSERT(select_lex->select_number == UINT_MAX ||
select_lex->select_number == INT_MAX ||
!output ||
!output->get_select(select_lex->select_number) ||
@@ -3391,9 +3867,8 @@ void JOIN::save_explain_data(Explain_query *output, bool can_overwrite,
/* It's a degenerate join */
message= zero_result_cause ? zero_result_cause : "No tables used";
}
- save_explain_data_intern(thd->lex->explain, need_tmp_table, need_order,
- distinct, message);
- return;
+ return save_explain_data_intern(thd->lex->explain, need_tmp_table, need_order,
+ distinct, message);
}
/*
@@ -3413,11 +3888,13 @@ void JOIN::save_explain_data(Explain_query *output, bool can_overwrite,
{
if (join_tab[i].filesort)
{
- join_tab[i].filesort->tracker=
- new Filesort_tracker(thd->lex->analyze_stmt);
+ if (!(join_tab[i].filesort->tracker=
+ new Filesort_tracker(thd->lex->analyze_stmt)))
+ return 1;
}
}
}
+ return 0;
}
@@ -3475,7 +3952,7 @@ void JOIN::exec_inner()
}
columns_list= &procedure_fields_list;
}
- if (result->prepare2())
+ if (result->prepare2(this))
DBUG_VOID_RETURN;
if (!tables_list && (table_count || !select_lex->with_sum_func) &&
@@ -3519,7 +3996,7 @@ void JOIN::exec_inner()
}
else
send_records= 0;
- if (!error)
+ if (likely(!error))
{
join_free(); // Unlock all cursors
error= (int) result->send_eof();
@@ -3545,7 +4022,7 @@ void JOIN::exec_inner()
/*
We've called exec_const_cond->val_int(). This may have caused an error.
*/
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
{
error= thd->is_error();
DBUG_VOID_RETURN;
@@ -3590,7 +4067,7 @@ void JOIN::exec_inner()
while ((cur_const_item= const_item_it++))
{
cur_const_item->val_str(); // This caches val_str() to Item::str_value
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
{
error= thd->is_error();
DBUG_VOID_RETURN;
@@ -3624,7 +4101,7 @@ void JOIN::exec_inner()
join_examined_rows= 0;
/* XXX: When can we have here thd->is_error() not zero? */
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
{
error= thd->is_error();
DBUG_VOID_RETURN;
@@ -3635,7 +4112,8 @@ void JOIN::exec_inner()
result->send_result_set_metadata(
procedure ? procedure_fields_list : *fields,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
- error= do_select(this, procedure);
+
+ error= result->view_structure_only() ? false : do_select(this, procedure);
/* Accumulate the counts from all join iterations of all join parts. */
thd->inc_examined_row_count(join_examined_rows);
DBUG_PRINT("counts", ("thd->examined_row_count: %lu",
@@ -3684,7 +4162,11 @@ JOIN::destroy()
cleanup_item_list(tmp_all_fields1);
cleanup_item_list(tmp_all_fields3);
destroy_sj_tmp_tables(this);
- delete_dynamic(&keyuse);
+ delete_dynamic(&keyuse);
+ if (save_qep)
+ delete(save_qep);
+ if (ext_keyuses_for_splitting)
+ delete(ext_keyuses_for_splitting);
delete procedure;
DBUG_RETURN(error);
}
@@ -3827,7 +4309,7 @@ mysql_select(THD *thd,
join->having_history= (join->having?join->having:join->tmp_having);
}
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
goto err;
join->exec();
@@ -3842,7 +4324,7 @@ err:
if (free_join)
{
THD_STAGE_INFO(thd, stage_end);
- err|= select_lex->cleanup();
+ err|= (int)(select_lex->cleanup());
DBUG_RETURN(err || thd->is_error());
}
DBUG_RETURN(join->error ? join->error: err);
@@ -3861,17 +4343,20 @@ static ha_rows get_quick_record_count(THD *thd, SQL_SELECT *select,
int error;
DBUG_ENTER("get_quick_record_count");
uchar buff[STACK_BUFF_ALLOC];
- if (check_stack_overrun(thd, STACK_MIN_SIZE, buff))
+ if (unlikely(check_stack_overrun(thd, STACK_MIN_SIZE, buff)))
DBUG_RETURN(0); // Fatal error flag is set
if (select)
{
select->head=table;
table->reginfo.impossible_range=0;
- if ((error= select->test_quick_select(thd, *(key_map *)keys,(table_map) 0,
- limit, 0, FALSE,
- TRUE /* remove_where_parts*/)) == 1)
+ if (likely((error=
+ select->test_quick_select(thd, *(key_map *)keys,
+ (table_map) 0,
+ limit, 0, FALSE,
+ TRUE /* remove_where_parts*/)) ==
+ 1))
DBUG_RETURN(select->quick->records);
- if (error == -1)
+ if (unlikely(error == -1))
{
table->reginfo.impossible_range=1;
DBUG_RETURN(0);
@@ -3896,6 +4381,84 @@ struct SARGABLE_PARAM
};
+/*
+ Mark all tables inside a join nest as constant.
+
+ @detail This is called when there is a local "Impossible WHERE" inside
+ a multi-table LEFT JOIN.
+*/
+
+void mark_join_nest_as_const(JOIN *join,
+ TABLE_LIST *join_nest,
+ table_map *found_const_table_map,
+ uint *const_count)
+{
+ List_iterator<TABLE_LIST> it(join_nest->nested_join->join_list);
+ TABLE_LIST *tbl;
+ while ((tbl= it++))
+ {
+ if (tbl->nested_join)
+ {
+ mark_join_nest_as_const(join, tbl, found_const_table_map, const_count);
+ continue;
+ }
+ JOIN_TAB *tab= tbl->table->reginfo.join_tab;
+
+ if (!(join->const_table_map & tab->table->map))
+ {
+ tab->type= JT_CONST;
+ tab->info= ET_IMPOSSIBLE_ON_CONDITION;
+ tab->table->const_table= 1;
+
+ join->const_table_map|= tab->table->map;
+ *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
+ }
+ }
+}
+
+
+/*
+ @brief Get the condition that can be used to do range analysis/partition
+ pruning/etc
+
+ @detail
+ Figure out which condition we can use:
+ - For INNER JOIN, we use the WHERE,
+ - "t1 LEFT JOIN t2 ON ..." uses t2's ON expression
+ - "t1 LEFT JOIN (...) ON ..." uses the join nest's ON expression.
+*/
+
+static Item **get_sargable_cond(JOIN *join, TABLE *table)
+{
+ Item **retval;
+ if (table->pos_in_table_list->on_expr)
+ {
+ /*
+ This is an inner table from a single-table LEFT JOIN, "t1 LEFT JOIN
+ t2 ON cond". Use the condition cond.
+ */
+ retval= &table->pos_in_table_list->on_expr;
+ }
+ else if (table->pos_in_table_list->embedding &&
+ !table->pos_in_table_list->embedding->sj_on_expr)
+ {
+ /*
+ This is the inner side of a multi-table outer join. Use the
+ appropriate ON expression.
+ */
+ retval= &(table->pos_in_table_list->embedding->on_expr);
+ }
+ else
+ {
+ /* The table is not inner wrt some LEFT JOIN. Use the WHERE clause */
+ retval= &join->conds;
+ }
+ return retval;
+}
+
+
/**
Calculate the best possible join and initialize the join structure.
@@ -3978,7 +4541,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
DBUG_EXECUTE_IF("bug11747970_raise_error",
{ join->thd->set_killed(KILL_QUERY_HARD); });
- if (error)
+ if (unlikely(error))
{
table->file->print_error(error, MYF(0));
goto error;
@@ -4214,6 +4777,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
keyuse->val->is_null() && keyuse->null_rejecting)
{
s->type= JT_CONST;
+ s->table->const_table= 1;
mark_as_null_row(table);
found_const_table_map|= table->map;
join->const_table_map|= table->map;
@@ -4319,6 +4883,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
s->type= JT_CONST;
join->const_table_map|=table->map;
set_position(join,const_count++,s,start_keyuse);
+ /* create_ref_for_key will set s->table->const_table */
if (create_ref_for_key(join, s, start_keyuse, FALSE,
found_const_table_map))
goto error;
@@ -4442,6 +5007,9 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
s->scan_time();
}
+ if (s->table->is_splittable())
+ s->add_keyuses_for_splitting();
+
/*
Set a max range of how many seeks we can expect when using keys
This is can't be to high as otherwise we are likely to use
@@ -4462,39 +5030,38 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
/*
Perform range analysis if there are keys it could use (1).
- Don't do range analysis if we're on the inner side of an outer join (2).
- Do range analysis if we're on the inner side of a semi-join (3).
- Don't do range analysis for materialized subqueries (4).
- Don't do range analysis for materialized derived tables (5)
+ Don't do range analysis for materialized subqueries (2).
+ Don't do range analysis for materialized derived tables (3)
*/
if ((!s->const_keys.is_clear_all() ||
!bitmap_is_clear_all(&s->table->cond_set)) && // (1)
- (!s->table->pos_in_table_list->embedding || // (2)
- (s->table->pos_in_table_list->embedding && // (3)
- s->table->pos_in_table_list->embedding->sj_on_expr)) && // (3)
- !s->table->is_filled_at_execution() && // (4)
- !(s->table->pos_in_table_list->derived && // (5)
- s->table->pos_in_table_list->is_materialized_derived())) // (5)
+ !s->table->is_filled_at_execution() && // (2)
+ !(s->table->pos_in_table_list->derived && // (3)
+ s->table->pos_in_table_list->is_materialized_derived())) // (3)
{
bool impossible_range= FALSE;
ha_rows records= HA_POS_ERROR;
SQL_SELECT *select= 0;
+ Item **sargable_cond= NULL;
if (!s->const_keys.is_clear_all())
{
+ sargable_cond= get_sargable_cond(join, s->table);
+
select= make_select(s->table, found_const_table_map,
found_const_table_map,
- *s->on_expr_ref ? *s->on_expr_ref : join->conds,
+ *sargable_cond,
(SORT_INFO*) 0,
1, &error);
if (!select)
goto error;
records= get_quick_record_count(join->thd, select, s->table,
&s->const_keys, join->row_limit);
- /* Range analyzer could modify the condition. */
- if (*s->on_expr_ref)
- *s->on_expr_ref= select->cond;
- else
- join->conds= select->cond;
+
+ /*
+ Range analyzer might have modified the condition. Put it the new
+ condition to where we got it from.
+ */
+ *sargable_cond= select->cond;
s->quick=select->quick;
s->needed_reg=select->needed_reg;
@@ -4503,10 +5070,11 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
}
if (!impossible_range)
{
+ if (!sargable_cond)
+ sargable_cond= get_sargable_cond(join, s->table);
if (join->thd->variables.optimizer_use_condition_selectivity > 1)
calculate_cond_selectivity_for_table(join->thd, s->table,
- *s->on_expr_ref ?
- s->on_expr_ref : &join->conds);
+ sargable_cond);
if (s->table->reginfo.impossible_range)
{
impossible_range= TRUE;
@@ -4515,23 +5083,33 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
}
if (impossible_range)
{
- /*
- Impossible WHERE or ON expression
- In case of ON, we mark that the we match one empty NULL row.
- In case of WHERE, don't set found_const_table_map to get the
- caller to abort with a zero row result.
- */
- join->const_table_map|= s->table->map;
- set_position(join,const_count++,s,(KEYUSE*) 0);
- s->type= JT_CONST;
- if (*s->on_expr_ref)
- {
- /* Generate empty row */
- s->info= ET_IMPOSSIBLE_ON_CONDITION;
- found_const_table_map|= s->table->map;
- s->type= JT_CONST;
- mark_as_null_row(s->table); // All fields are NULL
- }
+ /*
+ Impossible WHERE or ON expression
+ In case of ON, we mark that the we match one empty NULL row.
+ In case of WHERE, don't set found_const_table_map to get the
+ caller to abort with a zero row result.
+ */
+ TABLE_LIST *emb= s->table->pos_in_table_list->embedding;
+ if (emb && !emb->sj_on_expr)
+ {
+ /* Mark all tables in a multi-table join nest as const */
+ mark_join_nest_as_const(join, emb, &found_const_table_map,
+ &const_count);
+ }
+ else
+ {
+ join->const_table_map|= s->table->map;
+ set_position(join,const_count++,s,(KEYUSE*) 0);
+ s->type= JT_CONST;
+ s->table->const_table= 1;
+ if (*s->on_expr_ref)
+ {
+ /* Generate empty row */
+ s->info= ET_IMPOSSIBLE_ON_CONDITION;
+ found_const_table_map|= s->table->map;
+ mark_as_null_row(s->table); // All fields are NULL
+ }
+ }
}
if (records != HA_POS_ERROR)
{
@@ -4619,8 +5197,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
DEBUG_SYNC(join->thd, "inside_make_join_statistics");
- /* Generate an execution plan from the found optimal join order. */
- DBUG_RETURN(join->thd->check_killed() || join->get_best_combination());
+ DBUG_RETURN(0);
error:
/*
@@ -4647,23 +5224,6 @@ error:
keyuse Pointer to possible keys
*****************************************************************************/
-/// Used when finding key fields
-struct KEY_FIELD {
- Field *field;
- Item_bool_func *cond;
- Item *val; ///< May be empty if diff constant
- uint level;
- uint optimize;
- bool eq_func;
- /**
- If true, the condition this struct represents will not be satisfied
- when val IS NULL.
- */
- bool null_rejecting;
- bool *cond_guard; /* See KEYUSE::cond_guard */
- uint sj_pred_no; /* See KEYUSE::sj_pred_no */
-};
-
/**
Merge new key definitions to old ones, remove those not used in both.
@@ -5029,18 +5589,16 @@ add_key_field(JOIN *join,
(*key_fields)->level= and_level;
(*key_fields)->optimize= optimize;
/*
- If the condition has form "tbl.keypart = othertbl.field" and
- othertbl.field can be NULL, there will be no matches if othertbl.field
- has NULL value.
- We use null_rejecting in add_not_null_conds() to add
- 'othertbl.field IS NOT NULL' to tab->select_cond.
+ If the condition we are analyzing is NULL-rejecting and at least
+ one side of the equalities is NULLable, mark the KEY_FIELD object as
+ null-rejecting. This property is used by:
+ - add_not_null_conds() to add "column IS NOT NULL" conditions
+ - best_access_path() to produce better estimates for NULL-able unique keys.
*/
{
- Item *real= (*value)->real_item();
- if (((cond->functype() == Item_func::EQ_FUNC) ||
- (cond->functype() == Item_func::MULT_EQUAL_FUNC)) &&
- (real->type() == Item::FIELD_ITEM) &&
- ((Item_field*)real)->field->maybe_null())
+ if ((cond->functype() == Item_func::EQ_FUNC ||
+ cond->functype() == Item_func::MULT_EQUAL_FUNC) &&
+ ((*value)->maybe_null || field->real_maybe_null()))
(*key_fields)->null_rejecting= true;
else
(*key_fields)->null_rejecting= false;
@@ -5202,7 +5760,7 @@ Item_func_trig_cond::add_key_fields(JOIN *join, KEY_FIELD **key_fields,
if (!join->group_list && !join->order &&
join->unit->item &&
join->unit->item->substype() == Item_subselect::IN_SUBS &&
- !join->unit->is_union())
+ !join->unit->is_unit_op())
{
KEY_FIELD *save= *key_fields;
args[0]->add_key_fields(join, key_fields, and_level, usable_tables,
@@ -5314,7 +5872,7 @@ Item_func_ne::add_key_fields(JOIN *join, KEY_FIELD **key_fields,
/*
QQ: perhaps test for !is_local_field(args[1]) is not really needed here.
Other comparison functions, e.g. Item_func_le, Item_func_gt, etc,
- do not have this test. See Item_bool_func2::add_key_field_optimize_op().
+ do not have this test. See Item_bool_func2::add_key_fieldoptimize_op().
Check with the optimizer team.
*/
if (is_local_field(args[0]) && !is_local_field(args[1]))
@@ -5497,6 +6055,7 @@ add_keyuse(DYNAMIC_ARRAY *keyuse_array, KEY_FIELD *key_field,
keyuse.null_rejecting= key_field->null_rejecting;
keyuse.cond_guard= key_field->cond_guard;
keyuse.sj_pred_no= key_field->sj_pred_no;
+ keyuse.validity_ref= 0;
return (insert_dynamic(keyuse_array,(uchar*) &keyuse));
}
@@ -5542,7 +6101,9 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array, KEY_FIELD *key_field)
key_field->val->used_tables())
{
if (!field->can_optimize_hash_join(key_field->cond, key_field->val))
- return false;
+ return false;
+ if (form->is_splittable())
+ form->add_splitting_info_for_key_field(key_field);
/*
If a key use is extracted from an equi-join predicate then it is
added not only as a key use for every index whose component can
@@ -5556,7 +6117,6 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array, KEY_FIELD *key_field)
return FALSE;
}
-
static bool
add_ft_keys(DYNAMIC_ARRAY *keyuse_array,
JOIN_TAB *stat,COND *cond,table_map usable_tables)
@@ -5616,8 +6176,11 @@ add_ft_keys(DYNAMIC_ARRAY *keyuse_array,
keyuse.keypart= FT_KEYPART;
keyuse.used_tables=cond_func->key_item()->used_tables();
keyuse.optimize= 0;
+ keyuse.ref_table_rows= 0;
keyuse.keypart_map= 0;
keyuse.sj_pred_no= UINT_MAX;
+ keyuse.validity_ref= 0;
+ keyuse.null_rejecting= FALSE;
return insert_dynamic(keyuse_array,(uchar*) &keyuse);
}
@@ -5905,8 +6468,8 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab,
Special treatment for ft-keys.
*/
-static bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse,
- bool skip_unprefixed_keyparts)
+bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse,
+ bool skip_unprefixed_keyparts)
{
KEYUSE key_end, *prev, *save_pos, *use;
uint found_eq_constant, i;
@@ -5974,7 +6537,7 @@ static bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse,
Update some values in keyuse for faster choose_plan() loop.
*/
-static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array)
+void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array)
{
KEYUSE *end,*keyuse= dynamic_element(keyuse_array, 0, KEYUSE*);
@@ -6016,7 +6579,6 @@ static 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.
@@ -6212,6 +6774,7 @@ void set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key)
next=tmp;
}
join->best_ref[idx]=table;
+ join->positions[idx].spl_plan= 0;
}
@@ -6325,6 +6888,7 @@ best_access_path(JOIN *join,
bool best_uses_jbuf= FALSE;
MY_BITMAP *eq_join_set= &s->table->eq_join_set;
KEYUSE *hj_start_key= 0;
+ SplM_plan_info *spl_plan= 0;
disable_jbuf= disable_jbuf || idx == join->const_tables;
@@ -6334,7 +6898,10 @@ best_access_path(JOIN *join,
bitmap_clear_all(eq_join_set);
loose_scan_opt.init(join, s, remaining_tables);
-
+
+ if (s->table->is_splittable())
+ spl_plan= s->choose_best_splitting(record_count, remaining_tables);
+
if (s->keyuse)
{ /* Use key if possible */
KEYUSE *keyuse;
@@ -6351,6 +6918,7 @@ best_access_path(JOIN *join,
ulong key_flags;
uint key_parts;
key_part_map found_part= 0;
+ key_part_map notnull_part=0; // key parts which won't have NULL in lookup tuple.
table_map found_ref= 0;
uint key= keyuse->key;
bool ft_key= (keyuse->keypart == FT_KEYPART);
@@ -6384,7 +6952,7 @@ best_access_path(JOIN *join,
loose_scan_opt.next_ref_key();
DBUG_PRINT("info", ("Considering ref access on key %s",
- keyuse->table->key_info[keyuse->key].name));
+ keyuse->table->key_info[keyuse->key].name.str));
do /* For each keypart */
{
@@ -6399,6 +6967,7 @@ best_access_path(JOIN *join,
2. we won't get two ref-or-null's
*/
if (!(remaining_tables & keyuse->used_tables) &&
+ (!keyuse->validity_ref || *keyuse->validity_ref) &&
s->access_from_tables_is_allowed(keyuse->used_tables,
join->sjm_lookup_tables) &&
!(ref_or_null_part && (keyuse->optimize &
@@ -6408,6 +6977,9 @@ best_access_path(JOIN *join,
if (!(keyuse->used_tables & ~join->const_table_map))
const_part|= keyuse->keypart_map;
+ if (!keyuse->val->maybe_null || keyuse->null_rejecting)
+ notnull_part|=keyuse->keypart_map;
+
double tmp2= prev_record_reads(join_positions, idx,
(found_ref | keyuse->used_tables));
if (tmp2 < best_prev_record_reads)
@@ -6458,12 +7030,19 @@ best_access_path(JOIN *join,
loose_scan_opt.check_ref_access_part1(s, key, start_key, found_part);
/* Check if we found full key */
- if (found_part == PREV_BITS(uint, key_parts) &&
- !ref_or_null_part)
+ const key_part_map all_key_parts= PREV_BITS(uint, key_parts);
+ if (found_part == all_key_parts && !ref_or_null_part)
{ /* use eq key */
max_key_part= (uint) ~0;
- if ((key_flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME ||
- MY_TEST(key_flags & HA_EXT_NOSAME))
+ /*
+ If the index is a unique index (1), and
+ - all its columns are not null (2), or
+ - equalities we are using reject NULLs (3)
+ then the estimate is rows=1.
+ */
+ if ((key_flags & (HA_NOSAME | HA_EXT_NOSAME)) && // (1)
+ (!(key_flags & HA_NULL_PART_KEY) || // (2)
+ all_key_parts == notnull_part)) // (3)
{
tmp = prev_record_reads(join_positions, idx, found_ref);
records=1.0;
@@ -6712,6 +7291,7 @@ best_access_path(JOIN *join,
loose_scan_opt.check_ref_access_part2(key, start_key, records, tmp,
found_ref);
} /* not ft_key */
+
if (tmp + 0.0001 < best_time - records/(double) TIME_FOR_COMPARE)
{
best_time= COST_ADD(tmp, records/(double) TIME_FOR_COMPARE);
@@ -6872,7 +7452,11 @@ best_access_path(JOIN *join,
}
}
- tmp += s->startup_cost;
+ /* Splitting technique cannot be used with join cache */
+ if (s->table->is_splittable())
+ tmp+= s->table->get_materialization_cost();
+ else
+ tmp+= s->startup_cost;
/*
We estimate the cost of evaluating WHERE clause for found records
as record_count * rnd_records / TIME_FOR_COMPARE. This cost plus
@@ -6894,6 +7478,7 @@ best_access_path(JOIN *join,
best_ref_depends_map= 0;
best_uses_jbuf= MY_TEST(!disable_jbuf && !((s->table->map &
join->outer_join)));
+ spl_plan= 0;
}
}
@@ -6905,6 +7490,7 @@ best_access_path(JOIN *join,
pos->ref_depend_map= best_ref_depends_map;
pos->loosescan_picker.loosescan_key= MAX_KEY;
pos->use_join_buffer= best_uses_jbuf;
+ pos->spl_plan= spl_plan;
loose_scan_opt.save_to_position(s, loose_scan_pos);
@@ -7199,7 +7785,7 @@ static int compare_embedding_subqueries(JOIN_TAB *jt1, JOIN_TAB *jt2)
b: dependent = 0x0 table->map = 0x2 found_records = 3 ptr = 0x907e838
c: dependent = 0x6 table->map = 0x10 found_records = 2 ptr = 0x907ecd0
- As for subuqueries, this function must produce order that can be fed to
+ As for subqueries, this function must produce order that can be fed to
choose_initial_table_order().
@retval
@@ -7539,7 +8125,7 @@ greedy_search(JOIN *join,
'best_read < DBL_MAX' means that optimizer managed to find
some plan and updated 'best_positions' array accordingly.
*/
- DBUG_ASSERT(join->best_read < DBL_MAX);
+ DBUG_ASSERT(join->best_read < DBL_MAX);
if (size_remain <= search_depth)
{
@@ -7743,6 +8329,7 @@ void JOIN::get_prefix_cost_and_fanout(uint n_tables,
record_count= COST_MULT(record_count, best_positions[i].records_read);
read_time= COST_ADD(read_time, best_positions[i].read_time);
}
+ /* TODO: Take into account condition selectivities here */
}
*read_time_arg= read_time;// + record_count / TIME_FOR_COMPARE;
*record_count_arg= record_count;
@@ -7988,7 +8575,7 @@ 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))
+ if (!is_hash_join_key_no(key) && table->quick_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] &&
@@ -8279,7 +8866,7 @@ best_extension_by_limited_search(JOIN *join,
dbug_serve_apcs(thd, 1);
);
- if (thd->check_killed()) // Abort
+ if (unlikely(thd->check_killed())) // Abort
DBUG_RETURN(TRUE);
DBUG_EXECUTE("opt", print_plan(join, idx, read_time, record_count, idx,
@@ -8319,8 +8906,7 @@ best_extension_by_limited_search(JOIN *join,
/* Find the best access method from 's' to the current partial plan */
POSITION loose_scan_pos;
best_access_path(join, s, remaining_tables, join->positions, idx,
- disable_jbuf, record_count, join->positions + idx,
- &loose_scan_pos);
+ disable_jbuf, record_count, position, &loose_scan_pos);
/* Compute the cost of extending the plan with 's' */
current_record_count= COST_MULT(record_count, position->records_read);
@@ -8992,6 +9578,49 @@ JOIN_TAB *next_depth_first_tab(JOIN* join, JOIN_TAB* tab)
}
+bool JOIN::check_two_phase_optimization(THD *thd)
+{
+ if (check_for_splittable_materialized())
+ return true;
+ return false;
+}
+
+
+bool JOIN::inject_cond_into_where(Item *injected_cond)
+{
+ Item *where_item= injected_cond;
+ List<Item> *and_args= NULL;
+ if (conds && conds->type() == Item::COND_ITEM &&
+ ((Item_cond*) conds)->functype() == Item_func::COND_AND_FUNC)
+ {
+ and_args= ((Item_cond*) conds)->argument_list();
+ if (cond_equal)
+ and_args->disjoin((List<Item> *) &cond_equal->current_level);
+ }
+
+ where_item= and_items(thd, conds, where_item);
+ if (where_item->fix_fields_if_needed(thd, 0))
+ return true;
+ thd->change_item_tree(&select_lex->where, where_item);
+ select_lex->where->top_level_item();
+ conds= select_lex->where;
+
+ if (and_args && cond_equal)
+ {
+ and_args= ((Item_cond*) conds)->argument_list();
+ List_iterator<Item_equal> li(cond_equal->current_level);
+ Item_equal *elem;
+ while ((elem= li++))
+ {
+ and_args->push_back(elem, thd->mem_root);
+ }
+ }
+
+ return false;
+
+}
+
+
static Item * const null_ptr= NULL;
/*
@@ -9040,7 +9669,7 @@ bool JOIN::get_best_combination()
*/
uint aggr_tables= (group_list ? 1 : 0) +
(select_distinct ?
- (tmp_table_param. using_outer_summary_function ? 2 : 1) : 0) +
+ (tmp_table_param.using_outer_summary_function ? 2 : 1) : 0) +
(order ? 1 : 0) +
(select_options & (SELECT_BIG_RESULT | OPTION_BUFFER_RESULT) ? 1 : 0) ;
@@ -9273,7 +9902,8 @@ static bool create_hj_key_for_table(JOIN *join, JOIN_TAB *join_tab,
keyinfo->algorithm= HA_KEY_ALG_UNDEF;
keyinfo->flags= HA_GENERATED_KEY;
keyinfo->is_statistics_from_stat_tables= FALSE;
- keyinfo->name= (char *) "$hj";
+ keyinfo->name.str= "$hj";
+ keyinfo->name.length= 3;
keyinfo->rec_per_key= (ulong*) thd->calloc(sizeof(ulong)*key_parts);
if (!keyinfo->rec_per_key)
DBUG_RETURN(TRUE);
@@ -9398,6 +10028,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j,
do
{
if (!(~used_tables & keyuse->used_tables) &&
+ (!keyuse->validity_ref || *keyuse->validity_ref) &&
j->keyuse_is_valid_for_access_in_chosen_plan(join, keyuse))
{
if (are_tables_local(j, keyuse->val->used_tables()))
@@ -9452,6 +10083,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j,
uchar *key_buff=j->ref.key_buff, *null_ref_key= 0;
uint null_ref_part= NO_REF_PART;
bool keyuse_uses_no_tables= TRUE;
+ uint not_null_keyparts= 0;
if (ftkey)
{
j->ref.items[0]=((Item_func*)(keyuse->val))->key_item();
@@ -9468,6 +10100,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j,
for (i=0 ; i < keyparts ; keyuse++,i++)
{
while (((~used_tables) & keyuse->used_tables) ||
+ (keyuse->validity_ref && !(*keyuse->validity_ref)) ||
!j->keyuse_is_valid_for_access_in_chosen_plan(join, keyuse) ||
keyuse->keypart == NO_KEYPART ||
(keyuse->keypart !=
@@ -9479,35 +10112,49 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j,
uint maybe_null= MY_TEST(keyinfo->key_part[i].null_bit);
j->ref.items[i]=keyuse->val; // Save for cond removal
j->ref.cond_guards[i]= keyuse->cond_guard;
- if (keyuse->null_rejecting)
+
+ if (!keyuse->val->maybe_null || keyuse->null_rejecting)
+ not_null_keyparts++;
+ /*
+ Set ref.null_rejecting to true only if we are going to inject a
+ "keyuse->val IS NOT NULL" predicate.
+ */
+ Item *real= (keyuse->val)->real_item();
+ if (keyuse->null_rejecting && (real->type() == Item::FIELD_ITEM) &&
+ ((Item_field*)real)->field->maybe_null())
j->ref.null_rejecting|= (key_part_map)1 << i;
+
keyuse_uses_no_tables= keyuse_uses_no_tables && !keyuse->used_tables;
/*
- Todo: we should remove this check for thd->lex->describe on the next
- line. With SHOW EXPLAIN code, EXPLAIN printout code no longer depends
- on it. However, removing the check caused change in lots of query
- plans! Does the optimizer depend on the contents of
- table_ref->key_copy ? If yes, do we produce incorrect EXPLAINs?
+ We don't want to compute heavy expressions in EXPLAIN, an example would
+ select * from t1 where t1.key=(select thats very heavy);
+
+ (select thats very heavy) => is a constant here
+ eg: (select avg(order_cost) from orders) => constant but expensive
*/
if (!keyuse->val->used_tables() && !thd->lex->describe)
{ // Compare against constant
- store_key_item tmp(thd,
+ store_key_item tmp(thd,
keyinfo->key_part[i].field,
key_buff + maybe_null,
maybe_null ? key_buff : 0,
keyinfo->key_part[i].length,
keyuse->val,
FALSE);
- if (thd->is_fatal_error)
- DBUG_RETURN(TRUE);
- tmp.copy();
+ if (unlikely(thd->is_fatal_error))
+ DBUG_RETURN(TRUE);
+ tmp.copy();
j->ref.const_ref_part_map |= key_part_map(1) << i ;
}
else
- *ref_key++= get_store_key(thd,
- keyuse,join->const_table_map,
- &keyinfo->key_part[i],
- key_buff, maybe_null);
+ {
+ *ref_key++= get_store_key(thd,
+ keyuse,join->const_table_map,
+ &keyinfo->key_part[i],
+ key_buff, maybe_null);
+ if (!keyuse->val->used_tables())
+ j->ref.const_ref_part_map |= key_part_map(1) << i ;
+ }
/*
Remember if we are going to use REF_OR_NULL
But only if field _really_ can be null i.e. we force JT_REF
@@ -9527,12 +10174,18 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j,
ulong key_flags= j->table->actual_key_flags(keyinfo);
if (j->type == JT_CONST)
j->table->const_table= 1;
- else if (!((keyparts == keyinfo->user_defined_key_parts &&
- ((key_flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME)) ||
- (keyparts > keyinfo->user_defined_key_parts && // true only for extended keys
- MY_TEST(key_flags & HA_EXT_NOSAME) &&
- keyparts == keyinfo->ext_key_parts)) ||
- null_ref_key)
+ else if (!((keyparts == keyinfo->user_defined_key_parts &&
+ (
+ (key_flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME ||
+ /* Unique key and all keyparts are NULL rejecting */
+ ((key_flags & HA_NOSAME) && keyparts == not_null_keyparts)
+ )) ||
+ /* true only for extended keys */
+ (keyparts > keyinfo->user_defined_key_parts &&
+ MY_TEST(key_flags & HA_EXT_NOSAME) &&
+ keyparts == keyinfo->ext_key_parts)
+ ) ||
+ null_ref_key)
{
/* Must read with repeat */
j->type= null_ref_key ? JT_REF_OR_NULL : JT_REF;
@@ -9920,6 +10573,74 @@ make_outerjoin_info(JOIN *join)
}
+/*
+ @brief
+ Build a temporary join prefix condition for JOIN_TABs up to the last tab
+
+ @param ret OUT the condition is returned here
+
+ @return
+ false OK
+ true Out of memory
+
+ @detail
+ Walk through the join prefix (from the first table to the last_tab) and
+ build a condition:
+
+ join_tab_1_cond AND join_tab_2_cond AND ... AND last_tab_conds
+
+ The condition is only intended to be used by the range optimizer, so:
+ - it is not normalized (can have Item_cond_and inside another
+ Item_cond_and)
+ - it does not include join->exec_const_cond and other similar conditions.
+*/
+
+bool build_tmp_join_prefix_cond(JOIN *join, JOIN_TAB *last_tab, Item **ret)
+{
+ THD *const thd= join->thd;
+ Item_cond_and *all_conds= NULL;
+
+ Item *res= NULL;
+
+ // Pick the ON-expression. Use the same logic as in get_sargable_cond():
+ if (last_tab->on_expr_ref)
+ res= *last_tab->on_expr_ref;
+ else if (last_tab->table->pos_in_table_list &&
+ last_tab->table->pos_in_table_list->embedding &&
+ !last_tab->table->pos_in_table_list->embedding->sj_on_expr)
+ {
+ res= last_tab->table->pos_in_table_list->embedding->on_expr;
+ }
+
+ for (JOIN_TAB *tab= first_depth_first_tab(join);
+ tab;
+ tab= next_depth_first_tab(join, tab))
+ {
+ if (tab->select_cond)
+ {
+ if (!res)
+ res= tab->select_cond;
+ else
+ {
+ if (!all_conds)
+ {
+ if (!(all_conds= new (thd->mem_root)Item_cond_and(thd, res,
+ tab->select_cond)))
+ return true;
+ res= all_conds;
+ }
+ else
+ all_conds->add(tab->select_cond, thd->mem_root);
+ }
+ }
+ if (tab == last_tab)
+ break;
+ }
+ *ret= all_conds? all_conds: res;
+ return false;
+}
+
+
static bool
make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
{
@@ -10267,7 +10988,9 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
{
/* Join with outer join condition */
COND *orig_cond=sel->cond;
- sel->cond= and_conds(thd, sel->cond, *tab->on_expr_ref);
+
+ if (build_tmp_join_prefix_cond(join, tab, &sel->cond))
+ return true;
/*
We can't call sel->cond->fix_fields,
@@ -10343,6 +11066,13 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
if (i != join->const_tables && tab->use_quick != 2 &&
!tab->first_inner)
{ /* Read with cache */
+ /*
+ TODO: the execution also gets here when we will not be using
+ join buffer. Review these cases and perhaps, remove this call.
+ (The final decision whether to use join buffer is made in
+ check_join_cache_usage, so we should only call make_scan_filter()
+ there, too).
+ */
if (tab->make_scan_filter())
DBUG_RETURN(1);
}
@@ -10885,32 +11615,32 @@ pick_table_access_method(JOIN_TAB *tab)
{
case JT_REF:
tab->read_first_record= join_read_always_key;
- tab->read_record.read_record= join_read_next_same;
+ tab->read_record.read_record_func= join_read_next_same;
break;
case JT_REF_OR_NULL:
tab->read_first_record= join_read_always_key_or_null;
- tab->read_record.read_record= join_read_next_same_or_null;
+ tab->read_record.read_record_func= join_read_next_same_or_null;
break;
case JT_CONST:
tab->read_first_record= join_read_const;
- tab->read_record.read_record= join_no_more_records;
+ tab->read_record.read_record_func= join_no_more_records;
break;
case JT_EQ_REF:
tab->read_first_record= join_read_key;
- tab->read_record.read_record= join_no_more_records;
+ tab->read_record.read_record_func= join_no_more_records;
break;
case JT_FT:
tab->read_first_record= join_ft_read_first;
- tab->read_record.read_record= join_ft_read_next;
+ tab->read_record.read_record_func= join_ft_read_next;
break;
case JT_SYSTEM:
tab->read_first_record= join_read_system;
- tab->read_record.read_record= join_no_more_records;
+ tab->read_record.read_record_func= join_no_more_records;
break;
/* keep gcc happy */
@@ -11009,9 +11739,9 @@ end_sj_materialize(JOIN *join, JOIN_TAB *join_tab, bool end_of_records)
DBUG_RETURN(NESTED_LOOP_OK);
}
fill_record(thd, table, table->field, sjm->sjm_table_cols, TRUE, FALSE);
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
- if ((error= table->file->ha_write_tmp_row(table->record[0])))
+ if (unlikely((error= table->file->ha_write_tmp_row(table->record[0]))))
{
/* create_myisam_from_heap will generate error if needed */
if (table->file->is_fatal_error(error, HA_CHECK_DUP) &&
@@ -11305,6 +12035,9 @@ uint check_join_cache_usage(JOIN_TAB *tab,
if ((tab->cache= new (root) JOIN_CACHE_BNL(join, tab, prev_cache)))
{
tab->icp_other_tables_ok= FALSE;
+ /* If make_join_select() hasn't called make_scan_filter(), do it now */
+ if (!tab->cache_select && tab->make_scan_filter())
+ goto no_join_cache;
return (2 - MY_TEST(!prev_cache));
}
goto no_join_cache;
@@ -11959,7 +12692,10 @@ void JOIN_TAB::cleanup()
if (table)
{
table->file->ha_end_keyread();
- table->file->ha_index_or_rnd_end();
+ if (type == JT_FT)
+ table->file->ha_ft_end();
+ else
+ table->file->ha_index_or_rnd_end();
preread_init_done= FALSE;
if (table->pos_in_table_list &&
table->pos_in_table_list->jtbm_subselect)
@@ -12102,12 +12838,15 @@ bool JOIN_TAB::preread_init()
/* Materialize derived table/view. */
if ((!derived->get_unit()->executed ||
- derived->is_recursive_with_table()) &&
+ derived->is_recursive_with_table() ||
+ derived->get_unit()->uncacheable) &&
mysql_handle_single_derived(join->thd->lex,
derived, DT_CREATE | DT_FILL))
return TRUE;
- preread_init_done= TRUE;
+ if (!(derived->get_unit()->uncacheable & UNCACHEABLE_DEPENDENT) ||
+ derived->is_nonrecursive_derived_with_rec_ref())
+ preread_init_done= TRUE;
if (select && select->quick)
select->quick->replace_handler(table->file);
@@ -12120,7 +12859,8 @@ bool JOIN_TAB::preread_init()
/* init ftfuns for just initialized derived table */
if (table->fulltext_searched)
- init_ftfuncs(join->thd, join->select_lex, MY_TEST(join->order));
+ if (init_ftfuncs(join->thd, join->select_lex, MY_TEST(join->order)))
+ return TRUE;
return FALSE;
}
@@ -12680,7 +13420,7 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond,
*simple_order=0; // Must do a temp table to sort
else if (!(order_tables & not_const_tables))
{
- if (order->item[0]->has_subquery())
+ if (order->item[0]->with_subquery())
{
/*
Delay the evaluation of constant ORDER and/or GROUP expressions that
@@ -12808,7 +13548,7 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond,
if (prev_ptr == &first_order) // Nothing to sort/group
*simple_order=1;
#ifndef DBUG_OFF
- if (join->thd->is_error())
+ if (unlikely(join->thd->is_error()))
DBUG_PRINT("error",("Error from remove_const"));
#endif
DBUG_PRINT("exit",("simple_order: %d",(int) *simple_order));
@@ -12914,7 +13654,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;
- if (!send_error)
+ if (likely(!send_error))
result->send_eof(); // Should be safe
}
DBUG_RETURN(0);
@@ -12955,6 +13695,8 @@ public:
size_t size __attribute__((unused)))
{ TRASH_FREE(ptr, size); }
+ static void operator delete(void *, MEM_ROOT*) {}
+
Item *and_level;
Item_bool_func2 *cmp_func;
COND_CMP(Item *a,Item_bool_func2 *b) :and_level(a),cmp_func(b) {}
@@ -13186,10 +13928,15 @@ static bool check_simple_equality(THD *thd, const Item::Context &ctx,
else
{
/* None of the fields was found in multiple equalities */
- Item_equal *item_equal= new (thd->mem_root) Item_equal(thd,
- orig_left_item,
- orig_right_item,
- FALSE);
+ Type_handler_hybrid_field_type
+ tmp(orig_left_item->type_handler_for_comparison());
+ if (tmp.aggregate_for_comparison(orig_right_item->
+ type_handler_for_comparison()))
+ return false;
+ Item_equal *item_equal=
+ new (thd->mem_root) Item_equal(thd, tmp.type_handler(),
+ orig_left_item, orig_right_item,
+ false);
item_equal->set_context_field((Item_field*)left_item);
cond_equal->current_level.push_back(item_equal, thd->mem_root);
}
@@ -13274,8 +14021,14 @@ static bool check_simple_equality(THD *thd, const Item::Context &ctx,
}
else
{
- item_equal= new (thd->mem_root) Item_equal(thd, const_item2,
- orig_field_item, TRUE);
+ Type_handler_hybrid_field_type
+ tmp(orig_left_item->type_handler_for_comparison());
+ if (tmp.aggregate_for_comparison(orig_right_item->
+ type_handler_for_comparison()))
+ return false;
+ item_equal= new (thd->mem_root) Item_equal(thd, tmp.type_handler(),
+ const_item2,
+ orig_field_item, true);
item_equal->set_context_field(field_item);
cond_equal->current_level.push_back(item_equal, thd->mem_root);
}
@@ -13325,6 +14078,14 @@ static bool check_row_equality(THD *thd, const Arg_comparator *comparators,
if (left_item->type() == Item::ROW_ITEM &&
right_item->type() == Item::ROW_ITEM)
{
+ /*
+ Item_splocal for ROW SP variables return Item::ROW_ITEM.
+ Here we know that left_item and right_item are not Item_splocal,
+ because ROW SP variables with nested ROWs are not supported yet.
+ It's safe to cast left_item and right_item to Item_row.
+ */
+ DBUG_ASSERT(!left_item->get_item_splocal());
+ DBUG_ASSERT(!right_item->get_item_splocal());
is_converted= check_row_equality(thd,
comparators[i].subcomparators(),
(Item_row *) left_item,
@@ -13336,7 +14097,7 @@ static bool check_row_equality(THD *thd, const Arg_comparator *comparators,
const Arg_comparator *tmp= &comparators[i];
is_converted= check_simple_equality(thd,
Item::Context(Item::ANY_SUBST,
- tmp->compare_type(),
+ tmp->compare_type_handler(),
tmp->compare_collation()),
left_item, right_item,
cond_equal);
@@ -13395,6 +14156,15 @@ bool Item_func_eq::check_equality(THD *thd, COND_EQUAL *cond_equal,
if (left_item->type() == Item::ROW_ITEM &&
right_item->type() == Item::ROW_ITEM)
{
+ /*
+ Item_splocal::type() for ROW variables returns Item::ROW_ITEM.
+ Distinguish ROW-type Item_splocal from Item_row.
+ Example query:
+ SELECT 1 FROM DUAL WHERE row_sp_variable=ROW(100,200);
+ */
+ if (left_item->get_item_splocal() ||
+ right_item->get_item_splocal())
+ return false;
return check_row_equality(thd,
cmp.subcomparators(),
(Item_row *) left_item,
@@ -13403,7 +14173,7 @@ bool Item_func_eq::check_equality(THD *thd, COND_EQUAL *cond_equal,
}
return check_simple_equality(thd,
Context(ANY_SUBST,
- compare_type(),
+ compare_type_handler(),
compare_collation()),
left_item, right_item, cond_equal);
}
@@ -14130,7 +14900,7 @@ Item *eliminate_item_equal(THD *thd, COND *cond, COND_EQUAL *upper_levels,
equals on top level, or the constant.
*/
Item *head_item= (!item_const && current_sjm &&
- current_sjm_head != field_item) ? current_sjm_head: head;
+ current_sjm_head != field_item) ? current_sjm_head: head;
Item *head_real_item= head_item->real_item();
if (head_real_item->type() == Item::FIELD_ITEM)
head_item= head_real_item;
@@ -14295,7 +15065,7 @@ static COND* substitute_for_best_equal_field(THD *thd, JOIN_TAB *context_tab,
This works OK with PS/SP re-execution as changes are made to
the arguments of AND/OR items only
*/
- if (new_item != item)
+ if (new_item && new_item != item)
li.replace(new_item);
}
@@ -14374,7 +15144,9 @@ static COND* substitute_for_best_equal_field(THD *thd, JOIN_TAB *context_tab,
while((item_equal= it++))
{
REPLACE_EQUAL_FIELD_ARG arg= {item_equal, context_tab};
- cond= cond->transform(thd, &Item::replace_equal_field, (uchar *) &arg);
+ if (!(cond= cond->transform(thd, &Item::replace_equal_field,
+ (uchar *) &arg)))
+ return 0;
}
cond_equal= cond_equal->upper_levels;
}
@@ -14478,71 +15250,11 @@ can_change_cond_ref_to_const(Item_bool_func2 *target,
Item_bool_func2 *source,
Item *source_expr, Item *source_const)
{
- if (!target_expr->eq(source_expr,0) ||
- target_value == source_const ||
- target->compare_type() != source->compare_type())
- return false;
- if (target->compare_type() == STRING_RESULT)
- {
- /*
- In this example:
- SET NAMES utf8 COLLATE utf8_german2_ci;
- DROP TABLE IF EXISTS t1;
- CREATE TABLE t1 (a CHAR(10) CHARACTER SET utf8);
- INSERT INTO t1 VALUES ('o-umlaut'),('oe');
- SELECT * FROM t1 WHERE a='oe' COLLATE utf8_german2_ci AND a='oe';
-
- the query should return only the row with 'oe'.
- It should not return 'o-umlaut', because 'o-umlaut' does not match
- the right part of the condition: a='oe'
- ('o-umlaut' is not equal to 'oe' in utf8_general_ci,
- which is the collation of the field "a").
-
- If we change the right part from:
- ... AND a='oe'
- to
- ... AND 'oe' COLLATE utf8_german2_ci='oe'
- it will be evalulated to TRUE and removed from the condition,
- so the overall query will be simplified to:
-
- SELECT * FROM t1 WHERE a='oe' COLLATE utf8_german2_ci;
-
- which will erroneously start to return both 'oe' and 'o-umlaut'.
- So changing "expr" to "const" is not possible if the effective
- collations of "target" and "source" are not exactly the same.
-
- Note, the code before the fix for MDEV-7152 only checked that
- collations of "source_const" and "target_value" are the same.
- This was not enough, as the bug report demonstrated.
- */
- return
- target->compare_collation() == source->compare_collation() &&
- target_value->collation.collation == source_const->collation.collation;
- }
- if (target->compare_type() == TIME_RESULT)
- {
- if (target_value->cmp_type() != TIME_RESULT)
- {
- /*
- Can't rewrite:
- WHERE COALESCE(time_column)='00:00:00'
- AND COALESCE(time_column)=DATE'2015-09-11'
- to
- WHERE DATE'2015-09-11'='00:00:00'
- AND COALESCE(time_column)=DATE'2015-09-11'
- because the left part will erroneously try to parse '00:00:00'
- as DATE, not as TIME.
-
- TODO: It could still be rewritten to:
- WHERE DATE'2015-09-11'=TIME'00:00:00'
- AND COALESCE(time_column)=DATE'2015-09-11'
- i.e. we need to replace both target_expr and target_value
- at the same time. This is not supported yet.
- */
- return false;
- }
- }
- return true; // Non-string comparison
+ return target_expr->eq(source_expr,0) &&
+ target_value != source_const &&
+ target->compare_type_handler()->
+ can_change_cond_ref_to_const(target, target_expr, target_value,
+ source, source_expr, source_const);
}
@@ -14591,6 +15303,7 @@ change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list,
{
cond->marker=1;
COND_CMP *tmp2;
+ /* Will work, even if malloc would fail */
if ((tmp2= new (thd->mem_root) COND_CMP(and_father, func)))
save_list->push_back(tmp2);
}
@@ -14623,6 +15336,7 @@ change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list,
thd->change_item_tree(args + 1, value);
cond->marker=1;
COND_CMP *tmp2;
+ /* Will work, even if malloc would fail */
if ((tmp2=new (thd->mem_root) COND_CMP(and_father, func)))
save_list->push_back(tmp2);
}
@@ -15098,10 +15812,15 @@ static uint build_bitmap_for_nested_joins(List<TABLE_LIST> *join_list,
/**
- Set NESTED_JOIN::counter=0 in all nested joins in passed list.
+ Set NESTED_JOIN::counter and n_tables in all nested joins in passed list.
- Recursively set NESTED_JOIN::counter=0 for all nested joins contained in
- the passed join_list.
+ For all nested joins contained in the passed join_list (including its
+ children), set:
+ - nested_join->counter=0
+ - nested_join->n_tables= {number of non-degenerate direct children}.
+
+ Non-degenerate means non-const base table or a join nest that has a
+ non-degenerate child.
@param join_list List of nested joins to process. It may also contain base
tables which will be ignored.
@@ -15124,8 +15843,11 @@ static uint reset_nj_counters(JOIN *join, List<TABLE_LIST> *join_list)
if (!nested_join->n_tables)
is_eliminated_nest= TRUE;
}
- if ((table->nested_join && !is_eliminated_nest) ||
- (!table->nested_join && (table->table->map & ~join->eliminated_tables)))
+ const table_map removed_tables= join->eliminated_tables |
+ join->const_table_map;
+
+ if ((table->nested_join && !is_eliminated_nest) ||
+ (!table->nested_join && (table->table->map & ~removed_tables)))
n++;
}
DBUG_RETURN(n);
@@ -15437,10 +16159,20 @@ 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);
+ //TODO: take into account join condition selectivity here
+ double pushdown_cond_selectivity= 1.0;
+ table_map real_table_bit= rs->table->map;
+ if (join->thd->variables.optimizer_use_condition_selectivity > 1)
+ {
+ pushdown_cond_selectivity= table_cond_selectivity(join, i, rs,
+ reopt_remaining_tables &
+ ~real_table_bit);
+ }
+ (*outer_rec_count) *= pushdown_cond_selectivity;
if (!rs->emb_sj_nest)
*outer_rec_count= COST_MULT(*outer_rec_count, pos.records_read);
+
}
join->cur_sj_inner_tables= save_cur_sj_inner_tables;
@@ -16069,6 +16801,7 @@ Item_func_isnull::remove_eq_conds(THD *thd, Item::cond_result *cond_value,
query_cache_abort(thd, &thd->query_cache_tls);
#endif
COND *new_cond, *cond= this;
+ /* If this fails, we will catch it later before executing query */
if ((new_cond= new (thd->mem_root) Item_func_eq(thd, args[0],
new (thd->mem_root) Item_int(thd, "last_insert_id()",
thd->read_first_successful_insert_id_in_prev_stmt(),
@@ -16258,7 +16991,7 @@ const_expression_in_where(COND *cond, Item *comp_item, Field *comp_field,
*/
Field *create_tmp_field_from_field(THD *thd, Field *org_field,
- const char *name, TABLE *table,
+ LEX_CSTRING *name, TABLE *table,
Item_field *item)
{
Field *new_field;
@@ -16272,8 +17005,8 @@ Field *create_tmp_field_from_field(THD *thd, Field *org_field,
if (item)
item->result_field= new_field;
else
- new_field->field_name= name;
- new_field->flags|= (org_field->flags & NO_DEFAULT_VALUE_FLAG);
+ new_field->field_name= *name;
+ new_field->flags|= org_field->flags & NO_DEFAULT_VALUE_FLAG;
if (org_field->maybe_null() || (item && item->maybe_null))
new_field->flags&= ~NOT_NULL_FLAG; // Because of outer join
if (org_field->type() == MYSQL_TYPE_VAR_STRING ||
@@ -16291,45 +17024,33 @@ Field *create_tmp_field_from_field(THD *thd, Field *org_field,
}
-Field *Item::create_tmp_field(bool group, TABLE *table, uint convert_int_length)
+Field *Item::create_tmp_field_int(TABLE *table, uint convert_int_length)
+{
+ const Type_handler *h= &type_handler_long;
+ if (max_char_length() > convert_int_length)
+ h= &type_handler_longlong;
+ return h->make_and_init_table_field(&name, Record_addr(maybe_null),
+ *this, table);
+}
+
+
+Field *Item_sum::create_tmp_field(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)
- Field_double(max_length, maybe_null, name, decimals, TRUE);
+ Field_double(max_char_length(), maybe_null, &name, decimals, TRUE);
break;
+ }
case INT_RESULT:
- /*
- Select an integer type with the minimal fit precision.
- convert_int_length is sign inclusive, don't consider the sign.
- */
- if (max_char_length() > convert_int_length)
- new_field= new (mem_root)
- Field_longlong(max_char_length(), maybe_null, name, unsigned_flag);
- else
- new_field= new (mem_root)
- Field_long(max_char_length(), maybe_null, name, unsigned_flag);
- break;
case TIME_RESULT:
- new_field= tmp_table_field_from_field_type(table, true, false);
- break;
- case STRING_RESULT:
- DBUG_ASSERT(collation.collation);
- /*
- GEOMETRY fields have STRING_RESULT result type.
- To preserve type they needed to be handled separately.
- */
- if (field_type() == MYSQL_TYPE_GEOMETRY)
- new_field= tmp_table_field_from_field_type(table, true, false);
- else
- new_field= make_string_field(table);
- new_field->set_derivation(collation.derivation, collation.repertoire);
- break;
case DECIMAL_RESULT:
- new_field= Field_new_decimal::create_from_item(mem_root, this);
+ case STRING_RESULT:
+ new_field= tmp_table_field_from_field_type(table);
break;
case ROW_RESULT:
// This case should never be choosen
@@ -16343,6 +17064,22 @@ Field *Item::create_tmp_field(bool group, TABLE *table, uint convert_int_length)
}
+static void create_tmp_field_from_item_finalize(THD *thd,
+ Field *new_field,
+ Item *item,
+ Item ***copy_func,
+ bool modify_item)
+{
+ if (copy_func &&
+ (item->is_result_field() ||
+ (item->real_item()->is_result_field())))
+ *((*copy_func)++) = item; // Save for copy_funcs
+ if (modify_item)
+ item->set_result_field(new_field);
+ if (item->type() == Item::NULL_ITEM)
+ new_field->is_created_from_null_item= TRUE;
+}
+
/**
Create field for temporary table using type of given item.
@@ -16359,8 +17096,6 @@ Field *Item::create_tmp_field(bool group, TABLE *table, uint convert_int_length)
update the record in the original table.
If modify_item is 0 then fill_record() will
update the temporary table
- @param convert_blob_length If >0 create a varstring(convert_blob_length)
- field instead of blob.
@retval
0 on error
@@ -16372,16 +17107,10 @@ static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table,
Item ***copy_func, bool modify_item)
{
DBUG_ASSERT(thd == table->in_use);
- Field *new_field= item->Item::create_tmp_field(false, table);
-
- if (copy_func &&
- (item->is_result_field() ||
- (item->real_item()->is_result_field())))
- *((*copy_func)++) = item; // Save for copy_funcs
- if (modify_item)
- item->set_result_field(new_field);
- if (item->type() == Item::NULL_ITEM)
- new_field->is_created_from_null_item= TRUE;
+ Field* new_field= item->create_tmp_field(false, table);
+ if (new_field)
+ create_tmp_field_from_item_finalize(thd, new_field, item,
+ copy_func, modify_item);
return new_field;
}
@@ -16405,15 +17134,20 @@ Field *Item::create_field_for_schema(THD *thd, TABLE *table)
{
Field *field;
if (max_length > MAX_FIELD_VARCHARLENGTH)
- field= new Field_blob(max_length, maybe_null, name, collation.collation);
+ field= new (thd->mem_root) Field_blob(max_length, maybe_null, &name,
+ collation.collation);
+ else if (max_length > 0)
+ field= new (thd->mem_root) Field_varstring(max_length, maybe_null, &name,
+ table->s,
+ collation.collation);
else
- field= new Field_varstring(max_length, maybe_null, name,
- table->s, collation.collation);
+ field= new Field_null((uchar*) 0, 0, Field::NONE, &name,
+ collation.collation);
if (field)
field->init(table);
return field;
}
- return tmp_table_field_from_field_type(table, false, false);
+ return tmp_table_field_from_field_type(table);
}
@@ -16455,6 +17189,8 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
Item::Type orig_type= type;
Item *orig_item= 0;
+ DBUG_ASSERT(thd == table->in_use);
+
if (type != Item::FIELD_ITEM &&
item->real_item()->type() == Item::FIELD_ITEM)
{
@@ -16464,6 +17200,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
}
switch (type) {
+ case Item::TYPE_HOLDER:
case Item::SUM_FUNC_ITEM:
{
result= item->create_tmp_field(group, table);
@@ -16473,6 +17210,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
}
case Item::FIELD_ITEM:
case Item::DEFAULT_VALUE_ITEM:
+ case Item::CONTEXTUALLY_TYPED_VALUE_ITEM:
case Item::INSERT_VALUE_ITEM:
case Item::TRIGGER_FIELD_ITEM:
{
@@ -16507,24 +17245,34 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
if (result && modify_item)
field->result_field= result;
if (orig_item)
+ {
item->maybe_null= save_maybe_null;
+ result->field_name= orig_item->name;
+ }
}
else if (table_cant_handle_bit_fields && field->field->type() ==
MYSQL_TYPE_BIT)
{
+ const Type_handler *handler= item->type_handler_long_or_longlong();
*from_field= field->field;
- result= create_tmp_field_from_item(thd, item, table, copy_func,
- modify_item);
+ if ((result=
+ handler->make_and_init_table_field(&item->name,
+ Record_addr(item->maybe_null),
+ *item, table)))
+ create_tmp_field_from_item_finalize(thd, result, item,
+ copy_func, modify_item);
if (result && modify_item)
field->result_field= result;
}
else
+ {
+ LEX_CSTRING *tmp= orig_item ? &orig_item->name : &item->name;
result= create_tmp_field_from_field(thd, (*from_field= field->field),
- orig_item ? orig_item->name :
- item->name,
- table,
+ tmp, table,
modify_item ? field :
NULL);
+ }
+
if (orig_type == Item::REF_ITEM && orig_modify)
((Item_ref*)orig_item)->set_result_field(result);
/*
@@ -16552,11 +17300,10 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
{
*((*copy_func)++)= item;
}
-
Field *result_field=
create_tmp_field_from_field(thd,
sp_result_field,
- item_func_sp->name,
+ &item_func_sp->name,
table,
NULL);
@@ -16593,11 +17340,6 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
return create_tmp_field_from_item(thd, item, table,
(make_copy_field ? 0 : copy_func),
modify_item);
- case Item::TYPE_HOLDER:
- result= ((Item_type_holder *)item)->make_field_by_type(table);
- result->set_derivation(item->collation.derivation,
- item->collation.repertoire);
- return result;
default: // Dosen't have to be stored
return 0;
}
@@ -16675,13 +17417,17 @@ setup_tmp_table_column_bitmaps(TABLE *table, uchar *bitmaps)
temporary table
@param table_alias possible name of the temporary table that can
be used for name resolving; can be "".
+ @param do_not_open only create the TABLE object, do not
+ open the table in the engine
+ @param keep_row_order rows need to be read in the order they were
+ 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 char *table_alias, bool do_not_open,
+ const LEX_CSTRING *table_alias, bool do_not_open,
bool keep_row_order)
{
MEM_ROOT *mem_root_save, own_root;
@@ -16697,6 +17443,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
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;
@@ -16718,12 +17465,10 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
DBUG_ENTER("create_tmp_table");
DBUG_PRINT("enter",
("table_alias: '%s' distinct: %d save_sum_fields: %d "
- "rows_limit: %lu group: %d", table_alias,
+ "rows_limit: %lu group: %d", table_alias->str,
(int) distinct, (int) save_sum_fields,
(ulong) rows_limit, MY_TEST(group)));
- thd->query_plan_flags|= QPLAN_TMP_TABLE;
-
if (use_temp_pool && !(test_flags & TEST_KEEP_TMP_TABLES))
temp_pool_slot = bitmap_lock_set_next(&temp_pool);
@@ -16789,7 +17534,8 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
if (param->precomputed_group_by)
copy_func_count+= param->sum_func_count;
- init_sql_alloc(&own_root, TABLE_ALLOC_BLOCK_SIZE, 0, MYF(MY_THREAD_SPECIFIC));
+ init_sql_alloc(&own_root, "tmp_table", TABLE_ALLOC_BLOCK_SIZE, 0,
+ MYF(MY_THREAD_SPECIFIC));
if (!multi_alloc_root(&own_root,
&table, sizeof(*table),
@@ -16836,7 +17582,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
thd->mem_root= &table->mem_root;
table->field=reg_field;
- table->alias.set(table_alias, strlen(table_alias), table_alias_charset);
+ table->alias.set(table_alias->str, table_alias->length, table_alias_charset);
table->reginfo.lock_type=TL_WRITE; /* Will be updated */
table->map=1;
@@ -16906,13 +17652,15 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
Item *arg= sum_item->get_arg(i);
if (!arg->const_item())
{
- Field *new_field=
+ Item *tmp_item;
+ Field *new_field=
create_tmp_field(thd, table, arg, arg->type(), &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)
@@ -16930,7 +17678,10 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
string_total_length+= new_field->pack_length();
}
thd->mem_root= mem_root_save;
- arg= sum_item->set_arg(i, thd, new (thd->mem_root) Item_temptable_field(thd, new_field));
+ 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)
{
@@ -16982,12 +17733,13 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
item->marker == 4 || param->bit_fields_as_long,
force_copy_fields);
- if (!new_field)
+ if (unlikely(!new_field))
{
- if (thd->is_fatal_error)
+ 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;
@@ -17164,6 +17916,11 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
}
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++)
{
Field *field= *reg_field;
@@ -17191,7 +17948,7 @@ 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 initalize_tables
+ 'offset' fields generated by initialize_tables
*/
if (default_field[i] && default_field[i]->ptr)
{
@@ -17200,25 +17957,31 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
inherit the default value that is defined for the field referred
by the Item_field object from which 'field' has been created.
*/
- const Field *orig_field= default_field[i];
+ Field *orig_field= 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();
else
{
+ /*
+ Copy default value. We have to use field_conv() for copy, instead of
+ memcpy(), because bit_fields may be stored differently
+ */
+ my_ptrdiff_t ptr_diff= (orig_field->table->s->default_values -
+ orig_field->table->record[0]);
field->set_notnull();
- memcpy(field->ptr,
- orig_field->ptr_in_record(orig_field->table->s->default_values),
- field->pack_length_in_rec());
+ orig_field->move_field_offset(ptr_diff);
+ field_conv(field, orig_field);
+ orig_field->move_field_offset(-ptr_diff);
}
- }
+ }
if (from_field[i])
{ /* Not a table Item */
copy->set(field,from_field[i],save_sum_fields);
copy++;
}
- length=field->pack_length();
+ length=field->pack_length_in_rec();
pos+= length;
/* Make entry for create table */
@@ -17240,7 +18003,11 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
// fix table name in field entry
field->set_table_name(&table->alias);
}
+ /* Handle group_null_items */
+ bzero(pos, table->s->reclength - (pos - table->record[0]));
+ MEM_CHECK_DEFINED(table->record[0], table->s->reclength);
+ thd->abort_on_warning= save_abort_on_warning;
param->copy_field_end=copy;
param->recinfo= recinfo; // Pointer to after last field
store_record(table,s->default_values); // Make empty default record
@@ -17284,7 +18051,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
keyinfo->collected_stats= NULL;
keyinfo->algorithm= HA_KEY_ALG_UNDEF;
keyinfo->is_statistics_from_stat_tables= FALSE;
- keyinfo->name= (char*) "group_key";
+ keyinfo->name= group_key;
ORDER *cur_group= group;
for (; cur_group ; cur_group= cur_group->next, key_part_info++)
{
@@ -17395,7 +18162,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
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.
- keyinfo->name= (char*) "distinct_key";
+ keyinfo->name= distinct_key;
keyinfo->algorithm= HA_KEY_ALG_UNDEF;
keyinfo->is_statistics_from_stat_tables= FALSE;
keyinfo->read_stats= NULL;
@@ -17431,7 +18198,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
(uchar*) 0,
(uint) 0,
Field::NONE,
- NullS, &my_charset_bin);
+ &null_clex_str, &my_charset_bin);
if (!key_part_info->field)
goto err;
key_part_info->field->init(table);
@@ -17495,7 +18262,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
}
}
- if (thd->is_fatal_error) // If end of memory
+ if (unlikely(thd->is_fatal_error)) // If end of memory
goto err; /* purecov: inspected */
share->db_record_offset= 1;
table->used_for_duplicate_elimination= (param->sum_func_count == 0 &&
@@ -17509,8 +18276,9 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
goto err;
}
- // Make empty record so random data is not written to disk
- empty_record(table);
+ /* record[0] and share->default_values should now have been set up */
+ MEM_CHECK_DEFINED(table->record[0], table->s->reclength);
+ MEM_CHECK_DEFINED(share->default_values, table->s->reclength);
thd->mem_root= mem_root_save;
@@ -17525,7 +18293,6 @@ err:
}
-
/****************************************************************************/
void *Virtual_tmp_table::operator new(size_t size, THD *thd) throw()
@@ -17538,37 +18305,39 @@ bool Virtual_tmp_table::init(uint field_count)
{
uint *blob_field;
uchar *bitmaps;
+ DBUG_ENTER("Virtual_tmp_table::init");
if (!multi_alloc_root(in_use->mem_root,
&s, sizeof(*s),
&field, (field_count + 1) * sizeof(Field*),
&blob_field, (field_count + 1) * sizeof(uint),
&bitmaps, bitmap_buffer_size(field_count) * 6,
NullS))
- return true;
+ DBUG_RETURN(true);
s->reset();
s->blob_field= blob_field;
setup_tmp_table_column_bitmaps(this, bitmaps, field_count);
m_alloced_field_count= field_count;
- return false;
+ DBUG_RETURN(false);
};
-bool Virtual_tmp_table::add(List<Column_definition> &field_list)
+bool Virtual_tmp_table::add(List<Spvar_definition> &field_list)
{
/* Create all fields and calculate the total length of record */
- Column_definition *cdef; /* column definition */
- List_iterator_fast<Column_definition> it(field_list);
- for ( ; (cdef= it++); )
+ Spvar_definition *cdef; /* column definition */
+ List_iterator_fast<Spvar_definition> it(field_list);
+ DBUG_ENTER("Virtual_tmp_table::add");
+ while ((cdef= it++))
{
Field *tmp;
if (!(tmp= cdef->make_field(s, in_use->mem_root, 0,
(uchar*) (f_maybe_null(cdef->pack_flag) ? "" : 0),
f_maybe_null(cdef->pack_flag) ? 1 : 0,
- cdef->field_name)))
- return true;
- add(tmp);
+ &cdef->field_name)))
+ DBUG_RETURN(true);
+ add(tmp);
}
- return false;
+ DBUG_RETURN(false);
}
@@ -17633,12 +18402,76 @@ bool Virtual_tmp_table::open()
}
+bool Virtual_tmp_table::sp_find_field_by_name(uint *idx,
+ const LEX_CSTRING &name) const
+{
+ Field *f;
+ 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))
+ {
+ *idx= i;
+ return false;
+ }
+ }
+ return true;
+}
+
+
+bool
+Virtual_tmp_table::sp_find_field_by_name_or_error(uint *idx,
+ const LEX_CSTRING &var_name,
+ const LEX_CSTRING &field_name)
+ const
+{
+ if (sp_find_field_by_name(idx, field_name))
+ {
+ my_error(ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD, MYF(0),
+ var_name.str, field_name.str);
+ return true;
+ }
+ return false;
+}
+
+
+bool Virtual_tmp_table::sp_set_all_fields_from_item_list(THD *thd,
+ List<Item> &items)
+{
+ DBUG_ASSERT(s->fields == items.elements);
+ List_iterator<Item> it(items);
+ Item *item;
+ for (uint i= 0 ; (item= it++) ; i++)
+ {
+ if (field[i]->sp_prepare_and_store_item(thd, &item))
+ return true;
+ }
+ return false;
+}
+
+
+bool Virtual_tmp_table::sp_set_all_fields_from_item(THD *thd, Item *value)
+{
+ DBUG_ASSERT(value->fixed);
+ DBUG_ASSERT(value->cols() == s->fields);
+ for (uint i= 0; i < value->cols(); i++)
+ {
+ if (field[i]->sp_prepare_and_store_item(thd, value->addr(i)))
+ return true;
+ }
+ return false;
+}
+
+
bool open_tmp_table(TABLE *table)
{
int error;
- if ((error= table->file->ha_open(table, table->s->path.str, O_RDWR,
- HA_OPEN_TMP_TABLE |
- HA_OPEN_INTERNAL_TABLE)))
+ if (unlikely((error= table->file->ha_open(table, table->s->path.str, O_RDWR,
+ HA_OPEN_TMP_TABLE |
+ HA_OPEN_INTERNAL_TABLE))))
{
table->file->print_error(error, MYF(0)); /* purecov: inspected */
table->db_stat= 0;
@@ -17740,7 +18573,11 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
(*recinfo)->type= FIELD_CHECK;
(*recinfo)->length= MARIA_UNIQUE_HASH_LENGTH;
(*recinfo)++;
- share->reclength+= MARIA_UNIQUE_HASH_LENGTH;
+
+ /* Avoid warnings from valgrind */
+ bzero(table->record[0]+ share->reclength, MARIA_UNIQUE_HASH_LENGTH);
+ bzero(share->default_values+ share->reclength, MARIA_UNIQUE_HASH_LENGTH);
+ share->reclength+= MARIA_UNIQUE_HASH_LENGTH;
}
else
{
@@ -17832,10 +18669,14 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
}
}
- if ((error= maria_create(share->path.str, file_type, share->keys, &keydef,
- (uint) (*recinfo-start_recinfo), start_recinfo,
- share->uniques, &uniquedef, &create_info,
- create_flags)))
+ if (unlikely((error= maria_create(share->path.str,
+ file_type,
+ share->keys, &keydef,
+ (uint) (*recinfo-start_recinfo),
+ start_recinfo,
+ share->uniques, &uniquedef,
+ &create_info,
+ create_flags))))
{
table->file->print_error(error,MYF(0)); /* purecov: inspected */
table->db_stat=0;
@@ -17845,7 +18686,6 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
table->in_use->inc_status_created_tmp_disk_tables();
table->in_use->inc_status_created_tmp_tables();
- table->in_use->query_plan_flags|= QPLAN_TMP_DISK;
share->db_record_offset= 1;
table->set_created();
DBUG_RETURN(0);
@@ -17931,7 +18771,10 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
(*recinfo)->type= FIELD_CHECK;
(*recinfo)->length=MI_UNIQUE_HASH_LENGTH;
(*recinfo)++;
- share->reclength+=MI_UNIQUE_HASH_LENGTH;
+ /* Avoid warnings from valgrind */
+ bzero(table->record[0]+ share->reclength, MI_UNIQUE_HASH_LENGTH);
+ bzero(share->default_values+ share->reclength, MI_UNIQUE_HASH_LENGTH);
+ share->reclength+= MI_UNIQUE_HASH_LENGTH;
}
else
{
@@ -17984,13 +18827,17 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
bzero((char*) &create_info,sizeof(create_info));
create_info.data_file_length= table->in_use->variables.tmp_disk_table_size;
- if ((error=mi_create(share->path.str, share->keys, &keydef,
- (uint) (*recinfo-start_recinfo), start_recinfo,
- share->uniques, &uniquedef, &create_info,
- HA_CREATE_TMP_TABLE | HA_CREATE_INTERNAL_TABLE |
- ((share->db_create_options & HA_OPTION_PACK_RECORD) ?
- HA_PACK_RECORD : 0)
- )))
+ if (unlikely((error= mi_create(share->path.str, share->keys, &keydef,
+ (uint) (*recinfo-start_recinfo),
+ start_recinfo,
+ share->uniques, &uniquedef,
+ &create_info,
+ HA_CREATE_TMP_TABLE |
+ HA_CREATE_INTERNAL_TABLE |
+ ((share->db_create_options &
+ HA_OPTION_PACK_RECORD) ?
+ HA_PACK_RECORD : 0)
+ ))))
{
table->file->print_error(error,MYF(0)); /* purecov: inspected */
table->db_stat=0;
@@ -17998,7 +18845,6 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
}
table->in_use->inc_status_created_tmp_disk_tables();
table->in_use->inc_status_created_tmp_tables();
- table->in_use->query_plan_flags|= QPLAN_TMP_DISK;
share->db_record_offset= 1;
table->set_created();
DBUG_RETURN(0);
@@ -18045,11 +18891,11 @@ create_internal_tmp_table_from_heap(THD *thd, TABLE *table,
share= *table->s;
new_table.s= &share;
new_table.s->db_plugin= ha_lock_engine(thd, TMP_ENGINE_HTON);
- if (!(new_table.file= get_new_handler(&share, &new_table.mem_root,
- new_table.s->db_type())))
+ if (unlikely(!(new_table.file= get_new_handler(&share, &new_table.mem_root,
+ new_table.s->db_type()))))
DBUG_RETURN(1); // End of memory
- if (new_table.file->set_ha_share_ref(&share.ha_share))
+ if (unlikely(new_table.file->set_ha_share_ref(&share.ha_share)))
{
delete new_table.file;
DBUG_RETURN(1);
@@ -18092,16 +18938,13 @@ create_internal_tmp_table_from_heap(THD *thd, TABLE *table,
DBUG_EXECUTE_IF("raise_error", write_err= HA_ERR_FOUND_DUPP_KEY ;);
if (write_err)
goto err;
- if (thd->check_killed())
- {
- thd->send_kill_message();
+ if (unlikely(thd->check_killed()))
goto err_killed;
- }
}
if (!new_table.no_rows && new_table.file->ha_end_bulk_insert())
goto err;
/* copy row that filled HEAP table */
- if ((write_err=new_table.file->ha_write_tmp_row(table->record[0])))
+ if (unlikely((write_err=new_table.file->ha_write_tmp_row(table->record[0]))))
{
if (new_table.file->is_fatal_error(write_err, HA_CHECK_DUP) ||
!ignore_last_dupp_key_error)
@@ -18165,7 +19008,12 @@ free_tmp_table(THD *thd, TABLE *entry)
{
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);
delete entry->file;
@@ -18181,6 +19029,12 @@ free_tmp_table(THD *thd, TABLE *entry)
plugin_unlock(0, entry->s->db_plugin);
entry->alias.free();
+ if (entry->pos_in_table_list && entry->pos_in_table_list->table)
+ {
+ DBUG_ASSERT(entry->pos_in_table_list->table == entry);
+ entry->pos_in_table_list->table= NULL;
+ }
+
free_root(&own_root, MYF(0)); /* the table is allocated in its own root */
thd_proc_info(thd, save_proc_info);
@@ -18380,7 +19234,7 @@ do_select(JOIN *join, Procedure *procedure)
(the join condition and piece of where clause
relevant to this join table).
*/
- if (join->thd->is_error())
+ if (unlikely(join->thd->is_error()))
error= NESTED_LOOP_ERROR;
}
else
@@ -18398,13 +19252,14 @@ do_select(JOIN *join, Procedure *procedure)
error= NESTED_LOOP_NO_MORE_ROWS;
else
error= join->first_select(join,join_tab,0);
- if (error >= NESTED_LOOP_OK && join->thd->killed != ABORT_QUERY)
+ if (error >= NESTED_LOOP_OK && likely(join->thd->killed != ABORT_QUERY))
error= join->first_select(join,join_tab,1);
}
join->thd->limit_found_rows= join->send_records - join->duplicate_rows;
- if (error == NESTED_LOOP_NO_MORE_ROWS || join->thd->killed == ABORT_QUERY)
+ if (error == NESTED_LOOP_NO_MORE_ROWS ||
+ unlikely(join->thd->killed == ABORT_QUERY))
error= NESTED_LOOP_OK;
/*
@@ -18451,7 +19306,7 @@ do_select(JOIN *join, Procedure *procedure)
Sic: this branch works even if rc != 0, e.g. when
send_data above returns an error.
*/
- if (join->result->send_eof())
+ if (unlikely(join->result->send_eof()))
rc= 1; // Don't send error
DBUG_PRINT("info",("%ld records output", (long) join->send_records));
}
@@ -18471,7 +19326,7 @@ do_select(JOIN *join, Procedure *procedure)
int rr_sequential_and_unpack(READ_RECORD *info)
{
int error;
- if ((error= rr_sequential(info)))
+ if (unlikely((error= rr_sequential(info))))
return error;
for (Copy_field *cp= info->copy_field; cp != info->copy_field_end; cp++)
@@ -18507,11 +19362,15 @@ bool instantiate_tmp_table(TABLE *table, KEY *keyinfo,
{
if (table->s->db_type() == TMP_ENGINE_HTON)
{
+ /*
+ If it is not heap (in-memory) table then convert index to unique
+ constrain.
+ */
+ MEM_CHECK_DEFINED(table->record[0], table->s->reclength);
if (create_internal_tmp_table(table, keyinfo, start_recinfo, recinfo,
options))
return TRUE;
- // Make empty record so random data is not written to disk
- empty_record(table);
+ MEM_CHECK_DEFINED(table->record[0], table->s->reclength);
}
if (open_tmp_table(table))
return TRUE;
@@ -18638,10 +19497,9 @@ sub_select_cache(JOIN *join, JOIN_TAB *join_tab, bool end_of_records)
rc= sub_select(join, join_tab, end_of_records);
DBUG_RETURN(rc);
}
- if (join->thd->check_killed())
+ if (unlikely(join->thd->check_killed()))
{
/* The user has aborted the execution of the query */
- join->thd->send_kill_message();
DBUG_RETURN(NESTED_LOOP_KILLED);
}
if (!test_if_use_dynamic_range_scan(join_tab))
@@ -18872,12 +19730,12 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
skip_over= TRUE;
}
- error= info->read_record(info);
+ error= info->read_record();
- if (skip_over && !error)
+ if (skip_over && likely(!error))
{
- if(!key_cmp(join_tab->table->key_info[join_tab->loosescan_key].key_part,
- join_tab->loosescan_buf, join_tab->loosescan_key_len))
+ if (!key_cmp(join_tab->table->key_info[join_tab->loosescan_key].key_part,
+ join_tab->loosescan_buf, join_tab->loosescan_key_len))
{
/*
This is the LooseScan action: skip over records with the same key
@@ -18889,7 +19747,7 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
skip_over= FALSE;
}
- if (join_tab->keep_current_rowid && !error)
+ if (join_tab->keep_current_rowid && likely(!error))
join_tab->table->file->position(join_tab->table->record[0]);
rc= evaluate_join_record(join, join_tab, error);
@@ -18934,13 +19792,13 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
" cond: %p error: %d alias %s",
join, join_tab, select_cond, error,
join_tab->table->alias.ptr()));
- if (error > 0 || (join->thd->is_error())) // Fatal error
+
+ if (error > 0 || unlikely(join->thd->is_error())) // Fatal error
DBUG_RETURN(NESTED_LOOP_ERROR);
if (error < 0)
DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS);
- if (join->thd->check_killed()) // Aborted by user
+ if (unlikely(join->thd->check_killed())) // Aborted by user
{
- join->thd->send_kill_message();
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
}
@@ -18951,7 +19809,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
select_cond_result= MY_TEST(select_cond->val_int());
/* check for errors evaluating the condition */
- if (join->thd->is_error())
+ if (unlikely(join->thd->is_error()))
DBUG_RETURN(NESTED_LOOP_ERROR);
}
@@ -19081,7 +19939,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
join->return_tab= return_tab;
/* check for errors evaluating the condition */
- if (join->thd->is_error())
+ if (unlikely(join->thd->is_error()))
DBUG_RETURN(NESTED_LOOP_ERROR);
if (join->return_tab < join_tab)
@@ -19230,10 +20088,11 @@ int safe_index_read(JOIN_TAB *tab)
{
int error;
TABLE *table= tab->table;
- if ((error= table->file->ha_index_read_map(table->record[0],
- tab->ref.key_buff,
- make_prev_keypart_map(tab->ref.key_parts),
- HA_READ_KEY_EXACT)))
+ if (unlikely((error=
+ table->file->ha_index_read_map(table->record[0],
+ tab->ref.key_buff,
+ make_prev_keypart_map(tab->ref.key_parts),
+ HA_READ_KEY_EXACT))))
return report_error(table, error);
return 0;
}
@@ -19281,7 +20140,7 @@ join_read_const_table(THD *thd, JOIN_TAB *tab, POSITION *pos)
}
else if (tab->type == JT_SYSTEM)
{
- if ((error=join_read_system(tab)))
+ if (unlikely((error=join_read_system(tab))))
{ // Info for DESCRIBE
tab->info= ET_CONST_ROW_NOT_FOUND;
/* Mark for EXPLAIN that the row was not found */
@@ -19307,7 +20166,7 @@ join_read_const_table(THD *thd, JOIN_TAB *tab, POSITION *pos)
}
error=join_read_const(tab);
table->file->ha_end_keyread();
- if (error)
+ if (unlikely(error))
{
tab->info= ET_UNIQUE_ROW_NOT_FOUND;
/* Mark for EXPLAIN that the row was not found */
@@ -19385,11 +20244,13 @@ join_read_system(JOIN_TAB *tab)
int error;
if (table->status & STATUS_GARBAGE) // If first read
{
- if ((error= table->file->ha_read_first_row(table->record[0],
- table->s->primary_key)))
+ if (unlikely((error=
+ table->file->ha_read_first_row(table->record[0],
+ table->s->primary_key))))
{
if (error != HA_ERR_END_OF_FILE)
return report_error(table, error);
+ table->const_table= 1;
mark_as_null_row(tab->table);
empty_record(table); // Make empty record
return -1;
@@ -19430,7 +20291,7 @@ join_read_const(JOIN_TAB *tab)
make_prev_keypart_map(tab->ref.key_parts),
HA_READ_KEY_EXACT);
}
- if (error)
+ if (unlikely(error))
{
table->status= STATUS_NOT_FOUND;
mark_as_null_row(tab->table);
@@ -19486,7 +20347,7 @@ int join_read_key2(THD *thd, JOIN_TAB *tab, TABLE *table, TABLE_REF *table_ref)
if (!table->file->inited)
{
error= table->file->ha_index_init(table_ref->key, tab ? tab->sorted : TRUE);
- if (error)
+ if (unlikely(error))
{
(void) report_error(table, error);
return 1;
@@ -19526,10 +20387,11 @@ int join_read_key2(THD *thd, JOIN_TAB *tab, TABLE *table, TABLE_REF *table_ref)
table_ref->key_buff,
make_prev_keypart_map(table_ref->key_parts),
HA_READ_KEY_EXACT);
- if (error && error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
+ if (unlikely(error) &&
+ error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
return report_error(table, error);
- if (! error)
+ if (likely(!error))
{
table_ref->has_record= TRUE;
table_ref->use_count= 1;
@@ -19590,16 +20452,19 @@ join_read_always_key(JOIN_TAB *tab)
/* Initialize the index first */
if (!table->file->inited)
{
- if ((error= table->file->ha_index_init(tab->ref.key, tab->sorted)))
+ if (unlikely((error= table->file->ha_index_init(tab->ref.key,
+ tab->sorted))))
{
(void) report_error(table, error);
return 1;
}
}
- if (cp_buffer_from_ref(tab->join->thd, table, &tab->ref))
+ if (unlikely(cp_buffer_from_ref(tab->join->thd, table, &tab->ref)))
return -1;
- if ((error= table->file->prepare_index_key_scan_map(tab->ref.key_buff, make_prev_keypart_map(tab->ref.key_parts))))
+ if (unlikely((error=
+ table->file->prepare_index_key_scan_map(tab->ref.key_buff,
+ make_prev_keypart_map(tab->ref.key_parts)))))
{
report_error(table,error);
return -1;
@@ -19629,23 +20494,26 @@ join_read_last_key(JOIN_TAB *tab)
TABLE *table= tab->table;
if (!table->file->inited &&
- (error= table->file->ha_index_init(tab->ref.key, tab->sorted)))
+ unlikely((error= table->file->ha_index_init(tab->ref.key, tab->sorted))))
{
(void) report_error(table, error);
return 1;
}
- if (cp_buffer_from_ref(tab->join->thd, table, &tab->ref))
+ if (unlikely(cp_buffer_from_ref(tab->join->thd, table, &tab->ref)))
return -1;
- if ((error= table->file->prepare_index_key_scan_map(tab->ref.key_buff, make_prev_keypart_map(tab->ref.key_parts))))
+ if (unlikely((error=
+ table->file->prepare_index_key_scan_map(tab->ref.key_buff,
+ make_prev_keypart_map(tab->ref.key_parts)))) )
{
report_error(table,error);
return -1;
}
- if ((error= table->file->ha_index_read_map(table->record[0],
- tab->ref.key_buff,
- make_prev_keypart_map(tab->ref.key_parts),
- HA_READ_PREFIX_LAST)))
+ if (unlikely((error=
+ table->file->ha_index_read_map(table->record[0],
+ tab->ref.key_buff,
+ make_prev_keypart_map(tab->ref.key_parts),
+ HA_READ_PREFIX_LAST))))
{
if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
return report_error(table, error);
@@ -19670,9 +20538,9 @@ join_read_next_same(READ_RECORD *info)
TABLE *table= info->table;
JOIN_TAB *tab=table->reginfo.join_tab;
- if ((error= table->file->ha_index_next_same(table->record[0],
- tab->ref.key_buff,
- tab->ref.key_length)))
+ if (unlikely((error= table->file->ha_index_next_same(table->record[0],
+ tab->ref.key_buff,
+ tab->ref.key_length))))
{
if (error != HA_ERR_END_OF_FILE)
return report_error(table, error);
@@ -19690,7 +20558,7 @@ join_read_prev_same(READ_RECORD *info)
TABLE *table= info->table;
JOIN_TAB *tab=table->reginfo.join_tab;
- if ((error= table->file->ha_index_prev(table->record[0])))
+ if (unlikely((error= table->file->ha_index_prev(table->record[0]))))
return report_error(table, error);
if (key_cmp_if_same(table, tab->ref.key_buff, tab->ref.key,
tab->ref.key_length))
@@ -19713,9 +20581,9 @@ join_init_quick_read_record(JOIN_TAB *tab)
int read_first_record_seq(JOIN_TAB *tab)
{
- if (tab->read_record.table->file->ha_rnd_init_with_error(1))
+ if (unlikely(tab->read_record.table->file->ha_rnd_init_with_error(1)))
return 1;
- return (*tab->read_record.read_record)(&tab->read_record);
+ return tab->read_record.read_record();
}
static int
@@ -19779,7 +20647,7 @@ int join_init_read_record(JOIN_TAB *tab)
if (init_read_record(&tab->read_record, tab->join->thd, tab->table,
tab->select, tab->filesort_result, 1,1, FALSE))
return 1;
- return (*tab->read_record.read_record)(&tab->read_record);
+ return tab->read_record.read_record();
}
int
@@ -19799,9 +20667,9 @@ join_read_record_no_init(JOIN_TAB *tab)
tab->read_record.copy_field= save_copy;
tab->read_record.copy_field_end= save_copy_end;
- tab->read_record.read_record= rr_sequential_and_unpack;
+ tab->read_record.read_record_func= rr_sequential_and_unpack;
- return (*tab->read_record.read_record)(&tab->read_record);
+ return tab->read_record.read_record();
}
@@ -19834,15 +20702,14 @@ join_read_first(JOIN_TAB *tab)
!table->covering_keys.is_set(tab->index) ||
table->file->keyread == tab->index);
tab->table->status=0;
- tab->read_record.read_record=join_read_next;
+ tab->read_record.read_record_func= join_read_next;
tab->read_record.table=table;
- tab->read_record.index=tab->index;
- tab->read_record.record=table->record[0];
if (!table->file->inited)
error= table->file->ha_index_init(tab->index, tab->sorted);
- if (!error)
+ if (likely(!error))
error= table->file->prepare_index_scan();
- if (error || (error=tab->table->file->ha_index_first(tab->table->record[0])))
+ if (unlikely(error) ||
+ unlikely(error= tab->table->file->ha_index_first(tab->table->record[0])))
{
if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
report_error(table, error);
@@ -19856,7 +20723,7 @@ static int
join_read_next(READ_RECORD *info)
{
int error;
- if ((error= info->table->file->ha_index_next(info->record)))
+ if (unlikely((error= info->table->file->ha_index_next(info->record()))))
return report_error(info->table, error);
return 0;
@@ -19874,15 +20741,14 @@ join_read_last(JOIN_TAB *tab)
!table->covering_keys.is_set(tab->index) ||
table->file->keyread == tab->index);
tab->table->status=0;
- tab->read_record.read_record=join_read_prev;
+ tab->read_record.read_record_func= join_read_prev;
tab->read_record.table=table;
- tab->read_record.index=tab->index;
- tab->read_record.record=table->record[0];
if (!table->file->inited)
error= table->file->ha_index_init(tab->index, 1);
- if (!error)
+ if (likely(!error))
error= table->file->prepare_index_scan();
- if (error || (error= tab->table->file->ha_index_last(tab->table->record[0])))
+ if (unlikely(error) ||
+ unlikely(error= tab->table->file->ha_index_last(tab->table->record[0])))
DBUG_RETURN(report_error(table, error));
DBUG_RETURN(0);
@@ -19893,7 +20759,7 @@ static int
join_read_prev(READ_RECORD *info)
{
int error;
- if ((error= info->table->file->ha_index_prev(info->record)))
+ if (unlikely((error= info->table->file->ha_index_prev(info->record()))))
return report_error(info->table, error);
return 0;
}
@@ -19914,7 +20780,7 @@ join_ft_read_first(JOIN_TAB *tab)
table->file->ft_init();
- if ((error= table->file->ha_ft_read(table->record[0])))
+ if (unlikely((error= table->file->ha_ft_read(table->record[0]))))
return report_error(table, error);
return 0;
}
@@ -19923,7 +20789,7 @@ static int
join_ft_read_next(READ_RECORD *info)
{
int error;
- if ((error= info->table->file->ha_ft_read(info->table->record[0])))
+ if (unlikely((error= info->table->file->ha_ft_read(info->record()))))
return report_error(info->table, error);
return 0;
}
@@ -19953,7 +20819,7 @@ int
join_read_next_same_or_null(READ_RECORD *info)
{
int error;
- if ((error= join_read_next_same(info)) >= 0)
+ if (unlikely((error= join_read_next_same(info)) >= 0))
return error;
JOIN_TAB *tab= info->table->reginfo.join_tab;
@@ -20025,7 +20891,7 @@ 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 ((error= join->result->send_data(*fields)))
+ if (unlikely((error= join->result->send_data(*fields))))
{
if (error > 0)
DBUG_RETURN(NESTED_LOOP_ERROR);
@@ -20174,7 +21040,7 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (join->do_send_rows)
{
error=join->result->send_data(*fields);
- if (error < 0)
+ if (unlikely(error < 0))
{
/* Duplicate row, don't count */
join->duplicate_rows++;
@@ -20184,13 +21050,13 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
join->send_records++;
join->group_sent= true;
}
- if (join->rollup.state != ROLLUP::STATE_NONE && error <= 0)
+ if (unlikely(join->rollup.state != ROLLUP::STATE_NONE && error <= 0))
{
if (join->rollup_send_data((uint) (idx+1)))
error= 1;
}
}
- if (error > 0)
+ if (unlikely(error > 0))
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
if (end_of_records)
DBUG_RETURN(NESTED_LOOP_OK);
@@ -20260,14 +21126,14 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (copy_funcs(join_tab->tmp_table_param->items_to_copy, join->thd))
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
- if (!join_tab->having || join_tab->having->val_int())
+ if (likely(!join_tab->having || join_tab->having->val_int()))
{
int error;
join->found_records++;
if ((error= table->file->ha_write_tmp_row(table->record[0])))
{
- if (!table->file->is_fatal_error(error, HA_CHECK_DUP))
- goto end;
+ if (likely(!table->file->is_fatal_error(error, HA_CHECK_DUP)))
+ goto end; // Ignore duplicate keys
bool is_duplicate;
if (create_internal_tmp_table_from_heap(join->thd, table,
join_tab->tmp_table_param->start_recinfo,
@@ -20290,9 +21156,8 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
}
}
end:
- if (join->thd->check_killed())
+ if (unlikely(join->thd->check_killed()))
{
- join->thd->send_kill_message();
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
}
DBUG_RETURN(NESTED_LOOP_OK);
@@ -20349,8 +21214,8 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
{ /* Update old record */
restore_record(table,record[1]);
update_tmptable_sum_func(join->sum_funcs,table);
- if ((error= table->file->ha_update_tmp_row(table->record[1],
- table->record[0])))
+ if (unlikely((error= table->file->ha_update_tmp_row(table->record[1],
+ table->record[0]))))
{
table->file->print_error(error,MYF(0)); /* purecov: inspected */
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
@@ -20359,9 +21224,10 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
}
init_tmptable_sum_functions(join->sum_funcs);
- if (copy_funcs(join_tab->tmp_table_param->items_to_copy, join->thd))
+ if (unlikely(copy_funcs(join_tab->tmp_table_param->items_to_copy,
+ join->thd)))
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
- if ((error= table->file->ha_write_tmp_row(table->record[0])))
+ if (unlikely((error= table->file->ha_write_tmp_row(table->record[0]))))
{
if (create_internal_tmp_table_from_heap(join->thd, table,
join_tab->tmp_table_param->start_recinfo,
@@ -20369,7 +21235,7 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
error, 0, NULL))
DBUG_RETURN(NESTED_LOOP_ERROR); // Not a table_is_full error
/* Change method to update rows */
- if ((error= table->file->ha_index_init(0, 0)))
+ if (unlikely((error= table->file->ha_index_init(0, 0))))
{
table->file->print_error(error, MYF(0));
DBUG_RETURN(NESTED_LOOP_ERROR);
@@ -20379,9 +21245,8 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
}
join_tab->send_records++;
end:
- if (join->thd->check_killed())
+ if (unlikely(join->thd->check_killed()))
{
- join->thd->send_kill_message();
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
}
DBUG_RETURN(NESTED_LOOP_OK);
@@ -20406,11 +21271,11 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (copy_funcs(join_tab->tmp_table_param->items_to_copy, join->thd))
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
- if (!(error= table->file->ha_write_tmp_row(table->record[0])))
+ if (likely(!(error= table->file->ha_write_tmp_row(table->record[0]))))
join_tab->send_records++; // New group
else
{
- if ((int) table->file->get_dup_key(error) < 0)
+ if (unlikely((int) table->file->get_dup_key(error) < 0))
{
table->file->print_error(error,MYF(0)); /* purecov: inspected */
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
@@ -20424,15 +21289,15 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
table->file->print_error(error, MYF(0));
DBUG_RETURN(NESTED_LOOP_ERROR);
}
- if (table->file->ha_rnd_pos(table->record[1],table->file->dup_ref))
+ if (unlikely(table->file->ha_rnd_pos(table->record[1],table->file->dup_ref)))
{
table->file->print_error(error,MYF(0)); /* purecov: inspected */
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
}
restore_record(table,record[1]);
update_tmptable_sum_func(join->sum_funcs,table);
- if ((error= table->file->ha_update_tmp_row(table->record[1],
- table->record[0])))
+ if (unlikely((error= table->file->ha_update_tmp_row(table->record[1],
+ table->record[0]))))
{
table->file->print_error(error,MYF(0)); /* purecov: inspected */
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
@@ -20445,9 +21310,8 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(NESTED_LOOP_ERROR);
}
}
- if (join->thd->check_killed())
+ if (unlikely(join->thd->check_killed()))
{
- join->thd->send_kill_message();
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
}
DBUG_RETURN(NESTED_LOOP_OK);
@@ -20493,17 +21357,18 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (!join_tab->having || join_tab->having->val_int())
{
int error= table->file->ha_write_tmp_row(table->record[0]);
- if (error &&
+ if (unlikely(error) &&
create_internal_tmp_table_from_heap(join->thd, table,
join_tab->tmp_table_param->start_recinfo,
&join_tab->tmp_table_param->recinfo,
error, 0, NULL))
DBUG_RETURN(NESTED_LOOP_ERROR);
}
- if (join->rollup.state != ROLLUP::STATE_NONE)
+ if (unlikely(join->rollup.state != ROLLUP::STATE_NONE))
{
- if (join->rollup_write_data((uint) (idx+1),
- join_tab->tmp_table_param, table))
+ if (unlikely(join->rollup_write_data((uint) (idx+1),
+ join_tab->tmp_table_param,
+ table)))
{
DBUG_RETURN(NESTED_LOOP_ERROR);
}
@@ -20522,23 +21387,24 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (idx < (int) join->send_group_parts)
{
copy_fields(join_tab->tmp_table_param);
- if (copy_funcs(join_tab->tmp_table_param->items_to_copy, join->thd))
+ if (unlikely(copy_funcs(join_tab->tmp_table_param->items_to_copy,
+ join->thd)))
DBUG_RETURN(NESTED_LOOP_ERROR);
- if (init_sum_functions(join->sum_funcs, join->sum_funcs_end[idx+1]))
+ if (unlikely(init_sum_functions(join->sum_funcs,
+ join->sum_funcs_end[idx+1])))
DBUG_RETURN(NESTED_LOOP_ERROR);
- if (join->procedure)
+ if (unlikely(join->procedure))
join->procedure->add();
goto end;
}
}
- if (update_sum_func(join->sum_funcs))
+ if (unlikely(update_sum_func(join->sum_funcs)))
DBUG_RETURN(NESTED_LOOP_ERROR);
- if (join->procedure)
+ if (unlikely(join->procedure))
join->procedure->add();
end:
- if (join->thd->check_killed())
+ if (unlikely(join->thd->check_killed()))
{
- join->thd->send_kill_message();
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
}
DBUG_RETURN(NESTED_LOOP_OK);
@@ -21051,7 +21917,7 @@ part_of_refkey(TABLE *table,Field *field)
static int test_if_order_by_key(JOIN *join,
ORDER *order, TABLE *table, uint idx,
- uint *used_key_parts= NULL)
+ uint *used_key_parts)
{
KEY_PART_INFO *key_part,*key_part_end;
key_part=table->key_info[idx].key_part;
@@ -21910,7 +22776,7 @@ check_reverse_order:
with key part (A) and then traverse the index backwards.
*/
tab->read_first_record= join_read_last_key;
- tab->read_record.read_record= join_read_prev_same;
+ tab->read_record.read_record_func= join_read_prev_same;
/*
Cancel Pushed Index Condition, as it doesn't work for reverse scans.
*/
@@ -22040,7 +22906,8 @@ create_sort_index(THD *thd, JOIN *join, JOIN_TAB *tab, Filesort *fsort)
{
DBUG_ASSERT(tab->type == JT_REF || tab->type == JT_EQ_REF);
// Update ref value
- if ((cp_buffer_from_ref(thd, table, &tab->ref) && thd->is_fatal_error))
+ if (unlikely(cp_buffer_from_ref(thd, table, &tab->ref) &&
+ thd->is_fatal_error))
goto err; // out of memory
}
}
@@ -22048,7 +22915,7 @@ create_sort_index(THD *thd, JOIN *join, JOIN_TAB *tab, Filesort *fsort)
/* Fill schema tables with data before filesort if it's necessary */
if ((join->select_lex->options & OPTION_SCHEMA_TABLE) &&
- get_schema_tables_result(join, PROCESSED_BY_CREATE_SORT_INDEX))
+ unlikely(get_schema_tables_result(join, PROCESSED_BY_CREATE_SORT_INDEX)))
goto err;
if (table->s->tmp_table)
@@ -22072,7 +22939,7 @@ create_sort_index(THD *thd, JOIN *join, JOIN_TAB *tab, Filesort *fsort)
table->file->ha_end_keyread();
if (tab->type == JT_FT)
- table->file->ft_end();
+ table->file->ha_ft_end();
else
table->file->ha_index_or_rnd_end();
@@ -22216,37 +23083,31 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field,
int error;
DBUG_ENTER("remove_dup_with_compare");
- if (file->ha_rnd_init_with_error(1))
+ if (unlikely(file->ha_rnd_init_with_error(1)))
DBUG_RETURN(1);
error= file->ha_rnd_next(record);
for (;;)
{
- if (thd->check_killed())
+ if (unlikely(thd->check_killed()))
{
- thd->send_kill_message();
error=0;
goto err;
}
- if (error)
+ if (unlikely(error))
{
- if (error == HA_ERR_RECORD_DELETED)
- {
- error= file->ha_rnd_next(record);
- continue;
- }
if (error == HA_ERR_END_OF_FILE)
break;
goto err;
}
if (having && !having->val_int())
{
- if ((error= file->ha_delete_row(record)))
+ if (unlikely((error= file->ha_delete_row(record))))
goto err;
error= file->ha_rnd_next(record);
continue;
}
- if (copy_blobs(first_field))
+ if (unlikely(copy_blobs(first_field)))
{
my_message(ER_OUTOFMEMORY, ER_THD(thd,ER_OUTOFMEMORY),
MYF(ME_FATALERROR));
@@ -22259,30 +23120,28 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field,
bool found=0;
for (;;)
{
- if ((error= file->ha_rnd_next(record)))
+ if (unlikely((error= file->ha_rnd_next(record))))
{
- if (error == HA_ERR_RECORD_DELETED)
- continue;
if (error == HA_ERR_END_OF_FILE)
break;
goto err;
}
if (compare_record(table, first_field) == 0)
{
- if ((error= file->ha_delete_row(record)))
+ if (unlikely((error= file->ha_delete_row(record))))
goto err;
}
else if (!found)
{
found=1;
- if ((error= file->remember_rnd_pos()))
+ if (unlikely((error= file->remember_rnd_pos())))
goto err;
}
}
if (!found)
break; // End of file
/* Restart search on saved row */
- if ((error= file->restart_rnd_next(record)))
+ if (unlikely((error= file->restart_rnd_next(record))))
goto err;
}
@@ -22320,49 +23179,47 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
Field **ptr;
DBUG_ENTER("remove_dup_with_hash_index");
- if (!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 (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)))
DBUG_RETURN(1);
for (ptr= first_field, field_length=field_lengths ; *ptr ; ptr++)
(*field_length++)= (*ptr)->sort_length();
- if (my_hash_init(&hash, &my_charset_bin, (uint) file->stats.records, 0,
- key_length, (my_hash_get_key) 0, 0, 0))
+ if (unlikely(my_hash_init(&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);
}
- if ((error= file->ha_rnd_init(1)))
+ if (unlikely((error= file->ha_rnd_init(1))))
goto err;
key_pos=key_buffer;
for (;;)
{
uchar *org_key_pos;
- if (thd->check_killed())
+ if (unlikely(thd->check_killed()))
{
- thd->send_kill_message();
error=0;
goto err;
}
- if ((error= file->ha_rnd_next(record)))
+ if (unlikely((error= file->ha_rnd_next(record))))
{
- if (error == HA_ERR_RECORD_DELETED)
- continue;
if (error == HA_ERR_END_OF_FILE)
break;
goto err;
}
if (having && !having->val_int())
{
- if ((error= file->ha_delete_row(record)))
+ if (unlikely((error= file->ha_delete_row(record))))
goto err;
continue;
}
@@ -22379,7 +23236,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
if (my_hash_search(&hash, org_key_pos, key_length))
{
/* Duplicated found ; Remove the row */
- if ((error= file->ha_delete_row(record)))
+ if (unlikely((error= file->ha_delete_row(record))))
goto err;
}
else
@@ -22400,7 +23257,7 @@ err:
my_hash_free(&hash);
file->extra(HA_EXTRA_NO_CACHE);
(void) file->ha_rnd_end();
- if (error)
+ if (unlikely(error))
file->print_error(error,MYF(0));
DBUG_RETURN(1);
}
@@ -22570,8 +23427,8 @@ find_order_in_list(THD *thd, Ref_ptr_array ref_pointer_array,
original field name, we should additionally check if we have conflict
for this name (in case if we would perform lookup in all tables).
*/
- if (resolution == RESOLVED_BEHIND_ALIAS && !order_item->fixed &&
- order_item->fix_fields(thd, order->item))
+ if (resolution == RESOLVED_BEHIND_ALIAS &&
+ order_item->fix_fields_if_needed_for_order_by(thd, order->item))
return TRUE;
/* Lookup the current GROUP field in the FROM clause. */
@@ -22623,7 +23480,7 @@ find_order_in_list(THD *thd, Ref_ptr_array ref_pointer_array,
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_NON_UNIQ_ERROR,
ER_THD(thd, ER_NON_UNIQ_ERROR),
- ((Item_ident*) order_item)->field_name,
+ ((Item_ident*) order_item)->field_name.str,
thd->where);
}
}
@@ -22652,11 +23509,10 @@ find_order_in_list(THD *thd, Ref_ptr_array ref_pointer_array,
We check order_item->fixed because Item_func_group_concat can put
arguments for which fix_fields already was called.
*/
- if (!order_item->fixed &&
- (order_item->fix_fields(thd, order->item) ||
- (order_item= *order->item)->check_cols(1) ||
- thd->is_error()))
+ if (order_item->fix_fields_if_needed_for_order_by(thd, order->item) ||
+ thd->is_error())
return TRUE; /* Wrong field. */
+ order_item= *order->item; // Item can change during fix_fields()
if (!add_to_all_fields)
return FALSE;
@@ -22696,8 +23552,8 @@ int setup_order(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,
enum_parsing_place context_analysis_place=
thd->lex->current_select->context_analysis_place;
thd->where="order clause";
- const bool for_union = select->master_unit()->is_union() &&
- select == select->master_unit()->fake_select_lex;
+ const bool for_union= select->master_unit()->is_unit_op() &&
+ select == select->master_unit()->fake_select_lex;
for (uint number = 1; order; order=order->next, number++)
{
if (find_order_in_list(thd, ref_pointer_array, tables, order, fields,
@@ -22878,7 +23734,7 @@ setup_new_fields(THD *thd, List<Item> &fields,
enum_resolution_type not_used;
DBUG_ENTER("setup_new_fields");
- thd->mark_used_columns= MARK_COLUMNS_READ; // Not really needed, but...
+ thd->column_usage= MARK_COLUMNS_READ; // Not really needed, but...
for (; new_field ; new_field= new_field->next)
{
if ((item= find_item_in_list(*new_field->item, fields, &counter,
@@ -22901,6 +23757,10 @@ setup_new_fields(THD *thd, List<Item> &fields,
Try to use the fields in the order given by 'order' to allow one to
optimize away 'order by'.
+
+ @retval
+ 0 OOM error if thd->is_fatal_error is set. Otherwise group was eliminated
+ # Pointer to new group
*/
ORDER *
@@ -22963,6 +23823,8 @@ create_distinct_group(THD *thd, Ref_ptr_array ref_pointer_array,
BIT type and will be returned [el]client.
*/
Item_field *new_item= new (thd->mem_root) Item_field(thd, (Item_field*)item);
+ if (!new_item)
+ return 0;
int el= all_fields.elements;
orig_ref_pointer_array[el]= new_item;
all_fields.push_front(new_item, thd->mem_root);
@@ -23119,13 +23981,10 @@ get_sort_by_table(ORDER *a,ORDER *b, List<TABLE_LIST> &tables,
calc how big buffer we need for comparing group entries.
*/
-static void
-calc_group_buffer(JOIN *join,ORDER *group)
+void calc_group_buffer(TMP_TABLE_PARAM *param, ORDER *group)
{
uint key_length=0, parts=0, null_parts=0;
- if (group)
- join->group= 1;
for (; group ; group=group->next)
{
Item *group_item= *group->item;
@@ -23195,9 +24054,16 @@ calc_group_buffer(JOIN *join,ORDER *group)
if (group_item->maybe_null)
null_parts++;
}
- join->tmp_table_param.group_length=key_length+null_parts;
- join->tmp_table_param.group_parts=parts;
- join->tmp_table_param.group_null_parts=null_parts;
+ param->group_length= key_length + null_parts;
+ param->group_parts= parts;
+ param->group_null_parts= null_parts;
+}
+
+static void calc_group_buffer(JOIN *join, ORDER *group)
+{
+ if (group)
+ join->group= 1;
+ calc_group_buffer(&join->tmp_table_param, group);
}
@@ -23437,7 +24303,9 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,
real_pos->type() == Item::COND_ITEM) &&
!real_pos->with_sum_func)
{ // Save for send fields
+ LEX_CSTRING real_name= pos->name;
pos= real_pos;
+ pos->name= real_name;
/* TODO:
In most cases this result will be sent to the user.
This should be changed to use copy_int or copy_real depending
@@ -23646,7 +24514,10 @@ change_to_use_tmp_fields(THD *thd, Ref_ptr_array ref_pointer_array,
item->with_window_func)
item_field= item;
else if (item->type() == Item::FIELD_ITEM)
- item_field= item->get_tmp_table_item(thd);
+ {
+ if (!(item_field= item->get_tmp_table_item(thd)))
+ DBUG_RETURN(true);
+ }
else if (item->type() == Item::FUNC_ITEM &&
((Item_func*)item)->functype() == Item_func::SUSERVAR_FUNC)
{
@@ -23692,14 +24563,15 @@ change_to_use_tmp_fields(THD *thd, Ref_ptr_array ref_pointer_array,
ifield->db_name= iref->db_name;
}
#ifndef DBUG_OFF
- if (!item_field->name)
+ if (!item_field->name.str)
{
char buff[256];
String str(buff,sizeof(buff),&my_charset_bin);
str.length(0);
str.extra_allocation(1024);
item->print(&str, QT_ORDINARY);
- item_field->name= thd->strmake(str.ptr(),str.length());
+ item_field->name.str= thd->strmake(str.ptr(), str.length());
+ item_field->name.length= str.length();
}
#endif
}
@@ -23753,8 +24625,13 @@ change_refs_to_tmp_fields(THD *thd, Ref_ptr_array ref_pointer_array,
if (item->type() == Item::SUM_FUNC_ITEM && item->const_item())
new_item= item;
else
- new_item= item->get_tmp_table_item(thd);
- res_all_fields.push_back(new_item, thd->mem_root);
+ {
+ if (!(new_item= item->get_tmp_table_item(thd)))
+ return 1;
+ }
+
+ if (res_all_fields.push_back(new_item, thd->mem_root))
+ return 1;
ref_pointer_array[((i < border)? all_fields.elements-i-1 : i-border)]=
new_item;
}
@@ -23906,7 +24783,7 @@ copy_funcs(Item **func_ptr, const THD *thd)
TODO: change it for a real status check when Item::val_xxx()
are extended to return status code.
*/
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
return TRUE;
}
return FALSE;
@@ -23940,7 +24817,7 @@ static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab)
value),
thd->mem_root);
}
- if (thd->is_fatal_error)
+ if (unlikely(thd->is_fatal_error))
DBUG_RETURN(TRUE);
if (!cond->fixed)
{
@@ -23960,7 +24837,7 @@ static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab)
{
Item *new_cond= and_conds(thd, cond_copy,
join_tab->select->pre_idx_push_select_cond);
- if (!new_cond->fixed && new_cond->fix_fields(thd, &new_cond))
+ if (new_cond->fix_fields_if_needed(thd, &new_cond))
error= 1;
join_tab->pre_idx_push_select_cond=
join_tab->select->pre_idx_push_select_cond= new_cond;
@@ -24055,8 +24932,9 @@ static bool change_group_ref(THD *thd, Item_func *expr, ORDER *group_list,
if (item->eq(*group_tmp->item,0))
{
Item *new_item;
- if (!(new_item= new (thd->mem_root) Item_ref(thd, context, group_tmp->item, 0,
- item->name)))
+ if (!(new_item= new (thd->mem_root) Item_ref(thd, context,
+ group_tmp->item, 0,
+ &item->name)))
return 1; // fatal_error is set
thd->change_item_tree(arg, new_item);
arg_changed= TRUE;
@@ -24119,7 +24997,9 @@ bool JOIN::rollup_init()
*/
for (i= 0 ; i < send_group_parts ; i++)
{
- rollup.null_items[i]= new (thd->mem_root) Item_null_result(thd);
+ if (!(rollup.null_items[i]= new (thd->mem_root) Item_null_result(thd)))
+ return true;
+
List<Item> *rollup_fields= &rollup.fields[i];
rollup_fields->empty();
rollup.ref_pointer_arrays[i]= Ref_ptr_array(ref_array, all_fields.elements);
@@ -24429,7 +25309,8 @@ int JOIN::rollup_write_data(uint idx, TMP_TABLE_PARAM *tmp_table_param_arg, TABL
item->save_in_result_field(1);
}
copy_sum_funcs(sum_funcs_end[i+1], sum_funcs_end[i]);
- if ((write_error= table_arg->file->ha_write_tmp_row(table_arg->record[0])))
+ if (unlikely((write_error=
+ table_arg->file->ha_write_tmp_row(table_arg->record[0]))))
{
if (create_internal_tmp_table_from_heap(thd, table_arg,
tmp_table_param_arg->start_recinfo,
@@ -24463,8 +25344,12 @@ void JOIN::clear()
}
-/*
+/**
Print an EXPLAIN line with all NULLs and given message in the 'Extra' column
+
+ @retval
+ 0 ok
+ 1 OOM error or error from send_data()
*/
int print_explain_message_line(select_result_sink *result,
@@ -24523,7 +25408,7 @@ int print_explain_message_line(select_result_sink *result,
else
item_list.push_back(item_null, mem_root);
- if (result->send_data(item_list))
+ if (unlikely(thd->is_fatal_error) || unlikely(result->send_data(item_list)))
return 1;
return 0;
}
@@ -24557,13 +25442,14 @@ int append_possible_keys(MEM_ROOT *alloc, String_list &list, TABLE *table,
for (j=0 ; j < table->s->keys ; j++)
{
if (possible_keys.is_set(j))
- list.append_str(alloc, table->key_info[j].name);
+ if (!(list.append_str(alloc, table->key_info[j].name.str)))
+ return 1;
}
return 0;
}
-void JOIN_TAB::save_explain_data(Explain_table_access *eta,
+bool JOIN_TAB::save_explain_data(Explain_table_access *eta,
table_map prefix_tables,
bool distinct_arg, JOIN_TAB *first_top_tab)
{
@@ -24592,9 +25478,11 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta,
if (filesort)
{
- eta->pre_join_sort= new (thd->mem_root) Explain_aggr_filesort(thd->mem_root,
- thd->lex->analyze_stmt,
- filesort);
+ if (!(eta->pre_join_sort=
+ new (thd->mem_root) Explain_aggr_filesort(thd->mem_root,
+ thd->lex->analyze_stmt,
+ filesort)))
+ return 1;
}
tracker= &eta->tracker;
@@ -24610,7 +25498,7 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta,
if (table->derived_select_number)
{
/* Derived table name generation */
- int len= my_snprintf(table_name_buffer, sizeof(table_name_buffer)-1,
+ size_t len= my_snprintf(table_name_buffer, sizeof(table_name_buffer)-1,
"<derived%u>",
table->derived_select_number);
eta->table_name.copy(table_name_buffer, len, cs);
@@ -24619,7 +25507,7 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta,
{
JOIN_TAB *ctab= bush_children->start;
/* table */
- int len= my_snprintf(table_name_buffer,
+ size_t len= my_snprintf(table_name_buffer,
sizeof(table_name_buffer)-1,
"<subquery%d>",
ctab->emb_sj_nest->sj_subq_pred->get_identifier());
@@ -24648,7 +25536,7 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta,
}
}
}
- eta->table_name.copy(real_table->alias, strlen(real_table->alias), cs);
+ eta->table_name.copy(real_table->alias.str, real_table->alias.length, cs);
}
/* "partitions" column */
@@ -24691,7 +25579,8 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta,
// psergey-todo: why does this use thd MEM_ROOT??? Doesn't this
// break ANALYZE ? thd->mem_root will be freed, and after that we will
// attempt to print the query plan?
- append_possible_keys(thd->mem_root, eta->possible_keys, table, keys);
+ if (append_possible_keys(thd->mem_root, eta->possible_keys, table, keys))
+ return 1;
// psergey-todo: ^ check for error return code
/* Build "key", "key_len", and "ref" */
@@ -24712,7 +25601,8 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta,
*/
if (tab_select && tab_select->quick && tab_type != JT_CONST)
{
- eta->quick_info= tab_select->quick->get_explain(thd->mem_root);
+ if (!(eta->quick_info= tab_select->quick->get_explain(thd->mem_root)))
+ return 1;
}
if (key_info) /* 'index' or 'ref' access */
@@ -24725,10 +25615,23 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta,
for (uint kp= 0; kp < ref.key_parts; kp++)
{
if ((key_part_map(1) << kp) & ref.const_ref_part_map)
- eta->ref_list.append_str(thd->mem_root, "const");
+ {
+ if (!(eta->ref_list.append_str(thd->mem_root, "const")))
+ return 1;
+ /*
+ create_ref_for_key() handles keypart=const equalities as follows:
+ - non-EXPLAIN execution will copy the "const" to lookup tuple
+ immediately and will not add an element to ref.key_copy
+ - EXPLAIN will put an element into ref.key_copy. Since we've
+ just printed "const" for it, we should skip it here
+ */
+ if (thd->lex->describe)
+ key_ref++;
+ }
else
{
- eta->ref_list.append_str(thd->mem_root, (*key_ref)->name());
+ if (!(eta->ref_list.append_str(thd->mem_root, (*key_ref)->name())))
+ return 1;
key_ref++;
}
}
@@ -24962,13 +25865,13 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta,
{
char namebuf[NAME_LEN];
/* Derived table name generation */
- int len= my_snprintf(namebuf, sizeof(namebuf)-1,
+ size_t len= my_snprintf(namebuf, sizeof(namebuf)-1,
"<derived%u>",
prev_table->derived_select_number);
eta->firstmatch_table_name.append(namebuf, len);
}
else
- eta->firstmatch_table_name.append(prev_table->pos_in_table_list->alias);
+ eta->firstmatch_table_name.append(&prev_table->pos_in_table_list->alias);
}
}
@@ -24985,7 +25888,8 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta,
if (cache)
{
eta->push_extra(ET_USING_JOIN_BUFFER);
- cache->save_explain_data(&eta->bka_type);
+ if (cache->save_explain_data(&eta->bka_type))
+ return 1;
}
}
@@ -24998,15 +25902,21 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta,
/* The same for non-merged semi-joins */
eta->non_merged_sjm_number = get_non_merged_semijoin_select();
+
+ return 0;
}
/*
Walk through join->aggr_tables and save aggregation/grouping query plan into
an Explain_select object
+
+ @retval
+ 0 ok
+ 1 error
*/
-void save_agg_explain_data(JOIN *join, Explain_select *xpl_sel)
+bool save_agg_explain_data(JOIN *join, Explain_select *xpl_sel)
{
JOIN_TAB *join_tab=join->join_tab + join->exec_join_tab_cnt();
Explain_aggr_node *prev_node;
@@ -25018,7 +25928,8 @@ void save_agg_explain_data(JOIN *join, Explain_select *xpl_sel)
{
// Each aggregate means a temp.table
prev_node= node;
- node= new (thd->mem_root) Explain_aggr_tmp_table;
+ if (!(node= new (thd->mem_root) Explain_aggr_tmp_table))
+ return 1;
node->child= prev_node;
if (join_tab->window_funcs_step)
@@ -25026,19 +25937,20 @@ void save_agg_explain_data(JOIN *join, Explain_select *xpl_sel)
Explain_aggr_node *new_node=
join_tab->window_funcs_step->save_explain_plan(thd->mem_root,
is_analyze);
- if (new_node)
- {
- prev_node=node;
- node= new_node;
- node->child= prev_node;
- }
+ if (!new_node)
+ return 1;
+
+ prev_node=node;
+ node= new_node;
+ node->child= prev_node;
}
/* The below matches execution in join_init_read_record() */
if (join_tab->distinct)
{
prev_node= node;
- node= new (thd->mem_root) Explain_aggr_remove_dups;
+ if (!(node= new (thd->mem_root) Explain_aggr_remove_dups))
+ return 1;
node->child= prev_node;
}
@@ -25046,20 +25958,27 @@ void save_agg_explain_data(JOIN *join, Explain_select *xpl_sel)
{
Explain_aggr_filesort *eaf =
new (thd->mem_root) Explain_aggr_filesort(thd->mem_root, is_analyze, join_tab->filesort);
+ if (!eaf)
+ return 1;
prev_node= node;
node= eaf;
node->child= prev_node;
}
}
xpl_sel->aggr_tree= node;
+ return 0;
}
-/*
+/**
Save Query Plan Footprint
@note
Currently, this function may be called multiple times
+
+ @retval
+ 0 ok
+ 1 error
*/
int JOIN::save_explain_data_intern(Explain_query *output,
@@ -25068,7 +25987,6 @@ int JOIN::save_explain_data_intern(Explain_query *output,
const char *message)
{
JOIN *join= this; /* Legacy: this code used to be a non-member function */
- int cur_error= 0;
DBUG_ENTER("JOIN::save_explain_data_intern");
DBUG_PRINT("info", ("Select %p, type %s, message %s",
join->select_lex, join->select_lex->type,
@@ -25086,10 +26004,10 @@ int JOIN::save_explain_data_intern(Explain_query *output,
if (message)
{
- explain= new (output->mem_root) Explain_select(output->mem_root,
- thd->lex->analyze_stmt);
- if (!explain)
- DBUG_RETURN(1); // EoM
+ if (!(explain= new (output->mem_root)
+ Explain_select(output->mem_root,
+ thd->lex->analyze_stmt)))
+ DBUG_RETURN(1);
#ifndef DBUG_OFF
explain->select_lex= select_lex;
#endif
@@ -25097,6 +26015,7 @@ int JOIN::save_explain_data_intern(Explain_query *output,
explain->select_id= join->select_lex->select_number;
explain->select_type= join->select_lex->type;
+ explain->linkage= select_lex->linkage;
explain->using_temporary= need_tmp;
explain->using_filesort= need_order_arg;
/* Setting explain->message means that all other members are invalid */
@@ -25104,17 +26023,22 @@ int JOIN::save_explain_data_intern(Explain_query *output,
if (select_lex->master_unit()->derived)
explain->connection_type= Explain_node::EXPLAIN_NODE_DERIVED;
- save_agg_explain_data(this, explain);
+ if (save_agg_explain_data(this, explain))
+ DBUG_RETURN(1);
+
output->add_node(explain);
}
else if (pushdown_query)
{
- explain= new (output->mem_root) Explain_select(output->mem_root,
- thd->lex->analyze_stmt);
+ if (!(explain= new (output->mem_root)
+ Explain_select(output->mem_root,
+ thd->lex->analyze_stmt)))
+ DBUG_RETURN(1);
select_lex->set_explain_type(true);
explain->select_id= select_lex->select_number;
explain->select_type= select_lex->type;
+ explain->linkage= select_lex->linkage;
explain->using_temporary= need_tmp;
explain->using_filesort= need_order_arg;
explain->message= "Storage engine handles GROUP BY";
@@ -25129,15 +26053,22 @@ int JOIN::save_explain_data_intern(Explain_query *output,
explain= xpl_sel=
new (output->mem_root) Explain_select(output->mem_root,
thd->lex->analyze_stmt);
+ if (!explain)
+ DBUG_RETURN(1);
+
table_map used_tables=0;
join->select_lex->set_explain_type(true);
xpl_sel->select_id= join->select_lex->select_number;
xpl_sel->select_type= join->select_lex->type;
+ xpl_sel->linkage= select_lex->linkage;
+ xpl_sel->is_lateral= ((select_lex->linkage == DERIVED_TABLE_TYPE) &&
+ (select_lex->uncacheable & UNCACHEABLE_DEPENDENT));
if (select_lex->master_unit()->derived)
xpl_sel->connection_type= Explain_node::EXPLAIN_NODE_DERIVED;
- save_agg_explain_data(this, xpl_sel);
+ if (save_agg_explain_data(this, xpl_sel))
+ DBUG_RETURN(1);
xpl_sel->exec_const_cond= exec_const_cond;
xpl_sel->outer_ref_cond= outer_ref_cond;
@@ -25170,6 +26101,8 @@ int JOIN::save_explain_data_intern(Explain_query *output,
Explain_table_access *eta= (new (output->mem_root)
Explain_table_access(output->mem_root));
+ if (!eta)
+ DBUG_RETURN(1);
if (tab->bush_root_tab != prev_bush_root_tab)
{
if (tab->bush_root_tab)
@@ -25177,7 +26110,9 @@ int JOIN::save_explain_data_intern(Explain_query *output,
/*
We've entered an SJ-Materialization nest. Create an object for it.
*/
- cur_parent= new (output->mem_root) Explain_basic_join(output->mem_root);
+ if (!(cur_parent=
+ new (output->mem_root) Explain_basic_join(output->mem_root)))
+ DBUG_RETURN(1);
JOIN_TAB *first_child= tab->bush_root_tab->bush_children->start;
cur_parent->select_id=
@@ -25197,7 +26132,8 @@ int JOIN::save_explain_data_intern(Explain_query *output,
prev_bush_root_tab= tab->bush_root_tab;
cur_parent->add_table(eta, output);
- tab->save_explain_data(eta, used_tables, distinct_arg, first_top_tab);
+ if (tab->save_explain_data(eta, used_tables, distinct_arg, first_top_tab))
+ DBUG_RETURN(1);
if (saved_join_tab)
tab= saved_join_tab;
@@ -25229,10 +26165,10 @@ int JOIN::save_explain_data_intern(Explain_query *output,
}
}
- if (!cur_error && select_lex->is_top_level_node())
+ if (select_lex->is_top_level_node())
output->query_plan_ready();
- DBUG_RETURN(cur_error);
+ DBUG_RETURN(0);
}
@@ -25311,15 +26247,16 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
sl->options|= SELECT_DESCRIBE;
}
- if (unit->is_union())
+ if (unit->is_unit_op())
{
if (unit->union_needs_tmp_table() && unit->fake_select_lex)
{
unit->fake_select_lex->select_number= FAKE_SELECT_LEX_ID; // just for initialization
- unit->fake_select_lex->type= "UNION RESULT";
+ unit->fake_select_lex->type= unit_operation_text[unit->common_op()];
unit->fake_select_lex->options|= SELECT_DESCRIBE;
}
- if (!(res= unit->prepare(thd, result, SELECT_NO_UNLOCK | SELECT_DESCRIBE)))
+ if (!(res= unit->prepare(unit->derived, result,
+ SELECT_NO_UNLOCK | SELECT_DESCRIBE)))
res= unit->exec();
}
else
@@ -25540,8 +26477,8 @@ Index_hint::print(THD *thd, String *str)
strlen(primary_key_name)))
str->append(primary_key_name);
else
- append_identifier(thd, str, key_name.str, key_name.length);
- }
+ append_identifier(thd, str, &key_name);
+}
str->append(')');
}
@@ -25597,10 +26534,10 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str,
if (!(belong_to_view &&
belong_to_view->compact_view_format))
{
- append_identifier(thd, str, view_db.str, view_db.length);
+ append_identifier(thd, str, &view_db);
str->append('.');
}
- append_identifier(thd, str, view_name.str, view_name.length);
+ append_identifier(thd, str, &view_name);
cmp_name= view_name.str;
}
else if (derived)
@@ -25615,8 +26552,8 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str,
}
else
{
- append_identifier(thd, str, table_name, table_name_length);
- cmp_name= table_name;
+ append_identifier(thd, str, &table_name);
+ cmp_name= table_name.str;
}
}
else
@@ -25626,19 +26563,18 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str,
if (!(belong_to_view &&
belong_to_view->compact_view_format))
{
- append_identifier(thd, str, db, db_length);
+ append_identifier(thd, str, &db);
str->append('.');
}
if (schema_table)
{
- append_identifier(thd, str, schema_table_name,
- strlen(schema_table_name));
- cmp_name= schema_table_name;
+ append_identifier(thd, str, &schema_table_name);
+ cmp_name= schema_table_name.str;
}
else
{
- append_identifier(thd, str, table_name, table_name_length);
- cmp_name= table_name;
+ append_identifier(thd, str, &table_name);
+ cmp_name= table_name.str;
}
#ifdef WITH_PARTITION_STORAGE_ENGINE
if (partition_names && partition_names->elements)
@@ -25657,23 +26593,26 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str,
}
#endif /* WITH_PARTITION_STORAGE_ENGINE */
}
- if (my_strcasecmp(table_alias_charset, cmp_name, alias))
+ if (table && table->versioned())
+ vers_conditions.print(str, query_type);
+
+ if (my_strcasecmp(table_alias_charset, cmp_name, alias.str))
{
char t_alias_buff[MAX_ALIAS_NAME];
- const char *t_alias= alias;
+ LEX_CSTRING t_alias= alias;
str->append(' ');
if (lower_case_table_names == 1)
{
- if (alias && alias[0])
+ if (alias.str && alias.str[0])
{
- strmov(t_alias_buff, alias);
- my_casedn_str(files_charset_info, t_alias_buff);
- t_alias= t_alias_buff;
+ strmov(t_alias_buff, alias.str);
+ t_alias.length= my_casedn_str(files_charset_info, t_alias_buff);
+ t_alias.str= t_alias_buff;
}
}
- append_identifier(thd, str, t_alias, strlen(t_alias));
+ append_identifier(thd, str, &t_alias);
}
if (index_hints)
@@ -25694,6 +26633,23 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str,
void st_select_lex::print(THD *thd, String *str, enum_query_type query_type)
{
DBUG_ASSERT(thd);
+
+ if (tvc)
+ {
+ tvc->print(thd, str, query_type);
+ return;
+ }
+
+ if ((query_type & QT_SHOW_SELECT_NUMBER) &&
+ thd->lex->all_selects_list &&
+ thd->lex->all_selects_list->link_next &&
+ select_number != UINT_MAX &&
+ select_number != INT_MAX)
+ {
+ str->append("/* select#");
+ str->append_ulonglong(select_number);
+ str->append(" */ ");
+ }
str->append(STRING_WITH_LEN("select "));
@@ -25866,7 +26822,7 @@ bool JOIN::change_result(select_result *new_result, select_result *old_result)
{
result= new_result;
if (result->prepare(fields_list, select_lex->master_unit()) ||
- result->prepare2())
+ result->prepare2(this))
DBUG_RETURN(true); /* purecov: inspected */
DBUG_RETURN(false);
}
@@ -26339,7 +27295,15 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
*/
if (ref_key >= 0 && ref_key != MAX_KEY && tab->type == JT_REF)
{
- if (table->quick_keys.is_set(ref_key))
+ /*
+ If ref access uses keypart=const for all its key parts,
+ and quick select uses the same # of key parts, then they are equivalent.
+ Reuse #rows estimate from quick select as it is more precise.
+ */
+ 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];
else
{
@@ -26691,22 +27655,20 @@ ulong check_selectivity(THD *thd,
}
it.rewind();
- if (file->ha_rnd_init_with_error(1))
+ if (unlikely(file->ha_rnd_init_with_error(1)))
DBUG_RETURN(0);
do
{
error= file->ha_rnd_next(record);
- if (thd->killed)
+ if (unlikely(thd->killed))
{
thd->send_kill_message();
count= 0;
goto err;
}
- if (error)
+ if (unlikely(error))
{
- if (error == HA_ERR_RECORD_DELETED)
- continue;
if (error == HA_ERR_END_OF_FILE)
break;
goto err;
@@ -26760,7 +27722,6 @@ AGGR_OP::prepare_tmp_table()
join->select_options))
return true;
(void) table->file->extra(HA_EXTRA_WRITE_CACHE);
- empty_record(table);
}
/* If it wasn't already, start index scan for grouping using table index. */
if (!table->file->inited && table->group &&
@@ -26857,13 +27818,13 @@ AGGR_OP::end_send()
error= join_init_read_record(join_tab);
}
else
- error= join_tab->read_record.read_record(&join_tab->read_record);
+ error= join_tab->read_record.read_record();
- if (error > 0 || (join->thd->is_error())) // Fatal error
+ if (unlikely(error > 0 || (join->thd->is_error()))) // Fatal error
rc= NESTED_LOOP_ERROR;
else if (error < 0)
break;
- else if (join->thd->killed) // Aborted by user
+ else if (unlikely(join->thd->killed)) // Aborted by user
{
join->thd->send_kill_message();
rc= NESTED_LOOP_KILLED;
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 4584460ca3f..df9c9b2eb0e 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -73,9 +73,45 @@ typedef struct keyuse_t {
*/
uint sj_pred_no;
+ /*
+ If this is NULL than KEYUSE is always enabled.
+ Otherwise it points to the enabling flag for this keyuse (true <=> enabled)
+ */
+ bool *validity_ref;
+
bool is_for_hash_join() { return is_hash_join_key_no(key); }
} KEYUSE;
+
+struct KEYUSE_EXT: public KEYUSE
+{
+ /*
+ This keyuse can be used only when the partial join being extended
+ contains the tables from this table map
+ */
+ table_map needed_in_prefix;
+ /* The enabling flag for keyuses usable for splitting */
+ bool validity_var;
+};
+
+/// Used when finding key fields
+struct KEY_FIELD {
+ Field *field;
+ Item_bool_func *cond;
+ Item *val; ///< May be empty if diff constant
+ uint level;
+ uint optimize;
+ bool eq_func;
+ /**
+ If true, the condition this struct represents will not be satisfied
+ when val IS NULL.
+ */
+ bool null_rejecting;
+ bool *cond_guard; /* See KEYUSE::cond_guard */
+ uint sj_pred_no; /* See KEYUSE::sj_pred_no */
+};
+
+
#define NO_KEYPART ((uint)(-1))
class store_key;
@@ -201,6 +237,8 @@ class SJ_TMP_TABLE;
class JOIN_TAB_RANGE;
class AGGR_OP;
class Filesort;
+struct SplM_plan_info;
+class SplM_opt_info;
typedef struct st_join_table {
TABLE *table;
@@ -224,8 +262,12 @@ typedef struct st_join_table {
/*
Pointer to the associated ON expression. on_expr_ref=!NULL except for
degenerate joins.
- *on_expr_ref!=NULL for tables that are first inner tables within an outer
- join.
+
+ Optimization phase: *on_expr_ref!=NULL for tables that are the single
+ tables on the inner side of the outer join (t1 LEFT JOIN t2 ON...)
+
+ Execution phase: *on_expr_ref!=NULL for tables that are first inner tables
+ within an outer join (which may have multiple tables)
*/
Item **on_expr_ref;
COND_EQUAL *cond_equal; /**< multiple equalities for the on expression */
@@ -431,7 +473,7 @@ typedef struct st_join_table {
Window_funcs_computation* window_funcs_step;
/**
- List of topmost expressions in the select list. The *next* JOIN TAB
+ List of topmost expressions in the select list. The *next* JOIN_TAB
in the plan should use it to obtain correct values. Same applicable to
all_fields. These lists are needed because after tmp tables functions
will be turned to fields. These variables are pointing to
@@ -607,13 +649,19 @@ typedef struct st_join_table {
void remove_redundant_bnl_scan_conds();
- void save_explain_data(Explain_table_access *eta, table_map prefix_tables,
+ bool save_explain_data(Explain_table_access *eta, table_map prefix_tables,
bool distinct, struct st_join_table *first_top_tab);
bool use_order() const; ///< Use ordering provided by chosen index?
bool sort_table();
bool remove_duplicates();
+
void partial_cleanup();
+ void add_keyuses_for_splitting();
+ SplM_plan_info *choose_best_splitting(double record_count,
+ table_map remaining_tables);
+ bool fix_splitting(SplM_plan_info *spl_plan, table_map remaining_tables,
+ bool is_const_table);
} JOIN_TAB;
@@ -821,7 +869,7 @@ public:
void set_empty()
{
sjm_scan_need_tables= 0;
- LINT_INIT_STRUCT(sjm_scan_last_inner);
+ sjm_scan_last_inner= 0;
is_used= FALSE;
}
void set_from_prev(struct st_position *prev);
@@ -919,6 +967,9 @@ typedef struct st_position
Firstmatch_picker firstmatch_picker;
LooseScan_picker loosescan_picker;
Sj_materialization_picker sjmat_picker;
+
+ /* Info on splitting plan used at this position */
+ SplM_plan_info *spl_plan;
} POSITION;
typedef Bounds_checked_array<Item_null_result*> Item_null_array;
@@ -1052,14 +1103,16 @@ protected:
/* Support for plan reoptimization with rewritten conditions. */
enum_reopt_result reoptimize(Item *added_where, table_map join_tables,
Join_plan_state *save_to);
- void save_query_plan(Join_plan_state *save_to);
- void reset_query_plan();
- void restore_query_plan(Join_plan_state *restore_from);
/* Choose a subquery plan for a table-less subquery. */
bool choose_tableless_subquery_plan();
void handle_implicit_grouping_with_window_funcs();
public:
+ void save_query_plan(Join_plan_state *save_to);
+ void reset_query_plan();
+ void restore_query_plan(Join_plan_state *restore_from);
+
+public:
JOIN_TAB *join_tab, **best_ref;
/* List of fields that aren't under an aggregate function */
@@ -1275,6 +1328,8 @@ public:
and should be taken from the appropriate JOIN_TAB
*/
bool filesort_found_rows;
+
+ bool subq_exit_fl;
ROLLUP rollup; ///< Used with rollup
@@ -1381,7 +1436,8 @@ public:
enum join_optimization_state { NOT_OPTIMIZED=0,
OPTIMIZATION_IN_PROGRESS=1,
- OPTIMIZATION_DONE=2};
+ OPTIMIZATION_PHASE_1_DONE=2,
+ OPTIMIZATION_DONE=3};
// state of JOIN optimization
enum join_optimization_state optimization_state;
bool initialized; ///< flag to avoid double init_execution calls
@@ -1390,6 +1446,9 @@ public:
enum { QEP_NOT_PRESENT_YET, QEP_AVAILABLE, QEP_DELETED} have_query_plan;
+ // if keep_current_rowid=true, whether they should be saved in temporary table
+ bool tmp_table_keep_current_rowid;
+
/*
Additional WHERE and HAVING predicates to be considered for IN=>EXISTS
subquery transformation of a JOIN object.
@@ -1406,6 +1465,20 @@ public:
bool set_group_rpa;
/** Exec time only: TRUE <=> current group has been sent */
bool group_sent;
+ /**
+ TRUE if the query contains an aggregate function but has no GROUP
+ BY clause.
+ */
+ bool implicit_grouping;
+
+ bool with_two_phase_optimization;
+
+ /* Saved execution plan for this join */
+ Join_plan_state *save_qep;
+ /* Info on splittability of the table materialized by this plan*/
+ SplM_opt_info *spl_opt_info;
+ /* Contains info on keyuses usable for splitting */
+ Dynamic_array<KEYUSE_EXT> *ext_keyuses_for_splitting;
JOIN_TAB *sort_and_group_aggr_tab;
@@ -1424,7 +1497,7 @@ public:
table_count= 0;
top_join_tab_count= 0;
const_tables= 0;
- const_table_map= 0;
+ const_table_map= found_const_table_map= 0;
aggr_tables= 0;
eliminated_tables= 0;
join_list= 0;
@@ -1452,6 +1525,11 @@ public:
ordered_index_usage= ordered_index_void;
need_distinct= 0;
skip_sort_order= 0;
+ with_two_phase_optimization= 0;
+ save_qep= 0;
+ spl_opt_info= 0;
+ ext_keyuses_for_splitting= 0;
+ spl_opt_info= 0;
need_tmp= 0;
hidden_group_fields= 0; /*safety*/
error= 0;
@@ -1476,6 +1554,7 @@ public:
pushdown_query= 0;
original_join_tab= 0;
explain= NULL;
+ tmp_table_keep_current_rowid= 0;
all_fields= fields_arg;
if (&fields_list != &fields_arg) /* Avoid valgrind-warning */
@@ -1520,6 +1599,8 @@ public:
bool prepare_stage2();
int optimize();
int optimize_inner();
+ int optimize_stage2();
+ bool build_explain();
int reinit();
int init_execution();
void exec();
@@ -1665,12 +1746,20 @@ public:
{
return (unit->item && unit->item->is_in_predicate());
}
- void save_explain_data(Explain_query *output, bool can_overwrite,
+ bool save_explain_data(Explain_query *output, bool can_overwrite,
bool need_tmp_table, bool need_order, bool distinct);
int save_explain_data_intern(Explain_query *output, bool need_tmp_table,
bool need_order, bool distinct,
const char *message);
JOIN_TAB *first_breadth_first_tab() { return join_tab; }
+ bool check_two_phase_optimization(THD *thd);
+ bool inject_cond_into_where(Item *injected_cond);
+ bool check_for_splittable_materialized();
+ void add_keyuses_for_splitting();
+ bool inject_best_splitting_cond(table_map remaining_tables);
+ bool fix_all_splittings_in_plan();
+
+ bool transform_in_predicates_into_in_subq(THD *thd);
private:
/**
Create a temporary table to be used for processing DISTINCT/ORDER
@@ -1701,14 +1790,10 @@ private:
*/
void optimize_distinct();
- /**
- TRUE if the query contains an aggregate function but has no GROUP
- BY clause.
- */
- bool implicit_grouping;
void cleanup_item_list(List<Item> &items) const;
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);
};
enum enum_with_bush_roots { WITH_BUSH_ROOTS, WITHOUT_BUSH_ROOTS};
@@ -1740,7 +1825,7 @@ void copy_fields(TMP_TABLE_PARAM *param);
bool copy_funcs(Item **func_ptr, const THD *thd);
uint find_shortest_key(TABLE *table, const key_map *usable_keys);
Field* create_tmp_field_from_field(THD *thd, Field* org_field,
- const char *name, TABLE *table,
+ LEX_CSTRING *name, TABLE *table,
Item_field *item);
bool is_indexed_agg_distinct(JOIN *join, List<Item_field> *out_args);
@@ -2018,7 +2103,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
class Virtual_tmp_table: public TABLE
{
/**
- Destruct collected fields. This method is called on errors only,
+ Destruct collected fields. This method can be called on errors,
when we could not make the virtual temporary table completely,
e.g. when some of the fields could not be created or added.
@@ -2029,7 +2114,10 @@ class Virtual_tmp_table: public TABLE
void destruct_fields()
{
for (uint i= 0; i < s->fields; i++)
+ {
+ field[i]->free();
delete field[i]; // to invoke the field destructor
+ }
s->fields= 0; // safety
}
@@ -2056,13 +2144,16 @@ public:
@param thd - Current thread.
*/
static void *operator new(size_t size, THD *thd) throw();
- static void operator delete(void *ptr, size_t size) {TRASH_FREE(ptr, size);}
+ static void operator delete(void *ptr, size_t size) { TRASH_FREE(ptr, size); }
+ static void operator delete(void *, THD *) throw(){}
Virtual_tmp_table(THD *thd) : m_alloced_field_count(0)
{
reset();
temp_pool_slot= MY_BIT_NONE;
in_use= thd;
+ copy_blobs= true;
+ alias.set("", 0, &my_charset_bin);
}
~Virtual_tmp_table()
@@ -2101,16 +2192,16 @@ public:
DBUG_ASSERT(s->blob_fields <= m_alloced_field_count);
s->blob_field[s->blob_fields - 1]= s->fields;
}
- s->fields++;
+ new_field->field_index= s->fields++;
return false;
}
/**
- Add fields from a Column_definition list
+ Add fields from a Spvar_definition list
@returns false - on success.
@returns true - on error.
*/
- bool add(List<Column_definition> &field_list);
+ bool add(List<Spvar_definition> &field_list);
/**
Open a virtual table for read/write:
@@ -2124,6 +2215,48 @@ public:
@return true - on error (e.g. could not allocate the record buffer).
*/
bool open();
+
+ void set_all_fields_to_null()
+ {
+ for (uint i= 0; i < s->fields; i++)
+ field[i]->set_null();
+ }
+ /**
+ Set all fields from a compatible item list.
+ The number of fields in "this" must be equal to the number
+ of elements in "value".
+ */
+ bool sp_set_all_fields_from_item_list(THD *thd, List<Item> &items);
+
+ /**
+ Set all fields from a compatible item.
+ The number of fields in "this" must be the same with the number
+ of elements in "value".
+ */
+ bool sp_set_all_fields_from_item(THD *thd, Item *value);
+
+ /**
+ Find a ROW element index by its name
+ Assumes that "this" is used as a storage for a ROW-type SP variable.
+ @param [OUT] idx - the index of the found field is returned here
+ @param [IN] field_name - find a field with this name
+ @return true - on error (the field was not found)
+ @return false - on success (idx[0] was set to the field index)
+ */
+ bool sp_find_field_by_name(uint *idx, const LEX_CSTRING &name) const;
+
+ /**
+ Find a ROW element index by its name.
+ If the element is not found, and error is issued.
+ @param [OUT] idx - the index of the found field is returned here
+ @param [IN] var_name - the name of the ROW variable (for error reporting)
+ @param [IN] field_name - find a field with this name
+ @return true - on error (the field was not found)
+ @return false - on success (idx[0] was set to the field index)
+ */
+ bool sp_find_field_by_name_or_error(uint *idx,
+ const LEX_CSTRING &var_name,
+ const LEX_CSTRING &field_name) const;
};
@@ -2147,8 +2280,8 @@ public:
TABLE object ready for read and write in case of success
*/
-inline TABLE *
-create_virtual_tmp_table(THD *thd, List<Column_definition> &field_list)
+inline Virtual_tmp_table *
+create_virtual_tmp_table(THD *thd, List<Spvar_definition> &field_list)
{
Virtual_tmp_table *table;
if (!(table= new(thd) Virtual_tmp_table(thd)))
@@ -2210,6 +2343,10 @@ inline Item * and_items(THD *thd, Item* cond, Item *item)
{
return (cond ? (new (thd->mem_root) Item_cond_and(thd, cond, item)) : item);
}
+inline Item * or_items(THD *thd, Item* cond, Item *item)
+{
+ return (cond ? (new (thd->mem_root) Item_cond_or(thd, cond, item)) : item);
+}
bool choose_plan(JOIN *join, table_map join_tables);
void optimize_wo_join_buffering(JOIN *join, uint first_tab, uint last_tab,
table_map last_remaining_tables,
@@ -2268,10 +2405,11 @@ int append_possible_keys(MEM_ROOT *alloc, String_list &list, TABLE *table,
#define RATIO_TO_PACK_ROWS 2
#define MIN_STRING_LENGTH_TO_PACK_ROWS 10
+void calc_group_buffer(TMP_TABLE_PARAM *param, ORDER *group);
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 char* alias, bool do_not_open=FALSE,
+ const LEX_CSTRING *alias, bool do_not_open=FALSE,
bool keep_row_order= FALSE);
void free_tmp_table(THD *thd, TABLE *entry);
bool create_internal_tmp_table_from_heap(THD *thd, TABLE *table,
@@ -2291,6 +2429,11 @@ bool open_tmp_table(TABLE *table);
void setup_tmp_table_column_bitmaps(TABLE *table, uchar *bitmaps);
double prev_record_reads(const POSITION *positions, uint idx, table_map found_ref);
void fix_list_after_tbl_changes(SELECT_LEX *new_parent, List<TABLE_LIST> *tlist);
+double get_tmp_table_lookup_cost(THD *thd, double row_count, uint row_size);
+double get_tmp_table_write_cost(THD *thd, double row_count, uint row_size);
+void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array);
+bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse,
+ bool skip_unprefixed_keyparts);
struct st_cond_statistic
{
diff --git a/sql/sql_sequence.cc b/sql/sql_sequence.cc
new file mode 100644
index 00000000000..ffdb4b54c16
--- /dev/null
+++ b/sql/sql_sequence.cc
@@ -0,0 +1,995 @@
+/*
+ Copyright (c) 2017, MariaDB Corporation, Alibaba 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-1301 USA
+*/
+
+#include "mariadb.h"
+#include "sql_class.h"
+#include "sql_list.h"
+#include "sql_sequence.h"
+#include "ha_sequence.h"
+#include "sql_base.h"
+#include "sql_table.h" // write_bin_log
+#include "transaction.h"
+#include "lock.h"
+#include "sql_acl.h"
+
+struct Field_definition
+{
+ const char *field_name;
+ uint length;
+ const Type_handler *type_handler;
+ LEX_CSTRING comment;
+ ulong flags;
+};
+
+/*
+ Structure for all SEQUENCE tables
+
+ Note that the first field is named "next_val" to all us to have
+ NEXTVAL a reserved word that will on access be changed to
+ NEXTVAL(sequence_table). For this to work, the table can't have
+ a column named NEXTVAL.
+*/
+
+#define FL (NOT_NULL_FLAG | NO_DEFAULT_VALUE_FLAG)
+
+static Field_definition sequence_structure[]=
+{
+ {"next_not_cached_value", 21, &type_handler_longlong,
+ {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,
+ {STRING_WITH_LEN("increment value")}, FL},
+ {"cache_size", 21, &type_handler_longlong, {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")},
+ FL | UNSIGNED_FLAG },
+ {"cycle_count", 21, &type_handler_longlong,
+ {STRING_WITH_LEN("How many cycles have been done")}, FL},
+ {NULL, 0, &type_handler_longlong, {STRING_WITH_LEN("")}, 0}
+};
+
+#undef FL
+
+
+#define MAX_AUTO_INCREMENT_VALUE 65535
+
+/*
+ Check whether sequence values are valid.
+ Sets default values for fields that are not used, according to Oracle spec.
+
+ RETURN VALUES
+ false valid
+ true invalid
+*/
+
+bool sequence_definition::check_and_adjust(bool set_reserved_until)
+{
+ longlong max_increment;
+ DBUG_ENTER("sequence_definition::check");
+
+ if (!(real_increment= increment))
+ real_increment= global_system_variables.auto_increment_increment;
+
+ /*
+ If min_value is not set, set it to LONGLONG_MIN or 1, depending on
+ real_increment
+ */
+ if (!(used_fields & seq_field_used_min_value))
+ min_value= real_increment < 0 ? LONGLONG_MIN+1 : 1;
+
+ /*
+ If max_value is not set, set it to LONGLONG_MAX or -1, depending on
+ real_increment
+ */
+ if (!(used_fields & seq_field_used_max_value))
+ max_value= real_increment < 0 ? -1 : LONGLONG_MAX-1;
+
+ if (!(used_fields & seq_field_used_start))
+ {
+ /* Use min_value or max_value for start depending on real_increment */
+ start= real_increment < 0 ? max_value : min_value;
+ }
+
+ if (set_reserved_until)
+ reserved_until= start;
+
+ adjust_values(reserved_until);
+
+ /* To ensure that cache * real_increment will never overflow */
+ max_increment= (real_increment ?
+ llabs(real_increment) :
+ MAX_AUTO_INCREMENT_VALUE);
+
+ if (max_value >= start &&
+ max_value > min_value &&
+ start >= min_value &&
+ max_value != LONGLONG_MAX &&
+ min_value != LONGLONG_MIN &&
+ cache < (LONGLONG_MAX - max_increment) / max_increment &&
+ ((real_increment > 0 && reserved_until >= min_value) ||
+ (real_increment < 0 && reserved_until <= max_value)))
+ DBUG_RETURN(FALSE);
+
+ DBUG_RETURN(TRUE); // Error
+}
+
+
+/*
+ Read sequence values from a table
+*/
+
+void sequence_definition::read_fields(TABLE *table)
+{
+ my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
+ reserved_until= table->field[0]->val_int();
+ min_value= table->field[1]->val_int();
+ max_value= table->field[2]->val_int();
+ start= table->field[3]->val_int();
+ increment= table->field[4]->val_int();
+ cache= table->field[5]->val_int();
+ cycle= table->field[6]->val_int();
+ round= table->field[7]->val_int();
+ dbug_tmp_restore_column_map(table->read_set, old_map);
+ used_fields= ~(uint) 0;
+ print_dbug();
+}
+
+
+/*
+ Store sequence into a table row
+*/
+
+void sequence_definition::store_fields(TABLE *table)
+{
+ my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
+
+ /* zero possible delete markers & null bits */
+ memcpy(table->record[0], table->s->default_values, table->s->null_bytes);
+ table->field[0]->store(reserved_until, 0);
+ table->field[1]->store(min_value, 0);
+ table->field[2]->store(max_value, 0);
+ table->field[3]->store(start, 0);
+ table->field[4]->store(increment, 0);
+ table->field[5]->store(cache, 0);
+ table->field[6]->store((longlong) cycle != 0, 0);
+ table->field[7]->store((longlong) round, 1);
+
+ dbug_tmp_restore_column_map(table->write_set, old_map);
+ print_dbug();
+}
+
+
+/*
+ Check the sequence fields through seq_fields when creating a sequence.
+
+ RETURN VALUES
+ false Success
+ true Failure
+*/
+
+bool check_sequence_fields(LEX *lex, List<Create_field> *fields)
+{
+ Create_field *field;
+ List_iterator_fast<Create_field> it(*fields);
+ uint field_count;
+ uint field_no;
+ const char *reason;
+ DBUG_ENTER("check_sequence_fields");
+
+ field_count= fields->elements;
+ if (field_count != array_elements(sequence_structure)-1)
+ {
+ reason= "Wrong number of columns";
+ goto err;
+ }
+ if (lex->alter_info.key_list.elements > 0)
+ {
+ reason= "Sequence tables cannot have any keys";
+ goto err;
+ }
+ if (lex->alter_info.check_constraint_list.elements > 0)
+ {
+ reason= "Sequence tables cannot have any constraints";
+ goto err;
+ }
+ if (lex->alter_info.flags & ALTER_ORDER)
+ {
+ reason= "ORDER BY";
+ goto err;
+ }
+
+ for (field_no= 0; (field= it++); field_no++)
+ {
+ Field_definition *field_def= &sequence_structure[field_no];
+ if (my_strcasecmp(system_charset_info, field_def->field_name,
+ field->field_name.str) ||
+ field->flags != field_def->flags ||
+ field->type_handler() != field_def->type_handler ||
+ field->check_constraint || field->vcol_info)
+ {
+ reason= field->field_name.str;
+ goto err;
+ }
+ }
+ DBUG_RETURN(FALSE);
+
+err:
+ my_error(ER_SEQUENCE_INVALID_TABLE_STRUCTURE, MYF(0),
+ lex->select_lex.table_list.first->db.str,
+ lex->select_lex.table_list.first->table_name.str, reason);
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ Create the fields for a SEQUENCE TABLE
+
+ RETURN VALUES
+ false Success
+ true Failure (out of memory)
+*/
+
+bool prepare_sequence_fields(THD *thd, List<Create_field> *fields)
+{
+ Field_definition *field_info;
+ DBUG_ENTER("prepare_sequence_fields");
+
+ for (field_info= sequence_structure; field_info->field_name ; field_info++)
+ {
+ Create_field *new_field;
+ LEX_CSTRING field_name= {field_info->field_name,
+ strlen(field_info->field_name)};
+
+ if (unlikely(!(new_field= new Create_field())))
+ DBUG_RETURN(TRUE); /* purify inspected */
+
+ new_field->field_name= field_name;
+ new_field->set_handler(field_info->type_handler);
+ new_field->length= field_info->length;
+ new_field->char_length= field_info->length;
+ new_field->comment= field_info->comment;
+ new_field->flags= field_info->flags;
+ if (unlikely(fields->push_back(new_field)))
+ DBUG_RETURN(TRUE); /* purify inspected */
+ }
+ DBUG_RETURN(FALSE);
+}
+
+/*
+ Initialize the sequence table record as part of CREATE SEQUENCE
+
+ Store one row with sequence information.
+
+ RETURN VALUES
+ false Success
+ true Failure. Error reported.
+
+ NOTES
+ This function is called as part of CREATE SEQUENCE. When called
+ there are now active transactions and no open tables.
+ There is also a MDL lock on the table.
+*/
+
+bool sequence_insert(THD *thd, LEX *lex, TABLE_LIST *org_table_list)
+{
+ int error;
+ TABLE *table;
+ Reprepare_observer *save_reprepare_observer;
+ sequence_definition *seq= lex->create_info.seq_create_info;
+ bool temporary_table= org_table_list->table != 0;
+ Open_tables_backup open_tables_backup;
+ Query_tables_list query_tables_list_backup;
+ TABLE_LIST table_list; // For sequence table
+ DBUG_ENTER("sequence_insert");
+
+ /*
+ seq is 0 if sequence was created with CREATE TABLE instead of
+ CREATE SEQUENCE
+ */
+ if (!seq)
+ {
+ if (!(seq= new (thd->mem_root) sequence_definition))
+ DBUG_RETURN(TRUE);
+ }
+
+ /* If not temporary table */
+ if (!temporary_table)
+ {
+ /*
+ The following code works like open_system_tables_for_read() and
+ close_system_tables()
+ 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
+ environment for open tables without any tables opened
+ - open the newly sequence table for write
+ This is safe as the sequence table has a mdl lock thanks to the
+ create sequence statement that is calling this function
+ */
+
+ table_list.init_one_table(&org_table_list->db,
+ &org_table_list->table_name,
+ NULL, TL_WRITE_DEFAULT);
+ table_list.updating= 1;
+ table_list.open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
+ table_list.open_type= OT_BASE_ONLY;
+
+ DBUG_ASSERT(!thd->locked_tables_mode ||
+ (thd->variables.option_bits & OPTION_TABLE_LOCK));
+ lex->reset_n_backup_query_tables_list(&query_tables_list_backup);
+ thd->reset_n_backup_open_tables_state(&open_tables_backup);
+
+ /*
+ The FOR CREATE flag is needed to ensure that ha_open() doesn't try to
+ read the not yet existing row in the sequence table
+ */
+ thd->open_options|= HA_OPEN_FOR_CREATE;
+ /*
+ We have to reset the reprepare observer to be able to open the
+ table under prepared statements.
+ */
+ save_reprepare_observer= thd->m_reprepare_observer;
+ thd->m_reprepare_observer= 0;
+ lex->sql_command= SQLCOM_CREATE_SEQUENCE;
+ error= open_and_lock_tables(thd, &table_list, FALSE,
+ MYSQL_LOCK_IGNORE_TIMEOUT |
+ MYSQL_OPEN_HAS_MDL_LOCK);
+ thd->open_options&= ~HA_OPEN_FOR_CREATE;
+ thd->m_reprepare_observer= save_reprepare_observer;
+ if (unlikely(error))
+ {
+ lex->restore_backup_query_tables_list(&query_tables_list_backup);
+ thd->restore_backup_open_tables_state(&open_tables_backup);
+ DBUG_RETURN(error);
+ }
+ table= table_list.table;
+ }
+ else
+ table= org_table_list->table;
+
+ seq->reserved_until= seq->start;
+ error= seq->write_initial_sequence(table);
+
+ if (trans_commit_stmt(thd))
+ error= 1;
+ if (trans_commit_implicit(thd))
+ error= 1;
+
+ if (!temporary_table)
+ {
+ close_thread_tables(thd);
+ lex->restore_backup_query_tables_list(&query_tables_list_backup);
+ thd->restore_backup_open_tables_state(&open_tables_backup);
+
+ /* OPTION_TABLE_LOCK was reset in trans_commit_implicit */
+ if (thd->locked_tables_mode)
+ thd->variables.option_bits|= OPTION_TABLE_LOCK;
+ }
+ DBUG_RETURN(error);
+}
+
+
+/* Create a SQUENCE object */
+
+SEQUENCE::SEQUENCE() :all_values_used(0), initialized(SEQ_UNINTIALIZED)
+{
+ mysql_rwlock_init(key_LOCK_SEQUENCE, &mutex);
+}
+
+SEQUENCE::~SEQUENCE()
+{
+ mysql_rwlock_destroy(&mutex);
+}
+
+/*
+ The following functions is to ensure that we when reserve new values
+ trough sequence object sequence we have only one writer at at time.
+ A sequence table can have many readers (trough normal SELECT's).
+
+ We mark that we have a write lock in the table object so that
+ ha_sequence::ha_write() can check if we have a lock. If already locked, then
+ ha_write() knows that we are running a sequence operation. If not, then
+ ha_write() knows that it's an INSERT.
+*/
+
+void SEQUENCE::write_lock(TABLE *table)
+{
+ DBUG_ASSERT(((ha_sequence*) table->file)->is_locked() == 0);
+ mysql_rwlock_wrlock(&mutex);
+ ((ha_sequence*) table->file)->write_lock();
+}
+void SEQUENCE::write_unlock(TABLE *table)
+{
+ ((ha_sequence*) table->file)->unlock();
+ mysql_rwlock_unlock(&mutex);
+}
+void SEQUENCE::read_lock(TABLE *table)
+{
+ if (!((ha_sequence*) table->file)->is_locked())
+ mysql_rwlock_rdlock(&mutex);
+}
+void SEQUENCE::read_unlock(TABLE *table)
+{
+ if (!((ha_sequence*) table->file)->is_locked())
+ mysql_rwlock_unlock(&mutex);
+}
+
+/**
+ Read values from the sequence tables to table_share->sequence.
+ This is called from ha_open() when the table is not yet locked
+*/
+
+int SEQUENCE::read_initial_values(TABLE *table)
+{
+ int error= 0;
+ enum thr_lock_type save_lock_type;
+ MDL_request mdl_request; // Empty constructor!
+ DBUG_ENTER("SEQUENCE::read_initial_values");
+
+ if (likely(initialized != SEQ_UNINTIALIZED))
+ DBUG_RETURN(0);
+ write_lock(table);
+ if (likely(initialized == SEQ_UNINTIALIZED))
+ {
+ MYSQL_LOCK *lock;
+ bool mdl_lock_used= 0;
+ THD *thd= table->in_use;
+ 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.
+ */
+ if (table->mdl_ticket == 0)
+ {
+ MDL_request_list mdl_requests;
+ mdl_lock_used= 1;
+ /*
+ This happens if first request is SHOW CREATE TABLE or LIST FIELDS
+ 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_requests.push_front(&mdl_request);
+ if (thd->mdl_context.acquire_locks(&mdl_requests,
+ thd->variables.lock_wait_timeout))
+ {
+ write_unlock(table);
+ DBUG_RETURN(HA_ERR_LOCK_WAIT_TIMEOUT);
+ }
+ }
+ save_lock_type= table->reginfo.lock_type;
+ table->reginfo.lock_type= TL_READ;
+ if (!(lock= mysql_lock_tables(thd, &table, 1,
+ MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY)))
+ {
+ if (mdl_lock_used)
+ thd->mdl_context.release_lock(mdl_request.ticket);
+ write_unlock(table);
+
+ if (!has_active_transaction && !thd->transaction.stmt.is_empty() &&
+ !thd->in_sub_stmt)
+ trans_commit_stmt(thd);
+ DBUG_RETURN(HA_ERR_LOCK_WAIT_TIMEOUT);
+ }
+ DBUG_ASSERT(table->reginfo.lock_type == TL_READ);
+ if (likely(!(error= read_stored_values(table))))
+ initialized= SEQ_READY_TO_USE;
+ mysql_unlock_tables(thd, lock);
+ if (mdl_lock_used)
+ thd->mdl_context.release_lock(mdl_request.ticket);
+
+ /* Reset value to default */
+ table->reginfo.lock_type= save_lock_type;
+ /*
+ Doing mysql_lock_tables() may have started a read only transaction.
+ If that happend, it's better that we commit it now, as a lot of
+ code assumes that there is no active stmt transaction directly after
+ open_tables().
+ 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() &&
+ !thd->in_sub_stmt)
+ trans_commit_stmt(thd);
+ }
+ write_unlock(table);
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Do the actiual reading of data from sequence table and
+ update values in the sequence object.
+
+ Called once from when table is opened
+*/
+
+int SEQUENCE::read_stored_values(TABLE *table)
+{
+ int error;
+ my_bitmap_map *save_read_set;
+ DBUG_ENTER("SEQUENCE::read_stored_values");
+
+ save_read_set= tmp_use_all_columns(table, table->read_set);
+ error= table->file->ha_read_first_row(table->record[0], MAX_KEY);
+ tmp_restore_column_map(table->read_set, save_read_set);
+
+ if (unlikely(error))
+ {
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(error);
+ }
+ read_fields(table);
+ adjust_values(reserved_until);
+
+ all_values_used= 0;
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Adjust values after reading a the stored state
+*/
+
+void sequence_definition::adjust_values(longlong next_value)
+{
+ next_free_value= next_value;
+ if (!(real_increment= increment))
+ {
+ longlong offset= 0;
+ longlong off, to_add;
+ /* Use auto_increment_increment and auto_increment_offset */
+
+ if ((real_increment= global_system_variables.auto_increment_increment)
+ != 1)
+ offset= (global_system_variables.auto_increment_offset %
+ global_system_variables.auto_increment_increment);
+
+ /*
+ Ensure that next_free_value has the right offset, so that we
+ can generate a serie by just adding real_increment.
+ */
+ off= next_free_value % real_increment;
+ if (off < 0)
+ off+= real_increment;
+ to_add= (real_increment + offset - off) % real_increment;
+
+ /*
+ Check if add will make next_free_value bigger than max_value,
+ taken into account that next_free_value or max_value addition
+ may overflow
+ */
+ if (next_free_value > max_value - to_add ||
+ next_free_value + to_add > max_value)
+ next_free_value= max_value+1;
+ else
+ {
+ next_free_value+= to_add;
+ DBUG_ASSERT(llabs(next_free_value % real_increment) == offset);
+ }
+ }
+}
+
+
+/**
+ Write initial sequence information for CREATE and ALTER to sequence table
+*/
+
+int sequence_definition::write_initial_sequence(TABLE *table)
+{
+ int error;
+ THD *thd= table->in_use;
+ MY_BITMAP *save_write_set;
+
+ store_fields(table);
+ /* Store the sequence values in table share */
+ table->s->sequence->copy(this);
+ /*
+ Sequence values will be replicated as a statement
+ like 'create sequence'. So disable binary log temporarily
+ */
+ tmp_disable_binlog(thd);
+ 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));
+ else
+ {
+ /*
+ Sequence structure is up to date and table has one row,
+ sequence is now usable
+ */
+ table->s->sequence->initialized= SEQUENCE::SEQ_READY_TO_USE;
+ }
+ return error;
+}
+
+
+/**
+ Store current sequence values into the sequence table
+*/
+
+int sequence_definition::write(TABLE *table, bool all_fields)
+{
+ int error;
+ MY_BITMAP *save_rpl_write_set, *save_write_set, *save_read_set;
+ DBUG_ASSERT(((ha_sequence*) table->file)->is_locked());
+
+ save_rpl_write_set= table->rpl_write_set;
+ if (likely(!all_fields))
+ {
+ /* Only write next_value and round to binary log */
+ table->rpl_write_set= &table->def_rpl_write_set;
+ bitmap_clear_all(table->rpl_write_set);
+ bitmap_set_bit(table->rpl_write_set, NEXT_FIELD_NO);
+ bitmap_set_bit(table->rpl_write_set, ROUND_FIELD_NO);
+ }
+ else
+ table->rpl_write_set= &table->s->all_set;
+
+ /* Update table */
+ save_write_set= table->write_set;
+ save_read_set= table->read_set;
+ table->read_set= table->write_set= &table->s->all_set;
+ table->file->column_bitmaps_signal();
+ store_fields(table);
+ if (unlikely((error= table->file->ha_write_row(table->record[0]))))
+ table->file->print_error(error, MYF(0));
+ table->rpl_write_set= save_rpl_write_set;
+ table->read_set= save_read_set;
+ table->write_set= save_write_set;
+ table->file->column_bitmaps_signal();
+ return error;
+}
+
+
+/**
+ Get next value for sequence
+
+ @param in table Sequence table
+ @param in second_round
+ 1 if recursive call (out of values once)
+ @param out error Set this to <> 0 in case of error
+ push_warning_printf(WARN_LEVEL_WARN) has been called
+
+
+ @retval 0 Next number or error. Check error variable
+ # Next sequence number
+
+ NOTES:
+ Return next_free_value and increment next_free_value to next allowed
+ value or reserved_value if out of range
+ if next_free_value >= reserved_value reserve a new range by writing
+ a record to the sequence table.
+
+ The state of the variables:
+ next_free_value contains next value to use. It may be
+ bigger than max_value or less than min_value if end of sequence.
+ reserved_until contains the last value written to the file. All
+ values up to this one can be used.
+ If next_free_value >= reserved_until we have to reserve new
+ values from the sequence.
+*/
+
+longlong SEQUENCE::next_value(TABLE *table, bool second_round, int *error)
+{
+ longlong res_value, org_reserved_until, add_to;
+ bool out_of_values;
+ DBUG_ENTER("SEQUENCE::next_value");
+
+ *error= 0;
+ if (!second_round)
+ write_lock(table);
+
+ res_value= next_free_value;
+ next_free_value= increment_value(next_free_value);
+
+ if ((real_increment > 0 && res_value < reserved_until) ||
+ (real_increment < 0 && res_value > reserved_until))
+ {
+ write_unlock(table);
+ DBUG_RETURN(res_value);
+ }
+
+ if (all_values_used)
+ goto err;
+
+ org_reserved_until= reserved_until;
+
+ /*
+ Out of cached values, reserve 'cache' new ones
+ The cache value is checked on insert so the following can't
+ overflow
+ */
+ add_to= cache ? real_increment * cache : real_increment;
+ out_of_values= 0;
+
+ if (real_increment > 0)
+ {
+ if (reserved_until + add_to > max_value ||
+ reserved_until > max_value - add_to)
+ {
+ reserved_until= max_value + 1;
+ out_of_values= res_value >= reserved_until;
+ }
+ else
+ reserved_until+= add_to;
+ }
+ else
+ {
+ if (reserved_until + add_to < min_value ||
+ reserved_until < min_value - add_to)
+ {
+ reserved_until= min_value - 1;
+ out_of_values= res_value <= reserved_until;
+ }
+ else
+ reserved_until+= add_to;
+ }
+ if (out_of_values)
+ {
+ if (!cycle || second_round)
+ goto err;
+ round++;
+ reserved_until= real_increment >0 ? min_value : max_value;
+ adjust_values(reserved_until); // Fix next_free_value
+ /*
+ We have to do everything again to ensure that the given range was
+ not empty, which could happen if increment == 0
+ */
+ DBUG_RETURN(next_value(table, 1, error));
+ }
+
+ if (unlikely((*error= write(table, 0))))
+ {
+ reserved_until= org_reserved_until;
+ next_free_value= res_value;
+ }
+
+ write_unlock(table);
+ DBUG_RETURN(res_value);
+
+err:
+ write_unlock(table);
+ my_error(ER_SEQUENCE_RUN_OUT, MYF(0), table->s->db.str,
+ table->s->table_name.str);
+ *error= ER_SEQUENCE_RUN_OUT;
+ all_values_used= 1;
+ DBUG_RETURN(0);
+}
+
+
+/*
+ The following functions is to detect if a table has been dropped
+ and re-created since last call to PREVIOUS VALUE.
+
+ This is needed as we don't delete dropped sequences from THD->sequence
+ for DROP TABLE.
+*/
+
+bool SEQUENCE_LAST_VALUE::check_version(TABLE *table)
+{
+ DBUG_ASSERT(table->s->tabledef_version.length == MY_UUID_SIZE);
+ return memcmp(table->s->tabledef_version.str, table_version,
+ MY_UUID_SIZE) != 0;
+}
+
+void SEQUENCE_LAST_VALUE::set_version(TABLE *table)
+{
+ memcpy(table_version, table->s->tabledef_version.str, MY_UUID_SIZE);
+}
+
+/**
+ Set the next value for sequence
+
+ @param in table Sequence table
+ @param in next_val Next free value
+ @param in next_round Round for 'next_value' (in case of cycles)
+ @param in is_used 1 if next_val is already used
+
+ @retval 0 ok, value adjusted
+ -1 value was less than current value
+ 1 error when storing value
+
+ @comment
+ A new value is set only if "nextval,next_round" is less than
+ "next_free_value,round". This is needed as in replication
+ setvalue() calls may come out to the slave out-of-order.
+ Storing only the highest value ensures that sequence object will always
+ contain the highest used value when the slave is promoted to a master.
+*/
+
+int SEQUENCE::set_value(TABLE *table, longlong next_val, ulonglong next_round,
+ bool is_used)
+{
+ int error= -1;
+ bool needs_to_be_stored= 0;
+ longlong org_reserved_until= reserved_until;
+ longlong org_next_free_value= next_free_value;
+ ulonglong org_round= round;
+ DBUG_ENTER("SEQUENCE::set_value");
+
+ write_lock(table);
+ if (is_used)
+ next_val= increment_value(next_val);
+
+ if (round > next_round)
+ goto end; // error = -1
+ if (round == next_round)
+ {
+ if (real_increment > 0 ?
+ next_val < next_free_value :
+ next_val > next_free_value)
+ goto end; // error = -1
+ if (next_val == next_free_value)
+ {
+ error= 0;
+ goto end;
+ }
+ }
+ else if (cycle == 0)
+ {
+ // round < next_round && no cycles, which is impossible
+ my_error(ER_SEQUENCE_RUN_OUT, MYF(0), table->s->db.str,
+ table->s->table_name.str);
+ error= 1;
+ goto end;
+ }
+ else
+ needs_to_be_stored= 1;
+
+ round= next_round;
+ adjust_values(next_val);
+ if ((real_increment > 0 ?
+ next_free_value > reserved_until :
+ next_free_value < reserved_until) ||
+ needs_to_be_stored)
+ {
+ reserved_until= next_free_value;
+ if (write(table, 0))
+ {
+ reserved_until= org_reserved_until;
+ next_free_value= org_next_free_value;
+ round= org_round;
+ error= 1;
+ goto end;
+ }
+ }
+ error= 0;
+
+end:
+ write_unlock(table);
+ DBUG_RETURN(error);
+}
+
+
+bool Sql_cmd_alter_sequence::execute(THD *thd)
+{
+ int error= 0;
+ int trapped_errors= 0;
+ LEX *lex= thd->lex;
+ TABLE_LIST *first_table= lex->query_tables;
+ TABLE *table;
+ sequence_definition *new_seq= lex->create_info.seq_create_info;
+ SEQUENCE *seq;
+ No_such_table_error_handler no_such_table_handler;
+ DBUG_ENTER("Sql_cmd_alter_sequence::execute");
+
+ if (check_access(thd, ALTER_ACL, first_table->db.str,
+ &first_table->grant.privilege,
+ &first_table->grant.m_internal,
+ 0, 0))
+ DBUG_RETURN(TRUE); /* purecov: inspected */
+
+ if (check_grant(thd, ALTER_ACL, first_table, FALSE, 1, FALSE))
+ DBUG_RETURN(TRUE); /* purecov: inspected */
+
+ if (if_exists())
+ thd->push_internal_handler(&no_such_table_handler);
+ error= open_and_lock_tables(thd, first_table, FALSE, 0);
+ if (if_exists())
+ {
+ trapped_errors= no_such_table_handler.safely_trapped_errors();
+ thd->pop_internal_handler();
+ }
+ if (unlikely(error))
+ {
+ if (trapped_errors)
+ {
+ StringBuffer<FN_REFLEN> tbl_name;
+ tbl_name.append(&first_table->db);
+ tbl_name.append('.');
+ tbl_name.append(&first_table->table_name);
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_UNKNOWN_SEQUENCES,
+ ER_THD(thd, ER_UNKNOWN_SEQUENCES),
+ tbl_name.c_ptr_safe());
+ my_ok(thd);
+ DBUG_RETURN(FALSE);
+ }
+ DBUG_RETURN(TRUE);
+ }
+
+ table= first_table->table;
+ seq= table->s->sequence;
+ new_seq->reserved_until= seq->reserved_until;
+
+ /* Copy from old sequence those fields that the user didn't specified */
+ if (!(new_seq->used_fields & seq_field_used_increment))
+ new_seq->increment= seq->increment;
+ if (!(new_seq->used_fields & seq_field_used_min_value))
+ new_seq->min_value= seq->min_value;
+ if (!(new_seq->used_fields & seq_field_used_max_value))
+ new_seq->max_value= seq->max_value;
+ if (!(new_seq->used_fields & seq_field_used_start))
+ new_seq->start= seq->start;
+ if (!(new_seq->used_fields & seq_field_used_cache))
+ new_seq->cache= seq->cache;
+ if (!(new_seq->used_fields & seq_field_used_cycle))
+ new_seq->cycle= seq->cycle;
+
+ /* If we should restart from a new value */
+ if (new_seq->used_fields & seq_field_used_restart)
+ {
+ if (!(new_seq->used_fields & seq_field_used_restart_value))
+ new_seq->restart= new_seq->start;
+ new_seq->reserved_until= new_seq->restart;
+ }
+
+ /* Let check_and_adjust think all fields are used */
+ new_seq->used_fields= ~0;
+ if (new_seq->check_and_adjust(0))
+ {
+ my_error(ER_SEQUENCE_INVALID_DATA, MYF(0),
+ first_table->db.str,
+ first_table->table_name.str);
+ error= 1;
+ 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);
+ }
+ else
+ table->file->print_error(error, MYF(0));
+ table->s->sequence->write_unlock(table);
+ if (trans_commit_stmt(thd))
+ error= 1;
+ if (trans_commit_implicit(thd))
+ error= 1;
+ if (likely(!error))
+ error= write_bin_log(thd, 1, thd->query(), thd->query_length());
+ if (likely(!error))
+ my_ok(thd);
+
+end:
+ DBUG_RETURN(error);
+}
diff --git a/sql/sql_sequence.h b/sql/sql_sequence.h
new file mode 100644
index 00000000000..2d609d8591b
--- /dev/null
+++ b/sql/sql_sequence.h
@@ -0,0 +1,167 @@
+/* Copyright (c) 2017, 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-1301 USA
+*/
+
+#ifndef SQL_SEQUENCE_INCLUDED
+#define SQL_SEQUENCE_INCLUDED
+
+#define seq_field_used_min_value 1
+#define seq_field_used_max_value 2
+#define seq_field_used_start 4
+#define seq_field_used_increment 8
+#define seq_field_used_cache 16
+#define seq_field_used_cycle 32
+#define seq_field_used_restart 64
+#define seq_field_used_restart_value 128
+
+/* Field position in sequence table for some fields we refer to directly */
+#define NEXT_FIELD_NO 0
+#define MIN_VALUE_FIELD_NO 1
+#define ROUND_FIELD_NO 7
+
+/**
+ sequence_definition is used when defining a sequence as part of create
+*/
+
+class sequence_definition :public Sql_alloc
+{
+public:
+ sequence_definition():
+ min_value(1), max_value(LONGLONG_MAX-1), start(1), increment(1),
+ cache(1000), round(0), restart(0), cycle(0), used_fields(0)
+ {}
+ longlong reserved_until;
+ longlong min_value;
+ longlong max_value;
+ longlong start;
+ longlong increment;
+ longlong cache;
+ ulonglong round;
+ longlong restart; // alter sequence restart value
+ bool cycle;
+ uint used_fields; // Which fields where used in CREATE
+
+ bool check_and_adjust(bool set_reserved_until);
+ void store_fields(TABLE *table);
+ void read_fields(TABLE *table);
+ int write_initial_sequence(TABLE *table);
+ int write(TABLE *table, bool all_fields);
+ /* This must be called after sequence data has been updated */
+ void adjust_values(longlong next_value);
+ inline void print_dbug()
+ {
+ DBUG_PRINT("sequence", ("reserved: %lld start: %lld increment: %lld min_value: %lld max_value: %lld cache: %lld round: %lld",
+ reserved_until, start, increment, min_value,
+ max_value, cache, round));
+ }
+protected:
+ /*
+ The following values are the values from sequence_definition
+ merged with global auto_increment_offset and auto_increment_increment
+ */
+ longlong real_increment;
+ longlong next_free_value;
+};
+
+/**
+ SEQUENCE is in charge of managing the sequence values.
+ It's also responsible to generate new values and updating the sequence
+ table (engine=SQL_SEQUENCE) trough it's specialized handler interface.
+
+ If increment is 0 then the sequence will be be using
+ auto_increment_increment and auto_increment_offset variables, just like
+ AUTO_INCREMENT is using.
+*/
+
+class SEQUENCE :public sequence_definition
+{
+public:
+ enum seq_init { SEQ_UNINTIALIZED, SEQ_IN_PREPARE, SEQ_IN_ALTER,
+ SEQ_READY_TO_USE };
+ SEQUENCE();
+ ~SEQUENCE();
+ int read_initial_values(TABLE *table);
+ int read_stored_values(TABLE *table);
+ void write_lock(TABLE *table);
+ void write_unlock(TABLE *table);
+ void read_lock(TABLE *table);
+ void read_unlock(TABLE *table);
+ void copy(sequence_definition *seq)
+ {
+ sequence_definition::operator= (*seq);
+ adjust_values(reserved_until);
+ all_values_used= 0;
+ }
+ longlong next_value(TABLE *table, bool second_round, int *error);
+ int set_value(TABLE *table, longlong next_value, ulonglong round_arg,
+ bool is_used);
+ longlong increment_value(longlong value)
+ {
+ if (real_increment > 0)
+ {
+ if (value + real_increment > max_value ||
+ value > max_value - real_increment)
+ value= max_value + 1;
+ else
+ value+= real_increment;
+ }
+ else
+ {
+ if (value + real_increment < min_value ||
+ value < min_value - real_increment)
+ value= min_value - 1;
+ else
+ value+= real_increment;
+ }
+ return value;
+ }
+
+ bool all_values_used;
+ seq_init initialized;
+
+private:
+ mysql_rwlock_t mutex;
+};
+
+
+/**
+ Class to cache last value of NEXT VALUE from the sequence
+*/
+
+class SEQUENCE_LAST_VALUE
+{
+public:
+ SEQUENCE_LAST_VALUE(uchar *key_arg, uint length_arg)
+ :key(key_arg), length(length_arg)
+ {}
+ ~SEQUENCE_LAST_VALUE()
+ { my_free((void*) key); }
+ /* Returns 1 if table hasn't been dropped or re-created */
+ bool check_version(TABLE *table);
+ void set_version(TABLE *table);
+
+ const uchar *key;
+ uint length;
+ bool null_value;
+ longlong value;
+ uchar table_version[MY_UUID_SIZE];
+};
+
+
+class Create_field;
+extern bool prepare_sequence_fields(THD *thd, List<Create_field> *fields);
+extern bool check_sequence_fields(LEX *lex, List<Create_field> *fields);
+extern bool sequence_insert(THD *thd, LEX *lex, TABLE_LIST *table_list);
+#endif /* SQL_SEQUENCE_INCLUDED */
diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc
index 36147fc47fc..7913a7d2b9f 100644
--- a/sql/sql_servers.cc
+++ b/sql/sql_servers.cc
@@ -33,7 +33,7 @@
currently running transactions etc will not be disrupted.
*/
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "sql_servers.h"
#include "unireg.h"
@@ -54,6 +54,8 @@
static HASH servers_cache;
static MEM_ROOT mem;
static mysql_rwlock_t THR_LOCK_servers;
+static LEX_CSTRING MYSQL_SERVERS_NAME= {STRING_WITH_LEN("servers") };
+
static bool get_server_from_table_to_cache(TABLE *table);
@@ -64,7 +66,7 @@ static int insert_server_record_into_cache(FOREIGN_SERVER *server);
static FOREIGN_SERVER *
prepare_server_struct_for_insert(LEX_SERVER_OPTIONS *server_options);
/* drop functions */
-static int delete_server_record(TABLE *table, LEX_STRING *name);
+static int delete_server_record(TABLE *table, LEX_CSTRING *name);
static int delete_server_record_in_cache(LEX_SERVER_OPTIONS *server_options);
/* update functions */
@@ -83,7 +85,7 @@ static uchar *servers_cache_get_key(FOREIGN_SERVER *server, size_t *length,
my_bool not_used __attribute__((unused)))
{
DBUG_ENTER("servers_cache_get_key");
- DBUG_PRINT("info", ("server_name_length %d server_name %s",
+ DBUG_PRINT("info", ("server_name_length %zd server_name %s",
server->server_name_length,
server->server_name));
@@ -154,7 +156,8 @@ bool servers_init(bool dont_read_servers_table)
}
/* Initialize the mem root for data */
- init_sql_alloc(&mem, ACL_ALLOC_BLOCK_SIZE, 0, MYF(MY_THREAD_SPECIFIC));
+ init_sql_alloc(&mem, "servers", ACL_ALLOC_BLOCK_SIZE, 0,
+ MYF(MY_THREAD_SPECIFIC));
if (dont_read_servers_table)
goto end;
@@ -203,12 +206,12 @@ static bool servers_load(THD *thd, TABLE_LIST *tables)
my_hash_reset(&servers_cache);
free_root(&mem, MYF(0));
- init_sql_alloc(&mem, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
+ init_sql_alloc(&mem, "servers_load", ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
if (init_read_record(&read_record_info,thd,table=tables[0].table, NULL, NULL,
1,0, FALSE))
DBUG_RETURN(1);
- while (!(read_record_info.read_record(&read_record_info)))
+ while (!(read_record_info.read_record()))
{
/* return_val is already TRUE, so no need to set */
if ((get_server_from_table_to_cache(table)))
@@ -251,9 +254,10 @@ bool servers_reload(THD *thd)
DBUG_PRINT("info", ("locking servers_cache"));
mysql_rwlock_wrlock(&THR_LOCK_servers);
- tables[0].init_one_table("mysql", 5, "servers", 7, "servers", TL_READ);
+ tables[0].init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_SERVERS_NAME, 0, TL_READ);
- if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
+ if (unlikely(open_and_lock_tables(thd, tables, FALSE,
+ MYSQL_LOCK_IGNORE_TIMEOUT)))
{
/*
Execution might have been interrupted; only print the error message
@@ -383,21 +387,20 @@ insert_server(THD *thd, FOREIGN_SERVER *server)
int error= -1;
TABLE_LIST tables;
TABLE *table;
-
DBUG_ENTER("insert_server");
- tables.init_one_table("mysql", 5, "servers", 7, "servers", TL_WRITE);
+ tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_SERVERS_NAME, 0, TL_WRITE);
/* 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;
/* insert the server into the table */
- if ((error= insert_server_record(table, server)))
+ if (unlikely(error= insert_server_record(table, server)))
goto end;
/* insert the server into the cache */
- if ((error= insert_server_record_into_cache(server)))
+ if (unlikely((error= insert_server_record_into_cache(server))))
goto end;
end:
@@ -428,10 +431,10 @@ insert_server_record_into_cache(FOREIGN_SERVER *server)
int error=0;
DBUG_ENTER("insert_server_record_into_cache");
/*
- We succeded in insertion of the server to the table, now insert
+ We succeeded in insertion of the server to the table, now insert
the server to the cache
*/
- DBUG_PRINT("info", ("inserting server %s at %p, length %d",
+ DBUG_PRINT("info", ("inserting server %s at %p, length %zd",
server->server_name, server,
server->server_name_length));
if (my_hash_insert(&servers_cache, (uchar*) server))
@@ -540,10 +543,12 @@ int insert_server_record(TABLE *table, FOREIGN_SERVER *server)
system_charset_info);
/* read index until record is that specified in server_name */
- if ((error= table->file->ha_index_read_idx_map(table->record[0], 0,
- (uchar *)table->field[0]->ptr,
- HA_WHOLE_KEY,
- HA_READ_KEY_EXACT)))
+ if (unlikely((error=
+ table->file->ha_index_read_idx_map(table->record[0], 0,
+ (uchar *)table->field[0]->
+ ptr,
+ HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT))))
{
/* if not found, err */
if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
@@ -557,12 +562,8 @@ int insert_server_record(TABLE *table, FOREIGN_SERVER *server)
DBUG_PRINT("info",("record for server '%s' not found!",
server->server_name));
/* write/insert the new server */
- if ((error=table->file->ha_write_row(table->record[0])))
- {
+ if (unlikely(error=table->file->ha_write_row(table->record[0])))
table->file->print_error(error, MYF(0));
- }
- else
- error= 0;
}
else
error= ER_FOREIGN_SERVER_EXISTS;
@@ -603,13 +604,14 @@ static int drop_server_internal(THD *thd, LEX_SERVER_OPTIONS *server_options)
DBUG_PRINT("info", ("server name server->server_name %s",
server_options->server_name.str));
- tables.init_one_table("mysql", 5, "servers", 7, "servers", TL_WRITE);
+ tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_SERVERS_NAME, 0, TL_WRITE);
/* hit the memory hit first */
- if ((error= delete_server_record_in_cache(server_options)))
+ if (unlikely((error= delete_server_record_in_cache(server_options))))
goto end;
- if (! (table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
+ if (unlikely(!(table= open_ltable(thd, &tables, TL_WRITE,
+ MYSQL_LOCK_IGNORE_TIMEOUT))))
{
error= my_errno;
goto end;
@@ -684,10 +686,10 @@ delete_server_record_in_cache(LEX_SERVER_OPTIONS *server_options)
goto end;
}
/*
- We succeded in deletion of the server to the table, now delete
+ We succeeded in deletion of the server to the table, now delete
the server from the cache
*/
- DBUG_PRINT("info",("deleting server %s length %d",
+ DBUG_PRINT("info",("deleting server %s length %zd",
server->server_name,
server->server_name_length));
@@ -734,8 +736,7 @@ int update_server(THD *thd, FOREIGN_SERVER *existing, FOREIGN_SERVER *altered)
TABLE_LIST tables;
DBUG_ENTER("update_server");
- tables.init_one_table("mysql", 5, "servers", 7, "servers",
- TL_WRITE);
+ tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_SERVERS_NAME, 0, TL_WRITE);
if (!(table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
{
@@ -743,7 +744,7 @@ int update_server(THD *thd, FOREIGN_SERVER *existing, FOREIGN_SERVER *altered)
goto end;
}
- if ((error= update_server_record(table, altered)))
+ if (unlikely((error= update_server_record(table, altered))))
goto end;
error= update_server_record_in_cache(existing, altered);
@@ -891,10 +892,12 @@ update_server_record(TABLE *table, FOREIGN_SERVER *server)
server->server_name_length,
system_charset_info);
- if ((error= table->file->ha_index_read_idx_map(table->record[0], 0,
- (uchar *)table->field[0]->ptr,
- ~(longlong)0,
- HA_READ_KEY_EXACT)))
+ if (unlikely((error=
+ table->file->ha_index_read_idx_map(table->record[0], 0,
+ (uchar *)table->field[0]->
+ ptr,
+ ~(longlong)0,
+ HA_READ_KEY_EXACT))))
{
if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
table->file->print_error(error, MYF(0));
@@ -906,9 +909,9 @@ update_server_record(TABLE *table, FOREIGN_SERVER *server)
/* ok, so we can update since the record exists in the table */
store_record(table,record[1]);
store_server_fields(table, server);
- if ((error=table->file->ha_update_row(table->record[1],
- table->record[0])) &&
- error != HA_ERR_RECORD_IS_THE_SAME)
+ if (unlikely((error=table->file->ha_update_row(table->record[1],
+ table->record[0])) &&
+ error != HA_ERR_RECORD_IS_THE_SAME))
{
DBUG_PRINT("info",("problems with ha_update_row %d", error));
goto end;
@@ -939,7 +942,7 @@ end:
*/
static int
-delete_server_record(TABLE *table, LEX_STRING *name)
+delete_server_record(TABLE *table, LEX_CSTRING *name)
{
int error;
DBUG_ENTER("delete_server_record");
@@ -949,10 +952,12 @@ delete_server_record(TABLE *table, LEX_STRING *name)
/* set the field that's the PK to the value we're looking for */
table->field[0]->store(name->str, name->length, system_charset_info);
- if ((error= table->file->ha_index_read_idx_map(table->record[0], 0,
- (uchar *)table->field[0]->ptr,
- HA_WHOLE_KEY,
- HA_READ_KEY_EXACT)))
+ if (unlikely((error=
+ table->file->ha_index_read_idx_map(table->record[0], 0,
+ (uchar *)table->field[0]->
+ ptr,
+ HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT))))
{
if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
table->file->print_error(error, MYF(0));
@@ -961,7 +966,7 @@ delete_server_record(TABLE *table, LEX_STRING *name)
}
else
{
- if ((error= table->file->ha_delete_row(table->record[0])))
+ if (unlikely((error= table->file->ha_delete_row(table->record[0]))))
table->file->print_error(error, MYF(0));
}
@@ -1000,7 +1005,7 @@ int create_server(THD *thd, LEX_SERVER_OPTIONS *server_options)
{
if (thd->lex->create_info.or_replace())
{
- if ((error= drop_server_internal(thd, server_options)))
+ if (unlikely((error= drop_server_internal(thd, server_options))))
goto end;
}
else if (thd->lex->create_info.if_not_exists())
@@ -1031,7 +1036,7 @@ int create_server(THD *thd, LEX_SERVER_OPTIONS *server_options)
end:
mysql_rwlock_unlock(&THR_LOCK_servers);
- if (error)
+ if (unlikely(error))
{
DBUG_PRINT("info", ("problem creating server <%s>",
server_options->server_name.str));
diff --git a/sql/sql_servers.h b/sql/sql_servers.h
index 85e7e68e058..cb5703ef35d 100644
--- a/sql/sql_servers.h
+++ b/sql/sql_servers.h
@@ -16,7 +16,6 @@
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" /* uint */
#include "slave.h" // for tables_ok(), rpl_filter
class THD;
@@ -28,7 +27,7 @@ typedef struct st_federated_server
{
const char *server_name;
long port;
- uint server_name_length;
+ size_t server_name_length;
const char *db, *scheme, *username, *password, *socket, *owner, *host, *sport;
} FOREIGN_SERVER;
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 5544c765775..b9c914f9545 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -17,7 +17,7 @@
/* Function with list databases, tables or fields */
-#include "sql_plugin.h" // Includes my_global.h
+#include "sql_plugin.h" // SHOW_MY_BOOL
#include "sql_priv.h"
#include "unireg.h"
#include "sql_acl.h" // fill_schema_*_privileges
@@ -58,10 +58,12 @@
#include "lock.h" // MYSQL_OPEN_IGNORE_FLUSH
#include "debug_sync.h"
#include "keycaches.h"
-
+#include "ha_sequence.h"
#ifdef WITH_PARTITION_STORAGE_ENGINE
#include "ha_partition.h"
#endif
+#include "transaction.h"
+
enum enum_i_s_events_fields
{
ISE_EVENT_CATALOG= 0,
@@ -92,18 +94,17 @@ enum enum_i_s_events_fields
#define USERNAME_WITH_HOST_CHAR_LENGTH (USERNAME_CHAR_LENGTH + HOSTNAME_LENGTH + 2)
-
-static const LEX_STRING trg_action_time_type_names[]=
+static const LEX_CSTRING trg_action_time_type_names[]=
{
- { C_STRING_WITH_LEN("BEFORE") },
- { C_STRING_WITH_LEN("AFTER") }
+ { STRING_WITH_LEN("BEFORE") },
+ { STRING_WITH_LEN("AFTER") }
};
-static const LEX_STRING trg_event_type_names[]=
+static const LEX_CSTRING trg_event_type_names[]=
{
- { C_STRING_WITH_LEN("INSERT") },
- { C_STRING_WITH_LEN("UPDATE") },
- { C_STRING_WITH_LEN("DELETE") }
+ { STRING_WITH_LEN("INSERT") },
+ { STRING_WITH_LEN("UPDATE") },
+ { STRING_WITH_LEN("DELETE") }
};
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -130,11 +131,36 @@ static void get_cs_converted_string_value(THD *thd,
#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);
-static const LEX_STRING *view_algorithm(TABLE_LIST *table);
+static const LEX_CSTRING *view_algorithm(TABLE_LIST *table);
bool get_lookup_field_values(THD *, COND *, TABLE_LIST *, LOOKUP_FIELD_VALUES *);
+/**
+ Try to lock a mutex, but give up after a short while to not cause deadlocks
+
+ The loop is short, as the mutex we are trying to lock are mutex the should
+ never be locked a long time, just over a few instructions.
+
+ @return 0 ok
+ @return 1 error
+*/
+
+static bool trylock_short(mysql_mutex_t *mutex)
+{
+ uint i;
+ for (i= 0 ; i < 100 ; i++)
+ {
+ if (!mysql_mutex_trylock(mutex))
+ return 0;
+ LF_BACKOFF();
+ }
+ return 1;
+}
+
+
/***************************************************************************
** List all table types supported
***************************************************************************/
@@ -147,17 +173,17 @@ static bool is_show_command(THD *thd)
static int make_version_string(char *buf, int buf_length, uint version)
{
- return my_snprintf(buf, buf_length, "%d.%d", version>>8,version&0xff);
+ return (int)my_snprintf(buf, buf_length, "%d.%d", version>>8,version&0xff);
}
-static const LEX_STRING maturity_name[]={
- { C_STRING_WITH_LEN("Unknown") },
- { C_STRING_WITH_LEN("Experimental") },
- { C_STRING_WITH_LEN("Alpha") },
- { C_STRING_WITH_LEN("Beta") },
- { C_STRING_WITH_LEN("Gamma") },
- { C_STRING_WITH_LEN("Stable") }};
+static const LEX_CSTRING maturity_name[]={
+ { STRING_WITH_LEN("Unknown") },
+ { STRING_WITH_LEN("Experimental") },
+ { STRING_WITH_LEN("Alpha") },
+ { STRING_WITH_LEN("Beta") },
+ { STRING_WITH_LEN("Gamma") },
+ { STRING_WITH_LEN("Stable") }};
static my_bool show_plugins(THD *thd, plugin_ref plugin,
@@ -317,7 +343,7 @@ int fill_all_plugins(THD *thd, TABLE_LIST *tables, COND *cond)
for (uint i=0; i < (uint) dirp->number_of_files; i++)
{
FILEINFO *file= dirp->dir_entry+i;
- LEX_STRING dl= { file->name, strlen(file->name) };
+ LEX_CSTRING dl= { file->name, strlen(file->name) };
const char *dlend= dl.str + dl.length;
const size_t so_ext_len= sizeof(SO_EXT) - 1;
@@ -390,8 +416,8 @@ exit:
static int get_geometry_column_record(THD *thd, TABLE_LIST *tables,
TABLE *table, bool res,
- LEX_STRING *db_name,
- LEX_STRING *table_name)
+ const LEX_CSTRING *db_name,
+ const LEX_CSTRING *table_name)
{
CHARSET_INFO *cs= system_charset_info;
TABLE *show_table;
@@ -445,7 +471,8 @@ static int get_geometry_column_record(THD *thd, TABLE_LIST *tables,
/*G_TABLE_NAME*/
table->field[6]->store(table_name->str, table_name->length, cs);
/*G_GEOMETRY_COLUMN*/
- table->field[7]->store(field->field_name, strlen(field->field_name), cs);
+ 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*/
@@ -565,6 +592,7 @@ static struct show_privileges_st sys_privileges[]=
{"Create view", "Tables", "To create new views"},
{"Create user", "Server Admin", "To create new users"},
{"Delete", "Tables", "To delete existing rows"},
+ {"Delete history", "Tables", "To delete versioning table historical rows"},
{"Drop", "Databases,Tables", "To drop databases, tables, and views"},
#ifdef HAVE_EVENT_SCHEDULER
{"Event","Server Admin","To create, alter, drop and execute events"},
@@ -670,7 +698,7 @@ static bool skip_ignored_dir_check= TRUE;
bool
ignore_db_dirs_init()
{
- return my_init_dynamic_array(&ignore_db_dirs_array, sizeof(LEX_STRING *),
+ return my_init_dynamic_array(&ignore_db_dirs_array, sizeof(LEX_CSTRING *),
0, 0, MYF(0));
}
@@ -690,7 +718,7 @@ static uchar *
db_dirs_hash_get_key(const uchar *data, size_t *len_ret,
my_bool __attribute__((unused)))
{
- LEX_STRING *e= (LEX_STRING *) data;
+ LEX_CSTRING *e= (LEX_CSTRING *) data;
*len_ret= e->length;
return (uchar *) e->str;
@@ -711,7 +739,7 @@ db_dirs_hash_get_key(const uchar *data, size_t *len_ret,
bool
push_ignored_db_dir(char *path)
{
- LEX_STRING *new_elt;
+ LEX_CSTRING *new_elt;
char *new_elt_buffer;
size_t path_len= strlen(path);
@@ -720,7 +748,7 @@ push_ignored_db_dir(char *path)
// No need to normalize, it's only a directory name, not a path.
if (!my_multi_malloc(0,
- &new_elt, sizeof(LEX_STRING),
+ &new_elt, sizeof(LEX_CSTRING),
&new_elt_buffer, path_len + 1,
NullS))
return true;
@@ -742,8 +770,8 @@ push_ignored_db_dir(char *path)
void
ignore_db_dirs_reset()
{
- LEX_STRING **elt;
- while (NULL!= (elt= (LEX_STRING **) pop_dynamic(&ignore_db_dirs_array)))
+ LEX_CSTRING **elt;
+ while (NULL!= (elt= (LEX_CSTRING **) pop_dynamic(&ignore_db_dirs_array)))
if (elt && *elt)
my_free(*elt);
}
@@ -844,8 +872,7 @@ ignore_db_dirs_process_additions()
ulong i;
size_t len;
char *ptr;
- LEX_STRING *dir;
-
+ LEX_CSTRING *dir;
skip_ignored_dir_check= TRUE;
@@ -997,8 +1024,8 @@ enum find_files_result {
static find_files_result
-find_files(THD *thd, Dynamic_array<LEX_STRING*> *files, LEX_STRING *db,
- const char *path, const LEX_STRING *wild)
+find_files(THD *thd, Dynamic_array<LEX_CSTRING*> *files, LEX_CSTRING *db,
+ const char *path, const LEX_CSTRING *wild)
{
MY_DIR *dirp;
Discovered_table_list tl(thd, files, wild);
@@ -1049,14 +1076,19 @@ find_files(THD *thd, Dynamic_array<LEX_STRING*> *files, LEX_STRING *db,
if (ha_discover_table_names(thd, db, dirp, &tl, false))
goto err;
}
-#if MYSQL_VERSION_ID < 100300
- /* incomplete optimization, but a less drastic change in GA version */
- if (!thd->lex->select_lex.order_list.elements &&
- !thd->lex->select_lex.group_list.elements)
-#else
if (is_show_command(thd))
-#endif
tl.sort();
+#ifndef DBUG_OFF
+ else
+ {
+ /*
+ sort_desc() is used to find easier unstable mtr tests that query
+ INFORMATION_SCHEMA.{SCHEMATA|TABLES} without a proper ORDER BY.
+ This can be removed in some release after 10.3 (e.g. in 10.4).
+ */
+ tl.sort_desc();
+ }
+#endif
DBUG_PRINT("info",("found: %zu files", files->elements()));
my_dirend(dirp);
@@ -1209,17 +1241,17 @@ mysqld_show_create_get_fields(THD *thd, TABLE_LIST *table_list,
LEX *lex= thd->lex;
MEM_ROOT *mem_root= thd->mem_root;
DBUG_ENTER("mysqld_show_create_get_fields");
- DBUG_PRINT("enter",("db: %s table: %s",table_list->db,
- table_list->table_name));
+ DBUG_PRINT("enter",("db: %s table: %s",table_list->db.str,
+ table_list->table_name.str));
- if (lex->only_view)
+ if (lex->table_type == TABLE_TYPE_VIEW)
{
if (check_table_access(thd, SELECT_ACL, table_list, FALSE, 1, FALSE))
{
DBUG_PRINT("debug", ("check_table_access failed"));
my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
"SHOW", thd->security_ctx->priv_user,
- thd->security_ctx->host_or_ip, table_list->alias);
+ thd->security_ctx->host_or_ip, table_list->alias.str);
goto exit;
}
DBUG_PRINT("debug", ("check_table_access succeeded"));
@@ -1248,7 +1280,7 @@ mysqld_show_create_get_fields(THD *thd, TABLE_LIST *table_list,
{
my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
"SHOW", thd->security_ctx->priv_user,
- thd->security_ctx->host_or_ip, table_list->alias);
+ thd->security_ctx->host_or_ip, table_list->alias.str);
goto exit;
}
}
@@ -1272,15 +1304,23 @@ mysqld_show_create_get_fields(THD *thd, TABLE_LIST *table_list,
MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL) ||
mysql_handle_derived(lex, DT_INIT | DT_PREPARE);
thd->pop_internal_handler();
- if (open_error && (thd->killed || thd->is_error()))
+ if (unlikely(open_error && (thd->killed || thd->is_error())))
goto exit;
}
/* TODO: add environment variables show when it become possible */
- if (lex->only_view && !table_list->view)
+ if (lex->table_type == TABLE_TYPE_VIEW && !table_list->view)
{
my_error(ER_WRONG_OBJECT, MYF(0),
- table_list->db, table_list->table_name, "VIEW");
+ table_list->db.str, table_list->table_name.str, "VIEW");
+ goto exit;
+ }
+ else if (lex->table_type == TABLE_TYPE_SEQUENCE &&
+ (!table_list->table ||
+ table_list->table->s->table_type != TABLE_TYPE_SEQUENCE))
+ {
+ my_error(ER_NOT_SEQUENCE, MYF(0),
+ table_list->db.str, table_list->table_name.str);
goto exit;
}
@@ -1291,6 +1331,8 @@ mysqld_show_create_get_fields(THD *thd, TABLE_LIST *table_list,
if ((table_list->view ?
show_create_view(thd, table_list, buffer) :
+ lex->table_type == TABLE_TYPE_SEQUENCE ?
+ show_create_sequence(thd, table_list, buffer) :
show_create_table(thd, table_list, buffer, NULL, WITHOUT_DB_NAME)))
goto exit;
@@ -1354,8 +1396,8 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
List<Item> field_list;
bool error= TRUE;
DBUG_ENTER("mysqld_show_create");
- DBUG_PRINT("enter",("db: %s table: %s",table_list->db,
- table_list->table_name));
+ DBUG_PRINT("enter",("db: %s table: %s",table_list->db.str,
+ table_list->table_name.str));
/*
Metadata locks taken during SHOW CREATE should be released when
@@ -1363,6 +1405,7 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
*/
MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
+ TABLE_LIST archive;
if (mysqld_show_create_get_fields(thd, table_list, &field_list, &buffer))
goto exit;
@@ -1378,8 +1421,7 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
else
{
if (table_list->schema_table)
- protocol->store(table_list->schema_table->table_name,
- system_charset_info);
+ protocol->store(table_list->schema_table->table_name, system_charset_info);
else
protocol->store(table_list->table->alias.c_ptr(), system_charset_info);
}
@@ -1424,8 +1466,8 @@ void mysqld_show_create_db_get_fields(THD *thd, List<Item> *field_list)
}
-bool mysqld_show_create_db(THD *thd, LEX_STRING *dbname,
- LEX_STRING *orig_dbname,
+bool mysqld_show_create_db(THD *thd, LEX_CSTRING *dbname,
+ LEX_CSTRING *orig_dbname,
const DDL_options_st &options)
{
char buff[2048];
@@ -1460,7 +1502,7 @@ bool mysqld_show_create_db(THD *thd, LEX_STRING *dbname,
DBUG_RETURN(TRUE);
}
#endif
- if (is_infoschema_db(dbname->str))
+ if (is_infoschema_db(dbname))
{
*dbname= INFORMATION_SCHEMA_NAME;
create.default_table_charset= system_charset_info;
@@ -1489,7 +1531,7 @@ bool mysqld_show_create_db(THD *thd, LEX_STRING *dbname,
buffer.append(STRING_WITH_LEN("CREATE DATABASE "));
if (options.if_not_exists())
buffer.append(STRING_WITH_LEN("/*!32312 IF NOT EXISTS*/ "));
- append_identifier(thd, &buffer, dbname->str, dbname->length);
+ append_identifier(thd, &buffer, dbname);
if (create.default_table_charset)
{
@@ -1524,7 +1566,7 @@ mysqld_list_fields(THD *thd, TABLE_LIST *table_list, const char *wild)
TABLE *table;
MEM_ROOT *mem_root= thd->mem_root;
DBUG_ENTER("mysqld_list_fields");
- DBUG_PRINT("enter",("table: %s",table_list->table_name));
+ DBUG_PRINT("enter",("table: %s", table_list->table_name.str));
if (open_normal_and_derived_tables(thd, table_list,
MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL,
@@ -1538,7 +1580,7 @@ mysqld_list_fields(THD *thd, TABLE_LIST *table_list, const char *wild)
for (ptr=table->field ; (field= *ptr); ptr++)
{
if (!wild || !wild[0] ||
- !wild_case_compare(system_charset_info, field->field_name,wild))
+ !wild_case_compare(system_charset_info, field->field_name.str,wild))
{
if (table_list->view)
field_list.push_back(new (mem_root)
@@ -1607,7 +1649,7 @@ static const char *require_quotes(const char *name, uint name_length)
*/
bool
-append_identifier(THD *thd, String *packet, const char *name, uint length)
+append_identifier(THD *thd, String *packet, const char *name, size_t length)
{
const char *name_end;
char quote_char;
@@ -1686,11 +1728,11 @@ append_identifier(THD *thd, String *packet, const char *name, uint length)
# Quote character
*/
-int get_quote_char_for_identifier(THD *thd, const char *name, uint length)
+int get_quote_char_for_identifier(THD *thd, const char *name, size_t length)
{
if (length &&
- !is_keyword(name,length) &&
- !require_quotes(name, length) &&
+ !is_keyword(name,(uint)length) &&
+ !require_quotes(name, (uint)length) &&
!(thd->variables.option_bits & OPTION_QUOTE_SHOW_CREATE))
return EOF;
if (thd->variables.sql_mode & MODE_ANSI_QUOTES)
@@ -1706,7 +1748,7 @@ static void append_directory(THD *thd, String *packet, const char *dir_type,
{
if (filename && !(thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE))
{
- uint length= dirname_length(filename);
+ size_t length= dirname_length(filename);
packet->append(' ');
packet->append(dir_type);
packet->append(STRING_WITH_LEN(" DIRECTORY='"));
@@ -1767,6 +1809,7 @@ static bool get_field_default_value(THD *thd, Field *field, String *def_value,
has_default= (field->default_value ||
(!(field->flags & NO_DEFAULT_VALUE_FLAG) &&
+ !field->vers_sys_field() &&
field->unireg_check != Field::NEXT_NUMBER));
def_value->length(0);
@@ -1863,17 +1906,190 @@ static void append_create_options(THD *thd, String *packet,
DBUG_ASSERT(opt->value.str);
packet->append(' ');
- append_identifier(thd, packet, opt->name.str, opt->name.length);
+ append_identifier(thd, packet, &opt->name);
packet->append('=');
if (opt->quoted_value)
append_unescaped(packet, opt->value.str, opt->value.length);
else
- packet->append(opt->value.str, opt->value.length);
+ packet->append(&opt->value);
}
if (in_comment)
packet->append(STRING_WITH_LEN(" */"));
}
+/**
+ Add table options to end of CREATE statement
+
+ @param schema_table 1 if schema table
+ @param sequence 1 if sequence. If sequence, we flush out options
+ not relevant for sequences.
+*/
+
+static void add_table_options(THD *thd, TABLE *table,
+ Table_specification_st *create_info_arg,
+ bool schema_table, bool sequence,
+ String *packet)
+{
+ sql_mode_t sql_mode= thd->variables.sql_mode;
+ TABLE_SHARE *share= table->s;
+ handlerton *hton;
+ HA_CREATE_INFO create_info;
+ bool check_options= (!(sql_mode & MODE_IGNORE_BAD_TABLE_OPTIONS) &&
+ !create_info_arg);
+
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (table->part_info)
+ hton= table->part_info->default_engine_type;
+ else
+#endif
+ hton= table->file->ht;
+
+ bzero((char*) &create_info, sizeof(create_info));
+ /* Allow update_create_info to update row type, page checksums and options */
+ create_info.row_type= share->row_type;
+ create_info.page_checksum= share->page_checksum;
+ create_info.options= share->db_create_options;
+ table->file->update_create_info(&create_info);
+
+ /*
+ IF check_create_info
+ THEN add ENGINE only if it was used when creating the table
+ */
+ if (!create_info_arg ||
+ (create_info_arg->used_fields & HA_CREATE_USED_ENGINE))
+ {
+ LEX_CSTRING *engine_name= table->file->engine_name();
+
+ if (sql_mode & (MODE_MYSQL323 | MODE_MYSQL40))
+ packet->append(STRING_WITH_LEN(" TYPE="));
+ else
+ packet->append(STRING_WITH_LEN(" ENGINE="));
+
+ packet->append(engine_name->str, engine_name->length);
+ }
+
+ if (sequence)
+ goto end_options;
+
+ /*
+ Add AUTO_INCREMENT=... if there is an AUTO_INCREMENT column,
+ and NEXT_ID > 1 (the default). We must not print the clause
+ for engines that do not support this as it would break the
+ import of dumps, but as of this writing, the test for whether
+ AUTO_INCREMENT columns are allowed and wether AUTO_INCREMENT=...
+ is supported is identical, !(file->table_flags() & HA_NO_AUTO_INCREMENT))
+ Because of that, we do not explicitly test for the feature,
+ but may extrapolate its existence from that of an AUTO_INCREMENT column.
+ */
+
+ if (create_info.auto_increment_value > 1)
+ {
+ packet->append(STRING_WITH_LEN(" AUTO_INCREMENT="));
+ packet->append_ulonglong(create_info.auto_increment_value);
+ }
+
+ if (share->table_charset && !(sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) &&
+ share->table_type != TABLE_TYPE_SEQUENCE)
+ {
+ /*
+ IF check_create_info
+ THEN add DEFAULT CHARSET only if it was used when creating the table
+ */
+ if (!create_info_arg ||
+ (create_info_arg->used_fields & HA_CREATE_USED_DEFAULT_CHARSET))
+ {
+ packet->append(STRING_WITH_LEN(" DEFAULT CHARSET="));
+ packet->append(share->table_charset->csname);
+ if (!(share->table_charset->state & MY_CS_PRIMARY))
+ {
+ packet->append(STRING_WITH_LEN(" COLLATE="));
+ packet->append(table->s->table_charset->name);
+ }
+ }
+ }
+
+ if (share->min_rows)
+ {
+ packet->append(STRING_WITH_LEN(" MIN_ROWS="));
+ packet->append_ulonglong(share->min_rows);
+ }
+
+ if (share->max_rows && !schema_table && !sequence)
+ {
+ packet->append(STRING_WITH_LEN(" MAX_ROWS="));
+ packet->append_ulonglong(share->max_rows);
+ }
+
+ if (share->avg_row_length)
+ {
+ packet->append(STRING_WITH_LEN(" AVG_ROW_LENGTH="));
+ packet->append_ulonglong(share->avg_row_length);
+ }
+
+ if (create_info.options & HA_OPTION_PACK_KEYS)
+ packet->append(STRING_WITH_LEN(" PACK_KEYS=1"));
+ if (create_info.options & HA_OPTION_NO_PACK_KEYS)
+ packet->append(STRING_WITH_LEN(" PACK_KEYS=0"));
+ if (share->db_create_options & HA_OPTION_STATS_PERSISTENT)
+ packet->append(STRING_WITH_LEN(" STATS_PERSISTENT=1"));
+ if (share->db_create_options & HA_OPTION_NO_STATS_PERSISTENT)
+ packet->append(STRING_WITH_LEN(" STATS_PERSISTENT=0"));
+ if (share->stats_auto_recalc == HA_STATS_AUTO_RECALC_ON)
+ packet->append(STRING_WITH_LEN(" STATS_AUTO_RECALC=1"));
+ else if (share->stats_auto_recalc == HA_STATS_AUTO_RECALC_OFF)
+ packet->append(STRING_WITH_LEN(" STATS_AUTO_RECALC=0"));
+ if (share->stats_sample_pages != 0)
+ {
+ packet->append(STRING_WITH_LEN(" STATS_SAMPLE_PAGES="));
+ packet->append_ulonglong(share->stats_sample_pages);
+ }
+
+ /* We use CHECKSUM, instead of TABLE_CHECKSUM, for backward compability */
+ if (create_info.options & HA_OPTION_CHECKSUM)
+ packet->append(STRING_WITH_LEN(" CHECKSUM=1"));
+ if (create_info.page_checksum != HA_CHOICE_UNDEF)
+ {
+ packet->append(STRING_WITH_LEN(" PAGE_CHECKSUM="));
+ packet->append(ha_choice_values[create_info.page_checksum], 1);
+ }
+ if (create_info.options & HA_OPTION_DELAY_KEY_WRITE)
+ packet->append(STRING_WITH_LEN(" DELAY_KEY_WRITE=1"));
+ if (create_info.row_type != ROW_TYPE_DEFAULT)
+ {
+ packet->append(STRING_WITH_LEN(" ROW_FORMAT="));
+ packet->append(ha_row_type[(uint) create_info.row_type]);
+ }
+ if (share->transactional != HA_CHOICE_UNDEF)
+ {
+ packet->append(STRING_WITH_LEN(" TRANSACTIONAL="));
+ packet->append(ha_choice_values[(uint) share->transactional], 1);
+ }
+ if (share->table_type == TABLE_TYPE_SEQUENCE)
+ packet->append(STRING_WITH_LEN(" SEQUENCE=1"));
+ if (table->s->key_block_size)
+ {
+ packet->append(STRING_WITH_LEN(" KEY_BLOCK_SIZE="));
+ packet->append_ulonglong(table->s->key_block_size);
+ }
+ table->file->append_create_info(packet);
+
+end_options:
+ if (share->comment.length)
+ {
+ packet->append(STRING_WITH_LEN(" COMMENT="));
+ append_unescaped(packet, share->comment.str, share->comment.length);
+ }
+ if (share->connect_string.length)
+ {
+ packet->append(STRING_WITH_LEN(" CONNECTION="));
+ append_unescaped(packet, share->connect_string.str, share->connect_string.length);
+ }
+ append_create_options(thd, packet, share->option_list, check_options,
+ hton->table_options);
+ append_directory(thd, packet, "DATA", create_info.data_file_name);
+ append_directory(thd, packet, "INDEX", create_info.index_file_name);
+}
+
/*
Build a CREATE TABLE statement for a table.
@@ -1903,18 +2119,17 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
enum_with_db_name with_db_name)
{
List<Item> field_list;
- char tmp[MAX_FIELD_WIDTH], *for_str, buff[128], def_value_buf[MAX_FIELD_WIDTH];
- const char *alias;
+ char tmp[MAX_FIELD_WIDTH], *for_str, def_value_buf[MAX_FIELD_WIDTH];
+ LEX_CSTRING alias;
String type;
String def_value;
Field **ptr,*field;
uint primary_key;
KEY *key_info;
TABLE *table= table_list->table;
- handler *file= table->file;
TABLE_SHARE *share= table->s;
- HA_CREATE_INFO create_info;
sql_mode_t sql_mode= thd->variables.sql_mode;
+ bool explicit_fields= false;
bool foreign_db_mode= sql_mode & (MODE_POSTGRESQL | MODE_ORACLE |
MODE_MSSQL | MODE_DB2 |
MODE_MAXDB | MODE_ANSI);
@@ -1924,8 +2139,8 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
!foreign_db_mode;
bool check_options= !(sql_mode & MODE_IGNORE_BAD_TABLE_OPTIONS) &&
!create_info_arg;
- handlerton *hton;
my_bitmap_map *old_map;
+ handlerton *hton;
int error= 0;
DBUG_ENTER("show_create_table");
DBUG_PRINT("enter",("table: %s", table->s->table_name.str));
@@ -1935,7 +2150,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
hton= table->part_info->default_engine_type;
else
#endif
- hton= file->ht;
+ hton= table->file->ht;
restore_record(table, s->default_values); // Get empty record
@@ -1951,15 +2166,19 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
if (create_info_arg && create_info_arg->if_not_exists())
packet->append(STRING_WITH_LEN("IF NOT EXISTS "));
if (table_list->schema_table)
- alias= table_list->schema_table->table_name;
+ {
+ alias.str= table_list->schema_table->table_name;
+ alias.length= strlen(alias.str);
+ }
else
{
if (lower_case_table_names == 2)
- alias= table->alias.c_ptr();
- else
{
- alias= share->table_name.str;
+ alias.str= table->alias.c_ptr();
+ alias.length= table->alias.length();
}
+ else
+ alias= share->table_name;
}
/*
@@ -1971,16 +2190,16 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
*/
if (with_db_name == WITH_DB_NAME)
{
- const LEX_STRING *const db=
+ const LEX_CSTRING *const db=
table_list->schema_table ? &INFORMATION_SCHEMA_NAME : &table->s->db;
- if (!thd->db || strcmp(db->str, thd->db))
+ if (!thd->db.str || cmp(db, &thd->db))
{
- append_identifier(thd, packet, db->str, db->length);
+ append_identifier(thd, packet, db);
packet->append(STRING_WITH_LEN("."));
}
}
- append_identifier(thd, packet, alias, strlen(alias));
+ append_identifier(thd, packet, &alias);
packet->append(STRING_WITH_LEN(" (\n"));
/*
We need this to get default values from the table
@@ -1989,17 +2208,29 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
*/
old_map= tmp_use_all_columns(table, table->read_set);
+ bool not_the_first_field= false;
for (ptr=table->field ; (field= *ptr); ptr++)
{
+
uint flags = field->flags;
- if (ptr != table->field)
+ if (field->invisible > INVISIBLE_USER)
+ continue;
+ if (not_the_first_field)
packet->append(STRING_WITH_LEN(",\n"));
+ not_the_first_field= true;
packet->append(STRING_WITH_LEN(" "));
- append_identifier(thd,packet,field->field_name, strlen(field->field_name));
+ append_identifier(thd, packet, &field->field_name);
packet->append(' ');
+ const Type_handler *th= field->type_handler();
+ const Schema *implied_schema= Schema::find_implied(thd);
+ if (th != implied_schema->map_data_type(thd, th))
+ {
+ packet->append(th->schema()->name(), system_charset_info);
+ packet->append(STRING_WITH_LEN("."), system_charset_info);
+ }
type.set(tmp, sizeof(tmp), system_charset_info);
field->sql_type(type);
packet->append(type.ptr(), type.length(), system_charset_info);
@@ -2033,10 +2264,22 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
packet->append(STRING_WITH_LEN(" STORED"));
else
packet->append(STRING_WITH_LEN(" VIRTUAL"));
+ if (field->invisible == INVISIBLE_USER)
+ {
+ packet->append(STRING_WITH_LEN(" INVISIBLE"));
+ }
}
else
{
- if (flags & NOT_NULL_FLAG)
+ if (field->flags & VERS_SYS_START_FLAG)
+ {
+ packet->append(STRING_WITH_LEN(" GENERATED ALWAYS AS ROW START"));
+ }
+ else if (field->flags & VERS_SYS_END_FLAG)
+ {
+ packet->append(STRING_WITH_LEN(" GENERATED ALWAYS AS ROW END"));
+ }
+ else if (flags & NOT_NULL_FLAG)
packet->append(STRING_WITH_LEN(" NOT NULL"));
else if (field->type() == MYSQL_TYPE_TIMESTAMP)
{
@@ -2047,6 +2290,10 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
packet->append(STRING_WITH_LEN(" NULL"));
}
+ if (field->invisible == INVISIBLE_USER)
+ {
+ packet->append(STRING_WITH_LEN(" INVISIBLE"));
+ }
def_value.set(def_value_buf, sizeof(def_value_buf), system_charset_info);
if (get_field_default_value(thd, field, &def_value, 1))
{
@@ -2054,6 +2301,11 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
packet->append(def_value.ptr(), def_value.length(), system_charset_info);
}
+ if (field->vers_update_unversioned())
+ {
+ packet->append(STRING_WITH_LEN(" WITHOUT SYSTEM VERSIONING"));
+ }
+
if (!limited_mysql_mode &&
print_on_update_clause(field, &def_value, false))
{
@@ -2087,21 +2339,17 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
}
key_info= table->key_info;
- bzero((char*) &create_info, sizeof(create_info));
- /* Allow update_create_info to update row type, page checksums and options */
- create_info.row_type= share->row_type;
- create_info.page_checksum= share->page_checksum;
- create_info.options= share->db_create_options;
- file->update_create_info(&create_info);
primary_key= share->primary_key;
for (uint i=0 ; i < share->keys ; i++,key_info++)
{
+ if (key_info->flags & HA_INVISIBLE_KEY)
+ continue;
KEY_PART_INFO *key_part= key_info->key_part;
bool found_primary=0;
packet->append(STRING_WITH_LEN(",\n "));
- if (i == primary_key && !strcmp(key_info->name, primary_key_name))
+ if (i == primary_key && !strcmp(key_info->name.str, primary_key_name))
{
found_primary=1;
/*
@@ -2120,18 +2368,21 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
packet->append(STRING_WITH_LEN("KEY "));
if (!found_primary)
- append_identifier(thd, packet, key_info->name, strlen(key_info->name));
+ append_identifier(thd, packet, &key_info->name);
packet->append(STRING_WITH_LEN(" ("));
for (uint j=0 ; j < key_info->user_defined_key_parts ; j++,key_part++)
{
+ Field *field= key_part->field;
+ if (field->invisible > INVISIBLE_USER)
+ continue;
+
if (j)
packet->append(',');
if (key_part->field)
- append_identifier(thd,packet,key_part->field->field_name,
- strlen(key_part->field->field_name));
+ append_identifier(thd, packet, &key_part->field->field_name);
if (key_part->field &&
(key_part->length !=
table->field[key_part->fieldnr-1]->key_length() &&
@@ -2145,24 +2396,47 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
store_key_options(thd, packet, table, key_info);
if (key_info->parser)
{
- LEX_STRING *parser_name= plugin_name(key_info->parser);
+ LEX_CSTRING *parser_name= plugin_name(key_info->parser);
packet->append(STRING_WITH_LEN(" /*!50100 WITH PARSER "));
- append_identifier(thd, packet, parser_name->str, parser_name->length);
+ append_identifier(thd, packet, parser_name);
packet->append(STRING_WITH_LEN(" */ "));
}
append_create_options(thd, packet, key_info->option_list, check_options,
hton->index_options);
}
+ if (table->versioned())
+ {
+ const Field *fs = table->vers_start_field();
+ const Field *fe = table->vers_end_field();
+ DBUG_ASSERT(fs);
+ DBUG_ASSERT(fe);
+ explicit_fields= fs->invisible < INVISIBLE_SYSTEM;
+ DBUG_ASSERT(!explicit_fields || fe->invisible < INVISIBLE_SYSTEM);
+ if (explicit_fields)
+ {
+ packet->append(STRING_WITH_LEN(",\n PERIOD FOR SYSTEM_TIME ("));
+ append_identifier(thd,packet,fs->field_name.str, fs->field_name.length);
+ packet->append(STRING_WITH_LEN(", "));
+ append_identifier(thd,packet,fe->field_name.str, fe->field_name.length);
+ packet->append(STRING_WITH_LEN(")"));
+ }
+ else
+ {
+ DBUG_ASSERT(fs->invisible == INVISIBLE_SYSTEM);
+ DBUG_ASSERT(fe->invisible == INVISIBLE_SYSTEM);
+ }
+ }
+
/*
Get possible foreign key definitions stored in InnoDB and append them
to the CREATE TABLE statement
*/
- if ((for_str= file->get_foreign_key_create_info()))
+ if ((for_str= table->file->get_foreign_key_create_info()))
{
packet->append(for_str, strlen(for_str));
- file->free_foreign_key_create_info(for_str);
+ table->file->free_foreign_key_create_info(for_str);
}
/* Add table level check constraints */
@@ -2176,10 +2450,10 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
check->print(&str);
packet->append(STRING_WITH_LEN(",\n "));
- if (check->name.length)
+ if (check->name.str)
{
packet->append(STRING_WITH_LEN("CONSTRAINT "));
- append_identifier(thd, packet, check->name.str, check->name.length);
+ append_identifier(thd, packet, &check->name);
}
packet->append(STRING_WITH_LEN(" CHECK ("));
packet->append(str);
@@ -2189,146 +2463,12 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
packet->append(STRING_WITH_LEN("\n)"));
if (show_table_options)
- {
- /*
- IF check_create_info
- THEN add ENGINE only if it was used when creating the table
- */
- if (!create_info_arg ||
- (create_info_arg->used_fields & HA_CREATE_USED_ENGINE))
- {
- if (sql_mode & (MODE_MYSQL323 | MODE_MYSQL40))
- packet->append(STRING_WITH_LEN(" TYPE="));
- else
- packet->append(STRING_WITH_LEN(" ENGINE="));
- packet->append(hton_name(hton));
- }
-
- /*
- Add AUTO_INCREMENT=... if there is an AUTO_INCREMENT column,
- and NEXT_ID > 1 (the default). We must not print the clause
- for engines that do not support this as it would break the
- import of dumps, but as of this writing, the test for whether
- AUTO_INCREMENT columns are allowed and wether AUTO_INCREMENT=...
- is supported is identical, !(file->table_flags() & HA_NO_AUTO_INCREMENT))
- Because of that, we do not explicitly test for the feature,
- but may extrapolate its existence from that of an AUTO_INCREMENT column.
- */
-
- if (create_info.auto_increment_value > 1)
- {
- char *end;
- packet->append(STRING_WITH_LEN(" AUTO_INCREMENT="));
- end= longlong10_to_str(create_info.auto_increment_value, buff,10);
- packet->append(buff, (uint) (end - buff));
- }
-
- if (share->table_charset && !(sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)))
- {
- /*
- IF check_create_info
- THEN add DEFAULT CHARSET only if it was used when creating the table
- */
- if (!create_info_arg ||
- (create_info_arg->used_fields & HA_CREATE_USED_DEFAULT_CHARSET))
- {
- packet->append(STRING_WITH_LEN(" DEFAULT CHARSET="));
- packet->append(share->table_charset->csname);
- if (!(share->table_charset->state & MY_CS_PRIMARY))
- {
- packet->append(STRING_WITH_LEN(" COLLATE="));
- packet->append(table->s->table_charset->name);
- }
- }
- }
-
- if (share->min_rows)
- {
- char *end;
- packet->append(STRING_WITH_LEN(" MIN_ROWS="));
- end= longlong10_to_str(share->min_rows, buff, 10);
- packet->append(buff, (uint) (end- buff));
- }
+ add_table_options(thd, table, create_info_arg,
+ table_list->schema_table != 0, 0, packet);
- if (share->max_rows && !table_list->schema_table)
- {
- char *end;
- packet->append(STRING_WITH_LEN(" MAX_ROWS="));
- end= longlong10_to_str(share->max_rows, buff, 10);
- packet->append(buff, (uint) (end - buff));
- }
-
- if (share->avg_row_length)
- {
- char *end;
- packet->append(STRING_WITH_LEN(" AVG_ROW_LENGTH="));
- end= longlong10_to_str(share->avg_row_length, buff,10);
- packet->append(buff, (uint) (end - buff));
- }
+ if (table->versioned())
+ packet->append(STRING_WITH_LEN(" WITH SYSTEM VERSIONING"));
- if (create_info.options & HA_OPTION_PACK_KEYS)
- packet->append(STRING_WITH_LEN(" PACK_KEYS=1"));
- if (create_info.options & HA_OPTION_NO_PACK_KEYS)
- packet->append(STRING_WITH_LEN(" PACK_KEYS=0"));
- if (share->db_create_options & HA_OPTION_STATS_PERSISTENT)
- packet->append(STRING_WITH_LEN(" STATS_PERSISTENT=1"));
- if (share->db_create_options & HA_OPTION_NO_STATS_PERSISTENT)
- packet->append(STRING_WITH_LEN(" STATS_PERSISTENT=0"));
- if (share->stats_auto_recalc == HA_STATS_AUTO_RECALC_ON)
- packet->append(STRING_WITH_LEN(" STATS_AUTO_RECALC=1"));
- else if (share->stats_auto_recalc == HA_STATS_AUTO_RECALC_OFF)
- packet->append(STRING_WITH_LEN(" STATS_AUTO_RECALC=0"));
- if (share->stats_sample_pages != 0)
- {
- char *end;
- packet->append(STRING_WITH_LEN(" STATS_SAMPLE_PAGES="));
- end= longlong10_to_str(share->stats_sample_pages, buff, 10);
- packet->append(buff, (uint) (end - buff));
- }
-
- /* We use CHECKSUM, instead of TABLE_CHECKSUM, for backward compability */
- if (create_info.options & HA_OPTION_CHECKSUM)
- packet->append(STRING_WITH_LEN(" CHECKSUM=1"));
- if (create_info.page_checksum != HA_CHOICE_UNDEF)
- {
- packet->append(STRING_WITH_LEN(" PAGE_CHECKSUM="));
- packet->append(ha_choice_values[create_info.page_checksum], 1);
- }
- if (create_info.options & HA_OPTION_DELAY_KEY_WRITE)
- packet->append(STRING_WITH_LEN(" DELAY_KEY_WRITE=1"));
- if (create_info.row_type != ROW_TYPE_DEFAULT)
- {
- packet->append(STRING_WITH_LEN(" ROW_FORMAT="));
- packet->append(ha_row_type[(uint) create_info.row_type]);
- }
- if (share->transactional != HA_CHOICE_UNDEF)
- {
- packet->append(STRING_WITH_LEN(" TRANSACTIONAL="));
- packet->append(ha_choice_values[(uint) share->transactional], 1);
- }
- if (table->s->key_block_size)
- {
- char *end;
- packet->append(STRING_WITH_LEN(" KEY_BLOCK_SIZE="));
- end= longlong10_to_str(table->s->key_block_size, buff, 10);
- packet->append(buff, (uint) (end - buff));
- }
- table->file->append_create_info(packet);
- if (share->comment.length)
- {
- packet->append(STRING_WITH_LEN(" COMMENT="));
- append_unescaped(packet, share->comment.str, share->comment.length);
- }
- if (share->connect_string.length)
- {
- packet->append(STRING_WITH_LEN(" CONNECTION="));
- append_unescaped(packet, share->connect_string.str, share->connect_string.length);
- }
- append_create_options(thd, packet, share->option_list, check_options,
- hton->table_options);
- append_directory(thd, packet, "DATA", create_info.data_file_name);
- append_directory(thd, packet, "INDEX", create_info.index_file_name);
- }
#ifdef WITH_PARTITION_STORAGE_ENGINE
{
if (table->part_info &&
@@ -2425,11 +2565,11 @@ void view_store_options(THD *thd, TABLE_LIST *table, String *buff)
Returns ALGORITHM clause of a view
*/
-static const LEX_STRING *view_algorithm(TABLE_LIST *table)
+static const LEX_CSTRING *view_algorithm(TABLE_LIST *table)
{
- static const LEX_STRING undefined= { C_STRING_WITH_LEN("UNDEFINED") };
- static const LEX_STRING merge= { C_STRING_WITH_LEN("MERGE") };
- static const LEX_STRING temptable= { C_STRING_WITH_LEN("TEMPTABLE") };
+ static const LEX_CSTRING undefined= { STRING_WITH_LEN("UNDEFINED") };
+ static const LEX_CSTRING merge= { STRING_WITH_LEN("MERGE") };
+ static const LEX_CSTRING temptable= { STRING_WITH_LEN("TEMPTABLE") };
switch (table->algorithm) {
case VIEW_ALGORITHM_TMPTABLE:
return &temptable;
@@ -2437,11 +2577,23 @@ static const LEX_STRING *view_algorithm(TABLE_LIST *table)
return &merge;
default:
DBUG_ASSERT(0); // never should happen
+ /* fall through */
case VIEW_ALGORITHM_UNDEFINED:
return &undefined;
}
}
+
+static bool append_at_host(THD *thd, String *buffer, const LEX_CSTRING *host)
+{
+ if (!host->str || !host->str[0])
+ return false;
+ return
+ buffer->append('@') ||
+ append_identifier(thd, buffer, host);
+}
+
+
/*
Append DEFINER clause to the given buffer.
@@ -2453,17 +2605,14 @@ static const LEX_STRING *view_algorithm(TABLE_LIST *table)
definer_host [in] host name part of definer
*/
-void append_definer(THD *thd, String *buffer, const LEX_STRING *definer_user,
- const LEX_STRING *definer_host)
+bool append_definer(THD *thd, String *buffer, const LEX_CSTRING *definer_user,
+ const LEX_CSTRING *definer_host)
{
- buffer->append(STRING_WITH_LEN("DEFINER="));
- append_identifier(thd, buffer, definer_user->str, definer_user->length);
- if (definer_host->str && definer_host->str[0])
- {
- buffer->append('@');
- append_identifier(thd, buffer, definer_host->str, definer_host->length);
- }
- buffer->append(' ');
+ return
+ buffer->append(STRING_WITH_LEN("DEFINER=")) ||
+ append_identifier(thd, buffer, definer_user) ||
+ append_at_host(thd, buffer, definer_host) ||
+ buffer->append(' ');
}
@@ -2477,7 +2626,7 @@ static int show_create_view(THD *thd, TABLE_LIST *table, String *buff)
MODE_MAXDB |
MODE_ANSI)) != 0;
- if (!thd->db || strcmp(thd->db, table->view_db.str))
+ if (!thd->db.str || cmp(&thd->db, &table->view_db))
/*
print compact view name if the view belongs to the current database
*/
@@ -2494,7 +2643,7 @@ static int show_create_view(THD *thd, TABLE_LIST *table, String *buff)
tbl;
tbl= tbl->next_global)
{
- if (strcmp(table->view_db.str, tbl->view ? tbl->view_db.str :tbl->db)!= 0)
+ if (cmp(&table->view_db, tbl->view ? &tbl->view_db : &tbl->db))
{
table->compact_view_format= FALSE;
break;
@@ -2510,10 +2659,10 @@ static int show_create_view(THD *thd, TABLE_LIST *table, String *buff)
buff->append(STRING_WITH_LEN("VIEW "));
if (!compact_view_name)
{
- append_identifier(thd, buff, table->view_db.str, table->view_db.length);
+ append_identifier(thd, buff, &table->view_db);
buff->append('.');
}
- append_identifier(thd, buff, table->view_name.str, table->view_name.length);
+ append_identifier(thd, buff, &table->view_name);
buff->append(STRING_WITH_LEN(" AS "));
/*
@@ -2534,6 +2683,55 @@ 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)
+{
+ TABLE *table= table_list->table;
+ SEQUENCE *seq= table->s->sequence;
+ LEX_CSTRING alias;
+ sql_mode_t sql_mode= thd->variables.sql_mode;
+ bool foreign_db_mode= sql_mode & (MODE_POSTGRESQL | MODE_ORACLE |
+ MODE_MSSQL | MODE_DB2 |
+ MODE_MAXDB | MODE_ANSI);
+ bool show_table_options= !(sql_mode & MODE_NO_TABLE_OPTIONS) &&
+ !foreign_db_mode;
+
+ if (lower_case_table_names == 2)
+ {
+ alias.str= table->alias.c_ptr();
+ alias.length= table->alias.length();
+ }
+ else
+ alias= table->s->table_name;
+
+ packet->append(STRING_WITH_LEN("CREATE SEQUENCE "));
+ append_identifier(thd, packet, &alias);
+ packet->append(STRING_WITH_LEN(" start with "));
+ packet->append_longlong(seq->start);
+ packet->append(STRING_WITH_LEN(" minvalue "));
+ packet->append_longlong(seq->min_value);
+ packet->append(STRING_WITH_LEN(" maxvalue "));
+ packet->append_longlong(seq->max_value);
+ packet->append(STRING_WITH_LEN(" increment by "));
+ packet->append_longlong(seq->increment);
+ if (seq->cache)
+ {
+ packet->append(STRING_WITH_LEN(" cache "));
+ packet->append_longlong(seq->cache);
+ }
+ else
+ packet->append(STRING_WITH_LEN(" nocache"));
+ if (seq->cycle)
+ packet->append(STRING_WITH_LEN(" cycle"));
+ else
+ packet->append(STRING_WITH_LEN(" nocycle"));
+
+ if (show_table_options)
+ add_table_options(thd, table, 0, 0, 1, packet);
+ return 0;
+}
+
+
/****************************************************************************
Return info about all processes
returns for each thread: thread id, user, host, db, command, info
@@ -2546,6 +2744,7 @@ public:
static void operator delete(void *ptr __attribute__((unused)),
size_t size __attribute__((unused)))
{ TRASH_FREE(ptr, size); }
+ static void operator delete(void *, MEM_ROOT *){}
my_thread_id thread_id;
uint32 os_thread_id;
@@ -2563,23 +2762,31 @@ static const char *thread_state_info(THD *tmp)
{
if (tmp->net.reading_or_writing == 2)
return "Writing to net";
- else if (tmp->get_command() == COM_SLEEP)
+ if (tmp->get_command() == COM_SLEEP)
return "";
- else
- return "Reading from net";
+ return "Reading from net";
}
#else
if (tmp->get_command() == COM_SLEEP)
return "";
#endif
+
if (tmp->proc_info)
return tmp->proc_info;
- else if (tmp->mysys_var && tmp->mysys_var->current_cond)
- return "Waiting on cond";
- else
- return NULL;
+
+ /* Check if we are waiting on a condition */
+ if (!trylock_short(&tmp->LOCK_thd_kill))
+ {
+ /* mysys_var is protected by above mutex */
+ bool cond= tmp->mysys_var && tmp->mysys_var->current_cond;
+ mysql_mutex_unlock(&tmp->LOCK_thd_kill);
+ if (cond)
+ return "Waiting on cond";
+ }
+ return NULL;
}
+
void mysqld_list_processes(THD *thd,const char *user, bool verbose)
{
Item *field;
@@ -2642,7 +2849,7 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
while ((tmp=it++))
{
Security_context *tmp_sctx= tmp->security_ctx;
- struct st_my_thread_var *mysys_var;
+ bool got_thd_data;
if ((tmp->vio_ok() || tmp->system_thread) &&
(!user || (!tmp->system_thread &&
tmp_sctx->user && !strcmp(tmp_sctx->user, user))))
@@ -2666,48 +2873,61 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
tmp_sctx->host_or_ip :
tmp_sctx->host ? tmp_sctx->host : "");
thd_info->command=(int) tmp->get_command();
- mysql_mutex_lock(&tmp->LOCK_thd_data);
- if ((thd_info->db= tmp->db)) // Safe test
- thd_info->db= thd->strdup(thd_info->db);
- if ((mysys_var= tmp->mysys_var))
- mysql_mutex_lock(&mysys_var->mutex);
- thd_info->proc_info= (char*) (tmp->killed >= KILL_QUERY ?
- "Killed" : 0);
- thd_info->state_info= thread_state_info(tmp);
- if (mysys_var)
- mysql_mutex_unlock(&mysys_var->mutex);
- /* Lock THD mutex that protects its data when looking at it. */
- if (tmp->query())
+ if ((got_thd_data= !trylock_short(&tmp->LOCK_thd_data)))
{
- uint length= MY_MIN(max_query_length, tmp->query_length());
- char *q= thd->strmake(tmp->query(),length);
- /* Safety: in case strmake failed, we set length to 0. */
- thd_info->query_string=
- CSET_STRING(q, q ? length : 0, tmp->query_charset());
- }
+ /* This is an approximation */
+ thd_info->proc_info= (char*) (tmp->killed >= KILL_QUERY ?
+ "Killed" : 0);
+ /*
+ The following variables are only safe to access under a lock
+ */
- /*
- Progress report. We need to do this under a lock to ensure that all
- is from the same stage.
- */
- if (tmp->progress.max_counter)
- {
- uint max_stage= MY_MAX(tmp->progress.max_stage, 1);
- thd_info->progress= (((tmp->progress.stage / (double) max_stage) +
- ((tmp->progress.counter /
- (double) tmp->progress.max_counter) /
- (double) max_stage)) *
- 100.0);
- set_if_smaller(thd_info->progress, 100);
+ thd_info->db= 0;
+ if (tmp->db.str)
+ thd_info->db= thd->strmake(tmp->db.str, tmp->db.length);
+
+ if (tmp->query())
+ {
+ uint length= MY_MIN(max_query_length, tmp->query_length());
+ char *q= thd->strmake(tmp->query(),length);
+ /* Safety: in case strmake failed, we set length to 0. */
+ thd_info->query_string=
+ CSET_STRING(q, q ? length : 0, tmp->query_charset());
+ }
+
+ /*
+ Progress report. We need to do this under a lock to ensure that all
+ is from the same stage.
+ */
+ if (tmp->progress.max_counter)
+ {
+ uint max_stage= MY_MAX(tmp->progress.max_stage, 1);
+ thd_info->progress= (((tmp->progress.stage / (double) max_stage) +
+ ((tmp->progress.counter /
+ (double) tmp->progress.max_counter) /
+ (double) max_stage)) *
+ 100.0);
+ set_if_smaller(thd_info->progress, 100);
+ }
+ else
+ thd_info->progress= 0.0;
}
else
+ {
+ thd_info->proc_info= "Busy";
thd_info->progress= 0.0;
+ thd_info->db= "";
+ }
+
+ thd_info->state_info= thread_state_info(tmp);
thd_info->start_time= tmp->start_utime;
ulonglong utime_after_query_snapshot= tmp->utime_after_query;
if (thd_info->start_time < utime_after_query_snapshot)
thd_info->start_time= utime_after_query_snapshot; // COM_SLEEP
- mysql_mutex_unlock(&tmp->LOCK_thd_data);
+
+ if (got_thd_data)
+ mysql_mutex_unlock(&tmp->LOCK_thd_data);
thread_infos.append(thd_info);
}
}
@@ -2842,8 +3062,9 @@ int select_result_text_buffer::append_row(List<Item> &items, bool send_names)
if (send_names)
{
- data_ptr= item->name;
- data_len= strlen(item->name);
+ DBUG_ASSERT(strlen(item->name.str) == item->name.length);
+ data_ptr= item->name.str;
+ data_len= item->name.length;
}
else
{
@@ -2921,13 +3142,13 @@ int fill_show_explain(THD *thd, TABLE_LIST *table, COND *cond)
tmp_sctx->user)))
{
my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "PROCESS");
- mysql_mutex_unlock(&tmp->LOCK_thd_data);
+ mysql_mutex_unlock(&tmp->LOCK_thd_kill);
DBUG_RETURN(1);
}
if (tmp == thd)
{
- mysql_mutex_unlock(&tmp->LOCK_thd_data);
+ mysql_mutex_unlock(&tmp->LOCK_thd_kill);
my_error(ER_TARGET_NOT_EXPLAINABLE, MYF(0));
DBUG_RETURN(1);
}
@@ -2935,7 +3156,7 @@ int fill_show_explain(THD *thd, TABLE_LIST *table, COND *cond)
bool bres;
/*
Ok we've found the thread of interest and it won't go away because
- we're holding its LOCK_thd data. Post it a SHOW EXPLAIN request.
+ we're holding its LOCK_thd_kill. Post it a SHOW EXPLAIN request.
*/
bool timed_out;
int timeout_sec= 30;
@@ -2949,7 +3170,7 @@ int fill_show_explain(THD *thd, TABLE_LIST *table, COND *cond)
explain_req.request_thd= thd;
explain_req.failed_to_produce= FALSE;
- /* Ok, we have a lock on target->LOCK_thd_data, can call: */
+ /* Ok, we have a lock on target->LOCK_thd_kill, can call: */
bres= tmp->apc_target.make_apc_call(thd, &explain_req, timeout_sec, &timed_out);
if (bres || explain_req.failed_to_produce)
@@ -3028,9 +3249,9 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
while ((tmp= it++))
{
Security_context *tmp_sctx= tmp->security_ctx;
- struct st_my_thread_var *mysys_var;
- const char *val, *db;
+ const char *val;
ulonglong max_counter;
+ bool got_thd_data;
if ((!tmp->vio_ok() && !tmp->system_thread) ||
(user && (tmp->system_thread || !tmp_sctx->user ||
@@ -3056,23 +3277,26 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
else
table->field[2]->store(tmp_sctx->host_or_ip,
strlen(tmp_sctx->host_or_ip), cs);
- /* DB */
- mysql_mutex_lock(&tmp->LOCK_thd_data);
- if ((db= tmp->db))
+
+ if ((got_thd_data= !trylock_short(&tmp->LOCK_thd_data)))
{
- table->field[3]->store(db, strlen(db), cs);
- table->field[3]->set_notnull();
+ /* DB */
+ if (tmp->db.str)
+ {
+ table->field[3]->store(tmp->db.str, tmp->db.length, cs);
+ table->field[3]->set_notnull();
+ }
}
- if ((mysys_var= tmp->mysys_var))
- mysql_mutex_lock(&mysys_var->mutex);
/* COMMAND */
- if ((val= (char *) ((tmp->killed >= KILL_QUERY ?
+ if ((val= (char *) (!got_thd_data ? "Busy" :
+ (tmp->killed >= KILL_QUERY ?
"Killed" : 0))))
table->field[4]->store(val, strlen(val), cs);
else
table->field[4]->store(command_name[tmp->get_command()].str,
command_name[tmp->get_command()].length, cs);
+
/* MYSQL_TIME */
ulonglong utime= tmp->start_utime;
ulonglong utime_after_query_snapshot= tmp->utime_after_query;
@@ -3081,6 +3305,38 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
utime= utime && utime < unow ? unow - utime : 0;
table->field[5]->store(utime / HRTIME_RESOLUTION, TRUE);
+
+ if (got_thd_data)
+ {
+ if (tmp->query())
+ {
+ table->field[7]->store(tmp->query(),
+ MY_MIN(PROCESS_LIST_INFO_WIDTH,
+ tmp->query_length()), cs);
+ table->field[7]->set_notnull();
+
+ /* INFO_BINARY */
+ table->field[16]->store(tmp->query(),
+ MY_MIN(PROCESS_LIST_INFO_WIDTH,
+ tmp->query_length()),
+ &my_charset_bin);
+ table->field[16]->set_notnull();
+ }
+
+ /*
+ Progress report. We need to do this under a lock to ensure that all
+ is from the same stage.
+ */
+ if ((max_counter= tmp->progress.max_counter))
+ {
+ table->field[9]->store((longlong) tmp->progress.stage + 1, 1);
+ table->field[10]->store((longlong) tmp->progress.max_stage, 1);
+ table->field[11]->store((double) tmp->progress.counter /
+ (double) max_counter*100.0);
+ }
+ mysql_mutex_unlock(&tmp->LOCK_thd_data);
+ }
+
/* STATE */
if ((val= thread_state_info(tmp)))
{
@@ -3088,46 +3344,9 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
table->field[6]->set_notnull();
}
- if (mysys_var)
- mysql_mutex_unlock(&mysys_var->mutex);
- mysql_mutex_unlock(&tmp->LOCK_thd_data);
-
/* TIME_MS */
table->field[8]->store((double)(utime / (HRTIME_RESOLUTION / 1000.0)));
- /* INFO */
- /* Lock THD mutex that protects its data when looking at it. */
- mysql_mutex_lock(&tmp->LOCK_thd_data);
- if (tmp->query())
- {
- table->field[7]->store(tmp->query(),
- MY_MIN(PROCESS_LIST_INFO_WIDTH,
- tmp->query_length()), cs);
- table->field[7]->set_notnull();
- }
-
- /* INFO_BINARY */
- if (tmp->query())
- {
- table->field[15]->store(tmp->query(),
- MY_MIN(PROCESS_LIST_INFO_WIDTH,
- tmp->query_length()), &my_charset_bin);
- table->field[15]->set_notnull();
- }
-
- /*
- Progress report. We need to do this under a lock to ensure that all
- is from the same stage.
- */
- if ((max_counter= tmp->progress.max_counter))
- {
- table->field[9]->store((longlong) tmp->progress.stage + 1, 1);
- table->field[10]->store((longlong) tmp->progress.max_stage, 1);
- table->field[11]->store((double) tmp->progress.counter /
- (double) max_counter*100.0);
- }
- mysql_mutex_unlock(&tmp->LOCK_thd_data);
-
/*
This may become negative if we free a memory allocated by another
thread in this thread. However it's better that we notice it eventually
@@ -3135,14 +3354,14 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
*/
table->field[12]->store((longlong) tmp->status_var.local_memory_used,
FALSE);
- table->field[12]->set_notnull();
- table->field[13]->store((longlong) tmp->get_examined_row_count(), TRUE);
- table->field[13]->set_notnull();
+ table->field[13]->store((longlong) tmp->status_var.max_local_memory_used,
+ FALSE);
+ table->field[14]->store((longlong) tmp->get_examined_row_count(), TRUE);
/* QUERY_ID */
- table->field[14]->store(tmp->query_id, TRUE);
+ table->field[15]->store(tmp->query_id, TRUE);
- table->field[16]->store(tmp->os_thread_id);
+ table->field[17]->store(tmp->os_thread_id);
if (schema_table_store_record(thd, table))
{
@@ -3365,7 +3584,7 @@ const char* get_one_variable(THD *thd,
{
sys_var *var= (sys_var *) value;
show_type= var->show_type();
- value= var->value_ptr(thd, value_type, &null_lex_str);
+ value= var->value_ptr(thd, value_type, &null_clex_str);
*charset= var->charset(thd);
}
@@ -3403,6 +3622,9 @@ const char* get_one_variable(THD *thd,
case SHOW_MY_BOOL:
end= strmov(buff, *(my_bool*) value ? "ON" : "OFF");
break;
+ case SHOW_UINT32_STATUS:
+ value= ((char *) status_var + (intptr) value);
+ /* fall through */
case SHOW_UINT:
end= int10_to_str((long) *(uint*) value, buff, 10);
break;
@@ -3629,6 +3851,8 @@ uint calc_sum_of_all_status(STATUS_VAR *to)
add_to_status(to, &tmp->status_var);
to->local_memory_used+= tmp->status_var.local_memory_used;
}
+ if (tmp->get_command() != COM_SLEEP)
+ to->threads_running++;
}
mysql_mutex_unlock(&LOCK_thread_count);
@@ -3657,17 +3881,19 @@ bool schema_table_store_record(THD *thd, TABLE *table)
{
int error;
- if (thd->killed)
+ if (unlikely(thd->killed))
{
thd->send_kill_message();
return 1;
}
- if ((error= table->file->ha_write_tmp_row(table->record[0])))
+ if (unlikely((error= table->file->ha_write_tmp_row(table->record[0]))))
{
TMP_TABLE_PARAM *param= table->pos_in_table_list->schema_table_param;
- if (create_internal_tmp_table_from_heap(thd, table, param->start_recinfo,
- &param->recinfo, error, 0, NULL))
+ if (unlikely(create_internal_tmp_table_from_heap(thd, table,
+ param->start_recinfo,
+ &param->recinfo, error, 0,
+ NULL)))
return 1;
}
@@ -3676,10 +3902,10 @@ bool schema_table_store_record(THD *thd, TABLE *table)
static int make_table_list(THD *thd, SELECT_LEX *sel,
- LEX_STRING *db_name, LEX_STRING *table_name)
+ LEX_CSTRING *db_name, LEX_CSTRING *table_name)
{
Table_ident *table_ident;
- table_ident= new Table_ident(thd, *db_name, *table_name, 1);
+ table_ident= new Table_ident(thd, db_name, table_name, 1);
if (!sel->add_table_to_list(thd, table_ident, 0, 0, TL_READ, MDL_SHARED_READ))
return 1;
return 0;
@@ -3750,8 +3976,8 @@ bool get_lookup_value(THD *thd, Item_func *item_func,
/* Lookup value is database name */
if (!cs->coll->strnncollsp(cs, (uchar *) field_name1, strlen(field_name1),
- (uchar *) item_field->field_name,
- strlen(item_field->field_name)))
+ (uchar *) 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());
@@ -3759,8 +3985,8 @@ bool get_lookup_value(THD *thd, Item_func *item_func,
/* Lookup value is table name */
else if (!cs->coll->strnncollsp(cs, (uchar *) field_name2,
strlen(field_name2),
- (uchar *) item_field->field_name,
- strlen(item_field->field_name)))
+ (uchar *) 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());
@@ -3854,11 +4080,11 @@ bool uses_only_table_name_fields(Item *item, TABLE_LIST *table)
field_info[schema_table->idx_field2].field_name : "";
if (table->table != item_field->field->table ||
(cs->coll->strnncollsp(cs, (uchar *) field_name1, strlen(field_name1),
- (uchar *) item_field->field_name,
- strlen(item_field->field_name)) &&
+ (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,
- strlen(item_field->field_name))))
+ (uchar *) item_field->field_name.str,
+ item_field->field_name.length)))
return 0;
}
else if (item->type() == Item::EXPR_CACHE_ITEM)
@@ -3981,7 +4207,7 @@ bool get_lookup_field_values(THD *thd, COND *cond, TABLE_LIST *tables,
case SQLCOM_SHOW_TRIGGERS:
case SQLCOM_SHOW_EVENTS:
thd->make_lex_string(&lookup_field_values->db_value,
- lex->select_lex.db, strlen(lex->select_lex.db));
+ lex->select_lex.db.str, lex->select_lex.db.length);
if (wild)
{
thd->make_lex_string(&lookup_field_values->table_value,
@@ -4005,10 +4231,12 @@ bool get_lookup_field_values(THD *thd, COND *cond, TABLE_LIST *tables,
are allocating a new memory buffer for these strings.
*/
if (lookup_field_values->db_value.str && lookup_field_values->db_value.str[0])
- my_casedn_str(system_charset_info, lookup_field_values->db_value.str);
+ my_casedn_str(system_charset_info,
+ (char*) lookup_field_values->db_value.str);
if (lookup_field_values->table_value.str &&
lookup_field_values->table_value.str[0])
- my_casedn_str(system_charset_info, lookup_field_values->table_value.str);
+ my_casedn_str(system_charset_info,
+ (char*) lookup_field_values->table_value.str);
}
return rc;
@@ -4037,8 +4265,8 @@ enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table)
non-zero error
*/
-int make_db_list(THD *thd, Dynamic_array<LEX_STRING*> *files,
- LOOKUP_FIELD_VALUES *lookup_field_vals)
+static int make_db_list(THD *thd, Dynamic_array<LEX_CSTRING*> *files,
+ LOOKUP_FIELD_VALUES *lookup_field_vals)
{
if (lookup_field_vals->wild_db_value)
{
@@ -4077,8 +4305,7 @@ int make_db_list(THD *thd, Dynamic_array<LEX_STRING*> *files,
return 0;
}
- if (is_infoschema_db(lookup_field_vals->db_value.str,
- lookup_field_vals->db_value.length))
+ if (is_infoschema_db(&lookup_field_vals->db_value))
{
if (files->append_val(&INFORMATION_SCHEMA_NAME))
return 1;
@@ -4095,13 +4322,13 @@ int make_db_list(THD *thd, Dynamic_array<LEX_STRING*> *files,
*/
if (files->append_val(&INFORMATION_SCHEMA_NAME))
return 1;
- return find_files(thd, files, 0, mysql_data_home, &null_lex_str);
+ return find_files(thd, files, 0, mysql_data_home, &null_clex_str);
}
struct st_add_schema_table
{
- Dynamic_array<LEX_STRING*> *files;
+ Dynamic_array<LEX_CSTRING*> *files;
const char *wild;
};
@@ -4109,9 +4336,9 @@ struct st_add_schema_table
static my_bool add_schema_table(THD *thd, plugin_ref plugin,
void* p_data)
{
- LEX_STRING *file_name= 0;
+ LEX_CSTRING *file_name= 0;
st_add_schema_table *data= (st_add_schema_table *)p_data;
- Dynamic_array<LEX_STRING*> *file_list= data->files;
+ Dynamic_array<LEX_CSTRING*> *file_list= data->files;
const char *wild= data->wild;
ST_SCHEMA_TABLE *schema_table= plugin_data(plugin, ST_SCHEMA_TABLE *);
DBUG_ENTER("add_schema_table");
@@ -4131,18 +4358,18 @@ static my_bool add_schema_table(THD *thd, plugin_ref plugin,
DBUG_RETURN(0);
}
- if ((file_name= thd->make_lex_string(schema_table->table_name,
- strlen(schema_table->table_name))) &&
+ if ((file_name= thd->make_clex_string(schema_table->table_name,
+ strlen(schema_table->table_name))) &&
!file_list->append(file_name))
DBUG_RETURN(0);
DBUG_RETURN(1);
}
-int schema_tables_add(THD *thd, Dynamic_array<LEX_STRING*> *files,
+int schema_tables_add(THD *thd, Dynamic_array<LEX_CSTRING*> *files,
const char *wild)
{
- LEX_STRING *file_name= 0;
+ LEX_CSTRING *file_name;
ST_SCHEMA_TABLE *tmp_schema_table= schema_tables;
st_add_schema_table add_data;
DBUG_ENTER("schema_tables_add");
@@ -4164,8 +4391,8 @@ int schema_tables_add(THD *thd, Dynamic_array<LEX_STRING*> *files,
continue;
}
if ((file_name=
- thd->make_lex_string(tmp_schema_table->table_name,
- strlen(tmp_schema_table->table_name))) &&
+ thd->make_clex_string(tmp_schema_table->table_name,
+ strlen(tmp_schema_table->table_name))) &&
!files->append(file_name))
continue;
DBUG_RETURN(1);
@@ -4200,9 +4427,9 @@ int schema_tables_add(THD *thd, Dynamic_array<LEX_STRING*> *files,
*/
static int
-make_table_name_list(THD *thd, Dynamic_array<LEX_STRING*> *table_names,
+make_table_name_list(THD *thd, Dynamic_array<LEX_CSTRING*> *table_names,
LEX *lex, LOOKUP_FIELD_VALUES *lookup_field_vals,
- LEX_STRING *db_name)
+ LEX_CSTRING *db_name)
{
char path[FN_REFLEN + 1];
build_table_filename(path, sizeof(path) - 1, db_name->str, "", "", 0);
@@ -4219,13 +4446,13 @@ make_table_name_list(THD *thd, Dynamic_array<LEX_STRING*> *table_names,
}
if (db_name == &INFORMATION_SCHEMA_NAME)
{
- LEX_STRING *name;
+ LEX_CSTRING *name;
ST_SCHEMA_TABLE *schema_table=
- find_schema_table(thd, lookup_field_vals->table_value.str);
+ find_schema_table(thd, &lookup_field_vals->table_value);
if (schema_table && !schema_table->hidden)
{
- if (!(name= thd->make_lex_string(schema_table->table_name,
- strlen(schema_table->table_name))) ||
+ if (!(name= thd->make_clex_string(schema_table->table_name,
+ strlen(schema_table->table_name))) ||
table_names->append(name))
return 1;
}
@@ -4270,16 +4497,17 @@ make_table_name_list(THD *thd, Dynamic_array<LEX_STRING*> *table_names,
static void get_table_engine_for_i_s(THD *thd, char *buf, TABLE_LIST *tl,
- LEX_STRING *db, LEX_STRING *table)
+ LEX_CSTRING *db, LEX_CSTRING *table)
{
- LEX_STRING engine_name= { buf, 0 };
+ LEX_CSTRING engine_name= { buf, 0 };
if (thd->get_stmt_da()->sql_errno() == ER_UNKNOWN_STORAGE_ENGINE)
{
char path[FN_REFLEN];
build_table_filename(path, sizeof(path) - 1,
db->str, table->str, reg_ext, 0);
- if (dd_frm_type(thd, path, &engine_name) == FRMTYPE_TABLE)
+ bool is_sequence;
+ if (dd_frm_type(thd, path, &engine_name, &is_sequence) == TABLE_TYPE_NORMAL)
tl->option= engine_name.str;
}
}
@@ -4307,18 +4535,19 @@ static void get_table_engine_for_i_s(THD *thd, char *buf, TABLE_LIST *tl,
@retval TRUE - Failure.
*/
static bool
-fill_schema_table_by_open(THD *thd, bool is_show_fields_or_keys,
+fill_schema_table_by_open(THD *thd, MEM_ROOT *mem_root,
+ bool is_show_fields_or_keys,
TABLE *table, ST_SCHEMA_TABLE *schema_table,
- LEX_STRING *orig_db_name,
- LEX_STRING *orig_table_name,
+ LEX_CSTRING *orig_db_name,
+ LEX_CSTRING *orig_table_name,
Open_tables_backup *open_tables_state_backup,
bool can_deadlock)
{
- Query_arena i_s_arena(thd->mem_root,
+ Query_arena i_s_arena(mem_root,
Query_arena::STMT_CONVENTIONAL_EXECUTION),
backup_arena, *old_arena;
LEX *old_lex= thd->lex, temp_lex, *lex;
- LEX_STRING db_name, table_name;
+ LEX_CSTRING db_name, table_name;
TABLE_LIST *table_list;
bool result= true;
DBUG_ENTER("fill_schema_table_by_open");
@@ -4343,6 +4572,7 @@ fill_schema_table_by_open(THD *thd, bool is_show_fields_or_keys,
/* Prepare temporary LEX. */
thd->lex= lex= &temp_lex;
lex_start(thd);
+ lex->sql_command= old_lex->sql_command;
/* Disable constant subquery evaluation as we won't be locking tables. */
lex->context_analysis_only= CONTEXT_ANALYSIS_ONLY_VIEW;
@@ -4395,27 +4625,8 @@ fill_schema_table_by_open(THD *thd, bool is_show_fields_or_keys,
table_list->i_s_requested_object= schema_table->i_s_requested_object;
}
- /*
- Let us set fake sql_command so views won't try to merge
- themselves into main statement. If we don't do this,
- SELECT * from information_schema.xxxx will cause problems.
- SQLCOM_SHOW_FIELDS is used because it satisfies
- 'only_view_structure()'.
- */
- lex->sql_command= SQLCOM_SHOW_FIELDS;
- result= (thd->open_temporary_tables(table_list) ||
- open_normal_and_derived_tables(thd, table_list,
- (MYSQL_OPEN_IGNORE_FLUSH |
- MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL |
- (can_deadlock ?
- MYSQL_OPEN_FAIL_ON_MDL_CONFLICT : 0)),
- DT_INIT | DT_PREPARE | DT_CREATE));
-
- /*
- Restore old value of sql_command back as it is being looked at in
- process_table() function.
- */
- lex->sql_command= old_lex->sql_command;
+ DBUG_ASSERT(thd->lex == lex);
+ result= open_tables_only_view_structure(thd, table_list, can_deadlock);
DEBUG_SYNC(thd, "after_open_table_ignore_flush");
@@ -4432,7 +4643,8 @@ fill_schema_table_by_open(THD *thd, bool is_show_fields_or_keys,
*/
if (!is_show_fields_or_keys && result && thd->is_error() &&
(thd->get_stmt_da()->sql_errno() == ER_NO_SUCH_TABLE ||
- thd->get_stmt_da()->sql_errno() == ER_WRONG_OBJECT))
+ thd->get_stmt_da()->sql_errno() == ER_WRONG_OBJECT ||
+ thd->get_stmt_da()->sql_errno() == ER_NOT_SEQUENCE))
{
/*
Hide error for a non-existing table.
@@ -4446,7 +4658,7 @@ fill_schema_table_by_open(THD *thd, bool is_show_fields_or_keys,
else
{
char buf[NAME_CHAR_LEN + 1];
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
get_table_engine_for_i_s(thd, buf, table_list, &db_name, &table_name);
result= schema_table->process_table(thd, table_list,
@@ -4502,7 +4714,8 @@ end:
*/
static int fill_schema_table_names(THD *thd, TABLE_LIST *tables,
- LEX_STRING *db_name, LEX_STRING *table_name)
+ LEX_CSTRING *db_name,
+ LEX_CSTRING *table_name)
{
TABLE *table= tables->table;
if (db_name == &INFORMATION_SCHEMA_NAME)
@@ -4514,23 +4727,27 @@ static int fill_schema_table_names(THD *thd, TABLE_LIST *tables,
{
CHARSET_INFO *cs= system_charset_info;
handlerton *hton;
- if (ha_table_exists(thd, db_name->str, table_name->str, &hton))
+ bool is_sequence;
+ if (ha_table_exists(thd, db_name, table_name, &hton, &is_sequence))
{
if (hton == view_pseudo_hton)
table->field[3]->store(STRING_WITH_LEN("VIEW"), cs);
+ else if (is_sequence)
+ table->field[3]->store(STRING_WITH_LEN("SEQUENCE"), cs);
else
table->field[3]->store(STRING_WITH_LEN("BASE TABLE"), cs);
}
else
table->field[3]->store(STRING_WITH_LEN("ERROR"), cs);
- if (thd->is_error() && thd->get_stmt_da()->sql_errno() == ER_NO_SUCH_TABLE)
+ if (unlikely(thd->is_error() &&
+ thd->get_stmt_da()->sql_errno() == ER_NO_SUCH_TABLE))
{
thd->clear_error();
return 0;
}
}
- if (schema_table_store_record(thd, table))
+ if (unlikely(schema_table_store_record(thd, table)))
return 1;
return 0;
}
@@ -4614,7 +4831,7 @@ 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, table->table_name,
+ table->mdl_request.init(MDL_key::TABLE, table->db.str, table->table_name.str,
MDL_SHARED_HIGH_PRIO, MDL_TRANSACTION);
if (can_deadlock)
@@ -4667,8 +4884,8 @@ try_acquire_high_prio_shared_mdl_lock(THD *thd, TABLE_LIST *table,
static int fill_schema_table_from_frm(THD *thd, TABLE *table,
ST_SCHEMA_TABLE *schema_table,
- LEX_STRING *db_name,
- LEX_STRING *table_name,
+ LEX_CSTRING *db_name,
+ LEX_CSTRING *table_name,
Open_tables_backup *open_tables_state_backup,
bool can_deadlock)
{
@@ -4693,15 +4910,15 @@ static int fill_schema_table_from_frm(THD *thd, TABLE *table,
*/
strmov(db_name_buff, db_name->str);
strmov(table_name_buff, table_name->str);
- my_casedn_str(files_charset_info, db_name_buff);
- my_casedn_str(files_charset_info, table_name_buff);
- table_list.db= db_name_buff;
- table_list.table_name= table_name_buff;
+ table_list.db.length= my_casedn_str(files_charset_info, db_name_buff);
+ table_list.table_name.length= my_casedn_str(files_charset_info, table_name_buff);
+ table_list.db.str= db_name_buff;
+ table_list.table_name.str= table_name_buff;
}
else
{
- table_list.table_name= table_name->str;
- table_list.db= db_name->str;
+ table_list.table_name= *table_name;
+ table_list.db= *db_name;
}
/*
@@ -4731,15 +4948,16 @@ static int fill_schema_table_from_frm(THD *thd, TABLE *table,
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_I_S_SKIPPED_TABLE,
ER_THD(thd, ER_WARN_I_S_SKIPPED_TABLE),
- table_list.db, table_list.table_name);
+ table_list.db.str, table_list.table_name.str);
return 0;
}
if (schema_table->i_s_requested_object & OPEN_TRIGGER_ONLY)
{
- init_sql_alloc(&tbl.mem_root, TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0));
- if (!Table_triggers_list::check_n_load(thd, db_name->str,
- table_name->str, &tbl, 1))
+ init_sql_alloc(&tbl.mem_root, "fill_schema_table_from_frm",
+ TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0));
+ if (!Table_triggers_list::check_n_load(thd, db_name,
+ table_name, &tbl, 1))
{
table_list.table= &tbl;
res= schema_table->process_table(thd, &table_list, table,
@@ -4754,7 +4972,8 @@ static int fill_schema_table_from_frm(THD *thd, TABLE *table,
if (!share)
{
if (thd->get_stmt_da()->sql_errno() == ER_NO_SUCH_TABLE ||
- thd->get_stmt_da()->sql_errno() == ER_WRONG_OBJECT)
+ thd->get_stmt_da()->sql_errno() == ER_WRONG_OBJECT ||
+ thd->get_stmt_da()->sql_errno() == ER_NOT_SEQUENCE)
{
res= 0;
}
@@ -4795,7 +5014,7 @@ static int fill_schema_table_from_frm(THD *thd, TABLE *table,
goto end_share;
}
- if (!open_table_from_share(thd, share, table_name->str, 0,
+ if (!open_table_from_share(thd, share, table_name, 0,
(EXTRA_RECORD | OPEN_FRM_FILE_ONLY),
thd->open_options, &tbl, FALSE))
{
@@ -4852,13 +5071,12 @@ public:
if (*level != Sql_condition::WARN_LEVEL_ERROR)
return false;
- if (!thd->get_stmt_da()->is_error())
+ if (likely(!thd->get_stmt_da()->is_error()))
thd->get_stmt_da()->set_error_status(sql_errno, msg, sqlstate, *cond_hdl);
return true; // handled!
}
};
-
/**
@brief Fill I_S tables whose data are retrieved
from frm files and storage engine
@@ -4891,7 +5109,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_STRING*> db_names;
+ Dynamic_array<LEX_CSTRING*> db_names;
Item *partial_cond= plan->partial_cond;
int error= 1;
Open_tables_backup open_tables_state_backup;
@@ -4900,8 +5118,11 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
#endif
uint table_open_method= tables->table_open_method;
bool can_deadlock;
+ MEM_ROOT tmp_mem_root;
DBUG_ENTER("get_all_tables");
+ bzero(&tmp_mem_root, sizeof(tmp_mem_root));
+
/*
In cases when SELECT from I_S table being filled by this call is
part of statement which also uses other tables or is being executed
@@ -4929,17 +5150,10 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
*/
if (lsel && lsel->table_list.first)
{
- LEX_STRING db_name, table_name;
-
- db_name.str= lsel->table_list.first->db;
- db_name.length= lsel->table_list.first->db_length;
-
- table_name.str= lsel->table_list.first->table_name;
- table_name.length= lsel->table_list.first->table_name_length;
-
- error= fill_schema_table_by_open(thd, TRUE,
+ error= fill_schema_table_by_open(thd, thd->mem_root, TRUE,
table, schema_table,
- &db_name, &table_name,
+ &lsel->table_list.first->db,
+ &lsel->table_list.first->table_name,
&open_tables_state_backup,
can_deadlock);
goto err;
@@ -4962,9 +5176,14 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
if (make_db_list(thd, &db_names, &plan->lookup_field_vals))
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,
+ SHOW_ALLOC_BLOCK_SIZE, MY_THREAD_SPECIFIC);
+
for (size_t i=0; i < db_names.elements(); i++)
{
- LEX_STRING *db_name= db_names.at(i);
+ LEX_CSTRING *db_name= db_names.at(i);
DBUG_ASSERT(db_name->length <= NAME_LEN);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (!(check_access(thd, SELECT_ACL, db_name->str,
@@ -4974,26 +5193,24 @@ 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_STRING*> table_names;
+ Dynamic_array<LEX_CSTRING*> table_names;
int res= make_table_name_list(thd, &table_names, lex,
&plan->lookup_field_vals, db_name);
- if (res == 2) /* Not fatal error, continue */
+ if (unlikely(res == 2)) /* Not fatal error, continue */
continue;
- if (res)
+ if (unlikely(res))
goto err;
for (size_t i=0; i < table_names.elements(); i++)
{
- LEX_STRING *table_name= table_names.at(i);
+ LEX_CSTRING *table_name= table_names.at(i);
DBUG_ASSERT(table_name->length <= NAME_LEN);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (!(thd->col_access & TABLE_ACLS))
{
- table_acl_check.db= db_name->str;
- table_acl_check.db_length= db_name->length;
- table_acl_check.table_name= table_name->str;
- table_acl_check.table_name_length= table_name->length;
+ table_acl_check.db= *db_name;
+ table_acl_check.table_name= *table_name;
table_acl_check.grant.privilege= thd->col_access;
if (check_grant(thd, TABLE_ACLS, &table_acl_check, TRUE, 1, TRUE))
continue;
@@ -5047,12 +5264,13 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
}
DEBUG_SYNC(thd, "before_open_in_get_all_tables");
- if (fill_schema_table_by_open(thd, FALSE,
+ if (fill_schema_table_by_open(thd, &tmp_mem_root, FALSE,
table, schema_table,
db_name, table_name,
&open_tables_state_backup,
can_deadlock))
goto err;
+ free_root(&tmp_mem_root, MY_MARK_BLOCKS_FREE);
}
}
}
@@ -5062,12 +5280,13 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
error= 0;
err:
thd->restore_backup_open_tables_state(&open_tables_state_backup);
+ free_root(&tmp_mem_root, 0);
DBUG_RETURN(error);
}
-bool store_schema_shemata(THD* thd, TABLE *table, LEX_STRING *db_name,
+bool store_schema_shemata(THD* thd, TABLE *table, LEX_CSTRING *db_name,
CHARSET_INFO *cs)
{
restore_record(table, s->default_values);
@@ -5086,7 +5305,7 @@ bool store_schema_shemata(THD* thd, TABLE *table, LEX_STRING *db_name,
@retval true - on error, the database directory does not exists
@retval false - on success, the database directory exists
*/
-static bool verify_database_directory_exists(const LEX_STRING &dbname)
+static bool verify_database_directory_exists(const LEX_CSTRING &dbname)
{
DBUG_ENTER("verify_database_directory_exists");
char path[FN_REFLEN + 16];
@@ -5110,7 +5329,7 @@ int fill_schema_schemata(THD *thd, TABLE_LIST *tables, COND *cond)
*/
LOOKUP_FIELD_VALUES lookup_field_vals;
- Dynamic_array<LEX_STRING*> db_names;
+ Dynamic_array<LEX_CSTRING*> db_names;
Schema_specification_st create;
TABLE *table= tables->table;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -5137,7 +5356,7 @@ int fill_schema_schemata(THD *thd, TABLE_LIST *tables, COND *cond)
for (size_t i=0; i < db_names.elements(); i++)
{
- LEX_STRING *db_name= db_names.at(i);
+ LEX_CSTRING *db_name= db_names.at(i);
DBUG_ASSERT(db_name->length <= NAME_LEN);
if (db_name == &INFORMATION_SCHEMA_NAME)
{
@@ -5166,8 +5385,8 @@ int fill_schema_schemata(THD *thd, TABLE_LIST *tables, COND *cond)
static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
TABLE *table, bool res,
- LEX_STRING *db_name,
- LEX_STRING *table_name)
+ const LEX_CSTRING *db_name,
+ const LEX_CSTRING *table_name)
{
const char *tmp_buff;
MYSQL_TIME time;
@@ -5209,7 +5428,7 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
String str(option_buff,sizeof(option_buff), system_charset_info);
TABLE *show_table= tables->table;
TABLE_SHARE *share= show_table->s;
- handler *file= show_table->file;
+ handler *file= show_table->db_stat ? show_table->file : 0;
handlerton *tmp_db_type= share->db_type();
#ifdef WITH_PARTITION_STORAGE_ENGINE
bool is_partitioned= FALSE;
@@ -5217,13 +5436,18 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
if (share->tmp_table == SYSTEM_TMP_TABLE)
table->field[3]->store(STRING_WITH_LEN("SYSTEM VIEW"), cs);
+ else if (share->table_type == TABLE_TYPE_SEQUENCE)
+ table->field[3]->store(STRING_WITH_LEN("SEQUENCE"), cs);
else
{
DBUG_ASSERT(share->tmp_table == NO_TMP_TABLE);
- table->field[3]->store(STRING_WITH_LEN("BASE TABLE"), cs);
+ if (share->versioned)
+ table->field[3]->store(STRING_WITH_LEN("SYSTEM VERSIONED"), cs);
+ else
+ table->field[3]->store(STRING_WITH_LEN("BASE TABLE"), cs);
}
- for (int i= 4; i < 20; i++)
+ for (uint i= 4; i < table->s->fields; i++)
{
if (i == 7 || (i > 12 && i < 17) || i == 18)
continue;
@@ -5328,7 +5552,7 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
if (file)
{
HA_CREATE_INFO create_info;
- memset(&create_info, 0, sizeof(create_info));
+ create_info.init();
file->update_create_info(&create_info);
append_directory(thd, &str, "DATA", create_info.data_file_name);
append_directory(thd, &str, "INDEX", create_info.index_file_name);
@@ -5347,13 +5571,13 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
/* Collect table info from the storage engine */
- if(file)
+ if (file)
{
/* If info() fails, then there's nothing else to do */
- if ((info_error= file->info(HA_STATUS_VARIABLE |
- HA_STATUS_TIME |
- HA_STATUS_VARIABLE_EXTRA |
- HA_STATUS_AUTO)) != 0)
+ if (unlikely((info_error= file->info(HA_STATUS_VARIABLE |
+ HA_STATUS_TIME |
+ HA_STATUS_VARIABLE_EXTRA |
+ HA_STATUS_AUTO)) != 0))
{
file->print_error(info_error, MYF(0));
goto err;
@@ -5401,8 +5625,15 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
{
table->field[10]->store((longlong) file->stats.max_data_file_length,
TRUE);
+ table->field[10]->set_notnull();
}
table->field[11]->store((longlong) file->stats.index_file_length, TRUE);
+ if (file->stats.max_index_file_length)
+ {
+ table->field[21]->store((longlong) file->stats.max_index_file_length,
+ TRUE);
+ table->field[21]->set_notnull();
+ }
table->field[12]->store((longlong) file->stats.delete_length, TRUE);
if (show_table->found_next_number_field)
{
@@ -5433,14 +5664,19 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
}
if (file->ha_table_flags() & (HA_HAS_OLD_CHECKSUM | HA_HAS_NEW_CHECKSUM))
{
- table->field[18]->store((longlong) file->checksum(), TRUE);
+ table->field[18]->store((longlong) file->stats.checksum, TRUE);
table->field[18]->set_notnull();
}
}
+ /* If table is a temporary table */
+ LEX_CSTRING tmp= { STRING_WITH_LEN("N") };
+ if (show_table->s->tmp_table != NO_TMP_TABLE)
+ tmp.str= "Y";
+ table->field[22]->store(tmp.str, tmp.length, cs);
}
err:
- if (res || info_error)
+ if (unlikely(res || info_error))
{
/*
If an error was encountered, push a warning, set the TABLE COMMENT
@@ -5474,8 +5710,6 @@ err:
static void store_column_type(TABLE *table, Field *field, CHARSET_INFO *cs,
uint offset)
{
- bool is_blob;
- int decimals, field_length;
const char *tmp_buff;
char column_type_buff[MAX_FIELD_WIDTH];
String column_type(column_type_buff, sizeof(column_type_buff), cs);
@@ -5501,22 +5735,18 @@ static void store_column_type(TABLE *table, Field *field, CHARSET_INFO *cs,
(tmp_buff ? (uint)(tmp_buff - column_type.ptr()) :
column_type.length()), cs);
- is_blob= (field->type() == MYSQL_TYPE_BLOB);
- if (field->has_charset() || is_blob ||
- field->real_type() == MYSQL_TYPE_VARCHAR || // For varbinary type
- field->real_type() == MYSQL_TYPE_STRING) // For binary type
- {
- uint32 octet_max_length= field->max_display_length();
- if (is_blob && octet_max_length != (uint32) 4294967295U)
- octet_max_length /= field->charset()->mbmaxlen;
- longlong char_max_len= is_blob ?
- (longlong) octet_max_length / field->charset()->mbminlen :
- (longlong) octet_max_length / field->charset()->mbmaxlen;
+ Information_schema_character_attributes cattr=
+ field->information_schema_character_attributes();
+ if (cattr.has_char_length())
+ {
/* CHARACTER_MAXIMUM_LENGTH column*/
- table->field[offset + 1]->store(char_max_len, TRUE);
+ table->field[offset + 1]->store((longlong) cattr.char_length(), true);
table->field[offset + 1]->set_notnull();
+ }
+ if (cattr.has_octet_length())
+ {
/* CHARACTER_OCTET_LENGTH column */
- table->field[offset + 2]->store((longlong) octet_max_length, TRUE);
+ table->field[offset + 2]->store((longlong) cattr.octet_length(), true);
table->field[offset + 2]->set_notnull();
}
@@ -5525,35 +5755,10 @@ static void store_column_type(TABLE *table, Field *field, CHARSET_INFO *cs,
They are set to -1 if they should not be set (we should return NULL)
*/
- field_length= -1;
- decimals= field->decimals();
+ Information_schema_numeric_attributes num=
+ field->information_schema_numeric_attributes();
+
switch (field->type()) {
- case MYSQL_TYPE_NEWDECIMAL:
- field_length= ((Field_new_decimal*) field)->precision;
- break;
- case MYSQL_TYPE_DECIMAL:
- field_length= field->field_length - (decimals ? 2 : 1);
- break;
- case MYSQL_TYPE_TINY:
- case MYSQL_TYPE_SHORT:
- case MYSQL_TYPE_LONG:
- case MYSQL_TYPE_INT24:
- field_length= field->max_display_length() - 1;
- break;
- case MYSQL_TYPE_LONGLONG:
- field_length= field->max_display_length() -
- ((field->flags & UNSIGNED_FLAG) ? 0 : 1);
- break;
- case MYSQL_TYPE_BIT:
- field_length= field->max_display_length();
- decimals= -1; // return NULL
- break;
- case MYSQL_TYPE_FLOAT:
- case MYSQL_TYPE_DOUBLE:
- field_length= field->field_length;
- if (decimals == NOT_FIXED_DEC)
- decimals= -1; // return NULL
- break;
case MYSQL_TYPE_TIME:
case MYSQL_TYPE_TIMESTAMP:
case MYSQL_TYPE_DATETIME:
@@ -5566,15 +5771,15 @@ static void store_column_type(TABLE *table, Field *field, CHARSET_INFO *cs,
}
/* NUMERIC_PRECISION column */
- if (field_length >= 0)
+ if (num.has_precision())
{
- table->field[offset + 3]->store((longlong) field_length, TRUE);
+ table->field[offset + 3]->store((longlong) num.precision(), true);
table->field[offset + 3]->set_notnull();
/* NUMERIC_SCALE column */
- if (decimals >= 0)
+ if (num.has_scale())
{
- table->field[offset + 4]->store((longlong) decimals, TRUE);
+ table->field[offset + 4]->store((longlong) num.scale(), true);
table->field[offset + 4]->set_notnull();
}
}
@@ -5592,10 +5797,122 @@ static void store_column_type(TABLE *table, Field *field, CHARSET_INFO *cs,
}
+/*
+ Print DATA_TYPE independently from sql_mode.
+ It's only a brief human-readable description, without attributes,
+ so it should not be used by client programs to generate SQL scripts.
+*/
+static bool print_anchor_data_type(const Spvar_definition *def,
+ String *data_type)
+{
+ if (def->column_type_ref())
+ return data_type->append(STRING_WITH_LEN("TYPE OF"));
+ if (def->is_table_rowtype_ref())
+ return data_type->append(STRING_WITH_LEN("ROW TYPE OF"));
+ /*
+ "ROW TYPE OF cursor" is not possible yet.
+ May become possible when we add package-wide cursors.
+ */
+ DBUG_ASSERT(0);
+ return false;
+}
+
+
+/*
+ DTD_IDENTIFIER is the full data type description with attributes.
+ It can be used by client programs to generate SQL scripts.
+ Let's print it according to the current sql_mode.
+ It will make output in line with the value in mysql.proc.param_list,
+ so both I_S.XXX.DTD_IDENTIFIER and mysql.proc.param_list use the same notation:
+ default or Oracle, according to the sql_mode at the SP creation time.
+ The caller must make sure to set thd->variables.sql_mode to the routine sql_mode.
+*/
+static bool print_anchor_dtd_identifier(THD *thd, const Spvar_definition *def,
+ String *dtd_identifier)
+{
+ if (def->column_type_ref())
+ return (thd->variables.sql_mode & MODE_ORACLE) ?
+ def->column_type_ref()->append_to(thd, dtd_identifier) ||
+ dtd_identifier->append(STRING_WITH_LEN("%TYPE")) :
+ dtd_identifier->append(STRING_WITH_LEN("TYPE OF ")) ||
+ def->column_type_ref()->append_to(thd, dtd_identifier);
+ if (def->is_table_rowtype_ref())
+ return (thd->variables.sql_mode & MODE_ORACLE) ?
+ def->table_rowtype_ref()->append_to(thd, dtd_identifier) ||
+ dtd_identifier->append(STRING_WITH_LEN("%ROWTYPE")) :
+ dtd_identifier->append(STRING_WITH_LEN("ROW TYPE OF ")) ||
+ def->table_rowtype_ref()->append_to(thd, dtd_identifier);
+ DBUG_ASSERT(0); // See comments in print_anchor_data_type()
+ return false;
+}
+
+
+/*
+ Set columns DATA_TYPE and DTD_IDENTIFIER from an SP variable definition
+*/
+static void store_variable_type(THD *thd, const sp_variable *spvar,
+ TABLE *tmptbl,
+ TABLE_SHARE *tmpshare,
+ CHARSET_INFO *cs,
+ TABLE *table, uint offset)
+{
+ if (spvar->field_def.is_explicit_data_type())
+ {
+ if (spvar->field_def.is_row())
+ {
+ // Explicit ROW
+ table->field[offset]->store(STRING_WITH_LEN("ROW"), cs);
+ table->field[offset]->set_notnull();
+ // Perhaps eventually we need to print all ROW elements in DTD_IDENTIFIER
+ table->field[offset + 8]->store(STRING_WITH_LEN("ROW"), cs);
+ table->field[offset + 8]->set_notnull();
+ }
+ else
+ {
+ // Explicit scalar data type
+ Field *field= spvar->field_def.make_field(tmpshare, thd->mem_root,
+ &spvar->name);
+ field->table= tmptbl;
+ tmptbl->in_use= thd;
+ store_column_type(table, field, cs, offset);
+ }
+ }
+ else
+ {
+ StringBuffer<128> data_type(cs), dtd_identifier(cs);
+
+ if (print_anchor_data_type(&spvar->field_def, &data_type))
+ {
+ table->field[offset]->store(STRING_WITH_LEN("ERROR"), cs); // EOM?
+ table->field[offset]->set_notnull();
+ }
+ else
+ {
+ DBUG_ASSERT(data_type.length());
+ table->field[offset]->store(data_type.ptr(), data_type.length(), cs);
+ table->field[offset]->set_notnull();
+ }
+
+ if (print_anchor_dtd_identifier(thd, &spvar->field_def, &dtd_identifier))
+ {
+ table->field[offset + 8]->store(STRING_WITH_LEN("ERROR"), cs); // EOM?
+ table->field[offset + 8]->set_notnull();
+ }
+ else
+ {
+ DBUG_ASSERT(dtd_identifier.length());
+ table->field[offset + 8]->store(dtd_identifier.ptr(),
+ dtd_identifier.length(), cs);
+ table->field[offset + 8]->set_notnull();
+ }
+ }
+}
+
+
static int get_schema_column_record(THD *thd, TABLE_LIST *tables,
TABLE *table, bool res,
- LEX_STRING *db_name,
- LEX_STRING *table_name)
+ const LEX_CSTRING *db_name,
+ const LEX_CSTRING *table_name)
{
LEX *lex= thd->lex;
const char *wild= lex->wild ? lex->wild->ptr() : NullS;
@@ -5637,6 +5954,8 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables,
for (; (field= *ptr) ; ptr++)
{
+ if(field->invisible > INVISIBLE_USER)
+ continue;
uchar *pos;
char tmp[MAX_FIELD_WIDTH];
String type(tmp,sizeof(tmp), system_charset_info);
@@ -5644,7 +5963,7 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables,
DEBUG_SYNC(thd, "get_schema_column");
if (wild && wild[0] &&
- wild_case_compare(system_charset_info, field->field_name,wild))
+ wild_case_compare(system_charset_info, field->field_name.str, wild))
continue;
count++;
@@ -5657,7 +5976,7 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables,
&tables->grant.privilege, 0, 0, MY_TEST(tables->schema_table));
col_access= get_column_grant(thd, &tables->grant,
db_name->str, table_name->str,
- field->field_name) & COL_ACLS;
+ field->field_name.str) & COL_ACLS;
if (!tables->schema_table && !col_access)
continue;
char *end= tmp;
@@ -5675,7 +5994,7 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables,
table->field[0]->store(STRING_WITH_LEN("def"), cs);
table->field[1]->store(db_name->str, db_name->length, cs);
table->field[2]->store(table_name->str, table_name->length, cs);
- table->field[3]->store(field->field_name, strlen(field->field_name),
+ table->field[3]->store(field->field_name.str, field->field_name.length,
cs);
table->field[4]->store((longlong) count, TRUE);
@@ -5694,11 +6013,11 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables,
table->field[16]->store((const char*) pos,
strlen((const char*) pos), cs);
+ StringBuffer<256> buf;
if (field->unireg_check == Field::NEXT_NUMBER)
- table->field[17]->store(STRING_WITH_LEN("auto_increment"), cs);
+ buf.set(STRING_WITH_LEN("auto_increment"),cs);
if (print_on_update_clause(field, &type, true))
- table->field[17]->store(type.ptr(), type.length(), cs);
-
+ buf.set(type.ptr(), type.length(),cs);
if (field->vcol_info)
{
String gen_s(tmp,sizeof(tmp), system_charset_info);
@@ -5709,13 +6028,41 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables,
table->field[20]->store(STRING_WITH_LEN("ALWAYS"), cs);
if (field->vcol_info->stored_in_db)
- table->field[17]->store(STRING_WITH_LEN("STORED GENERATED"), cs);
+ buf.set(STRING_WITH_LEN("STORED GENERATED"), cs);
else
- table->field[17]->store(STRING_WITH_LEN("VIRTUAL GENERATED"), cs);
+ buf.set(STRING_WITH_LEN("VIRTUAL GENERATED"), cs);
+ }
+ else if (field->flags & VERS_SYSTEM_FIELD)
+ {
+ if (field->flags & VERS_SYS_START_FLAG)
+ {
+ table->field[21]->store(STRING_WITH_LEN("ROW START"), cs);
+ buf.set(STRING_WITH_LEN("STORED GENERATED"), cs);
+ }
+ else
+ {
+ table->field[21]->store(STRING_WITH_LEN("ROW END"), cs);
+ buf.set(STRING_WITH_LEN("STORED GENERATED"), cs);
+ }
+ table->field[21]->set_notnull();
+ table->field[20]->store(STRING_WITH_LEN("ALWAYS"), cs);
}
else
table->field[20]->store(STRING_WITH_LEN("NEVER"), cs);
-
+ /*Invisible can coexist with auto_increment and virtual */
+ if (field->invisible == INVISIBLE_USER)
+ {
+ if (buf.length())
+ buf.append(STRING_WITH_LEN(", "));
+ buf.append(STRING_WITH_LEN("INVISIBLE"),cs);
+ }
+ if (field->vers_update_unversioned())
+ {
+ if (buf.length())
+ buf.append(STRING_WITH_LEN(", "));
+ buf.append(STRING_WITH_LEN("WITHOUT SYSTEM VERSIONING"), cs);
+ }
+ table->field[17]->store(buf.ptr(), buf.length(), cs);
table->field[19]->store(field->comment.str, field->comment.length, cs);
if (schema_table_store_record(thd, table))
DBUG_RETURN(1);
@@ -5778,7 +6125,7 @@ static my_bool iter_schema_engines(THD *thd, plugin_ref plugin,
{
restore_record(table, s->default_values);
table->field[0]->store(plug->name, strlen(plug->name), scs);
- table->field[1]->store(C_STRING_WITH_LEN("NO"), scs);
+ table->field[1]->store(STRING_WITH_LEN("NO"), scs);
table->field[2]->store(plug->descr, strlen(plug->descr), scs);
if (schema_table_store_record(thd, table))
DBUG_RETURN(1);
@@ -5788,13 +6135,13 @@ static my_bool iter_schema_engines(THD *thd, plugin_ref plugin,
if (!(hton->flags & HTON_HIDDEN))
{
- LEX_STRING *name= plugin_name(plugin);
+ LEX_CSTRING *name= plugin_name(plugin);
if (!(wild && wild[0] &&
wild_case_compare(scs, name->str,wild)))
{
- LEX_STRING yesno[2]= {{ C_STRING_WITH_LEN("NO") },
- { C_STRING_WITH_LEN("YES") }};
- LEX_STRING *tmp;
+ 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];
restore_record(table, s->default_values);
@@ -5946,67 +6293,64 @@ bool store_schema_params(THD *thd, TABLE *table, TABLE *proc_table,
TABLE_SHARE share;
TABLE tbl;
CHARSET_INFO *cs= system_charset_info;
- char params_buff[MAX_FIELD_WIDTH], returns_buff[MAX_FIELD_WIDTH],
- sp_db_buff[NAME_LEN], sp_name_buff[NAME_LEN], path[FN_REFLEN],
- definer_buff[DEFINER_LENGTH + 1];
- String params(params_buff, sizeof(params_buff), cs);
- String returns(returns_buff, sizeof(returns_buff), cs);
- String sp_db(sp_db_buff, sizeof(sp_db_buff), cs);
- String sp_name(sp_name_buff, sizeof(sp_name_buff), cs);
- String definer(definer_buff, sizeof(definer_buff), cs);
+ LEX_CSTRING definer, params, returns= empty_clex_str;
+ LEX_CSTRING db, name;
+ char path[FN_REFLEN];
sp_head *sp;
- stored_procedure_type routine_type;
+ const Sp_handler *sph;
bool free_sp_head;
bool error= 0;
+ sql_mode_t sql_mode;
DBUG_ENTER("store_schema_params");
bzero((char*) &tbl, sizeof(TABLE));
(void) build_table_filename(path, sizeof(path), "", "", "", 0);
init_tmp_table_share(thd, &share, "", 0, "", path);
- get_field(thd->mem_root, proc_table->field[MYSQL_PROC_FIELD_DB], &sp_db);
- get_field(thd->mem_root, proc_table->field[MYSQL_PROC_FIELD_NAME], &sp_name);
- get_field(thd->mem_root,proc_table->field[MYSQL_PROC_FIELD_DEFINER],&definer);
- routine_type= (stored_procedure_type) proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_int();
+ 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);
+ sql_mode= (sql_mode_t) proc_table->field[MYSQL_PROC_FIELD_SQL_MODE]->val_int();
+ sph= Sp_handler::handler_mysql_proc((stored_procedure_type)
+ proc_table->field[MYSQL_PROC_MYSQL_TYPE]->
+ val_int());
+ if (!sph || sph->type() == TYPE_ENUM_PACKAGE ||
+ sph->type() == TYPE_ENUM_PACKAGE_BODY)
+ DBUG_RETURN(0);
if (!full_access)
- full_access= !strcmp(sp_user, definer.ptr());
+ full_access= !strcmp(sp_user, definer.str);
if (!full_access &&
- check_some_routine_access(thd, sp_db.ptr(),sp_name.ptr(),
- routine_type == TYPE_ENUM_PROCEDURE))
+ check_some_routine_access(thd, db.str, name.str, sph))
DBUG_RETURN(0);
- params.length(0);
- get_field(thd->mem_root, proc_table->field[MYSQL_PROC_FIELD_PARAM_LIST],
- &params);
- returns.length(0);
- if (routine_type == TYPE_ENUM_FUNCTION)
- get_field(thd->mem_root, proc_table->field[MYSQL_PROC_FIELD_RETURNS],
- &returns);
-
- sp= sp_load_for_information_schema(thd, proc_table, &sp_db, &sp_name,
- (ulong) proc_table->
- field[MYSQL_PROC_FIELD_SQL_MODE]->val_int(),
- routine_type,
- returns.c_ptr_safe(),
- params.c_ptr_safe(),
- &free_sp_head);
-
+ proc_table->field[MYSQL_PROC_FIELD_PARAM_LIST]->val_str_nopad(thd->mem_root,
+ &params);
+ if (sph->type() == TYPE_ENUM_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,
+ params, returns, sql_mode,
+ &free_sp_head);
if (sp)
{
Field *field;
- String tmp_string;
- if (routine_type == TYPE_ENUM_FUNCTION)
+ LEX_CSTRING tmp_string;
+ Sql_mode_save sql_mode_backup(thd);
+ thd->variables.sql_mode= sql_mode;
+
+ if (sph->type() == TYPE_ENUM_FUNCTION)
{
restore_record(table, s->default_values);
table->field[0]->store(STRING_WITH_LEN("def"), cs);
- table->field[1]->store(sp_db.ptr(), sp_db.length(), cs);
- table->field[2]->store(sp_name.ptr(), sp_name.length(), cs);
+ table->field[1]->store(db, cs);
+ table->field[2]->store(name, cs);
table->field[3]->store((longlong) 0, TRUE);
- get_field(thd->mem_root, proc_table->field[MYSQL_PROC_MYSQL_TYPE],
- &tmp_string);
- table->field[15]->store(tmp_string.ptr(), tmp_string.length(), cs);
- field= sp->m_return_field_def.make_field(&share, thd->mem_root, "");
+ proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_str_nopad(thd->mem_root,
+ &tmp_string);
+ table->field[15]->store(tmp_string, cs);
+ field= sp->m_return_field_def.make_field(&share, thd->mem_root,
+ &empty_clex_str);
field->table= &tbl;
tbl.in_use= thd;
store_column_type(table, field, cs, 6);
@@ -6042,22 +6386,18 @@ bool store_schema_params(THD *thd, TABLE *table, TABLE *proc_table,
restore_record(table, s->default_values);
table->field[0]->store(STRING_WITH_LEN("def"), cs);
- table->field[1]->store(sp_db.ptr(), sp_db.length(), cs);
- table->field[2]->store(sp_name.ptr(), sp_name.length(), cs);
+ table->field[1]->store(db, cs);
+ table->field[2]->store(name, cs);
table->field[3]->store((longlong) i + 1, TRUE);
table->field[4]->store(tmp_buff, strlen(tmp_buff), cs);
table->field[4]->set_notnull();
table->field[5]->store(spvar->name.str, spvar->name.length, cs);
table->field[5]->set_notnull();
- get_field(thd->mem_root, proc_table->field[MYSQL_PROC_MYSQL_TYPE],
- &tmp_string);
- table->field[15]->store(tmp_string.ptr(), tmp_string.length(), cs);
+ proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_str_nopad(thd->mem_root,
+ &tmp_string);
+ table->field[15]->store(tmp_string, cs);
- field= spvar->field_def.make_field(&share, thd->mem_root,
- spvar->name.str);
- field->table= &tbl;
- tbl.in_use= thd;
- store_column_type(table, field, cs, 6);
+ store_variable_type(thd, spvar, &tbl, &share, cs, table, 6);
if (schema_table_store_record(thd, table))
{
error= 1;
@@ -6078,63 +6418,55 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table,
MYSQL_TIME time;
LEX *lex= thd->lex;
CHARSET_INFO *cs= system_charset_info;
- char sp_db_buff[SAFE_NAME_LEN + 1], sp_name_buff[NAME_LEN + 1],
- definer_buff[DEFINER_LENGTH + 1],
- returns_buff[MAX_FIELD_WIDTH];
-
- String sp_db(sp_db_buff, sizeof(sp_db_buff), cs);
- String sp_name(sp_name_buff, sizeof(sp_name_buff), cs);
- String definer(definer_buff, sizeof(definer_buff), cs);
- String returns(returns_buff, sizeof(returns_buff), cs);
-
- proc_table->field[MYSQL_PROC_FIELD_DB]->val_str(&sp_db);
- proc_table->field[MYSQL_PROC_FIELD_NAME]->val_str(&sp_name);
- proc_table->field[MYSQL_PROC_FIELD_DEFINER]->val_str(&definer);
+ const Sp_handler *sph;
+ LEX_CSTRING db, name, definer, returns= empty_clex_str;
+
+ 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)
+ proc_table->field[MYSQL_PROC_MYSQL_TYPE]->
+ val_int());
+ if (!sph)
+ return 0;
if (!full_access)
- full_access= !strcmp(sp_user, definer.c_ptr_safe());
+ full_access= !strcmp(sp_user, definer.str);
if (!full_access &&
- check_some_routine_access(thd, sp_db.c_ptr_safe(), sp_name.c_ptr_safe(),
- proc_table->field[MYSQL_PROC_MYSQL_TYPE]->
- val_int() == TYPE_ENUM_PROCEDURE))
+ check_some_routine_access(thd, db.str, name.str, sph))
return 0;
if (!is_show_command(thd) ||
- (lex->sql_command == SQLCOM_SHOW_STATUS_PROC &&
- proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_int() ==
- TYPE_ENUM_PROCEDURE) ||
- (lex->sql_command == SQLCOM_SHOW_STATUS_FUNC &&
- proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_int() ==
- TYPE_ENUM_FUNCTION))
+ sph == Sp_handler::handler(lex->sql_command))
{
restore_record(table, s->default_values);
if (!wild || !wild[0] || !wild_case_compare(system_charset_info,
- sp_name.c_ptr_safe(), wild))
+ name.str, wild))
{
int enum_idx= (int) proc_table->field[MYSQL_PROC_FIELD_ACCESS]->val_int();
- table->field[3]->store(sp_name.ptr(), sp_name.length(), cs);
+ table->field[3]->store(name, cs);
copy_field_as_string(table->field[0],
proc_table->field[MYSQL_PROC_FIELD_SPECIFIC_NAME]);
table->field[1]->store(STRING_WITH_LEN("def"), cs);
- table->field[2]->store(sp_db.ptr(), sp_db.length(), cs);
+ table->field[2]->store(db, cs);
copy_field_as_string(table->field[4],
proc_table->field[MYSQL_PROC_MYSQL_TYPE]);
- if (proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_int() ==
- TYPE_ENUM_FUNCTION)
+ if (sph->type() == TYPE_ENUM_FUNCTION)
{
sp_head *sp;
bool free_sp_head;
- proc_table->field[MYSQL_PROC_FIELD_RETURNS]->val_str(&returns);
- sp= sp_load_for_information_schema(thd, proc_table, &sp_db, &sp_name,
- (ulong) proc_table->
- field[MYSQL_PROC_FIELD_SQL_MODE]->
- val_int(),
- TYPE_ENUM_FUNCTION,
- returns.c_ptr_safe(),
- "", &free_sp_head);
-
+ 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,
+ empty_clex_str /*params*/,
+ returns,
+ (ulong) proc_table->
+ field[MYSQL_PROC_FIELD_SQL_MODE]->
+ val_int(),
+ &free_sp_head);
if (sp)
{
char path[FN_REFLEN];
@@ -6145,7 +6477,8 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table,
bzero((char*) &tbl, sizeof(TABLE));
(void) build_table_filename(path, sizeof(path), "", "", "", 0);
init_tmp_table_share(thd, &share, "", 0, "", path);
- field= sp->m_return_field_def.make_field(&share, thd->mem_root, "");
+ field= sp->m_return_field_def.make_field(&share, thd->mem_root,
+ &empty_clex_str);
field->table= &tbl;
tbl.in_use= thd;
store_column_type(table, field, cs, 5);
@@ -6183,7 +6516,7 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table,
copy_field_as_string(table->field[26],
proc_table->field[MYSQL_PROC_FIELD_COMMENT]);
- table->field[27]->store(definer.ptr(), definer.length(), cs);
+ table->field[27]->store(definer, cs);
copy_field_as_string(table->field[28],
proc_table->
field[MYSQL_PROC_FIELD_CHARACTER_SET_CLIENT]);
@@ -6218,10 +6551,9 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond)
thd->security_ctx->priv_host, NullS);
/* We use this TABLE_LIST instance only for checking of privileges. */
bzero((char*) &proc_tables,sizeof(proc_tables));
- proc_tables.db= (char*) "mysql";
- proc_tables.db_length= 5;
- proc_tables.table_name= proc_tables.alias= (char*) "proc";
- proc_tables.table_name_length= 4;
+ proc_tables.db= MYSQL_SCHEMA_NAME;
+ proc_tables.table_name= MYSQL_PROC_NAME;
+ proc_tables.alias= MYSQL_PROC_NAME;
proc_tables.lock_type= TL_READ;
full_access= !check_table_access(thd, SELECT_ACL, &proc_tables, FALSE,
1, TRUE);
@@ -6276,8 +6608,8 @@ err:
static int get_schema_stat_record(THD *thd, TABLE_LIST *tables,
TABLE *table, bool res,
- LEX_STRING *db_name,
- LEX_STRING *table_name)
+ const LEX_CSTRING *db_name,
+ const LEX_CSTRING *table_name)
{
CHARSET_INFO *cs= system_charset_info;
DBUG_ENTER("get_schema_stat_record");
@@ -6289,7 +6621,7 @@ static int get_schema_stat_record(THD *thd, TABLE_LIST *tables,
I.e. we are in SELECT FROM INFORMATION_SCHEMA.STATISTICS
rather than in SHOW KEYS
*/
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
thd->get_stmt_da()->sql_errno(),
thd->get_stmt_da()->message());
@@ -6312,8 +6644,12 @@ static int get_schema_stat_record(THD *thd, TABLE_LIST *tables,
}
for (uint i=0 ; i < show_table->s->keys ; i++,key_info++)
{
+ if ((key_info->flags & HA_INVISIBLE_KEY) &&
+ DBUG_EVALUATE_IF("test_invisible_index", 0, 1))
+ continue;
KEY_PART_INFO *key_part= key_info->key_part;
- const char *str;
+ LEX_CSTRING *str;
+ LEX_CSTRING unknown= {STRING_WITH_LEN("?unknown field?") };
for (uint j=0 ; j < key_info->user_defined_key_parts ; j++,key_part++)
{
restore_record(table, s->default_values);
@@ -6323,11 +6659,11 @@ static int get_schema_stat_record(THD *thd, TABLE_LIST *tables,
table->field[3]->store((longlong) ((key_info->flags &
HA_NOSAME) ? 0 : 1), TRUE);
table->field[4]->store(db_name->str, db_name->length, cs);
- table->field[5]->store(key_info->name, strlen(key_info->name), cs);
+ table->field[5]->store(key_info->name.str, key_info->name.length, cs);
table->field[6]->store((longlong) (j+1), TRUE);
- str=(key_part->field ? key_part->field->field_name :
- "?unknown field?");
- table->field[7]->store(str, strlen(str), cs);
+ str= (key_part->field ? &key_part->field->field_name :
+ &unknown);
+ table->field[7]->store(str->str, str->length, cs);
if (show_table->file)
{
if (show_table->file->index_flags(i, j, 0) & HA_READ_ORDER)
@@ -6345,8 +6681,8 @@ static int get_schema_stat_record(THD *thd, TABLE_LIST *tables,
table->field[9]->store((longlong) records, TRUE);
table->field[9]->set_notnull();
}
- str= show_table->file->index_type(i);
- table->field[13]->store(str, strlen(str), cs);
+ const char *tmp= show_table->file->index_type(i);
+ table->field[13]->store(tmp, strlen(tmp), cs);
}
if (!(key_info->flags & HA_FULLTEXT) &&
(key_part->field &&
@@ -6381,8 +6717,8 @@ static int get_schema_stat_record(THD *thd, TABLE_LIST *tables,
static int get_schema_views_record(THD *thd, TABLE_LIST *tables,
TABLE *table, bool res,
- LEX_STRING *db_name,
- LEX_STRING *table_name)
+ const LEX_CSTRING *db_name,
+ const LEX_CSTRING *table_name)
{
CHARSET_INFO *cs= system_charset_info;
char definer[USER_HOST_BUFF_SIZE];
@@ -6514,7 +6850,7 @@ static int get_schema_views_record(THD *thd, TABLE_LIST *tables,
if (schema_table_store_record(thd, table))
DBUG_RETURN(1);
- if (res && thd->is_error())
+ if (unlikely(res && thd->is_error()))
push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
thd->get_stmt_da()->sql_errno(),
thd->get_stmt_da()->message());
@@ -6525,9 +6861,10 @@ static int get_schema_views_record(THD *thd, TABLE_LIST *tables,
}
-bool store_constraints(THD *thd, TABLE *table, LEX_STRING *db_name,
- LEX_STRING *table_name, const char *key_name,
- uint key_len, const char *con_type, uint con_len)
+static bool
+store_constraints(THD *thd, TABLE *table, const LEX_CSTRING *db_name,
+ const LEX_CSTRING *table_name, const char *key_name,
+ size_t key_len, const char *con_type, size_t con_len)
{
CHARSET_INFO *cs= system_charset_info;
restore_record(table, s->default_values);
@@ -6542,8 +6879,8 @@ bool store_constraints(THD *thd, TABLE *table, LEX_STRING *db_name,
static int get_check_constraints_record(THD *thd, TABLE_LIST *tables,
TABLE *table, bool res,
- LEX_STRING *db_name,
- LEX_STRING *table_name)
+ const LEX_CSTRING *db_name,
+ const LEX_CSTRING *table_name)
{
DBUG_ENTER("get_check_constraints_record");
if (res)
@@ -6567,10 +6904,8 @@ static int get_check_constraints_record(THD *thd, TABLE_LIST *tables,
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (!(thd->col_access & TABLE_ACLS))
{
- table_acl_check.db= db_name->str;
- table_acl_check.db_length= db_name->length;
- table_acl_check.table_name= table_name->str;
- table_acl_check.table_name_length= table_name->length;
+ table_acl_check.db= *db_name;
+ table_acl_check.table_name= *table_name;
table_acl_check.grant.privilege= thd->col_access;
if (check_grant(thd, TABLE_ACLS, &table_acl_check, FALSE, 1, TRUE))
continue;
@@ -6593,13 +6928,13 @@ static int get_check_constraints_record(THD *thd, TABLE_LIST *tables,
static int get_schema_constraints_record(THD *thd, TABLE_LIST *tables,
TABLE *table, bool res,
- LEX_STRING *db_name,
- LEX_STRING *table_name)
+ const LEX_CSTRING *db_name,
+ const LEX_CSTRING *table_name)
{
DBUG_ENTER("get_schema_constraints_record");
if (res)
{
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
thd->get_stmt_da()->sql_errno(),
thd->get_stmt_da()->message());
@@ -6620,17 +6955,17 @@ static int get_schema_constraints_record(THD *thd, TABLE_LIST *tables,
if (i != primary_key && !(key_info->flags & HA_NOSAME))
continue;
- if (i == primary_key && !strcmp(key_info->name, primary_key_name))
+ if (i == primary_key && !strcmp(key_info->name.str, primary_key_name))
{
- if (store_constraints(thd, table, db_name, table_name, key_info->name,
- strlen(key_info->name),
+ if (store_constraints(thd, table, db_name, table_name,
+ key_info->name.str, key_info->name.length,
STRING_WITH_LEN("PRIMARY KEY")))
DBUG_RETURN(1);
}
else if (key_info->flags & HA_NOSAME)
{
- if (store_constraints(thd, table, db_name, table_name, key_info->name,
- strlen(key_info->name),
+ if (store_constraints(thd, table, db_name, table_name,
+ key_info->name.str, key_info->name.length,
STRING_WITH_LEN("UNIQUE")))
DBUG_RETURN(1);
}
@@ -6666,14 +7001,15 @@ static int get_schema_constraints_record(THD *thd, TABLE_LIST *tables,
static bool store_trigger(THD *thd, Trigger *trigger,
- TABLE *table, LEX_STRING *db_name,
- LEX_STRING *table_name)
+ TABLE *table, const LEX_CSTRING *db_name,
+ const LEX_CSTRING *table_name)
{
CHARSET_INFO *cs= system_charset_info;
- LEX_STRING sql_mode_rep;
+ LEX_CSTRING sql_mode_rep;
MYSQL_TIME timestamp;
char definer_holder[USER_HOST_BUFF_SIZE];
- LEX_STRING definer_buffer, trigger_stmt, trigger_body;
+ LEX_STRING definer_buffer;
+ LEX_CSTRING trigger_stmt, trigger_body;
definer_buffer.str= definer_holder;
trigger->get_trigger_info(&trigger_stmt, &trigger_body, &definer_buffer);
@@ -6722,8 +7058,8 @@ static bool store_trigger(THD *thd, Trigger *trigger,
static int get_schema_triggers_record(THD *thd, TABLE_LIST *tables,
TABLE *table, bool res,
- LEX_STRING *db_name,
- LEX_STRING *table_name)
+ const LEX_CSTRING *db_name,
+ const LEX_CSTRING *table_name)
{
DBUG_ENTER("get_schema_triggers_record");
/*
@@ -6732,7 +7068,7 @@ static int get_schema_triggers_record(THD *thd, TABLE_LIST *tables,
*/
if (res)
{
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
thd->get_stmt_da()->sql_errno(),
thd->get_stmt_da()->message());
@@ -6769,10 +7105,11 @@ ret:
}
-void store_key_column_usage(TABLE *table, LEX_STRING *db_name,
- LEX_STRING *table_name, const char *key_name,
- uint key_len, const char *con_type, uint con_len,
- longlong idx)
+static void
+store_key_column_usage(TABLE *table, const LEX_CSTRING *db_name,
+ const LEX_CSTRING *table_name, const char *key_name,
+ size_t key_len, const char *con_type, size_t con_len,
+ longlong idx)
{
CHARSET_INFO *cs= system_charset_info;
table->field[0]->store(STRING_WITH_LEN("def"), cs);
@@ -6789,13 +7126,13 @@ void store_key_column_usage(TABLE *table, LEX_STRING *db_name,
static int get_schema_key_column_usage_record(THD *thd,
TABLE_LIST *tables,
TABLE *table, bool res,
- LEX_STRING *db_name,
- LEX_STRING *table_name)
+ const LEX_CSTRING *db_name,
+ const LEX_CSTRING *table_name)
{
DBUG_ENTER("get_schema_key_column_usage_record");
if (res)
{
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
thd->get_stmt_da()->sql_errno(),
thd->get_stmt_da()->message());
@@ -6824,10 +7161,9 @@ static int get_schema_key_column_usage_record(THD *thd,
f_idx++;
restore_record(table, s->default_values);
store_key_column_usage(table, db_name, table_name,
- key_info->name,
- strlen(key_info->name),
- key_part->field->field_name,
- strlen(key_part->field->field_name),
+ key_info->name.str, key_info->name.length,
+ key_part->field->field_name.str,
+ key_part->field->field_name.length,
(longlong) f_idx);
if (schema_table_store_record(thd, table))
DBUG_RETURN(1);
@@ -6840,9 +7176,9 @@ static int get_schema_key_column_usage_record(THD *thd,
List_iterator_fast<FOREIGN_KEY_INFO> fkey_it(f_key_list);
while ((f_key_info= fkey_it++))
{
- LEX_STRING *f_info;
- LEX_STRING *r_info;
- List_iterator_fast<LEX_STRING> it(f_key_info->foreign_fields),
+ LEX_CSTRING *f_info;
+ LEX_CSTRING *r_info;
+ List_iterator_fast<LEX_CSTRING> it(f_key_info->foreign_fields),
it1(f_key_info->referenced_fields);
uint f_idx= 0;
while ((f_info= it++))
@@ -6878,10 +7214,10 @@ static int get_schema_key_column_usage_record(THD *thd,
#ifdef WITH_PARTITION_STORAGE_ENGINE
-static void collect_partition_expr(THD *thd, List<char> &field_list,
+static void collect_partition_expr(THD *thd, List<const char> &field_list,
String *str)
{
- List_iterator<char> part_it(field_list);
+ List_iterator<const char> part_it(field_list);
ulong no_fields= field_list.elements;
const char *field_str;
str->length(0);
@@ -7012,22 +7348,15 @@ static void store_schema_partitions_record(THD *thd, TABLE *schema_table,
strlen(part_elem->tablespace_name), cs);
else
{
- char *ts= showing_table->s->tablespace;
- if(ts)
- table->field[24]->store(ts, strlen(ts), cs);
- else
- table->field[24]->set_null();
+ table->field[24]->set_null();
}
}
return;
}
#ifdef WITH_PARTITION_STORAGE_ENGINE
-static int
-get_partition_column_description(THD *thd,
- partition_info *part_info,
- part_elem_value *list_value,
- String &tmp_str)
+static int get_partition_column_description(THD *thd, partition_info *part_info,
+ part_elem_value *list_value, String &tmp_str)
{
uint num_elements= part_info->part_field_list.elements;
uint i;
@@ -7070,8 +7399,8 @@ get_partition_column_description(THD *thd,
static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables,
TABLE *table, bool res,
- LEX_STRING *db_name,
- LEX_STRING *table_name)
+ const LEX_CSTRING *db_name,
+ const LEX_CSTRING *table_name)
{
CHARSET_INFO *cs= system_charset_info;
char buff[61];
@@ -7086,7 +7415,7 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables,
if (res)
{
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
thd->get_stmt_da()->sql_errno(),
thd->get_stmt_da()->message());
@@ -7131,6 +7460,9 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables,
tmp_res.append(STRING_WITH_LEN("HASH"));
table->field[7]->store(tmp_res.ptr(), tmp_res.length(), cs);
break;
+ case VERSIONING_PARTITION:
+ table->field[7]->store(STRING_WITH_LEN("SYSTEM_TIME"), cs);
+ break;
default:
DBUG_ASSERT(0);
my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
@@ -7197,13 +7529,9 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables,
List_iterator<part_elem_value> list_val_it(part_elem->list_val_list);
part_elem_value *list_value= list_val_it++;
tmp_str.length(0);
- if (get_partition_column_description(thd,
- part_info,
- list_value,
+ if (get_partition_column_description(thd, part_info, list_value,
tmp_str))
- {
DBUG_RETURN(1);
- }
table->field[11]->store(tmp_str.ptr(), tmp_str.length(), cs);
}
else
@@ -7224,7 +7552,7 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables,
tmp_res.length(0);
if (part_elem->has_null_value)
{
- tmp_str.append("NULL");
+ tmp_str.append(STRING_WITH_LEN("NULL"));
if (num_items > 0)
tmp_str.append(",");
}
@@ -7233,14 +7561,10 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables,
if (part_info->column_list)
{
if (part_info->part_field_list.elements > 1U)
- tmp_str.append("(");
- if (get_partition_column_description(thd,
- part_info,
- list_value,
+ tmp_str.append(STRING_WITH_LEN("("));
+ if (get_partition_column_description(thd, part_info, list_value,
tmp_str))
- {
DBUG_RETURN(1);
- }
if (part_info->part_field_list.elements > 1U)
tmp_str.append(")");
}
@@ -7258,6 +7582,19 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables,
table->field[11]->store(tmp_str.ptr(), tmp_str.length(), cs);
table->field[11]->set_notnull();
}
+ else if (part_info->part_type == VERSIONING_PARTITION)
+ {
+ if (part_elem == part_info->vers_info->now_part)
+ {
+ table->field[11]->store(STRING_WITH_LEN("CURRENT"), cs);
+ table->field[11]->set_notnull();
+ }
+ else if (part_info->vers_info->interval.is_set())
+ {
+ table->field[11]->store_timestamp((my_time_t)part_elem->range_value, 0);
+ table->field[11]->set_notnull();
+ }
+ }
if (part_elem->subpartitions.elements)
{
@@ -7365,7 +7702,7 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table)
/* SQL_MODE */
{
- LEX_STRING sql_mode;
+ LEX_CSTRING sql_mode;
sql_mode_string_representation(thd, et.sql_mode, &sql_mode);
sch_table->field[ISE_SQL_MODE]->
store(sql_mode.str, sql_mode.length, scs);
@@ -7387,7 +7724,7 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table)
sch_table->field[ISE_INTERVAL_VALUE]->
store(show_str.ptr(), show_str.length(), scs);
- LEX_STRING *ival= &interval_type_to_name[et.interval];
+ LEX_CSTRING *ival= &interval_type_to_name[et.interval];
sch_table->field[ISE_INTERVAL_FIELD]->set_notnull();
sch_table->field[ISE_INTERVAL_FIELD]->store(ival->str, ival->length, scs);
@@ -7490,8 +7827,9 @@ int fill_open_tables(THD *thd, TABLE_LIST *tables, COND *cond)
TABLE *table= tables->table;
CHARSET_INFO *cs= system_charset_info;
OPEN_TABLE_LIST *open_list;
- if (!(open_list=list_open_tables(thd,thd->lex->select_lex.db, wild))
- && thd->is_fatal_error)
+ if (unlikely(!(open_list= list_open_tables(thd, thd->lex->select_lex.db.str,
+ wild))) &&
+ unlikely(thd->is_fatal_error))
DBUG_RETURN(1);
for (; open_list ; open_list=open_list->next)
@@ -7501,7 +7839,7 @@ int fill_open_tables(THD *thd, TABLE_LIST *tables, COND *cond)
table->field[1]->store(open_list->table, strlen(open_list->table), cs);
table->field[2]->store((longlong) open_list->in_use, TRUE);
table->field[3]->store((longlong) open_list->locked, TRUE);
- if (schema_table_store_record(thd, table))
+ if (unlikely(schema_table_store_record(thd, table)))
DBUG_RETURN(1);
}
DBUG_RETURN(0);
@@ -7621,7 +7959,8 @@ int fill_status(THD *thd, TABLE_LIST *tables, COND *cond)
static int
get_referential_constraints_record(THD *thd, TABLE_LIST *tables,
TABLE *table, bool res,
- LEX_STRING *db_name, LEX_STRING *table_name)
+ const LEX_CSTRING *db_name,
+ const LEX_CSTRING *table_name)
{
CHARSET_INFO *cs= system_charset_info;
LEX_CSTRING *s;
@@ -7629,7 +7968,7 @@ get_referential_constraints_record(THD *thd, TABLE_LIST *tables,
if (res)
{
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
thd->get_stmt_da()->sql_errno(),
thd->get_stmt_da()->message());
@@ -7709,7 +8048,8 @@ static my_bool find_schema_table_in_plugin(THD *thd, plugin_ref plugin,
if (!my_strcasecmp(system_charset_info,
schema_table->table_name,
- table_name)) {
+ table_name))
+ {
my_plugin_lock(thd, plugin);
p_schema_table->schema_table= schema_table;
DBUG_RETURN(1);
@@ -7720,7 +8060,7 @@ static my_bool find_schema_table_in_plugin(THD *thd, plugin_ref plugin,
/*
- Find schema_tables elment by name
+ Find schema_tables element by name
SYNOPSIS
find_schema_table()
@@ -7732,7 +8072,7 @@ static my_bool find_schema_table_in_plugin(THD *thd, plugin_ref plugin,
# pointer to 'schema_tables' element
*/
-ST_SCHEMA_TABLE *find_schema_table(THD *thd, const char* table_name,
+ST_SCHEMA_TABLE *find_schema_table(THD *thd, const LEX_CSTRING *table_name,
bool *in_plugin)
{
schema_table_ref schema_table_a;
@@ -7744,12 +8084,12 @@ ST_SCHEMA_TABLE *find_schema_table(THD *thd, const char* table_name,
{
if (!my_strcasecmp(system_charset_info,
schema_table->table_name,
- table_name))
+ table_name->str))
DBUG_RETURN(schema_table);
}
*in_plugin= true;
- schema_table_a.table_name= table_name;
+ schema_table_a.table_name= table_name->str;
if (plugin_foreach(thd, find_schema_table_in_plugin,
MYSQL_INFORMATION_SCHEMA_PLUGIN, &schema_table_a))
DBUG_RETURN(schema_table_a.schema_table);
@@ -7763,6 +8103,50 @@ ST_SCHEMA_TABLE *get_schema_table(enum enum_schema_tables schema_table_idx)
return &schema_tables[schema_table_idx];
}
+static void
+mark_all_fields_used_in_query(THD *thd,
+ ST_FIELD_INFO *schema_fields,
+ MY_BITMAP *bitmap,
+ Item *all_items)
+{
+ Item *item;
+ DBUG_ENTER("mark_all_fields_used_in_query");
+
+ /* If not SELECT command, return all columns */
+ if (thd->lex->sql_command != SQLCOM_SELECT &&
+ thd->lex->sql_command != SQLCOM_SET_OPTION)
+ {
+ bitmap_set_all(bitmap);
+ DBUG_VOID_RETURN;
+ }
+
+ for (item= all_items ; item ; item= item->next)
+ {
+ if (item->type() == Item::FIELD_ITEM)
+ {
+ ST_FIELD_INFO *fields= schema_fields;
+ uint count;
+ Item_field *item_field= (Item_field*) item;
+
+ /* item_field can be '*' as this function is called before fix_fields */
+ if (item_field->field_name.str == star_clex_str.str)
+ {
+ bitmap_set_all(bitmap);
+ break;
+ }
+ for (count=0; fields->field_name; fields++, count++)
+ {
+ if (!my_strcasecmp(system_charset_info, fields->field_name,
+ item_field->field_name.str))
+ {
+ bitmap_set_bit(bitmap, count);
+ break;
+ }
+ }
+ }
+ }
+ DBUG_VOID_RETURN;
+}
/**
Create information_schema table using schema_table data.
@@ -7787,18 +8171,39 @@ ST_SCHEMA_TABLE *get_schema_table(enum enum_schema_tables schema_table_idx)
TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list)
{
- int field_count= 0;
- Item *item;
+ uint field_count;
+ Item *item, *all_items;
TABLE *table;
List<Item> field_list;
ST_SCHEMA_TABLE *schema_table= table_list->schema_table;
ST_FIELD_INFO *fields_info= schema_table->fields_info;
+ ST_FIELD_INFO *fields;
CHARSET_INFO *cs= system_charset_info;
MEM_ROOT *mem_root= thd->mem_root;
+ MY_BITMAP bitmap;
+ my_bitmap_map *buf;
DBUG_ENTER("create_schema_table");
- for (; fields_info->field_name; fields_info++)
+ for (field_count= 0, fields= fields_info; fields->field_name; fields++)
+ field_count++;
+ if (!(buf= (my_bitmap_map*) thd->alloc(bitmap_buffer_size(field_count))))
+ DBUG_RETURN(NULL);
+ my_bitmap_init(&bitmap, buf, field_count, 0);
+
+ if (!thd->stmt_arena->is_conventional() &&
+ thd->mem_root != thd->stmt_arena->mem_root)
+ all_items= thd->stmt_arena->free_list;
+ else
+ all_items= thd->free_list;
+
+ if (table_list->part_of_natural_join)
+ bitmap_set_all(&bitmap);
+ else
+ mark_all_fields_used_in_query(thd, fields_info, &bitmap, all_items);
+
+ for (field_count=0; 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:
@@ -7818,14 +8223,14 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list)
case MYSQL_TYPE_DATE:
if (!(item=new (mem_root)
Item_return_date_time(thd, fields_info->field_name,
- strlen(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,
- strlen(fields_info->field_name),
+ (uint)field_name_length,
fields_info->field_type)))
DBUG_RETURN(0);
break;
@@ -7833,8 +8238,9 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list)
case MYSQL_TYPE_DATETIME:
if (!(item=new (mem_root)
Item_return_date_time(thd, fields_info->field_name,
- strlen(fields_info->field_name),
- fields_info->field_type)))
+ (uint)field_name_length,
+ fields_info->field_type,
+ fields_info->field_length)))
DBUG_RETURN(0);
item->decimals= fields_info->field_length;
break;
@@ -7866,33 +8272,50 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list)
item->max_length+= 1;
if (item->decimals > 0)
item->max_length+= 1;
- item->set_name(thd, fields_info->field_name,
- strlen(fields_info->field_name), cs);
+ 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)))
+ if (bitmap_is_set(&bitmap, field_count))
{
- DBUG_RETURN(0);
+ if (!(item= new (mem_root)
+ Item_blob(thd, fields_info->field_name,
+ fields_info->field_length)))
+ {
+ DBUG_RETURN(0);
+ }
+ }
+ else
+ {
+ if (!(item= new (mem_root)
+ Item_empty_string(thd, "", 0, cs)))
+ {
+ DBUG_RETURN(0);
+ }
+ item->set_name(thd, fields_info->field_name,
+ field_name_length, cs);
}
break;
default:
+ {
+ bool show_field;
/* Don't let unimplemented types pass through. Could be a grave error. */
DBUG_ASSERT(fields_info->field_type == MYSQL_TYPE_STRING);
+ show_field= bitmap_is_set(&bitmap, field_count);
if (!(item= new (mem_root)
- Item_empty_string(thd, "", fields_info->field_length, cs)))
+ Item_empty_string(thd, "",
+ show_field ? fields_info->field_length : 0, cs)))
{
DBUG_RETURN(0);
}
item->set_name(thd, fields_info->field_name,
- strlen(fields_info->field_name), cs);
+ 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);
field_count++;
@@ -7908,7 +8331,7 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list)
field_list, (ORDER*) 0, 0, 0,
(select_lex->options | thd->variables.option_bits |
TMP_TABLE_ALL_COLUMNS), HA_POS_ERROR,
- table_list->alias, false, keep_row_order)))
+ &table_list->alias, false, keep_row_order)))
DBUG_RETURN(0);
my_bitmap_map* bitmaps=
(my_bitmap_map*) thd->alloc(bitmap_buffer_size(field_count));
@@ -7944,8 +8367,10 @@ static int make_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table)
{
if (field_info->old_name)
{
+ 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_info->field_name);
+ Item_field(thd, context, NullS, NullS, &field_name);
if (field)
{
field->set_name(thd, field_info->old_name,
@@ -7971,8 +8396,11 @@ 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_info->field_name);
+ NullS, NullS, &field_name);
if (!field || add_item_to_list(thd, field))
return 1;
buffer.length(0);
@@ -7995,11 +8423,13 @@ int make_table_names_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table)
String buffer(tmp,sizeof(tmp), thd->charset());
LEX *lex= thd->lex;
Name_resolution_context *context= &lex->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) };
+
buffer.length(0);
buffer.append(field_info->old_name);
- buffer.append(lex->select_lex.db);
+ buffer.append(&lex->select_lex.db);
if (lex->wild && lex->wild->ptr())
{
buffer.append(STRING_WITH_LEN(" ("));
@@ -8007,15 +8437,17 @@ int make_table_names_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table)
buffer.append(')');
}
Item_field *field= new (thd->mem_root) Item_field(thd, context,
- NullS, NullS, field_info->field_name);
+ NullS, NullS, &field_name);
if (add_item_to_list(thd, field))
return 1;
field->set_name(thd, buffer.ptr(), buffer.length(), system_charset_info);
if (thd->lex->verbose)
{
- field->set_name(thd, buffer.ptr(), buffer.length(), system_charset_info);
field_info= &schema_table->fields_info[3];
- field= new (thd->mem_root) Item_field(thd, context, NullS, NullS, field_info->field_name);
+ 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);
if (add_item_to_list(thd, field))
return 1;
field->set_name(thd, field_info->old_name, strlen(field_info->old_name),
@@ -8035,12 +8467,14 @@ 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_info->field_name);
+ NullS, NullS, &field_name);
if (field)
{
field->set_name(thd, field_info->old_name,
@@ -8064,8 +8498,10 @@ 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_info->field_name);
+ NullS, NullS, &field_name);
if (field)
{
field->set_name(thd, field_info->old_name,
@@ -8089,8 +8525,10 @@ 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_info->field_name);
+ NullS, NullS, &field_name);
if (field)
{
field->set_name(thd, field_info->old_name,
@@ -8133,10 +8571,10 @@ int mysql_schema_table(THD *thd, LEX *lex, TABLE_LIST *table_list)
views
working correctly
*/
- if (table_list->schema_table_name)
+ if (table_list->schema_table_name.str)
table->alias_name_used= my_strcasecmp(table_alias_charset,
- table_list->schema_table_name,
- table_list->alias);
+ table_list->schema_table_name.str,
+ table_list->alias.str);
table_list->table= table;
table->next= thd->derived_tables;
thd->derived_tables= table;
@@ -8154,8 +8592,7 @@ int mysql_schema_table(THD *thd, LEX *lex, TABLE_LIST *table_list)
Field_translator *end= table_list->field_translation_end;
for (transl= table_list->field_translation; transl < end; transl++)
{
- if (!transl->item->fixed &&
- transl->item->fix_fields(thd, &transl->item))
+ if (transl->item->fix_fields_if_needed(thd, &transl->item))
DBUG_RETURN(1);
}
DBUG_RETURN(0);
@@ -8172,10 +8609,8 @@ int mysql_schema_table(THD *thd, LEX *lex, TABLE_LIST *table_list)
{
transl->item= item;
transl->name= item->name;
- if (!item->fixed && item->fix_fields(thd, &transl->item))
- {
+ if (item->fix_fields_if_needed(thd, &transl->item))
DBUG_RETURN(1);
- }
}
table_list->field_translation= org_transl;
table_list->field_translation_end= transl;
@@ -8202,7 +8637,7 @@ int mysql_schema_table(THD *thd, LEX *lex, TABLE_LIST *table_list)
int make_schema_select(THD *thd, SELECT_LEX *sel,
ST_SCHEMA_TABLE *schema_table)
{
- LEX_STRING db, table;
+ LEX_CSTRING db, table;
DBUG_ENTER("make_schema_select");
DBUG_PRINT("enter", ("mysql_schema_select: %s", schema_table->table_name));
/*
@@ -8220,7 +8655,7 @@ int make_schema_select(THD *thd, SELECT_LEX *sel,
if (schema_table->old_format(thd, schema_table))
DBUG_RETURN(1);
- if (!sel->add_table_to_list(thd, new Table_ident(thd, db, table, 0),
+ if (!sel->add_table_to_list(thd, new Table_ident(thd, &db, &table, 0),
0, 0, TL_READ, MDL_SHARED_READ))
DBUG_RETURN(1);
@@ -8477,7 +8912,7 @@ bool get_schema_tables_result(JOIN *join,
}
}
thd->pop_internal_handler();
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
{
/*
This hack is here, because I_S code uses thd->clear_error() a lot.
@@ -8537,7 +8972,7 @@ int hton_fill_schema_table(THD *thd, TABLE_LIST *tables, COND *cond)
static
int store_key_cache_table_record(THD *thd, TABLE *table,
- const char *name, uint name_length,
+ const char *name, size_t name_length,
KEY_CACHE *key_cache,
uint partitions, uint partition_no)
{
@@ -8670,6 +9105,9 @@ ST_FIELD_INFO tables_fields_info[]=
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}
};
@@ -8827,7 +9265,7 @@ ST_FIELD_INFO proc_fields_info[]=
SKIP_OPEN_TABLE},
{"ROUTINE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name",
SKIP_OPEN_TABLE},
- {"ROUTINE_TYPE", 9, MYSQL_TYPE_STRING, 0, 0, "Type", 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},
@@ -9150,6 +9588,7 @@ ST_FIELD_INFO processlist_fields_info[]=
{"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,
@@ -9402,6 +9841,7 @@ ST_FIELD_INFO spatial_ref_sys_fields_info[]=
};
#endif /*HAVE_SPATIAL*/
+
ST_FIELD_INFO check_constraints_fields_info[]=
{
{"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
@@ -9414,7 +9854,6 @@ ST_FIELD_INFO check_constraints_fields_info[]=
OPEN_FULL_TABLE},
{0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
};
-
/*
Description of ST_FIELD_INFO in table.h
@@ -9612,8 +10051,8 @@ static bool show_create_trigger_impl(THD *thd, Trigger *trigger)
int ret_code;
Protocol *p= thd->protocol;
List<Item> fields;
- LEX_STRING trg_sql_mode_str, trg_body;
- LEX_STRING trg_sql_original_stmt;
+ LEX_CSTRING trg_sql_mode_str, trg_body;
+ LEX_CSTRING trg_sql_original_stmt;
LEX_STRING trg_definer;
CHARSET_INFO *trg_client_cs;
MEM_ROOT *mem_root= thd->mem_root;
@@ -9645,7 +10084,7 @@ static bool show_create_trigger_impl(THD *thd, Trigger *trigger)
fields.push_back(new (mem_root) Item_empty_string(thd, "Trigger", NAME_LEN),
mem_root);
fields.push_back(new (mem_root)
- Item_empty_string(thd, "sql_mode", trg_sql_mode_str.length),
+ Item_empty_string(thd, "sql_mode", (uint)trg_sql_mode_str.length),
mem_root);
{
@@ -9656,7 +10095,7 @@ static bool show_create_trigger_impl(THD *thd, Trigger *trigger)
Item_empty_string *stmt_fld=
new (mem_root) Item_empty_string(thd, "SQL Original Statement",
- MY_MAX(trg_sql_original_stmt.length,
+ (uint)MY_MAX(trg_sql_original_stmt.length,
1024));
stmt_fld->maybe_null= TRUE;
@@ -9766,12 +10205,12 @@ static
TABLE_LIST *get_trigger_table(THD *thd, const sp_name *trg_name)
{
char trn_path_buff[FN_REFLEN];
- LEX_STRING trn_path= { trn_path_buff, 0 };
- LEX_STRING db;
- LEX_STRING tbl_name;
+ LEX_CSTRING trn_path= { trn_path_buff, 0 };
+ LEX_CSTRING db;
+ LEX_CSTRING tbl_name;
TABLE_LIST *table;
- build_trn_path(thd, trg_name, &trn_path);
+ build_trn_path(thd, trg_name, (LEX_STRING*) &trn_path);
if (check_trn_exists(&trn_path))
{
@@ -9790,15 +10229,14 @@ TABLE_LIST *get_trigger_table(THD *thd, const sp_name *trg_name)
db.str= thd->strmake(db.str, db.length);
if (lower_case_table_names)
- db.length= my_casedn_str(files_charset_info, db.str);
+ db.length= my_casedn_str(files_charset_info, (char*) db.str);
tbl_name.str= thd->strmake(tbl_name.str, tbl_name.length);
if (db.str == NULL || tbl_name.str == NULL)
return NULL;
- table->init_one_table(db.str, db.length, tbl_name.str, tbl_name.length,
- tbl_name.str, TL_IGNORE);
+ table->init_one_table(&db, &tbl_name, 0, TL_IGNORE);
return table;
}
@@ -9846,7 +10284,7 @@ bool show_create_trigger(THD *thd, const sp_name *trg_name)
{
my_error(ER_TRG_CANT_OPEN_TABLE, MYF(0),
(const char *) trg_name->m_db.str,
- (const char *) lst->table_name);
+ (const char *) lst->table_name.str);
goto exit;
@@ -9867,8 +10305,7 @@ bool show_create_trigger(THD *thd, const sp_name *trg_name)
{
my_error(ER_TRG_CORRUPTED_FILE, MYF(0),
(const char *) trg_name->m_db.str,
- (const char *) lst->table_name);
-
+ (const char *) lst->table_name.str);
goto exit;
}
@@ -9988,14 +10425,14 @@ static void get_cs_converted_string_value(THD *thd,
try_val.copy(input_str->ptr(), input_str->length(), cs,
thd->variables.character_set_client, &try_conv_error);
- if (!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 (!conv_error)
+ if (likely(!conv_error))
{
append_unescaped(output_str, val.ptr(), val.length());
return;
@@ -10051,11 +10488,11 @@ char *thd_get_error_context_description(THD *thd, char *buffer,
String str(buffer, length, &my_charset_latin1);
const Security_context *sctx= &thd->main_security_ctx;
char header[256];
- int len;
+ size_t len;
len= my_snprintf(header, sizeof(header),
- "MySQL thread id %lu, OS thread handle %lu, query id %lu",
- thd->thread_id, (ulong) thd->real_id, (ulong) thd->query_id);
+ "MySQL thread id %u, OS thread handle %lu, query id %llu",
+ (uint)thd->thread_id, (ulong) thd->real_id, (ulonglong) thd->query_id);
str.length(0);
str.append(header, len);
diff --git a/sql/sql_show.h b/sql/sql_show.h
index ff0f6efc1be..39cbc35230a 100644
--- a/sql/sql_show.h
+++ b/sql/sql_show.h
@@ -82,16 +82,19 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
int copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table);
-bool append_identifier(THD *thd, String *packet, const char *name,
- uint length);
+bool append_identifier(THD *thd, String *packet, const char *name, size_t length);
+static inline bool append_identifier(THD *thd, String *packet, const LEX_CSTRING *name)
+{
+ return append_identifier(thd, packet, name->str, name->length);
+}
void mysqld_list_fields(THD *thd,TABLE_LIST *table, const char *wild);
int mysqld_dump_create_info(THD *thd, TABLE_LIST *table_list, int fd);
bool mysqld_show_create_get_fields(THD *thd, TABLE_LIST *table_list,
List<Item> *field_list, String *buffer);
bool mysqld_show_create(THD *thd, TABLE_LIST *table_list);
void mysqld_show_create_db_get_fields(THD *thd, List<Item> *field_list);
-bool mysqld_show_create_db(THD *thd, LEX_STRING *db_name,
- LEX_STRING *orig_db_name,
+bool mysqld_show_create_db(THD *thd, LEX_CSTRING *db_name,
+ LEX_CSTRING *orig_db_name,
const DDL_options_st &options);
void mysqld_list_processes(THD *thd,const char *user,bool verbose);
@@ -103,8 +106,8 @@ bool mysqld_show_contributors(THD *thd);
bool mysqld_show_privileges(THD *thd);
char *make_backup_log_name(char *buff, const char *name, const char* log_ext);
uint calc_sum_of_all_status(STATUS_VAR *to);
-void append_definer(THD *thd, String *buffer, const LEX_STRING *definer_user,
- const LEX_STRING *definer_host);
+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);
void init_status_vars();
@@ -118,8 +121,9 @@ 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 char* table_name, bool *in_plugin);
-static inline ST_SCHEMA_TABLE *find_schema_table(THD *thd, const char* table_name)
+ST_SCHEMA_TABLE *find_schema_table(THD *thd, const LEX_CSTRING *table_name,
+ bool *in_plugin);
+static inline ST_SCHEMA_TABLE *find_schema_table(THD *thd, const LEX_CSTRING *table_name)
{ bool unused; return find_schema_table(thd, table_name, &unused); }
ST_SCHEMA_TABLE *get_schema_table(enum enum_schema_tables schema_table_idx);
@@ -138,7 +142,7 @@ const char* get_one_variable(THD *thd, const SHOW_VAR *variable,
size_t *length);
/* These functions were under INNODB_COMPATIBILITY_HOOKS */
-int get_quote_char_for_identifier(THD *thd, const char *name, uint length);
+int get_quote_char_for_identifier(THD *thd, const char *name, size_t length);
THD *find_thread_by_id(longlong id, bool query_id= false);
class select_result_explain_buffer;
@@ -180,13 +184,13 @@ typedef struct st_lookup_field_values
Note that this value length may exceed @c NAME_LEN.
@sa wild_db_value
*/
- LEX_STRING db_value;
+ LEX_CSTRING db_value;
/**
Value of a TABLE_NAME clause.
Note that this value length may exceed @c NAME_LEN.
@sa wild_table_value
*/
- LEX_STRING table_value;
+ LEX_CSTRING table_value;
/**
True when @c db_value is a LIKE clause,
false when @c db_value is an '=' clause.
@@ -199,7 +203,6 @@ typedef struct st_lookup_field_values
bool wild_table_value;
} LOOKUP_FIELD_VALUES;
-
/*
INFORMATION_SCHEMA: Execution plan for get_all_tables() call
*/
diff --git a/sql/sql_signal.cc b/sql/sql_signal.cc
index bb3b99ba4f6..bf0757f78fe 100644
--- a/sql/sql_signal.cc
+++ b/sql/sql_signal.cc
@@ -13,7 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "sp_head.h"
#include "sp_pcontext.h"
@@ -29,38 +29,38 @@
*/
#define MAX_MYSQL_ERRNO 65534
-const LEX_STRING Diag_condition_item_names[]=
+const LEX_CSTRING Diag_condition_item_names[]=
{
- { C_STRING_WITH_LEN("CLASS_ORIGIN") },
- { C_STRING_WITH_LEN("SUBCLASS_ORIGIN") },
- { C_STRING_WITH_LEN("CONSTRAINT_CATALOG") },
- { C_STRING_WITH_LEN("CONSTRAINT_SCHEMA") },
- { C_STRING_WITH_LEN("CONSTRAINT_NAME") },
- { C_STRING_WITH_LEN("CATALOG_NAME") },
- { C_STRING_WITH_LEN("SCHEMA_NAME") },
- { C_STRING_WITH_LEN("TABLE_NAME") },
- { C_STRING_WITH_LEN("COLUMN_NAME") },
- { C_STRING_WITH_LEN("CURSOR_NAME") },
- { C_STRING_WITH_LEN("MESSAGE_TEXT") },
- { C_STRING_WITH_LEN("MYSQL_ERRNO") },
-
- { C_STRING_WITH_LEN("CONDITION_IDENTIFIER") },
- { C_STRING_WITH_LEN("CONDITION_NUMBER") },
- { C_STRING_WITH_LEN("CONNECTION_NAME") },
- { C_STRING_WITH_LEN("MESSAGE_LENGTH") },
- { C_STRING_WITH_LEN("MESSAGE_OCTET_LENGTH") },
- { C_STRING_WITH_LEN("PARAMETER_MODE") },
- { C_STRING_WITH_LEN("PARAMETER_NAME") },
- { C_STRING_WITH_LEN("PARAMETER_ORDINAL_POSITION") },
- { C_STRING_WITH_LEN("RETURNED_SQLSTATE") },
- { C_STRING_WITH_LEN("ROUTINE_CATALOG") },
- { C_STRING_WITH_LEN("ROUTINE_NAME") },
- { C_STRING_WITH_LEN("ROUTINE_SCHEMA") },
- { C_STRING_WITH_LEN("SERVER_NAME") },
- { C_STRING_WITH_LEN("SPECIFIC_NAME") },
- { C_STRING_WITH_LEN("TRIGGER_CATALOG") },
- { C_STRING_WITH_LEN("TRIGGER_NAME") },
- { C_STRING_WITH_LEN("TRIGGER_SCHEMA") }
+ { STRING_WITH_LEN("CLASS_ORIGIN") },
+ { STRING_WITH_LEN("SUBCLASS_ORIGIN") },
+ { STRING_WITH_LEN("CONSTRAINT_CATALOG") },
+ { STRING_WITH_LEN("CONSTRAINT_SCHEMA") },
+ { STRING_WITH_LEN("CONSTRAINT_NAME") },
+ { STRING_WITH_LEN("CATALOG_NAME") },
+ { STRING_WITH_LEN("SCHEMA_NAME") },
+ { STRING_WITH_LEN("TABLE_NAME") },
+ { STRING_WITH_LEN("COLUMN_NAME") },
+ { STRING_WITH_LEN("CURSOR_NAME") },
+ { STRING_WITH_LEN("MESSAGE_TEXT") },
+ { STRING_WITH_LEN("MYSQL_ERRNO") },
+
+ { STRING_WITH_LEN("CONDITION_IDENTIFIER") },
+ { STRING_WITH_LEN("CONDITION_NUMBER") },
+ { STRING_WITH_LEN("CONNECTION_NAME") },
+ { STRING_WITH_LEN("MESSAGE_LENGTH") },
+ { STRING_WITH_LEN("MESSAGE_OCTET_LENGTH") },
+ { STRING_WITH_LEN("PARAMETER_MODE") },
+ { STRING_WITH_LEN("PARAMETER_NAME") },
+ { STRING_WITH_LEN("PARAMETER_ORDINAL_POSITION") },
+ { STRING_WITH_LEN("RETURNED_SQLSTATE") },
+ { STRING_WITH_LEN("ROUTINE_CATALOG") },
+ { STRING_WITH_LEN("ROUTINE_NAME") },
+ { STRING_WITH_LEN("ROUTINE_SCHEMA") },
+ { STRING_WITH_LEN("SERVER_NAME") },
+ { STRING_WITH_LEN("SPECIFIC_NAME") },
+ { STRING_WITH_LEN("TRIGGER_CATALOG") },
+ { STRING_WITH_LEN("TRIGGER_NAME") },
+ { STRING_WITH_LEN("TRIGGER_SCHEMA") }
};
@@ -75,63 +75,6 @@ void Set_signal_information::clear()
memset(m_item, 0, sizeof(m_item));
}
-void
-Sql_cmd_common_signal::assign_defaults(Sql_condition *cond,
- bool set_level_code,
- Sql_condition::enum_warning_level level,
- int sqlcode)
-{
- if (set_level_code)
- {
- cond->m_level= level;
- cond->m_sql_errno= sqlcode;
- }
- if (! cond->get_message_text())
- cond->set_builtin_message_text(ER(sqlcode));
-}
-
-void Sql_cmd_common_signal::eval_defaults(THD *thd, Sql_condition *cond)
-{
- DBUG_ASSERT(cond);
-
- const char* sqlstate;
- bool set_defaults= (m_cond != 0);
-
- if (set_defaults)
- {
- /*
- SIGNAL is restricted in sql_yacc.yy to only signal SQLSTATE conditions.
- */
- DBUG_ASSERT(m_cond->type == sp_condition_value::SQLSTATE);
- sqlstate= m_cond->sql_state;
- cond->set_sqlstate(sqlstate);
- }
- else
- sqlstate= cond->get_sqlstate();
-
- DBUG_ASSERT(sqlstate);
- /* SQLSTATE class "00": illegal, rejected in the parser. */
- DBUG_ASSERT((sqlstate[0] != '0') || (sqlstate[1] != '0'));
-
- if ((sqlstate[0] == '0') && (sqlstate[1] == '1'))
- {
- /* SQLSTATE class "01": warning. */
- assign_defaults(cond, set_defaults,
- Sql_condition::WARN_LEVEL_WARN, ER_SIGNAL_WARN);
- }
- else if ((sqlstate[0] == '0') && (sqlstate[1] == '2'))
- {
- /* SQLSTATE class "02": not found. */
- assign_defaults(cond, set_defaults,
- Sql_condition::WARN_LEVEL_ERROR, ER_SIGNAL_NOT_FOUND);
- }
- else
- {
- /* other SQLSTATE classes : error. */
- assign_defaults(cond, set_defaults,
- Sql_condition::WARN_LEVEL_ERROR, ER_SIGNAL_EXCEPTION);
- }
-}
static bool assign_fixed_string(MEM_ROOT *mem_root,
CHARSET_INFO *dst_cs,
@@ -267,7 +210,7 @@ int Sql_cmd_common_signal::eval_signal_informations(THD *thd, Sql_condition *con
int result= 1;
enum enum_diag_condition_item_name item_enum;
String *member;
- const LEX_STRING *name;
+ const LEX_CSTRING *name;
DBUG_ENTER("Sql_cmd_common_signal::eval_signal_informations");
@@ -275,16 +218,9 @@ int Sql_cmd_common_signal::eval_signal_informations(THD *thd, Sql_condition *con
i <= LAST_DIAG_SET_PROPERTY;
i++)
{
- set= m_set_signal_information.m_item[i];
- if (set)
- {
- if (! set->fixed)
- {
- if (set->fix_fields(thd, & set))
- goto end;
- m_set_signal_information.m_item[i]= set;
- }
- }
+ if ((set= m_set_signal_information.m_item[i]) &&
+ set->fix_fields_if_needed(thd, &m_set_signal_information.m_item[i]))
+ goto end;
}
/*
@@ -319,12 +255,13 @@ int Sql_cmd_common_signal::eval_signal_informations(THD *thd, Sql_condition *con
}
/*
Enforce that SET MESSAGE_TEXT = <value> evaluates the value
- as VARCHAR(128) CHARACTER SET UTF8.
+ as VARCHAR(MYSQL_ERRMSG_SIZE) CHARACTER SET UTF8.
*/
bool truncated;
String utf8_text;
str= set->val_str(& str_value);
- truncated= assign_fixed_string(thd->mem_root, & my_charset_utf8_bin, 128,
+ truncated= assign_fixed_string(thd->mem_root, & my_charset_utf8_bin,
+ MYSQL_ERRMSG_SIZE,
& utf8_text, str);
if (truncated)
{
@@ -375,7 +312,7 @@ int Sql_cmd_common_signal::eval_signal_informations(THD *thd, Sql_condition *con
The various item->val_xxx() methods don't return an error code,
but flag thd in case of failure.
*/
- if (! thd->is_error())
+ if (likely(!thd->is_error()))
result= 0;
end:
@@ -402,23 +339,19 @@ bool Sql_cmd_common_signal::raise_condition(THD *thd, Sql_condition *cond)
DBUG_ASSERT(thd->lex->query_tables == NULL);
- eval_defaults(thd, cond);
+ cond->assign_defaults(thd, m_cond);
if (eval_signal_informations(thd, cond))
DBUG_RETURN(result);
- /* SIGNAL should not signal WARN_LEVEL_NOTE */
- DBUG_ASSERT((cond->m_level == Sql_condition::WARN_LEVEL_WARN) ||
- (cond->m_level == Sql_condition::WARN_LEVEL_ERROR));
+ /* SIGNAL should not signal WARN_LEVEL_NOTE, but RESIGNAL can */
+ DBUG_ASSERT(cond->m_level == Sql_condition::WARN_LEVEL_ERROR ||
+ cond->m_level != Sql_condition::WARN_LEVEL_NOTE ||
+ sql_command_code() == SQLCOM_RESIGNAL);
- Sql_condition *raised= NULL;
- raised= thd->raise_condition(cond->get_sql_errno(),
- cond->get_sqlstate(),
- cond->get_level(),
- cond->get_message_text());
- if (raised)
- raised->copy_opt_attributes(cond);
+ (void) thd->raise_condition(cond);
- if (cond->m_level == Sql_condition::WARN_LEVEL_WARN)
+ if (cond->m_level == Sql_condition::WARN_LEVEL_WARN ||
+ cond->m_level == Sql_condition::WARN_LEVEL_NOTE)
{
my_ok(thd);
result= FALSE;
@@ -430,7 +363,8 @@ bool Sql_cmd_common_signal::raise_condition(THD *thd, Sql_condition *cond)
bool Sql_cmd_signal::execute(THD *thd)
{
bool result= TRUE;
- Sql_condition cond(thd->mem_root);
+ DBUG_ASSERT(m_cond);
+ Sql_condition cond(thd->mem_root, m_cond->get_user_condition_identity());
DBUG_ENTER("Sql_cmd_signal::execute");
@@ -484,11 +418,7 @@ bool Sql_cmd_resignal::execute(THD *thd)
DBUG_RETURN(result);
}
- Sql_condition signaled_err(thd->mem_root);
- signaled_err.set(signaled->sql_errno,
- signaled->sql_state,
- signaled->level,
- signaled->message);
+ Sql_condition signaled_err(thd->mem_root, *signaled, signaled->message);
if (m_cond)
{
@@ -500,13 +430,19 @@ bool Sql_cmd_resignal::execute(THD *thd)
/* Check if the old condition still exists. */
if (da->has_sql_condition(signaled->message, strlen(signaled->message)))
{
- /* Make room for the new RESIGNAL condition. */
- da->reserve_space(thd, 1);
+ /*
+ Make room for the new RESIGNAL condition and one for the stack trace
+ note.
+ */
+ da->reserve_space(thd, 2);
}
else
{
- /* Make room for old condition + the new RESIGNAL condition. */
- da->reserve_space(thd, 2);
+ /*
+ Make room for old condition + the new RESIGNAL condition + the stack
+ trace note.
+ */
+ da->reserve_space(thd, 3);
da->push_warning(thd, &signaled_err);
}
diff --git a/sql/sql_signal.h b/sql/sql_signal.h
index b73233ef16e..bf42cdb5f07 100644
--- a/sql/sql_signal.h
+++ b/sql/sql_signal.h
@@ -40,27 +40,6 @@ protected:
{}
/**
- Assign the condition items 'MYSQL_ERRNO', 'level' and 'MESSAGE_TEXT'
- default values of a condition.
- @param cond the condition to update.
- @param set_level_code true if 'level' and 'MYSQL_ERRNO' needs to be overwritten
- @param level the level to assign
- @param sqlcode the sql code to assign
- */
- static void assign_defaults(Sql_condition *cond,
- bool set_level_code,
- Sql_condition::enum_warning_level level,
- int sqlcode);
-
- /**
- Evaluate the condition items 'SQLSTATE', 'MYSQL_ERRNO', 'level' and 'MESSAGE_TEXT'
- default values for this statement.
- @param thd the current thread.
- @param cond the condition to update.
- */
- void eval_defaults(THD *thd, Sql_condition *cond);
-
- /**
Evaluate each signal condition items for this statement.
@param thd the current thread.
@param cond the condition to update.
diff --git a/sql/sql_sort.h b/sql/sql_sort.h
index 43e425ec107..93bcaf45a2c 100644
--- a/sql/sql_sort.h
+++ b/sql/sql_sort.h
@@ -17,7 +17,7 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
#include "my_base.h" /* ha_rows */
-#include "my_sys.h" /* qsort2_cmp */
+#include <my_sys.h> /* qsort2_cmp */
#include "queues.h"
typedef struct st_buffpek BUFFPEK;
@@ -100,12 +100,12 @@ public:
int merge_many_buff(Sort_param *param, uchar *sort_buffer,
BUFFPEK *buffpek,
uint *maxbuffer, IO_CACHE *t_file);
-uint read_to_buffer(IO_CACHE *fromfile,BUFFPEK *buffpek,
- uint sort_length);
-int merge_buffers(Sort_param *param,IO_CACHE *from_file,
- IO_CACHE *to_file, uchar *sort_buffer,
- BUFFPEK *lastbuff,BUFFPEK *Fb,
- BUFFPEK *Tb,int flag);
+ulong read_to_buffer(IO_CACHE *fromfile,BUFFPEK *buffpek,
+ uint sort_length);
+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);
diff --git a/sql/sql_state.c b/sql/sql_state.c
index dcffe49a783..66c63dca2d6 100644
--- a/sql/sql_state.c
+++ b/sql/sql_state.c
@@ -16,7 +16,7 @@
/* Functions to map mysqld errno to sql_state */
-#include <my_global.h>
+#include "mariadb.h"
#include <mysqld_error.h>
#include <my_base.h>
diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc
index d22921ba1e2..df4d67d3d11 100644
--- a/sql/sql_statistics.cc
+++ b/sql/sql_statistics.cc
@@ -1,4 +1,5 @@
/* Copyright (C) 2009 MySQL AB
+ Copyright (c) 2019, 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
@@ -23,7 +24,7 @@
@{
*/
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_base.h"
#include "key.h"
#include "sql_statistics.h"
@@ -66,16 +67,13 @@ static const uint STATISTICS_TABLES= 3;
The names of the statistical tables in this array must correspond the
definitions of the tables in the file ../scripts/mysql_system_tables.sql
*/
-static const LEX_STRING stat_table_name[STATISTICS_TABLES]=
+static const LEX_CSTRING stat_table_name[STATISTICS_TABLES]=
{
- { C_STRING_WITH_LEN("table_stats") },
- { C_STRING_WITH_LEN("column_stats") },
- { C_STRING_WITH_LEN("index_stats") }
+ { STRING_WITH_LEN("table_stats") },
+ { STRING_WITH_LEN("column_stats") },
+ { STRING_WITH_LEN("index_stats") }
};
-/* Name of database to which the statistical tables belong */
-static const LEX_STRING stat_tables_db_name= { C_STRING_WITH_LEN("mysql") };
-
/**
@details
@@ -94,10 +92,9 @@ inline void init_table_list_for_stat_tables(TABLE_LIST *tables, bool for_write)
for (i= 0; i < STATISTICS_TABLES; i++)
{
- tables[i].db= stat_tables_db_name.str;
- tables[i].db_length= stat_tables_db_name.length;
- tables[i].alias= tables[i].table_name= stat_table_name[i].str;
- tables[i].table_name_length= stat_table_name[i].length;
+ tables[i].db= MYSQL_SCHEMA_NAME;
+ tables[i].table_name= stat_table_name[i];
+ tables[i].alias= stat_table_name[i];
tables[i].lock_type= for_write ? TL_WRITE : TL_READ;
if (i < STATISTICS_TABLES - 1)
tables[i].next_global= tables[i].next_local=
@@ -116,17 +113,16 @@ inline void init_table_list_for_stat_tables(TABLE_LIST *tables, bool for_write)
otherwise it is set to TL_WRITE.
*/
-static
-inline void init_table_list_for_single_stat_table(TABLE_LIST *tbl,
- const LEX_STRING *stat_tab_name,
- bool for_write)
+static inline
+void init_table_list_for_single_stat_table(TABLE_LIST *tbl,
+ const LEX_CSTRING *stat_tab_name,
+ bool for_write)
{
memset((char *) tbl, 0, sizeof(TABLE_LIST));
- tbl->db= stat_tables_db_name.str;
- tbl->db_length= stat_tables_db_name.length;
- tbl->alias= tbl->table_name= stat_tab_name->str;
- tbl->table_name_length= stat_tab_name->length;
+ tbl->db= MYSQL_SCHEMA_NAME;
+ tbl->table_name= *stat_tab_name;
+ tbl->alias= *stat_tab_name;
tbl->lock_type= for_write ? TL_WRITE : TL_READ;
}
@@ -137,18 +133,18 @@ static const
TABLE_FIELD_TYPE table_stat_fields[TABLE_STAT_N_FIELDS] =
{
{
- { C_STRING_WITH_LEN("db_name") },
- { C_STRING_WITH_LEN("varchar(64)") },
- { C_STRING_WITH_LEN("utf8") }
+ { STRING_WITH_LEN("db_name") },
+ { STRING_WITH_LEN("varchar(64)") },
+ { STRING_WITH_LEN("utf8") }
},
{
- { C_STRING_WITH_LEN("table_name") },
- { C_STRING_WITH_LEN("varchar(64)") },
- { C_STRING_WITH_LEN("utf8") }
+ { STRING_WITH_LEN("table_name") },
+ { STRING_WITH_LEN("varchar(64)") },
+ { STRING_WITH_LEN("utf8") }
},
{
- { C_STRING_WITH_LEN("cardinality") },
- { C_STRING_WITH_LEN("bigint(21)") },
+ { STRING_WITH_LEN("cardinality") },
+ { STRING_WITH_LEN("bigint(21)") },
{ NULL, 0 }
},
};
@@ -160,58 +156,58 @@ static const
TABLE_FIELD_TYPE column_stat_fields[COLUMN_STAT_N_FIELDS] =
{
{
- { C_STRING_WITH_LEN("db_name") },
- { C_STRING_WITH_LEN("varchar(64)") },
- { C_STRING_WITH_LEN("utf8") }
+ { STRING_WITH_LEN("db_name") },
+ { STRING_WITH_LEN("varchar(64)") },
+ { STRING_WITH_LEN("utf8") }
},
{
- { C_STRING_WITH_LEN("table_name") },
- { C_STRING_WITH_LEN("varchar(64)") },
- { C_STRING_WITH_LEN("utf8") }
+ { STRING_WITH_LEN("table_name") },
+ { STRING_WITH_LEN("varchar(64)") },
+ { STRING_WITH_LEN("utf8") }
},
{
- { C_STRING_WITH_LEN("column_name") },
- { C_STRING_WITH_LEN("varchar(64)") },
- { C_STRING_WITH_LEN("utf8") }
+ { STRING_WITH_LEN("column_name") },
+ { STRING_WITH_LEN("varchar(64)") },
+ { STRING_WITH_LEN("utf8") }
},
{
- { C_STRING_WITH_LEN("min_value") },
- { C_STRING_WITH_LEN("varbinary(255)") },
+ { STRING_WITH_LEN("min_value") },
+ { STRING_WITH_LEN("varbinary(255)") },
{ NULL, 0 }
},
{
- { C_STRING_WITH_LEN("max_value") },
- { C_STRING_WITH_LEN("varbinary(255)") },
+ { STRING_WITH_LEN("max_value") },
+ { STRING_WITH_LEN("varbinary(255)") },
{ NULL, 0 }
},
{
- { C_STRING_WITH_LEN("nulls_ratio") },
- { C_STRING_WITH_LEN("decimal(12,4)") },
+ { STRING_WITH_LEN("nulls_ratio") },
+ { STRING_WITH_LEN("decimal(12,4)") },
{ NULL, 0 }
},
{
- { C_STRING_WITH_LEN("avg_length") },
- { C_STRING_WITH_LEN("decimal(12,4)") },
+ { STRING_WITH_LEN("avg_length") },
+ { STRING_WITH_LEN("decimal(12,4)") },
{ NULL, 0 }
},
{
- { C_STRING_WITH_LEN("avg_frequency") },
- { C_STRING_WITH_LEN("decimal(12,4)") },
+ { STRING_WITH_LEN("avg_frequency") },
+ { STRING_WITH_LEN("decimal(12,4)") },
{ NULL, 0 }
},
{
- { C_STRING_WITH_LEN("hist_size") },
- { C_STRING_WITH_LEN("tinyint(3)") },
+ { STRING_WITH_LEN("hist_size") },
+ { STRING_WITH_LEN("tinyint(3)") },
{ NULL, 0 }
},
{
- { C_STRING_WITH_LEN("hist_type") },
- { C_STRING_WITH_LEN("enum('SINGLE_PREC_HB','DOUBLE_PREC_HB')") },
- { C_STRING_WITH_LEN("utf8") }
+ { STRING_WITH_LEN("hist_type") },
+ { STRING_WITH_LEN("enum('SINGLE_PREC_HB','DOUBLE_PREC_HB')") },
+ { STRING_WITH_LEN("utf8") }
},
{
- { C_STRING_WITH_LEN("histogram") },
- { C_STRING_WITH_LEN("varbinary(255)") },
+ { STRING_WITH_LEN("histogram") },
+ { STRING_WITH_LEN("varbinary(255)") },
{ NULL, 0 }
}
};
@@ -223,28 +219,28 @@ static const
TABLE_FIELD_TYPE index_stat_fields[INDEX_STAT_N_FIELDS] =
{
{
- { C_STRING_WITH_LEN("db_name") },
- { C_STRING_WITH_LEN("varchar(64)") },
- { C_STRING_WITH_LEN("utf8") }
+ { STRING_WITH_LEN("db_name") },
+ { STRING_WITH_LEN("varchar(64)") },
+ { STRING_WITH_LEN("utf8") }
},
{
- { C_STRING_WITH_LEN("table_name") },
- { C_STRING_WITH_LEN("varchar(64)") },
- { C_STRING_WITH_LEN("utf8") }
+ { STRING_WITH_LEN("table_name") },
+ { STRING_WITH_LEN("varchar(64)") },
+ { STRING_WITH_LEN("utf8") }
},
{
- { C_STRING_WITH_LEN("index") },
- { C_STRING_WITH_LEN("varchar(64)") },
- { C_STRING_WITH_LEN("utf8") }
+ { STRING_WITH_LEN("index") },
+ { STRING_WITH_LEN("varchar(64)") },
+ { STRING_WITH_LEN("utf8") }
},
{
- { C_STRING_WITH_LEN("prefix_arity") },
- { C_STRING_WITH_LEN("int(11)") },
+ { STRING_WITH_LEN("prefix_arity") },
+ { STRING_WITH_LEN("int(11)") },
{ NULL, 0 }
},
{
- { C_STRING_WITH_LEN("avg_frequency") },
- { C_STRING_WITH_LEN("decimal(12,4)") },
+ { STRING_WITH_LEN("avg_frequency") },
+ { STRING_WITH_LEN("decimal(12,4)") },
{ NULL, 0 }
}
};
@@ -295,7 +291,7 @@ static int open_stat_tables(THD *thd, TABLE_LIST *tables,
*/
static
inline int open_single_stat_table(THD *thd, TABLE_LIST *table,
- const LEX_STRING *stat_tab_name,
+ const LEX_CSTRING *stat_tab_name,
Open_tables_backup *backup,
bool for_write)
{
@@ -472,9 +468,9 @@ protected:
/* Table for which statistical data is read / updated */
TABLE *table;
- TABLE_SHARE *table_share; /* Table share for 'table */
- LEX_STRING *db_name; /* Name of the database containing 'table' */
- LEX_STRING *table_name; /* Name of the table 'table' */
+ TABLE_SHARE *table_share; /* Table share for 'table */
+ const LEX_CSTRING *db_name; /* Name of the database containing 'table' */
+ const LEX_CSTRING *table_name; /* Name of the table 'table' */
void store_record_for_update()
{
@@ -529,12 +525,10 @@ public:
by the database name 'db' and the table name 'tab'.
*/
- Stat_table(TABLE *stat, LEX_STRING *db, LEX_STRING *tab)
- :stat_table(stat), table_share(NULL)
+ Stat_table(TABLE *stat, const LEX_CSTRING *db, const LEX_CSTRING *tab)
+ :stat_table(stat), table_share(NULL),db_name(db), table_name(tab)
{
common_init_stat_table();
- db_name= db;
- table_name= tab;
}
@@ -554,7 +548,7 @@ public:
The method is called by the update_table_name_key_parts function.
*/
- virtual void change_full_table_name(LEX_STRING *db, LEX_STRING *tab)= 0;
+ virtual void change_full_table_name(const LEX_CSTRING *db, const LEX_CSTRING *tab)= 0;
/**
@@ -667,16 +661,22 @@ public:
{
if (find_stat())
{
+ bool res;
store_record_for_update();
store_stat_fields();
- return update_record();
+ res= update_record();
+ DBUG_ASSERT(res == 0);
+ return res;
}
else
{
int err;
store_stat_fields();
if ((err= stat_file->ha_write_row(record[0])))
+ {
+ DBUG_ASSERT(0);
return TRUE;
+ }
/* Make change permanent and avoid 'table is marked as crashed' errors */
stat_file->extra(HA_EXTRA_FLUSH);
}
@@ -704,7 +704,7 @@ public:
to store the new names in the record buffer used for updates.
*/
- bool update_table_name_key_parts(LEX_STRING *db, LEX_STRING *tab)
+ bool update_table_name_key_parts(const LEX_CSTRING *db, const LEX_CSTRING *tab)
{
store_record_for_update();
change_full_table_name(db, tab);
@@ -766,7 +766,7 @@ private:
table_name_field= stat_table->field[TABLE_STAT_TABLE_NAME];
}
- void change_full_table_name(LEX_STRING *db, LEX_STRING *tab)
+ void change_full_table_name(const LEX_CSTRING *db, const LEX_CSTRING *tab)
{
db_name_field->store(db->str, db->length, system_charset_info);
table_name_field->store(tab->str, tab->length, system_charset_info);
@@ -796,7 +796,7 @@ public:
from the database 'db'.
*/
- Table_stat(TABLE *stat, LEX_STRING *db, LEX_STRING *tab)
+ Table_stat(TABLE *stat, const LEX_CSTRING *db, const LEX_CSTRING *tab)
:Stat_table(stat, db, tab)
{
common_init_table_stat();
@@ -910,7 +910,7 @@ private:
column_name_field= stat_table->field[COLUMN_STAT_COLUMN_NAME];
}
- void change_full_table_name(LEX_STRING *db, LEX_STRING *tab)
+ void change_full_table_name(const LEX_CSTRING *db, const LEX_CSTRING *tab)
{
db_name_field->store(db->str, db->length, system_charset_info);
table_name_field->store(tab->str, tab->length, system_charset_info);
@@ -940,7 +940,7 @@ public:
from the database 'db'.
*/
- Column_stat(TABLE *stat, LEX_STRING *db, LEX_STRING *tab)
+ Column_stat(TABLE *stat, const LEX_CSTRING *db, const LEX_CSTRING *tab)
:Stat_table(stat, db, tab)
{
common_init_column_stat_table();
@@ -984,8 +984,7 @@ public:
void set_key_fields(Field *col)
{
set_full_table_name();
- const char *column_name= col->field_name;
- column_name_field->store(column_name, strlen(column_name),
+ column_name_field->store(col->field_name.str, col->field_name.length,
system_charset_info);
table_field= col;
}
@@ -1047,7 +1046,6 @@ public:
my_bitmap_map *old_map;
old_map= dbug_tmp_use_all_columns(stat_table, stat_table->read_set);
-
for (uint i= COLUMN_STAT_MIN_VALUE; i <= COLUMN_STAT_HISTOGRAM; i++)
{
Field *stat_field= stat_table->field[i];
@@ -1063,7 +1061,7 @@ public:
else
{
table_field->collected_stats->min_value->val_str(&val);
- uint32 length= Well_formed_prefix(val.charset(), val.ptr(),
+ 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);
}
@@ -1074,7 +1072,7 @@ public:
else
{
table_field->collected_stats->max_value->val_str(&val);
- uint32 length= Well_formed_prefix(val.charset(), val.ptr(),
+ 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);
}
@@ -1254,7 +1252,7 @@ private:
prefix_arity_field= stat_table->field[INDEX_STAT_PREFIX_ARITY];
}
- void change_full_table_name(LEX_STRING *db, LEX_STRING *tab)
+ void change_full_table_name(const LEX_CSTRING *db, const LEX_CSTRING *tab)
{
db_name_field->store(db->str, db->length, system_charset_info);
table_name_field->store(tab->str, tab->length, system_charset_info);
@@ -1286,7 +1284,7 @@ public:
from the database 'db'.
*/
- Index_stat(TABLE *stat, LEX_STRING *db, LEX_STRING *tab)
+ Index_stat(TABLE *stat, const LEX_CSTRING *db, const LEX_CSTRING *tab)
:Stat_table(stat, db, tab)
{
common_init_index_stat_table();
@@ -1329,8 +1327,8 @@ public:
void set_index_prefix_key_fields(KEY *index_info)
{
set_full_table_name();
- char *index_name= index_info->name;
- index_name_field->store(index_name, strlen(index_name),
+ const char *index_name= index_info->name.str;
+ index_name_field->store(index_name, index_info->name.length,
system_charset_info);
table_key_info= index_info;
}
@@ -1973,7 +1971,7 @@ void create_min_max_statistical_fields_for_table(TABLE *table)
for (uint i=0; i < 2; i++, record+= rec_buff_length)
{
- for (Field **field_ptr= table->field; *field_ptr; field_ptr++)
+ for (Field **field_ptr= table->field; *field_ptr; field_ptr++)
{
Field *fld;
Field *table_field= *field_ptr;
@@ -2042,7 +2040,7 @@ void create_min_max_statistical_fields_for_table_share(THD *thd,
for (uint i=0; i < 2; i++, record+= rec_buff_length)
{
- for (Field **field_ptr= table_share->field; *field_ptr; field_ptr++)
+ for (Field **field_ptr= table_share->field; *field_ptr; field_ptr++)
{
Field *fld;
Field *table_field= *field_ptr;
@@ -2359,7 +2357,7 @@ bool Column_statistics_collected::add(ha_rows rowno)
set_not_null(COLUMN_STAT_MIN_VALUE);
if (max_value && column->update_max(max_value, rowno == nulls))
set_not_null(COLUMN_STAT_MAX_VALUE);
- if (count_distinct)
+ if (count_distinct)
err= count_distinct->add();
}
return err;
@@ -2617,11 +2615,7 @@ int collect_statistics_for_table(THD *thd, TABLE *table)
break;
if (rc)
- {
- if (rc == HA_ERR_RECORD_DELETED)
- continue;
break;
- }
for (field_ptr= table->field; *field_ptr; field_ptr++)
{
@@ -3135,7 +3129,7 @@ int read_statistics_for_tables(THD *thd, TABLE_LIST *tables)
}
statistics_for_tables_is_needed= true;
}
- else if (is_stat_table(tl->db, tl->alias))
+ else if (is_stat_table(&tl->db, &tl->alias))
found_stat_table= true;
}
}
@@ -3202,7 +3196,7 @@ int read_statistics_for_tables(THD *thd, TABLE_LIST *tables)
The function is called when executing the statement DROP TABLE 'tab'.
*/
-int delete_statistics_for_table(THD *thd, LEX_STRING *db, LEX_STRING *tab)
+int delete_statistics_for_table(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *tab)
{
int err;
enum_binlog_format save_binlog_format;
@@ -3440,8 +3434,8 @@ int delete_statistics_for_index(THD *thd, TABLE *tab, KEY *key_info,
The function is called when executing any statement that renames a table
*/
-int rename_table_in_stat_tables(THD *thd, LEX_STRING *db, LEX_STRING *tab,
- LEX_STRING *new_db, LEX_STRING *new_tab)
+int rename_table_in_stat_tables(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *tab,
+ const LEX_CSTRING *new_db, const LEX_CSTRING *new_tab)
{
int err;
enum_binlog_format save_binlog_format;
@@ -3449,7 +3443,6 @@ int rename_table_in_stat_tables(THD *thd, LEX_STRING *db, LEX_STRING *tab,
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))
@@ -3923,15 +3916,15 @@ double Histogram::point_selectivity(double pos, double avg_sel)
/*
Check whether the table is one of the persistent statistical tables.
*/
-bool is_stat_table(const char *db, const char *table)
+bool is_stat_table(const LEX_CSTRING *db, LEX_CSTRING *table)
{
- DBUG_ASSERT(db && table);
+ DBUG_ASSERT(db->str && table->str);
- if (!my_strcasecmp(table_alias_charset, db, stat_tables_db_name.str))
+ if (!my_strcasecmp(table_alias_charset, db->str, MYSQL_SCHEMA_NAME.str))
{
for (uint i= 0; i < STATISTICS_TABLES; i ++)
{
- if (!my_strcasecmp(table_alias_charset, table, stat_table_name[i].str))
+ if (!my_strcasecmp(table_alias_charset, table->str, stat_table_name[i].str))
return true;
}
}
diff --git a/sql/sql_statistics.h b/sql/sql_statistics.h
index fa3e8d6b894..198052d2237 100644
--- a/sql/sql_statistics.h
+++ b/sql/sql_statistics.h
@@ -94,12 +94,12 @@ int collect_statistics_for_table(THD *thd, TABLE *table);
void delete_stat_values_for_table_share(TABLE_SHARE *table_share);
int alloc_statistics_for_table(THD *thd, TABLE *table);
int update_statistics_for_table(THD *thd, TABLE *table);
-int delete_statistics_for_table(THD *thd, LEX_STRING *db, LEX_STRING *tab);
+int delete_statistics_for_table(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *tab);
int delete_statistics_for_column(THD *thd, TABLE *tab, Field *col);
int delete_statistics_for_index(THD *thd, TABLE *tab, KEY *key_info,
bool ext_prefixes_only);
-int rename_table_in_stat_tables(THD *thd, LEX_STRING *db, LEX_STRING *tab,
- LEX_STRING *new_db, LEX_STRING *new_tab);
+int rename_table_in_stat_tables(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *tab,
+ const LEX_CSTRING *new_db, const LEX_CSTRING *new_tab);
int rename_column_in_stat_tables(THD *thd, TABLE *tab, Field *col,
const char *new_name);
void set_statistics_for_table(THD *thd, TABLE *table);
@@ -110,7 +110,7 @@ double get_column_range_cardinality(Field *field,
key_range *min_endp,
key_range *max_endp,
uint range_flag);
-bool is_stat_table(const char *db, const char *table);
+bool is_stat_table(const LEX_CSTRING *db, LEX_CSTRING *table);
bool is_eits_usable(Field* field);
class Histogram
@@ -256,18 +256,6 @@ public:
class Columns_statistics;
class Index_statistics;
-static inline
-int rename_table_in_stat_tables(THD *thd, const char *db, const char *tab,
- const char *new_db, const char *new_tab)
-{
- LEX_STRING od= { const_cast<char*>(db), strlen(db) };
- LEX_STRING ot= { const_cast<char*>(tab), strlen(tab) };
- LEX_STRING nd= { const_cast<char*>(new_db), strlen(new_db) };
- LEX_STRING nt= { const_cast<char*>(new_tab), strlen(new_tab) };
- return rename_table_in_stat_tables(thd, &od, &ot, &nd, &nt);
-}
-
-
/* Statistical data on a table */
class Table_statistics
diff --git a/sql/sql_string.cc b/sql/sql_string.cc
index 177d2a77d09..0cc653c29a9 100644
--- a/sql/sql_string.cc
+++ b/sql/sql_string.cc
@@ -20,8 +20,7 @@
#pragma implementation // gcc: Class implementation
#endif
-#include <my_global.h>
-#include <my_sys.h>
+#include "mariadb.h"
#include <m_string.h>
#include <m_ctype.h>
#include <mysql_com.h>
@@ -132,6 +131,61 @@ bool String::set_int(longlong num, bool unsigned_flag, CHARSET_INFO *cs)
return FALSE;
}
+
+// Convert a number into its HEX representation
+bool String::set_hex(ulonglong num)
+{
+ char *n_end;
+ if (alloc(65) || !(n_end= longlong2str(num, Ptr, 16)))
+ return true;
+ length((uint32) (n_end - Ptr));
+ set_charset(&my_charset_latin1);
+ return false;
+}
+
+
+/**
+ Append a hex representation of the byte "value" into "to".
+ Note:
+ "to" is incremented for the caller by two bytes. It's passed by reference!
+ So it resembles a macros, hence capital letters in the name.
+*/
+static inline void APPEND_HEX(char *&to, uchar value)
+{
+ *to++= _dig_vec_upper[((uchar) value) >> 4];
+ *to++= _dig_vec_upper[((uchar) value) & 0x0F];
+}
+
+
+void String::qs_append_hex(const char *str, uint32 len)
+{
+ const char *str_end= str + len;
+ for (char *to= Ptr + str_length ; str < str_end; str++)
+ APPEND_HEX(to, (uchar) *str);
+ str_length+= len * 2;
+}
+
+
+// Convert a string to its HEX representation
+bool String::set_hex(const char *str, uint32 len)
+{
+ /*
+ Safety: cut the source string if "len" is too large.
+ Note, alloc() can allocate some more space than requested, due to:
+ - ALIGN_SIZE
+ - one extra byte for a null terminator
+ So cut the source string to 0x7FFFFFF0 rather than 0x7FFFFFFE.
+ */
+ set_if_smaller(len, 0x7FFFFFF0);
+ if (alloc(len * 2))
+ return true;
+ length(0);
+ qs_append_hex(str, len);
+ set_charset(&my_charset_latin1);
+ return false;
+}
+
+
bool String::set_real(double num,uint decimals, CHARSET_INFO *cs)
{
char buff[FLOATING_POINT_BUFFER];
@@ -244,7 +298,7 @@ bool String::copy_or_move(const char *str,size_t arg_length, CHARSET_INFO *cs)
character_set_results is NULL.
*/
-bool String::needs_conversion(uint32 arg_length,
+bool String::needs_conversion(size_t arg_length,
CHARSET_INFO *from_cs,
CHARSET_INFO *to_cs,
uint32 *offset)
@@ -255,7 +309,7 @@ bool String::needs_conversion(uint32 arg_length,
(to_cs == from_cs) ||
my_charset_same(from_cs, to_cs) ||
((from_cs == &my_charset_bin) &&
- (!(*offset=(arg_length % to_cs->mbminlen)))))
+ (!(*offset=(uint32)(arg_length % to_cs->mbminlen)))))
return FALSE;
return TRUE;
}
@@ -273,7 +327,7 @@ bool String::needs_conversion(uint32 arg_length,
@return conversion needed
*/
-bool String::needs_conversion_on_storage(uint32 arg_length,
+bool String::needs_conversion_on_storage(size_t arg_length,
CHARSET_INFO *cs_from,
CHARSET_INFO *cs_to)
{
@@ -322,14 +376,14 @@ bool String::needs_conversion_on_storage(uint32 arg_length,
1 error
*/
-bool String::copy_aligned(const char *str,uint32 arg_length, uint32 offset,
+bool String::copy_aligned(const char *str, size_t arg_length, size_t offset,
CHARSET_INFO *cs)
{
/* How many bytes are in incomplete character */
offset= cs->mbminlen - offset; /* How many zeros we should prepend */
DBUG_ASSERT(offset && offset != cs->mbminlen);
- uint32 aligned_length= arg_length + offset;
+ size_t aligned_length= arg_length + offset;
if (alloc(aligned_length))
return TRUE;
@@ -342,17 +396,17 @@ bool String::copy_aligned(const char *str,uint32 arg_length, uint32 offset,
memcpy(Ptr + offset, str, arg_length);
Ptr[aligned_length]=0;
/* str_length is always >= 0 as arg_length is != 0 */
- str_length= aligned_length;
+ str_length= (uint32)aligned_length;
str_charset= cs;
return FALSE;
}
-bool String::set_or_copy_aligned(const char *str,uint32 arg_length,
+bool String::set_or_copy_aligned(const char *str, size_t arg_length,
CHARSET_INFO *cs)
{
/* How many bytes are in incomplete character */
- uint32 offset= (arg_length % cs->mbminlen);
+ size_t offset= (arg_length % cs->mbminlen);
if (!offset)
{
@@ -374,7 +428,7 @@ bool String::set_or_copy_aligned(const char *str,uint32 arg_length,
*/
-bool String::copy(const char *str, uint32 arg_length,
+bool String::copy(const char *str, size_t arg_length,
CHARSET_INFO *from_cs, CHARSET_INFO *to_cs, uint *errors)
{
uint32 offset;
@@ -391,7 +445,7 @@ bool String::copy(const char *str, uint32 arg_length,
*errors= 0;
return copy_aligned(str, arg_length, offset, to_cs);
}
- uint32 new_length= to_cs->mbmaxlen*arg_length;
+ size_t new_length= to_cs->mbmaxlen*arg_length;
if (alloc(new_length))
return TRUE;
str_length=copy_and_convert((char*) Ptr, new_length, to_cs,
@@ -420,7 +474,7 @@ bool String::copy(const char *str, uint32 arg_length,
*/
-bool String::set_ascii(const char *str, uint32 arg_length)
+bool String::set_ascii(const char *str, size_t arg_length)
{
if (str_charset->mbminlen == 1)
{
@@ -428,7 +482,7 @@ bool String::set_ascii(const char *str, uint32 arg_length)
return 0;
}
uint dummy_errors;
- return copy(str, arg_length, &my_charset_latin1, str_charset, &dummy_errors);
+ return copy(str, (uint32)arg_length, &my_charset_latin1, str_charset, &dummy_errors);
}
@@ -537,13 +591,13 @@ bool String::append_ulonglong(ulonglong val)
with character set recoding
*/
-bool String::append(const char *s,uint32 arg_length, CHARSET_INFO *cs)
+bool String::append(const char *s, size_t arg_length, CHARSET_INFO *cs)
{
uint32 offset;
- if (needs_conversion(arg_length, cs, str_charset, &offset))
+ if (needs_conversion((uint32)arg_length, cs, str_charset, &offset))
{
- uint32 add_length;
+ size_t add_length;
if ((cs == &my_charset_bin) && offset)
{
DBUG_ASSERT(str_charset->mbminlen > offset);
@@ -553,7 +607,7 @@ bool String::append(const char *s,uint32 arg_length, CHARSET_INFO *cs)
return TRUE;
bzero((char*) Ptr + str_length, offset);
memcpy(Ptr + str_length + offset, s, arg_length);
- str_length+= add_length;
+ str_length+= (uint32)add_length;
return FALSE;
}
@@ -561,15 +615,15 @@ bool String::append(const char *s,uint32 arg_length, CHARSET_INFO *cs)
uint dummy_errors;
if (realloc_with_extra_if_needed(str_length + add_length))
return TRUE;
- str_length+= copy_and_convert(Ptr+str_length, add_length, str_charset,
- s, arg_length, cs, &dummy_errors);
+ str_length+= copy_and_convert(Ptr+str_length, (uint32)add_length, str_charset,
+ s, (uint32)arg_length, cs, &dummy_errors);
}
else
{
if (realloc_with_extra_if_needed(str_length + arg_length))
return TRUE;
memcpy(Ptr + str_length, s, arg_length);
- str_length+= arg_length;
+ str_length+= (uint32)arg_length;
}
return FALSE;
}
@@ -580,7 +634,7 @@ bool String::append(IO_CACHE* file, uint32 arg_length)
return TRUE;
if (my_b_read(file, (uchar*) Ptr + str_length, arg_length))
{
- shrink(str_length);
+ shrink(str_length ? str_length : 1);
return TRUE;
}
str_length+=arg_length;
@@ -734,7 +788,7 @@ bool String::replace(uint32 offset,uint32 arg_length,
// added by Holyfoot for "geometry" needs
-int String::reserve(uint32 space_needed, uint32 grow_by)
+int String::reserve(size_t space_needed, size_t grow_by)
{
if (Alloced_length < str_length + space_needed)
{
@@ -744,10 +798,10 @@ int String::reserve(uint32 space_needed, uint32 grow_by)
return FALSE;
}
-void String::qs_append(const char *str, uint32 len)
+void String::qs_append(const char *str, size_t len)
{
memcpy(Ptr + str_length, str, len + 1);
- str_length += len;
+ str_length += (uint32)len;
}
void String::qs_append(double d)
@@ -1022,8 +1076,7 @@ my_copy_with_hex_escaping(CHARSET_INFO *cs,
break; /* purecov: inspected */
*dst++= '\\';
*dst++= 'x';
- *dst++= _dig_vec_upper[((unsigned char) *src) >> 4];
- *dst++= _dig_vec_upper[((unsigned char) *src) & 15];
+ APPEND_HEX(dst, (uchar) *src);
src++;
dstlen-= 4;
}
@@ -1068,10 +1121,9 @@ my_copy_with_hex_escaping(CHARSET_INFO *cs,
*/
uint
String_copier::well_formed_copy(CHARSET_INFO *to_cs,
- char *to, uint to_length,
+ char *to, size_t to_length,
CHARSET_INFO *from_cs,
- const char *from, uint from_length,
- uint nchars)
+ const char *from, size_t from_length, size_t nchars)
{
if ((to_cs == &my_charset_bin) ||
(from_cs == &my_charset_bin) ||
@@ -1094,7 +1146,7 @@ String_copier::well_formed_copy(CHARSET_INFO *to_cs,
Does not add the enclosing quotes, this is left up to caller.
*/
#define APPEND(X) if (append(X)) return 1; else break
-bool String::append_for_single_quote(const char *st, uint len)
+bool String::append_for_single_quote(const char *st, size_t len)
{
const char *end= st+len;
for (; st < end; st++)
@@ -1208,8 +1260,7 @@ uint convert_to_printable(char *to, size_t to_len,
break;
*t++= '\\';
*t++= 'x';
- *t++= _dig_vec_upper[((unsigned char) *f) >> 4];
- *t++= _dig_vec_upper[((unsigned char) *f) & 0x0F];
+ APPEND_HEX(t, *f);
}
if (t_end - t >= 3) // '...'
dots= t;
diff --git a/sql/sql_string.h b/sql/sql_string.h
index 63404587404..f56540c2975 100644
--- a/sql/sql_string.h
+++ b/sql/sql_string.h
@@ -25,7 +25,7 @@
#endif
#include "m_ctype.h" /* my_charset_bin */
-#include "my_sys.h" /* alloc_root, my_free, my_realloc */
+#include <my_sys.h> /* alloc_root, my_free, my_realloc */
#include "m_string.h" /* TRASH */
#include "sql_list.h"
@@ -36,12 +36,12 @@ typedef struct st_mem_root MEM_ROOT;
#include "pack.h"
int sortcmp(const String *a,const String *b, CHARSET_INFO *cs);
String *copy_if_not_alloced(String *a,String *b,uint32 arg_length);
-inline uint32 copy_and_convert(char *to, uint32 to_length,
+inline uint32 copy_and_convert(char *to, size_t to_length,
CHARSET_INFO *to_cs,
- const char *from, uint32 from_length,
+ const char *from, size_t from_length,
CHARSET_INFO *from_cs, uint *errors)
{
- return my_convert(to, to_length, to_cs, from, from_length, from_cs, errors);
+ return my_convert(to, (uint)to_length, to_cs, from, (uint)from_length, from_cs, errors);
}
@@ -79,7 +79,7 @@ public:
Well_formed_prefix(CHARSET_INFO *cs, const char *str, size_t length)
:Well_formed_prefix_status(cs, str, str + length, length), m_str(str)
{ }
- Well_formed_prefix(CHARSET_INFO *cs, LEX_STRING str, size_t nchars)
+ Well_formed_prefix(CHARSET_INFO *cs, LEX_CSTRING str, size_t nchars)
:Well_formed_prefix_status(cs, str.str, str.str + str.length, nchars),
m_str(str.str)
{ }
@@ -102,9 +102,8 @@ public:
Convert a string between character sets.
"dstcs" and "srccs" cannot be &my_charset_bin.
*/
- size_t convert_fix(CHARSET_INFO *dstcs, char *dst, uint dst_length,
- CHARSET_INFO *srccs, const char *src, uint src_length,
- uint nchars)
+ size_t convert_fix(CHARSET_INFO *dstcs, char *dst, size_t dst_length,
+ CHARSET_INFO *srccs, const char *src, size_t src_length, size_t nchars)
{
return my_convert_fix(dstcs, dst, dst_length,
srccs, src, src_length, nchars, this, this);
@@ -112,13 +111,11 @@ public:
/*
Copy a string. Fix bad bytes/characters to '?'.
*/
- uint well_formed_copy(CHARSET_INFO *to_cs, char *to, uint to_length,
- CHARSET_INFO *from_cs, const char *from,
- uint from_length, uint nchars);
+ uint well_formed_copy(CHARSET_INFO *to_cs, char *to, size_t to_length,
+ CHARSET_INFO *from_cs, const char *from, size_t from_length, size_t nchars);
// Same as above, but without the "nchars" limit.
- uint well_formed_copy(CHARSET_INFO *to_cs, char *to, uint to_length,
- CHARSET_INFO *from_cs, const char *from,
- uint from_length)
+ uint well_formed_copy(CHARSET_INFO *to_cs, char *to, size_t to_length,
+ CHARSET_INFO *from_cs, const char *from, size_t from_length)
{
return well_formed_copy(to_cs, to, to_length,
from_cs, from, from_length,
@@ -147,7 +144,7 @@ public:
alloced= thread_specific= 0;
str_charset= &my_charset_bin;
}
- String(uint32 length_arg)
+ String(size_t length_arg)
{
alloced= thread_specific= 0;
Alloced_length= extra_alloc= 0; (void) real_alloc(length_arg);
@@ -165,15 +162,15 @@ public:
contructors need the size of memory for STR to be at least LEN+1 (to make
room for zero termination).
*/
- String(const char *str,uint32 len, CHARSET_INFO *cs)
+ String(const char *str,size_t len, CHARSET_INFO *cs)
{
- Ptr=(char*) str; str_length=len; Alloced_length= extra_alloc=0;
+ Ptr=(char*) str; str_length=(uint32)len; Alloced_length= extra_alloc=0;
alloced= thread_specific= 0;
str_charset=cs;
}
- String(char *str,uint32 len, CHARSET_INFO *cs)
+ String(char *str,size_t len, CHARSET_INFO *cs)
{
- Ptr=(char*) str; Alloced_length=str_length=len; extra_alloc= 0;
+ Ptr=(char*) str; Alloced_length=str_length=(uint32)len; extra_alloc= 0;
alloced= thread_specific= 0;
str_charset=cs;
}
@@ -198,9 +195,9 @@ public:
inline uint32 length() const { return str_length;}
inline uint32 alloced_length() const { return Alloced_length;}
inline uint32 extra_allocation() const { return extra_alloc;}
- inline char& operator [] (uint32 i) const { return Ptr[i]; }
- inline void length(uint32 len) { str_length=len ; }
- inline void extra_allocation(uint32 len) { extra_alloc= len; }
+ inline char& operator [] (size_t i) const { return Ptr[i]; }
+ inline void length(size_t len) { str_length=(uint32)len ; }
+ inline void extra_allocation(size_t len) { extra_alloc= (uint32)len; }
inline bool is_empty() const { return (str_length == 0); }
inline void mark_as_const() { Alloced_length= 0;}
inline const char *ptr() const { return Ptr; }
@@ -244,13 +241,13 @@ public:
return str_charset->cset->lengthsp(str_charset, Ptr, str_length);
}
- void set(String &str,uint32 offset,uint32 arg_length)
+ void set(String &str,size_t offset,size_t arg_length)
{
DBUG_ASSERT(&str != this);
free();
- Ptr=(char*) str.ptr()+offset; str_length=arg_length;
+ Ptr=(char*) str.ptr()+offset; str_length=(uint32)arg_length;
if (str.Alloced_length)
- Alloced_length=str.Alloced_length-offset;
+ Alloced_length=(uint32)(str.Alloced_length-offset);
str_charset=str.str_charset;
}
@@ -263,24 +260,24 @@ public:
@param cs Character set to use for interpreting string data.
@note The new buffer will not be null terminated.
*/
- inline void set(char *str,uint32 arg_length, CHARSET_INFO *cs)
+ inline void set(char *str,size_t arg_length, CHARSET_INFO *cs)
{
free();
- Ptr=(char*) str; str_length=Alloced_length=arg_length;
+ Ptr=(char*) str; str_length=Alloced_length=(uint32)arg_length;
str_charset=cs;
}
- inline void set(const char *str,uint32 arg_length, CHARSET_INFO *cs)
+ inline void set(const char *str,size_t arg_length, CHARSET_INFO *cs)
{
free();
- Ptr=(char*) str; str_length=arg_length;
+ Ptr=(char*) str; str_length=(uint32)arg_length;
str_charset=cs;
}
- bool set_ascii(const char *str, uint32 arg_length);
- inline void set_quick(char *str,uint32 arg_length, CHARSET_INFO *cs)
+ bool set_ascii(const char *str, size_t arg_length);
+ inline void set_quick(char *str,size_t arg_length, CHARSET_INFO *cs)
{
if (!alloced)
{
- Ptr=(char*) str; str_length=Alloced_length=arg_length;
+ Ptr=(char*) str; str_length=Alloced_length=(uint32)arg_length;
}
str_charset=cs;
}
@@ -293,14 +290,17 @@ public:
bool set(ulonglong num, CHARSET_INFO *cs) { return set_int((longlong)num, true, cs); }
bool set_real(double num,uint decimals, CHARSET_INFO *cs);
+ bool set_hex(ulonglong num);
+ bool set_hex(const char *str, uint32 len);
+
/* Take over handling of buffer from some other object */
- void reset(char *ptr_arg, uint32 length_arg, uint32 alloced_length_arg,
+ void reset(char *ptr_arg, size_t length_arg, size_t alloced_length_arg,
CHARSET_INFO *cs)
{
free();
Ptr= ptr_arg;
- str_length= length_arg;
- Alloced_length= alloced_length_arg;
+ str_length= (uint32)length_arg;
+ Alloced_length= (uint32)alloced_length_arg;
str_charset= cs;
alloced= ptr_arg != 0;
}
@@ -395,9 +395,10 @@ public:
if (ALIGN_SIZE(arg_length+1) < Alloced_length)
{
char *new_ptr;
- if (!(new_ptr=(char*)
- my_realloc(Ptr, arg_length,MYF((thread_specific ?
- MY_THREAD_SPECIFIC : 0)))))
+ if (unlikely(!(new_ptr=(char*)
+ my_realloc(Ptr,
+ arg_length,MYF((thread_specific ?
+ MY_THREAD_SPECIFIC : 0))))))
{
Alloced_length = 0;
real_alloc(arg_length);
@@ -430,29 +431,29 @@ public:
bool copy(const String &s); // Allocate new string
bool copy(const char *s,size_t arg_length, CHARSET_INFO *cs); // Allocate new string
bool copy_or_move(const char *s,size_t arg_length, CHARSET_INFO *cs);
- static bool needs_conversion(uint32 arg_length,
+ static bool needs_conversion(size_t arg_length,
CHARSET_INFO *cs_from, CHARSET_INFO *cs_to,
uint32 *offset);
- static bool needs_conversion_on_storage(uint32 arg_length,
+ static bool needs_conversion_on_storage(size_t arg_length,
CHARSET_INFO *cs_from,
CHARSET_INFO *cs_to);
- bool copy_aligned(const char *s, uint32 arg_length, uint32 offset,
+ bool copy_aligned(const char *s, size_t arg_length, size_t offset,
CHARSET_INFO *cs);
- bool set_or_copy_aligned(const char *s, uint32 arg_length, CHARSET_INFO *cs);
- bool copy(const char*s,uint32 arg_length, CHARSET_INFO *csfrom,
+ bool set_or_copy_aligned(const char *s, size_t arg_length, CHARSET_INFO *cs);
+ 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)
{
return copy(str->ptr(), str->length(), str->charset(), tocs, errors);
}
bool copy(CHARSET_INFO *tocs,
- CHARSET_INFO *fromcs, const char *src, uint32 src_length,
- uint32 nchars, String_copier *copier)
+ CHARSET_INFO *fromcs, const char *src, size_t src_length,
+ size_t nchars, String_copier *copier)
{
- if (alloc(tocs->mbmaxlen * src_length))
+ if (unlikely(alloc(tocs->mbmaxlen * src_length)))
return true;
str_length= copier->well_formed_copy(tocs, Ptr, Alloced_length,
- fromcs, src, src_length, nchars);
+ fromcs, src, (uint)src_length, (uint)nchars);
str_charset= tocs;
return false;
}
@@ -467,10 +468,30 @@ public:
}
bool append(const String &s);
bool append(const char *s);
- bool append(const LEX_STRING *ls) { return append(ls->str, (uint32) ls->length); }
- bool append(const LEX_CSTRING *ls) { return append(ls->str, (uint32) ls->length); }
+ bool append(const LEX_STRING *ls)
+ {
+ DBUG_ASSERT(ls->length < UINT_MAX32 &&
+ ((ls->length == 0 && !ls->str) ||
+ ls->length == strlen(ls->str)));
+ return append(ls->str, (uint32) ls->length);
+ }
+ bool append(const LEX_CSTRING *ls)
+ {
+ DBUG_ASSERT(ls->length < UINT_MAX32 &&
+ ((ls->length == 0 && !ls->str) ||
+ ls->length == strlen(ls->str)));
+ return append(ls->str, (uint32) ls->length);
+ }
+ bool append(const LEX_CSTRING &ls)
+ {
+ return append(&ls);
+ }
bool append(const char *s, size_t size);
- bool append(const char *s, uint arg_length, CHARSET_INFO *cs);
+ bool append(const char *s, size_t arg_length, CHARSET_INFO *cs);
+ bool append(const LEX_CSTRING &s, CHARSET_INFO *cs)
+ {
+ return append(s.str, s.length, cs);
+ }
bool append_ulonglong(ulonglong val);
bool append_longlong(longlong val);
bool append(IO_CACHE* file, uint32 arg_length);
@@ -489,7 +510,7 @@ public:
}
else
{
- if (realloc_with_extra(str_length + 1))
+ if (unlikely(realloc_with_extra(str_length + 1)))
return 1;
Ptr[str_length++]=chr;
}
@@ -499,8 +520,8 @@ public:
{
for (const char *src_end= src + srclen ; src != src_end ; src++)
{
- if (append(_dig_vec_lower[((uchar) *src) >> 4]) ||
- append(_dig_vec_lower[((uchar) *src) & 0x0F]))
+ if (unlikely(append(_dig_vec_lower[((uchar) *src) >> 4])) ||
+ unlikely(append(_dig_vec_lower[((uchar) *src) & 0x0F])))
return true;
}
return false;
@@ -518,11 +539,11 @@ public:
uint32 numchars() const;
int charpos(longlong i,uint32 offset=0);
- int reserve(uint32 space_needed)
+ int reserve(size_t space_needed)
{
return realloc(str_length + space_needed);
}
- int reserve(uint32 space_needed, uint32 grow_by);
+ int reserve(size_t space_needed, size_t grow_by);
/*
The following append operations do NOT check alloced memory
@@ -559,6 +580,13 @@ public:
DBUG_ASSERT(str_length <= UINT_MAX32 - data_len);
str_length += (uint)data_len;
}
+ void q_append(const LEX_CSTRING *ls)
+ {
+ DBUG_ASSERT(ls->length < UINT_MAX32 &&
+ ((ls->length == 0 && !ls->str) ||
+ ls->length == strlen(ls->str)));
+ q_append(ls->str, (uint32) ls->length);
+ }
void write_at_position(int position, uint32 value)
{
@@ -569,7 +597,15 @@ public:
{
qs_append(str, (uint32)strlen(str));
}
- void qs_append(const char *str, uint32 len);
+ void qs_append(const LEX_CSTRING *ls)
+ {
+ DBUG_ASSERT(ls->length < UINT_MAX32 &&
+ ((ls->length == 0 && !ls->str) ||
+ ls->length == strlen(ls->str)));
+ qs_append(ls->str, (uint32)ls->length);
+ }
+ 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);
inline void qs_append(const char c)
@@ -591,7 +627,7 @@ public:
{
char *buff= Ptr + str_length;
char *end= ll2str(i, buff, radix, 0);
- str_length+= (int) (end-buff);
+ str_length+= uint32(end-buff);
}
/* Inline (general) functions used by the protocol functions */
@@ -601,7 +637,7 @@ public:
uint32 new_length= arg_length + str_length;
if (new_length > Alloced_length)
{
- if (realloc(new_length + step_alloc))
+ if (unlikely(realloc(new_length + step_alloc)))
return 0;
}
uint32 old_length= str_length;
@@ -613,7 +649,8 @@ public:
inline bool append(const char *s, uint32 arg_length, uint32 step_alloc)
{
uint32 new_length= arg_length + str_length;
- if (new_length > Alloced_length && realloc(new_length + step_alloc))
+ if (new_length > Alloced_length &&
+ unlikely(realloc(new_length + step_alloc)))
return TRUE;
memcpy(Ptr+str_length, s, arg_length);
str_length+= arg_length;
@@ -629,7 +666,7 @@ public:
print_with_conversion(to, cs);
}
- bool append_for_single_quote(const char *st, uint len);
+ bool append_for_single_quote(const char *st, size_t len);
bool append_for_single_quote(const String *s)
{
return append_for_single_quote(s->ptr(), s->length());
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 57284272316..7c448dea4b1 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -18,7 +18,7 @@
/* drop and alter of tables */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#include "debug_sync.h"
@@ -53,6 +53,8 @@
#include "sql_show.h"
#include "transaction.h"
#include "sql_audit.h"
+#include "sql_sequence.h"
+#include "tztime.h"
#ifdef __WIN__
@@ -61,12 +63,16 @@
const char *primary_key_name="PRIMARY";
-static bool check_if_keyname_exists(const char *name,KEY *start, KEY *end);
+static int check_if_keyname_exists(const char *name,KEY *start, KEY *end);
static char *make_unique_key_name(THD *thd, const char *field_name, KEY *start,
KEY *end);
-static bool make_unique_constraint_name(THD *thd, LEX_STRING *name,
+static bool make_unique_constraint_name(THD *thd, LEX_CSTRING *name,
List<Virtual_column_info> *vcol,
uint *nr);
+static const
+char * make_unique_invisible_field_name(THD *thd, const char *field_name,
+ List<Create_field> *fields);
+
static int copy_data_between_tables(THD *thd, TABLE *from,TABLE *to,
List<Create_field> &create, bool ignore,
uint order_num, ORDER *order,
@@ -74,7 +80,6 @@ static int copy_data_between_tables(THD *thd, TABLE *from,TABLE *to,
Alter_info::enum_enable_or_disable keys_onoff,
Alter_table_ctx *alter_ctx);
-static bool prepare_blob_field(THD *thd, Column_definition *sql_field);
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);
@@ -90,7 +95,7 @@ static bool fix_constraints_names(THD *thd, List<Virtual_column_info>
@param name_len Length of the name, in bytes
*/
static char* add_identifier(THD* thd, char *to_p, const char * end_p,
- const char* name, uint name_len)
+ const char* name, size_t name_len)
{
uint res;
uint errors;
@@ -111,7 +116,7 @@ static char* add_identifier(THD* thd, char *to_p, const char * end_p,
res= strconvert(&my_charset_filename, conv_name, name_len,
system_charset_info,
conv_string, FN_REFLEN, &errors);
- if (!res || errors)
+ if (unlikely(!res || errors))
{
DBUG_PRINT("error", ("strconvert of '%s' failed with %u (errors: %u)", conv_name, res, errors));
conv_name= name;
@@ -124,7 +129,9 @@ static char* add_identifier(THD* thd, char *to_p, const char * end_p,
conv_name_end= conv_string + res;
}
- quote = thd ? get_quote_char_for_identifier(thd, conv_name, res - 1) : '`';
+ quote= (likely(thd) ?
+ get_quote_char_for_identifier(thd, conv_name, res - 1) :
+ '`');
if (quote != EOF && (end_p - to_p > 2))
{
@@ -210,13 +217,13 @@ uint explain_filename(THD* thd,
char *to_p= to;
char *end_p= to_p + to_length;
const char *db_name= NULL;
- int db_name_len= 0;
+ size_t db_name_len= 0;
const char *table_name;
- int table_name_len= 0;
+ size_t table_name_len= 0;
const char *part_name= NULL;
- int part_name_len= 0;
+ size_t part_name_len= 0;
const char *subpart_name= NULL;
- int subpart_name_len= 0;
+ size_t subpart_name_len= 0;
uint part_type= NORMAL_PART_NAME;
const char *tmp_p;
@@ -376,7 +383,7 @@ uint explain_filename(THD* thd,
Table name length.
*/
-uint filename_to_tablename(const char *from, char *to, uint to_length,
+uint filename_to_tablename(const char *from, char *to, size_t to_length,
bool stay_quiet)
{
uint errors;
@@ -386,7 +393,7 @@ uint filename_to_tablename(const char *from, char *to, uint to_length,
res= strconvert(&my_charset_filename, from, FN_REFLEN,
system_charset_info, to, to_length, &errors);
- if (errors) // Old 5.0 name
+ if (unlikely(errors)) // Old 5.0 name
{
res= (strxnmov(to, to_length, MYSQL50_TABLE_NAME_PREFIX, from, NullS) -
to);
@@ -395,7 +402,7 @@ uint filename_to_tablename(const char *from, char *to, uint to_length,
}
DBUG_PRINT("exit", ("to '%s'", to));
- DBUG_RETURN(res);
+ DBUG_RETURN((uint)res);
}
@@ -431,7 +438,7 @@ bool check_mysql50_prefix(const char *name)
non-0 result string length
*/
-uint check_n_cut_mysql50_prefix(const char *from, char *to, uint to_length)
+uint check_n_cut_mysql50_prefix(const char *from, char *to, size_t to_length)
{
if (check_mysql50_prefix(from))
return (uint) (strmake(to, from + MYSQL50_TABLE_NAME_PREFIX_LENGTH,
@@ -440,6 +447,13 @@ uint check_n_cut_mysql50_prefix(const char *from, char *to, uint to_length)
}
+static bool check_if_frm_exists(char *path, const char *db, const char *table)
+{
+ fn_format(path, table, db, reg_ext, MYF(0));
+ return !access(path, F_OK);
+}
+
+
/*
Translate a table name to a file name (WL #1324).
@@ -453,7 +467,7 @@ uint check_n_cut_mysql50_prefix(const char *from, char *to, uint to_length)
File name length.
*/
-uint tablename_to_filename(const char *from, char *to, uint to_length)
+uint tablename_to_filename(const char *from, char *to, size_t to_length)
{
uint errors, length;
DBUG_ENTER("tablename_to_filename");
@@ -530,13 +544,18 @@ uint build_table_filename(char *buff, size_t bufflen, const char *db,
DBUG_PRINT("enter", ("db: '%s' table_name: '%s' ext: '%s' flags: %x",
db, table_name, ext, flags));
+ (void) tablename_to_filename(db, dbbuff, sizeof(dbbuff));
+
+ /* Check if this is a temporary table name. Allow it if a corresponding .frm file exists */
+ if (is_prefix(table_name, tmp_file_prefix) && strlen(table_name) < NAME_CHAR_LEN &&
+ check_if_frm_exists(tbbuff, dbbuff, table_name))
+ flags|= FN_IS_TMP;
+
if (flags & FN_IS_TMP) // FN_FROM_IS_TMP | FN_TO_IS_TMP
strmake(tbbuff, table_name, sizeof(tbbuff)-1);
else
(void) tablename_to_filename(table_name, tbbuff, sizeof(tbbuff));
- (void) tablename_to_filename(db, dbbuff, sizeof(dbbuff));
-
char *end = buff + bufflen;
/* Don't add FN_ROOTDIR if mysql_data_home already includes it */
char *pos = strnmov(buff, mysql_data_home, bufflen);
@@ -591,7 +610,7 @@ uint build_tmptable_filename(THD* thd, char *buff, size_t bufflen)
size_t length= unpack_filename(buff, buff);
DBUG_PRINT("exit", ("buff: '%s'", buff));
- DBUG_RETURN(length);
+ DBUG_RETURN((uint)length);
}
/*
@@ -1086,7 +1105,7 @@ static bool deactivate_ddl_log_entry_no_lock(uint entry_no)
static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry)
{
bool frm_action= FALSE;
- LEX_STRING handler_name;
+ LEX_CSTRING handler_name;
handler *file= NULL;
MEM_ROOT mem_root;
int error= TRUE;
@@ -1114,7 +1133,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, TABLE_ALLOC_BLOCK_SIZE, 0, MYF(MY_THREAD_SPECIFIC));
+ init_sql_alloc(&mem_root, "execute_ddl_log_action", TABLE_ALLOC_BLOCK_SIZE,
+ 0, MYF(MY_THREAD_SPECIFIC));
if (!strcmp(ddl_log_entry->handler_name, reg_ext))
frm_action= TRUE;
else
@@ -1127,11 +1147,8 @@ static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry)
}
hton= plugin_data(plugin, handlerton*);
file= get_new_handler((TABLE_SHARE*)0, &mem_root, hton);
- if (!file)
- {
- mem_alloc_error(sizeof(handler));
+ if (unlikely(!file))
goto error;
- }
}
switch (ddl_log_entry->action_type)
{
@@ -1143,7 +1160,8 @@ static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry)
if (frm_action)
{
strxmov(to_path, ddl_log_entry->name, reg_ext, NullS);
- if ((error= mysql_file_delete(key_file_frm, to_path, MYF(MY_WME))))
+ if (unlikely((error= mysql_file_delete(key_file_frm, to_path,
+ MYF(MY_WME)))))
{
if (my_errno != ENOENT)
break;
@@ -1155,7 +1173,7 @@ static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry)
}
else
{
- if ((error= file->ha_delete_table(ddl_log_entry->name)))
+ if (unlikely((error= file->ha_delete_table(ddl_log_entry->name))))
{
if (error != ENOENT && error != HA_ERR_NO_SUCH_TABLE)
break;
@@ -1408,19 +1426,19 @@ bool write_ddl_log_entry(DDL_LOG_ENTRY *ddl_log_entry,
+ (2*FN_REFLEN)],
(char*) &global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS
+ (3*FN_REFLEN)]));
- if (write_ddl_log_file_entry((*active_entry)->entry_pos))
+ if (unlikely(write_ddl_log_file_entry((*active_entry)->entry_pos)))
{
error= TRUE;
sql_print_error("Failed to write entry_no = %u",
(*active_entry)->entry_pos);
}
- if (write_header && !error)
+ if (write_header && likely(!error))
{
(void) sync_ddl_log_no_lock();
if (write_ddl_log_header())
error= TRUE;
}
- if (error)
+ if (unlikely(error))
release_ddl_log_memory_entry(*active_entry);
DBUG_RETURN(error);
}
@@ -1751,9 +1769,10 @@ 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);
- return build_table_filename(buff, bufflen, lpt->db, tmp_name, "", FN_IS_TMP);
+ my_snprintf(tmp_name, sizeof (tmp_name), "%s-%s", tmp_file_prefix,
+ lpt->table_name.str);
+ return build_table_filename(buff, bufflen, lpt->db.str, tmp_name, "",
+ FN_IS_TMP);
}
@@ -1832,8 +1851,7 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags)
#endif
/* Write shadow frm file */
lpt->create_info->table_options= lpt->db_options;
- LEX_CUSTRING frm= build_frm_image(lpt->thd,
- lpt->table_name,
+ LEX_CUSTRING frm= build_frm_image(lpt->thd, &lpt->table_name,
lpt->create_info,
lpt->alter_info->create_list,
lpt->key_count, lpt->key_info_buffer,
@@ -1844,12 +1862,14 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags)
goto end;
}
- int error= writefrm(shadow_path, lpt->db, lpt->table_name,
+ int error= writefrm(shadow_path, lpt->db.str, lpt->table_name.str,
lpt->create_info->tmp_table(), frm.str, frm.length);
my_free(const_cast<uchar*>(frm.str));
- if (error || lpt->table->file->ha_create_partitioning_metadata(shadow_path,
- NULL, CHF_CREATE_FLAG))
+ if (unlikely(error) ||
+ unlikely(lpt->table->file->
+ ha_create_partitioning_metadata(shadow_path,
+ NULL, CHF_CREATE_FLAG)))
{
mysql_file_delete(key_file_frm, shadow_frm_name, MYF(0));
error= 1;
@@ -1864,8 +1884,8 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags)
/*
Build frm file name
*/
- build_table_filename(path, sizeof(path) - 1, lpt->db,
- lpt->table_name, "", 0);
+ build_table_filename(path, sizeof(path) - 1, lpt->db.str,
+ lpt->table_name.str, "", 0);
strxnmov(frm_name, sizeof(frm_name), path, reg_ext, NullS);
/*
When we are changing to use new frm file we need to ensure that we
@@ -1973,7 +1993,7 @@ int write_bin_log(THD *thd, bool clear_error,
errcode= query_error_code(thd, TRUE);
error= thd->binlog_query(THD::STMT_QUERY_TYPE,
query, query_length, is_trans, FALSE, FALSE,
- errcode);
+ errcode) > 0;
thd_proc_info(thd, 0);
}
return error;
@@ -1988,6 +2008,8 @@ int write_bin_log(THD *thd, bool clear_error,
thd Thread handle
tables List of tables to delete
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
NOTES
Will delete all tables that can be deleted and give a compact error
@@ -2004,8 +2026,8 @@ int write_bin_log(THD *thd, bool clear_error,
*/
-bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
- my_bool drop_temporary)
+bool mysql_rm_table(THD *thd,TABLE_LIST *tables, bool if_exists,
+ bool drop_temporary, bool drop_sequence)
{
bool error;
Drop_table_error_handler err_handler;
@@ -2025,8 +2047,8 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
{
for (table= tables; table; table= table->next_local)
{
- LEX_STRING db_name= { table->db, table->db_length };
- LEX_STRING table_name= { table->table_name, table->table_name_length };
+ LEX_CSTRING db_name= table->db;
+ LEX_CSTRING table_name= table->table_name;
if (table->open_type == OT_BASE_ONLY ||
!thd->find_temporary_table(table))
(void) delete_statistics_for_table(thd, &db_name, &table_name);
@@ -2035,6 +2057,25 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
if (!thd->locked_tables_mode)
{
+ if (drop_sequence)
+ {
+ /* We are trying to drop a sequence.
+ Change all temporary tables that are not sequences to
+ normal tables so that we can try to drop them instead.
+ If we don't do this, we will get an error 'not a sequence'
+ when trying to drop a sequence that is hidden by a temporary
+ table.
+ */
+ for (table= tables; table; table= table->next_global)
+ {
+ if (table->open_type == OT_TEMPORARY_OR_BASE &&
+ is_temporary_table(table) && !table->table->s->sequence)
+ {
+ thd->mark_tmp_table_as_free_for_reuse(table->table);
+ table->table= NULL;
+ }
+ }
+ }
if (lock_table_names(thd, tables, NULL,
thd->variables.lock_wait_timeout, 0))
DBUG_RETURN(true);
@@ -2069,8 +2110,8 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
by parser) it is safe to cache pointer to the TABLE instances
in its elements.
*/
- table->table= find_table_for_mdl_upgrade(thd, table->db,
- table->table_name, NULL);
+ table->table= find_table_for_mdl_upgrade(thd, table->db.str,
+ table->table_name.str, NULL);
if (!table->table)
DBUG_RETURN(true);
table->mdl_request.ticket= table->table->mdl_ticket;
@@ -2087,10 +2128,10 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
/* mark for close and remove all cached entries */
thd->push_internal_handler(&err_handler);
error= mysql_rm_table_no_locks(thd, tables, if_exists, drop_temporary,
- false, false, false);
+ false, drop_sequence, false, false);
thd->pop_internal_handler();
- if (error)
+ if (unlikely(error))
DBUG_RETURN(TRUE);
my_ok(thd);
DBUG_RETURN(FALSE);
@@ -2176,11 +2217,13 @@ static uint32 comment_length(THD *thd, uint32 comment_pos,
int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
bool drop_temporary, bool drop_view,
+ bool drop_sequence,
bool dont_log_query,
bool dont_free_locks)
{
TABLE_LIST *table;
- char path[FN_REFLEN + 1], wrong_tables_buff[160], *alias= NULL;
+ char path[FN_REFLEN + 1], wrong_tables_buff[160];
+ 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;
@@ -2190,7 +2233,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
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;
+ bool was_view= 0, was_table= 0, is_sequence;
String built_query;
String built_trans_tmp_query, built_non_trans_tmp_query;
DBUG_ENTER("mysql_rm_table_no_locks");
@@ -2232,17 +2275,21 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
*/
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("DROP TABLE IF EXISTS ");
- else
- built_query.append("DROP TABLE ");
+ 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);
@@ -2250,33 +2297,28 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
}
}
+ built_trans_tmp_query.set_charset(system_charset_info);
+ built_trans_tmp_query.append("DROP TEMPORARY ");
+ built_trans_tmp_query.append(object_to_drop);
+ built_trans_tmp_query.append(' ');
if (thd->is_current_stmt_binlog_format_row() || if_exists)
{
is_drop_tmp_if_exists_added= true;
- built_trans_tmp_query.set_charset(system_charset_info);
- built_trans_tmp_query.append("DROP TEMPORARY TABLE IF EXISTS ");
- built_non_trans_tmp_query.set_charset(system_charset_info);
- built_non_trans_tmp_query.append("DROP TEMPORARY TABLE IF EXISTS ");
- }
- else
- {
- built_trans_tmp_query.set_charset(system_charset_info);
- built_trans_tmp_query.append("DROP TEMPORARY TABLE ");
- built_non_trans_tmp_query.set_charset(system_charset_info);
- built_non_trans_tmp_query.append("DROP TEMPORARY TABLE ");
+ built_trans_tmp_query.append("IF EXISTS ");
}
+ built_non_trans_tmp_query.set_charset(system_charset_info);
+ built_non_trans_tmp_query.copy(built_trans_tmp_query);
}
for (table= tables; table; table= table->next_local)
{
bool is_trans= 0;
bool table_creation_was_logged= 0;
- char *db=table->db;
- size_t db_length= table->db_length;
+ LEX_CSTRING db= table->db;
handlerton *table_type= 0;
DBUG_PRINT("table", ("table_l: '%s'.'%s' table: %p s: %p",
- table->db, table->table_name, table->table,
+ table->db.str, table->table_name.str, table->table,
table->table ? table->table->s : NULL));
/*
@@ -2289,7 +2331,8 @@ 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))
+ if (table->open_type == OT_BASE_ONLY || !is_temporary_table(table) ||
+ (drop_sequence && table->table->s->table_type != TABLE_TYPE_SEQUENCE))
error= 1;
else
{
@@ -2334,14 +2377,13 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
thd->db is NULL or 'IF EXISTS' clause is present in 'DROP TEMPORARY'
query.
*/
- if (thd->db == NULL || strcmp(db,thd->db) != 0
- || is_drop_tmp_if_exists_added )
+ if (thd->db.str == NULL || cmp(&db, &thd->db) ||
+ is_drop_tmp_if_exists_added )
{
- append_identifier(thd, built_ptr_query, db, db_length);
+ append_identifier(thd, built_ptr_query, &db);
built_ptr_query->append(".");
}
- append_identifier(thd, built_ptr_query, table->table_name,
- table->table_name_length);
+ append_identifier(thd, built_ptr_query, &table->table_name);
built_ptr_query->append(",");
}
/*
@@ -2355,39 +2397,43 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
{
non_temp_tables_count++;
- DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, table->db,
- table->table_name,
+ DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, table->db.str,
+ table->table_name.str,
MDL_SHARED));
alias= (lower_case_table_names == 2) ? table->alias : table->table_name;
/* remove .frm file and engine files */
- path_length= build_table_filename(path, sizeof(path) - 1, db, alias,
+ path_length= build_table_filename(path, sizeof(path) - 1, db.str, alias.str,
reg_ext, 0);
-
}
DEBUG_SYNC(thd, "rm_table_no_locks_before_delete_table");
error= 0;
if (drop_temporary ||
- (ha_table_exists(thd, db, alias, &table_type) == 0 && table_type == 0) ||
- (!drop_view && (was_view= (table_type == view_pseudo_hton))))
+ (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))
{
/*
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 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(&db);
tbl_name.append('.');
- tbl_name.append(table->table_name);
+ tbl_name.append(&table->table_name);
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
- ER_BAD_TABLE_ERROR,
- ER_THD(thd, ER_BAD_TABLE_ERROR),
+ err, ER_THD(thd, err),
tbl_name.c_ptr_safe());
/*
@@ -2406,6 +2452,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
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
@@ -2433,18 +2480,18 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
table->table= 0;
}
else
- tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name,
+ tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db.str, table->table_name.str,
false);
/* Check that we have an exclusive lock on the table to be dropped. */
- DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, table->db,
- table->table_name,
+ DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, table->db.str,
+ table->table_name.str,
MDL_EXCLUSIVE));
// Remove extension for delete
*(end= path + path_length - reg_ext_length)= '\0';
- if ((error= ha_delete_table(thd, table_type, path, db, table->table_name,
+ if ((error= ha_delete_table(thd, table_type, path, &db, &table->table_name,
!dont_log_query)))
{
if (thd->is_killed())
@@ -2455,7 +2502,6 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
}
else
{
- int frm_delete_error, trigger_drop_error= 0;
/* Delete the table definition file */
strmov(end,reg_ext);
if (table_type && table_type != view_pseudo_hton &&
@@ -2466,21 +2512,27 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
Delete it silently if it exists
*/
(void) mysql_file_delete(key_file_frm, path, MYF(0));
- frm_delete_error= 0;
}
- else
- frm_delete_error= mysql_file_delete(key_file_frm, path,
- MYF(MY_WME));
- if (frm_delete_error)
+ else if (unlikely(mysql_file_delete(key_file_frm, path,
+ MYF(MY_WME))))
+ {
frm_delete_error= my_errno;
- else
+ DBUG_ASSERT(frm_delete_error);
+ }
+ }
+
+ if (likely(!error))
+ {
+ int trigger_drop_error= 0;
+
+ if (likely(!frm_delete_error))
{
non_tmp_table_deleted= TRUE;
trigger_drop_error=
- Table_triggers_list::drop_all_triggers(thd, db, table->table_name);
+ Table_triggers_list::drop_all_triggers(thd, &db, &table->table_name);
}
- if (trigger_drop_error ||
+ if (unlikely(trigger_drop_error) ||
(frm_delete_error && frm_delete_error != ENOENT))
error= 1;
else if (frm_delete_error && if_exists)
@@ -2488,19 +2540,20 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
}
non_tmp_error|= MY_TEST(error);
}
+
if (error)
{
if (wrong_tables.length())
wrong_tables.append(',');
- wrong_tables.append(db);
+ wrong_tables.append(&db);
wrong_tables.append('.');
- wrong_tables.append(table->table_name);
+ wrong_tables.append(&table->table_name);
errors++;
}
else
{
- PSI_CALL_drop_table_share(false, table->db, table->db_length,
- table->table_name, table->table_name_length);
+ PSI_CALL_drop_table_share(false, table->db.str, (uint)table->db.length,
+ table->table_name.str, (uint)table->table_name.length);
mysql_audit_drop_table(thd, table);
}
@@ -2509,20 +2562,18 @@ log_query:
{
non_tmp_table_deleted= (if_exists ? TRUE : non_tmp_table_deleted);
/*
- Don't write the database name if it is the current one (or if
- thd->db is NULL).
- */
- if (thd->db == NULL || strcmp(db,thd->db) != 0)
+ 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, db_length);
+ append_identifier(thd, &built_query, &db);
built_query.append(".");
}
- append_identifier(thd, &built_query, table->table_name,
- table->table_name_length);
+ append_identifier(thd, &built_query, &table->table_name);
built_query.append(",");
}
-
DBUG_PRINT("table", ("table: %p s: %p", table->table,
table->table ? table->table->s : NULL));
}
@@ -2535,8 +2586,12 @@ err:
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(ER_BAD_TABLE_ERROR, MYF(0), wrong_tables.c_ptr_safe());
+ my_error((drop_sequence ? ER_UNKNOWN_SEQUENCES :
+ ER_BAD_TABLE_ERROR),
+ MYF(0), wrong_tables.c_ptr_safe());
error= 1;
}
@@ -2569,12 +2624,12 @@ err:
#ifdef WITH_WSREP
thd->wsrep_skip_wsrep_GTID = true;
#endif /* WITH_WSREP */
- error |= thd->binlog_query(THD::STMT_QUERY_TYPE,
- built_non_trans_tmp_query.ptr(),
- built_non_trans_tmp_query.length(),
- FALSE, FALSE,
- is_drop_tmp_if_exists_added,
- 0);
+ error |= (thd->binlog_query(THD::STMT_QUERY_TYPE,
+ built_non_trans_tmp_query.ptr(),
+ built_non_trans_tmp_query.length(),
+ FALSE, FALSE,
+ is_drop_tmp_if_exists_added,
+ 0) > 0);
}
if (trans_tmp_table_deleted)
{
@@ -2584,12 +2639,12 @@ err:
#ifdef WITH_WSREP
thd->wsrep_skip_wsrep_GTID = true;
#endif /* WITH_WSREP */
- error |= thd->binlog_query(THD::STMT_QUERY_TYPE,
- built_trans_tmp_query.ptr(),
- built_trans_tmp_query.length(),
- TRUE, FALSE,
- is_drop_tmp_if_exists_added,
- 0);
+ error |= (thd->binlog_query(THD::STMT_QUERY_TYPE,
+ built_trans_tmp_query.ptr(),
+ built_trans_tmp_query.length(),
+ TRUE, FALSE,
+ is_drop_tmp_if_exists_added,
+ 0) > 0);
}
if (non_tmp_table_deleted)
{
@@ -2601,11 +2656,11 @@ err:
#ifdef WITH_WSREP
thd->wsrep_skip_wsrep_GTID = false;
#endif /* WITH_WSREP */
- error |= thd->binlog_query(THD::STMT_QUERY_TYPE,
- built_query.ptr(),
- built_query.length(),
- TRUE, FALSE, FALSE,
- error_code);
+ error |= (thd->binlog_query(THD::STMT_QUERY_TYPE,
+ built_query.ptr(),
+ built_query.length(),
+ TRUE, FALSE, FALSE,
+ error_code) > 0);
}
}
}
@@ -2667,8 +2722,8 @@ end:
when the original table was dropped but we could not create the new one.
*/
-bool log_drop_table(THD *thd, const char *db_name, size_t db_name_length,
- const char *table_name, size_t table_name_length,
+bool log_drop_table(THD *thd, const LEX_CSTRING *db_name,
+ const LEX_CSTRING *table_name,
bool temporary_table)
{
char buff[NAME_LEN*2 + 80];
@@ -2684,14 +2739,14 @@ bool log_drop_table(THD *thd, const char *db_name, size_t db_name_length,
if (temporary_table)
query.append(STRING_WITH_LEN("TEMPORARY "));
query.append(STRING_WITH_LEN("TABLE IF EXISTS "));
- append_identifier(thd, &query, db_name, db_name_length);
+ append_identifier(thd, &query, db_name);
query.append(".");
- append_identifier(thd, &query, table_name, table_name_length);
+ append_identifier(thd, &query, table_name);
query.append(STRING_WITH_LEN("/* Generated to handle "
"failed CREATE OR REPLACE */"));
error= thd->binlog_query(THD::STMT_QUERY_TYPE,
query.ptr(), query.length(),
- FALSE, FALSE, temporary_table, 0);
+ FALSE, FALSE, temporary_table, 0) > 0;
DBUG_RETURN(error);
}
@@ -2709,16 +2764,16 @@ bool log_drop_table(THD *thd, const char *db_name, size_t db_name_length,
@return False in case of success, True otherwise.
*/
-bool quick_rm_table(THD *thd, handlerton *base, const char *db,
- const char *table_name, uint flags, const char *table_path)
+bool quick_rm_table(THD *thd, handlerton *base, const LEX_CSTRING *db,
+ const LEX_CSTRING *table_name, uint flags, const char *table_path)
{
char path[FN_REFLEN + 1];
- bool error= 0;
+ int error= 0;
DBUG_ENTER("quick_rm_table");
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, table_name, reg_ext, flags);
+ 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 */
path[path_length - reg_ext_length]= '\0'; // Remove reg_ext
@@ -2735,8 +2790,8 @@ bool quick_rm_table(THD *thd, handlerton *base, const char *db,
if (likely(error == 0))
{
- PSI_CALL_drop_table_share(flags & FN_IS_TMP, db, strlen(db),
- table_name, strlen(table_name));
+ PSI_CALL_drop_table_share(flags & FN_IS_TMP, db->str, (uint)db->length,
+ table_name->str, (uint)table_name->length);
}
DBUG_RETURN(error);
@@ -2769,9 +2824,9 @@ static int sort_keys(KEY *a, KEY *b)
/* Sort NOT NULL keys before other keys */
return (a_flags & HA_NULL_PART_KEY) ? 1 : -1;
}
- if (a->name == primary_key_name)
+ if (a->name.str == primary_key_name)
return -1;
- if (b->name == primary_key_name)
+ if (b->name.str == primary_key_name)
return 1;
/* Sort keys don't containing partial segments before others */
if ((a_flags ^ b_flags) & HA_KEY_HAS_PART_KEY_SEG)
@@ -2847,205 +2902,84 @@ bool check_duplicates_in_interval(const char *set_or_name,
}
-/*
- Check TYPELIB (set or enum) max and total lengths
+bool Column_definition::prepare_stage2_blob(handler *file,
+ ulonglong table_flags,
+ uint field_flags)
+{
+ if (table_flags & HA_NO_BLOBS)
+ {
+ my_error(ER_TABLE_CANT_HANDLE_BLOB, MYF(0), file->table_type());
+ return true;
+ }
+ pack_flag= field_flags |
+ pack_length_to_packflag(pack_length - portable_sizeof_char_ptr);
+ if (charset->state & MY_CS_BINSORT)
+ pack_flag|= FIELDFLAG_BINARY;
+ length= 8; // Unireg field length
+ return false;
+}
- SYNOPSIS
- calculate_interval_lengths()
- cs charset+collation pair of the interval
- typelib list of values for the column
- max_length length of the longest item
- tot_length sum of the item lengths
- DESCRIPTION
- After this function call:
- - ENUM uses max_length
- - SET uses tot_length.
+bool Column_definition::prepare_stage2_typelib(const char *type_name,
+ uint field_flags,
+ uint *dup_val_count)
+{
+ pack_flag= pack_length_to_packflag(pack_length) | field_flags;
+ if (charset->state & MY_CS_BINSORT)
+ pack_flag|= FIELDFLAG_BINARY;
+ return check_duplicates_in_interval(type_name, field_name.str, interval,
+ charset, dup_val_count);
+}
- RETURN VALUES
- void
-*/
-void calculate_interval_lengths(CHARSET_INFO *cs, TYPELIB *interval,
- uint32 *max_length, uint32 *tot_length)
+
+uint Column_definition::pack_flag_numeric(uint dec) const
{
- const char **pos;
- uint *len;
- *max_length= *tot_length= 0;
- for (pos= interval->type_names, len= interval->type_lengths;
- *pos ; pos++, len++)
- {
- size_t length= cs->cset->numchars(cs, *pos, *pos + *len);
- *tot_length+= length;
- set_if_bigger(*max_length, (uint32)length);
- }
+ return (FIELDFLAG_NUMBER |
+ (flags & UNSIGNED_FLAG ? 0 : FIELDFLAG_DECIMAL) |
+ (flags & ZEROFILL_FLAG ? FIELDFLAG_ZEROFILL : 0) |
+ (dec << FIELDFLAG_DEC_SHIFT));
}
-/*
- Prepare a create_table instance for packing
+bool Column_definition::prepare_stage2_varchar(ulonglong table_flags)
+{
+ pack_flag= (charset->state & MY_CS_BINSORT) ? FIELDFLAG_BINARY : 0;
+ return false;
+}
- SYNOPSIS
- prepare_create_field()
- sql_field field to prepare for packing
- blob_columns count for BLOBs
- table_flags table flags
- DESCRIPTION
- This function prepares a Create_field instance.
- Fields such as pack_flag are valid after this call.
+/*
+ Prepare a Column_definition instance for packing
+ Members such as pack_flag are valid after this call.
- RETURN VALUES
- 0 ok
- 1 Error
+ @param IN handler - storage engine handler,
+ or NULL if preparing for an SP variable
+ @param IN table_flags - table flags
+
+ @retval false - ok
+ @retval true - error (not supported type, bad definition, etc)
*/
-int prepare_create_field(Column_definition *sql_field,
- uint *blob_columns,
- ulonglong table_flags)
+bool Column_definition::prepare_stage2(handler *file,
+ ulonglong table_flags)
{
- uint dup_val_count;
- uint decimals= sql_field->decimals;
- DBUG_ENTER("prepare_create_field");
+ DBUG_ENTER("Column_definition::prepare_stage2");
/*
This code came from mysql_prepare_create_table.
Indent preserved to make patching easier
*/
- DBUG_ASSERT(sql_field->charset);
+ DBUG_ASSERT(charset);
- switch (sql_field->sql_type) {
- case MYSQL_TYPE_BLOB:
- case MYSQL_TYPE_MEDIUM_BLOB:
- case MYSQL_TYPE_TINY_BLOB:
- case MYSQL_TYPE_LONG_BLOB:
- sql_field->pack_flag=FIELDFLAG_BLOB |
- pack_length_to_packflag(sql_field->pack_length -
- portable_sizeof_char_ptr);
- if (sql_field->charset->state & MY_CS_BINSORT)
- sql_field->pack_flag|=FIELDFLAG_BINARY;
- sql_field->length=8; // Unireg field length
- (*blob_columns)++;
- break;
- case MYSQL_TYPE_GEOMETRY:
-#ifdef HAVE_SPATIAL
- if (!(table_flags & HA_CAN_GEOMETRY))
- {
- my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "GEOMETRY");
- DBUG_RETURN(1);
- }
- sql_field->pack_flag=FIELDFLAG_GEOM |
- pack_length_to_packflag(sql_field->pack_length -
- portable_sizeof_char_ptr);
- if (sql_field->charset->state & MY_CS_BINSORT)
- sql_field->pack_flag|=FIELDFLAG_BINARY;
- sql_field->length=8; // Unireg field length
- (*blob_columns)++;
- break;
-#else
- my_error(ER_FEATURE_DISABLED, MYF(0),
- sym_group_geom.name, sym_group_geom.needed_define);
- DBUG_RETURN(1);
-#endif /*HAVE_SPATIAL*/
- case MYSQL_TYPE_VARCHAR:
-#ifndef QQ_ALL_HANDLERS_SUPPORT_VARCHAR
- if (table_flags & HA_NO_VARCHAR)
- {
- /* convert VARCHAR to CHAR because handler is not yet up to date */
- sql_field->sql_type= MYSQL_TYPE_VAR_STRING;
- sql_field->pack_length= calc_pack_length(sql_field->sql_type,
- (uint) sql_field->length);
- if ((sql_field->length / sql_field->charset->mbmaxlen) >
- MAX_FIELD_CHARLENGTH)
- {
- my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), sql_field->field_name,
- static_cast<ulong>(MAX_FIELD_CHARLENGTH));
- DBUG_RETURN(1);
- }
- }
-#endif
- /* fall through */
- case MYSQL_TYPE_STRING:
- sql_field->pack_flag=0;
- if (sql_field->charset->state & MY_CS_BINSORT)
- sql_field->pack_flag|=FIELDFLAG_BINARY;
- break;
- case MYSQL_TYPE_ENUM:
- sql_field->pack_flag=pack_length_to_packflag(sql_field->pack_length) |
- FIELDFLAG_INTERVAL;
- if (sql_field->charset->state & MY_CS_BINSORT)
- sql_field->pack_flag|=FIELDFLAG_BINARY;
- if (check_duplicates_in_interval("ENUM",sql_field->field_name,
- sql_field->interval,
- sql_field->charset, &dup_val_count))
- DBUG_RETURN(1);
- break;
- case MYSQL_TYPE_SET:
- sql_field->pack_flag=pack_length_to_packflag(sql_field->pack_length) |
- FIELDFLAG_BITFIELD;
- if (sql_field->charset->state & MY_CS_BINSORT)
- sql_field->pack_flag|=FIELDFLAG_BINARY;
- if (check_duplicates_in_interval("SET",sql_field->field_name,
- sql_field->interval,
- sql_field->charset, &dup_val_count))
- DBUG_RETURN(1);
- /* Check that count of unique members is not more then 64 */
- if (sql_field->interval->count - dup_val_count > sizeof(longlong)*8)
- {
- my_error(ER_TOO_BIG_SET, MYF(0), sql_field->field_name);
- DBUG_RETURN(1);
- }
- break;
- case MYSQL_TYPE_DATE: // Rest of string types
- case MYSQL_TYPE_NEWDATE:
- case MYSQL_TYPE_TIME:
- case MYSQL_TYPE_DATETIME:
- case MYSQL_TYPE_TIME2:
- case MYSQL_TYPE_DATETIME2:
- case MYSQL_TYPE_NULL:
- sql_field->pack_flag=f_settype((uint) sql_field->sql_type);
- break;
- case MYSQL_TYPE_BIT:
- /*
- We have sql_field->pack_flag already set here, see
- mysql_prepare_create_table().
- */
- break;
- case MYSQL_TYPE_NEWDECIMAL:
- sql_field->pack_flag=(FIELDFLAG_NUMBER |
- (sql_field->flags & UNSIGNED_FLAG ? 0 :
- FIELDFLAG_DECIMAL) |
- (sql_field->flags & ZEROFILL_FLAG ?
- FIELDFLAG_ZEROFILL : 0) |
- (decimals << FIELDFLAG_DEC_SHIFT));
- break;
- case MYSQL_TYPE_FLOAT:
- case MYSQL_TYPE_DOUBLE:
- /*
- User specified FLOAT() or DOUBLE() without precision. Change to
- FLOATING_POINT_DECIMALS to keep things compatible with earlier MariaDB
- versions.
- */
- if (decimals >= FLOATING_POINT_DECIMALS)
- decimals= FLOATING_POINT_DECIMALS;
- /* fall through */
- case MYSQL_TYPE_TIMESTAMP:
- case MYSQL_TYPE_TIMESTAMP2:
- default:
- sql_field->pack_flag=(FIELDFLAG_NUMBER |
- (sql_field->flags & UNSIGNED_FLAG ? 0 :
- FIELDFLAG_DECIMAL) |
- (sql_field->flags & ZEROFILL_FLAG ?
- FIELDFLAG_ZEROFILL : 0) |
- f_settype((uint) sql_field->sql_type) |
- (decimals << FIELDFLAG_DEC_SHIFT));
- break;
- }
- if (!(sql_field->flags & NOT_NULL_FLAG) ||
- (sql_field->vcol_info)) /* Make virtual columns allow NULL values */
- sql_field->pack_flag|= FIELDFLAG_MAYBE_NULL;
- if (sql_field->flags & NO_DEFAULT_VALUE_FLAG)
- sql_field->pack_flag|= FIELDFLAG_NO_DEFAULT;
- DBUG_RETURN(0);
+ if (type_handler()->Column_definition_prepare_stage2(this, file, table_flags))
+ DBUG_RETURN(true);
+
+ if (!(flags & NOT_NULL_FLAG) ||
+ (vcol_info)) /* Make virtual columns allow NULL values */
+ pack_flag|= FIELDFLAG_MAYBE_NULL;
+ if (flags & NO_DEFAULT_VALUE_FLAG)
+ pack_flag|= FIELDFLAG_NO_DEFAULT;
+ DBUG_RETURN(false);
}
@@ -3062,7 +2996,7 @@ int prepare_create_field(Column_definition *sql_field,
cs Character set
*/
-CHARSET_INFO* get_sql_field_charset(Create_field *sql_field,
+CHARSET_INFO* get_sql_field_charset(Column_definition *sql_field,
HA_CREATE_INFO *create_info)
{
CHARSET_INFO *cs= sql_field->charset;
@@ -3099,18 +3033,20 @@ void promote_first_timestamp_column(List<Create_field> *column_definitions)
while ((column_definition= it++) != NULL)
{
- if (is_timestamp_type(column_definition->sql_type) || // TIMESTAMP
+ if (column_definition->is_timestamp_type() || // TIMESTAMP
column_definition->unireg_check == Field::TIMESTAMP_OLD_FIELD) // Legacy
{
+ DBUG_PRINT("info", ("field-ptr:%p", column_definition->field));
if ((column_definition->flags & NOT_NULL_FLAG) != 0 && // NOT NULL,
column_definition->default_value == NULL && // no constant default,
column_definition->unireg_check == Field::NONE && // no function default
- column_definition->vcol_info == NULL)
+ column_definition->vcol_info == NULL &&
+ !(column_definition->flags & VERS_SYSTEM_FIELD)) // column isn't generated
{
DBUG_PRINT("info", ("First TIMESTAMP column '%s' was promoted to "
"DEFAULT CURRENT_TIMESTAMP ON UPDATE "
"CURRENT_TIMESTAMP",
- column_definition->field_name
+ column_definition->field_name.str
));
column_definition->unireg_check= Field::TIMESTAMP_DNUN_FIELD;
}
@@ -3167,40 +3103,236 @@ static void check_duplicate_key(THD *thd, Key *key, KEY *key_info,
*/
List_iterator_fast<Key_part_spec> k_column_iterator(k->columns);
-
- bool all_columns_are_identical= true;
-
+ uint i;
key_column_iterator.rewind();
- for (uint i= 0; i < key->columns.elements; ++i)
+ for (i= 0; i < key->columns.elements; ++i)
{
Key_part_spec *c1= key_column_iterator++;
Key_part_spec *c2= k_column_iterator++;
DBUG_ASSERT(c1 && c2);
- if (my_strcasecmp(system_charset_info,
- c1->field_name.str, c2->field_name.str) ||
+ if (lex_string_cmp(system_charset_info,
+ &c1->field_name, &c2->field_name) ||
(c1->length != c2->length))
- {
- all_columns_are_identical= false;
break;
- }
}
// Report a warning if we have two identical keys.
- if (all_columns_are_identical)
+ if (i == key->columns.elements)
{
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_DUP_INDEX, ER_THD(thd, ER_DUP_INDEX),
- key_info->name);
+ key_info->name.str);
break;
}
}
}
+bool Column_definition::prepare_stage1_typelib(THD *thd,
+ MEM_ROOT *mem_root,
+ handler *file,
+ ulonglong table_flags)
+{
+ /*
+ Pass the last parameter to prepare_interval_field() as follows:
+ - If we are preparing for an SP variable (file is NULL), we pass "false",
+ to force allocation and full copying of TYPELIB values on the given
+ mem_root, even if no character set conversion is needed. This is needed
+ because a life cycle of an SP variable is longer than the current query.
+
+ - If we are preparing for a CREATE TABLE, (file != NULL), we pass "true".
+ This will create the typelib in runtime memory - we will free the
+ occupied memory at the same time when we free this
+ sql_field -- at the end of execution.
+ Pass "true" as the last argument to reuse "interval_list"
+ values in "interval" in cases when no character conversion is needed,
+ to avoid extra copying.
+ */
+ if (prepare_interval_field(mem_root, file != NULL))
+ return true; // E.g. wrong values with commas: SET('a,b')
+ create_length_to_internal_length_typelib();
+
+ DBUG_ASSERT(file || !default_value); // SP variables have no default_value
+ if (default_value && default_value->expr->basic_const_item())
+ {
+ if ((charset != default_value->expr->collation.collation &&
+ prepare_stage1_convert_default(thd, mem_root, charset)) ||
+ prepare_stage1_check_typelib_default())
+ return true;
+ }
+ return false;
+}
+
+
+bool Column_definition::prepare_stage1_string(THD *thd,
+ MEM_ROOT *mem_root,
+ handler *file,
+ ulonglong table_flags)
+{
+ create_length_to_internal_length_string();
+ if (prepare_blob_field(thd))
+ return true;
+ DBUG_ASSERT(file || !default_value); // SP variables have no default_value
+ /*
+ Convert the default value from client character
+ set into the column character set if necessary.
+ We can only do this for constants as we have not yet run fix_fields.
+ But not for blobs, as they will be stored as SQL expressions, not
+ written down into the record image.
+ */
+ if (!(flags & BLOB_FLAG) && default_value &&
+ default_value->expr->basic_const_item() &&
+ charset != default_value->expr->collation.collation)
+ {
+ if (prepare_stage1_convert_default(thd, mem_root, charset))
+ return true;
+ }
+ return false;
+}
+
+
+bool Column_definition::prepare_stage1_bit(THD *thd,
+ MEM_ROOT *mem_root,
+ handler *file,
+ ulonglong table_flags)
+{
+ pack_flag= FIELDFLAG_NUMBER;
+ if (!(table_flags & HA_CAN_BIT_FIELD))
+ pack_flag|= FIELDFLAG_TREAT_BIT_AS_CHAR;
+ create_length_to_internal_length_bit();
+ return false;
+}
+
+
+bool Column_definition::prepare_stage1(THD *thd,
+ MEM_ROOT *mem_root,
+ handler *file,
+ ulonglong table_flags)
+{
+ return type_handler()->Column_definition_prepare_stage1(thd, mem_root,
+ this, file,
+ table_flags);
+}
+
+
+bool Column_definition::prepare_stage1_convert_default(THD *thd,
+ MEM_ROOT *mem_root,
+ CHARSET_INFO *cs)
+{
+ DBUG_ASSERT(thd->mem_root == mem_root);
+ Item *item;
+ if (!(item= default_value->expr->safe_charset_converter(thd, cs)))
+ {
+ my_error(ER_INVALID_DEFAULT, MYF(0), field_name.str);
+ return true; // Could not convert
+ }
+ /* Fix for prepare statement */
+ thd->change_item_tree(&default_value->expr, item);
+ return false;
+}
+
+
+bool Column_definition::prepare_stage1_check_typelib_default()
+{
+ StringBuffer<MAX_FIELD_WIDTH> str;
+ String *def= default_value->expr->val_str(&str);
+ bool not_found;
+ if (def == NULL) /* SQL "NULL" maps to NULL */
+ {
+ not_found= flags & NOT_NULL_FLAG;
+ }
+ else
+ {
+ not_found= false;
+ if (real_field_type() == MYSQL_TYPE_SET)
+ {
+ char *not_used;
+ uint not_used2;
+ find_set(interval, def->ptr(), def->length(),
+ charset, &not_used, &not_used2, &not_found);
+ }
+ else /* MYSQL_TYPE_ENUM */
+ {
+ def->length(charset->cset->lengthsp(charset,
+ def->ptr(), def->length()));
+ not_found= !find_type2(interval, def->ptr(), def->length(), charset);
+ }
+ }
+ if (not_found)
+ {
+ my_error(ER_INVALID_DEFAULT, MYF(0), field_name.str);
+ return true;
+ }
+ return false;
+}
+/*
+ This function adds a invisible field to field_list
+ SYNOPSIS
+ mysql_add_invisible_field()
+ thd Thread Object
+ field_list list of all table fields
+ field_name name/prefix of invisible field
+ ( Prefix in the case when it is
+ *INVISIBLE_FULL*
+ and given name is duplicate)
+ type_handler field data type
+ invisible
+ default value
+ RETURN VALUE
+ Create_field pointer
+*/
+int mysql_add_invisible_field(THD *thd, List<Create_field> * field_list,
+ const char *field_name, Type_handler *type_handler,
+ field_visibility_t invisible, Item* default_value)
+{
+ Create_field *fld= new(thd->mem_root)Create_field();
+ const char *new_name= NULL;
+ /* Get unique field name if invisible == INVISIBLE_FULL */
+ if (invisible == INVISIBLE_FULL)
+ {
+ if ((new_name= make_unique_invisible_field_name(thd, field_name,
+ field_list)))
+ {
+ fld->field_name.str= new_name;
+ fld->field_name.length= strlen(new_name);
+ }
+ else
+ return 1; //Should not happen
+ }
+ else
+ {
+ fld->field_name.str= thd->strmake(field_name, strlen(field_name));
+ fld->field_name.length= strlen(field_name);
+ }
+ fld->set_handler(type_handler);
+ fld->invisible= invisible;
+ if (default_value)
+ {
+ Virtual_column_info *v= new (thd->mem_root) Virtual_column_info();
+ v->expr= default_value;
+ v->utf8= 0;
+ fld->default_value= v;
+ }
+ field_list->push_front(fld, thd->mem_root);
+ return 0;
+}
+
+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->columns.push_back(new(thd->mem_root) Key_part_spec(field_name, 0),
+ thd->mem_root);
+ key_list->push_back(key, thd->mem_root);
+ return key;
+}
/*
Preparation for table creation
@@ -3235,7 +3367,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
{
const char *key_name;
Create_field *sql_field,*dup_field;
- uint field,null_fields,blob_columns,max_key_length;
+ uint field,null_fields,max_key_length;
ulong record_offset= 0;
KEY *key_info;
KEY_PART_INFO *key_part_info;
@@ -3248,7 +3380,24 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
bool tmp_table= create_table_mode == C_ALTER_TABLE;
DBUG_ENTER("mysql_prepare_create_table");
- LEX_STRING* connect_string = &create_info->connect_string;
+ DBUG_EXECUTE_IF("test_pseudo_invisible",{
+ mysql_add_invisible_field(thd, &alter_info->create_list,
+ "invisible", &type_handler_long, 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,
+ new (thd->mem_root)Item_int(thd, 9));
+ });
+ DBUG_EXECUTE_IF("test_invisible_index",{
+ LEX_CSTRING temp;
+ temp.str= "invisible";
+ temp.length= strlen("invisible");
+ mysql_add_invisible_index(thd, &alter_info->key_list
+ , &temp, Key::MULTIPLE);
+ });
+ 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,
@@ -3264,14 +3413,27 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
}
select_field_pos= alter_info->create_list.elements - select_field_count;
- null_fields=blob_columns=0;
+ null_fields= 0;
create_info->varchar= 0;
max_key_length= file->max_key_length();
- for (field_no=0; (sql_field=it++) ; field_no++)
+ /* Handle creation of sequences */
+ if (create_info->sequence)
{
- CHARSET_INFO *save_cs;
+ if (!(file->ha_table_flags() & HA_CAN_TABLES_WITHOUT_ROLLBACK))
+ {
+ my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0), file->table_type(),
+ "SEQUENCE");
+ DBUG_RETURN(TRUE);
+ }
+ /* The user specified fields: check that structure is ok */
+ if (check_sequence_fields(thd->lex, &alter_info->create_list))
+ DBUG_RETURN(TRUE);
+ }
+
+ for (field_no=0; (sql_field=it++) ; field_no++)
+ {
/*
Initialize length from its original value (number of characters),
which was set in the parser. This is necessary if we're
@@ -3279,198 +3441,47 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
*/
sql_field->length= sql_field->char_length;
/* Set field charset. */
- save_cs= sql_field->charset= get_sql_field_charset(sql_field, create_info);
+ sql_field->charset= get_sql_field_charset(sql_field, create_info);
if ((sql_field->flags & BINCMP_FLAG) &&
- !(sql_field->charset= find_bin_collation(sql_field->charset)))
- DBUG_RETURN(TRUE);
-
- if (sql_field->sql_type == MYSQL_TYPE_SET ||
- sql_field->sql_type == MYSQL_TYPE_ENUM)
- {
- uint32 dummy;
- CHARSET_INFO *cs= sql_field->charset;
- TYPELIB *interval= sql_field->interval;
-
- /*
- Create typelib from interval_list, and if necessary
- convert strings from client character set to the
- column character set.
- */
- if (!interval)
- {
- /*
- Create the typelib in runtime memory - we will free the
- occupied memory at the same time when we free this
- sql_field -- at the end of execution.
- */
- interval= sql_field->interval= typelib(thd->mem_root,
- sql_field->interval_list);
- List_iterator<String> int_it(sql_field->interval_list);
- String conv, *tmp;
- char comma_buf[5]; /* 5 bytes for 'filename' charset */
- DBUG_ASSERT(sizeof(comma_buf) >= cs->mbmaxlen);
- int comma_length= cs->cset->wc_mb(cs, ',', (uchar*) comma_buf,
- (uchar*) comma_buf +
- sizeof(comma_buf));
- DBUG_ASSERT(comma_length > 0);
- for (uint i= 0; (tmp= int_it++); i++)
- {
- size_t lengthsp;
- if (String::needs_conversion(tmp->length(), tmp->charset(),
- cs, &dummy))
- {
- uint cnv_errs;
- conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), cs, &cnv_errs);
- interval->type_names[i]= strmake_root(thd->mem_root, conv.ptr(),
- conv.length());
- interval->type_lengths[i]= conv.length();
- }
-
- // Strip trailing spaces.
- lengthsp= cs->cset->lengthsp(cs, interval->type_names[i],
- interval->type_lengths[i]);
- interval->type_lengths[i]= lengthsp;
- ((uchar *)interval->type_names[i])[lengthsp]= '\0';
- if (sql_field->sql_type == MYSQL_TYPE_SET)
- {
- if (cs->coll->instr(cs, interval->type_names[i],
- interval->type_lengths[i],
- comma_buf, comma_length, NULL, 0))
- {
- ErrConvString err(tmp->ptr(), tmp->length(), cs);
- my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "set", err.ptr());
- DBUG_RETURN(TRUE);
- }
- }
- }
- sql_field->interval_list.empty(); // Don't need interval_list anymore
- }
-
- if (sql_field->sql_type == MYSQL_TYPE_SET)
- {
- uint32 field_length;
- calculate_interval_lengths(cs, interval, &dummy, &field_length);
- sql_field->length= field_length + (interval->count - 1);
- }
- else /* MYSQL_TYPE_ENUM */
- {
- uint32 field_length;
- DBUG_ASSERT(sql_field->sql_type == MYSQL_TYPE_ENUM);
- calculate_interval_lengths(cs, interval, &field_length, &dummy);
- sql_field->length= field_length;
- }
- set_if_smaller(sql_field->length, MAX_FIELD_WIDTH-1);
- }
-
- if (sql_field->sql_type == MYSQL_TYPE_BIT)
- {
- sql_field->pack_flag= FIELDFLAG_NUMBER;
- if (file->ha_table_flags() & HA_CAN_BIT_FIELD)
- total_uneven_bit_length+= sql_field->length & 7;
- else
- sql_field->pack_flag|= FIELDFLAG_TREAT_BIT_AS_CHAR;
- }
-
- sql_field->create_length_to_internal_length();
- if (prepare_blob_field(thd, sql_field))
- DBUG_RETURN(TRUE);
-
- /*
- Convert the default value from client character
- set into the column character set if necessary.
- We can only do this for constants as we have not yet run fix_fields.
- */
- if (sql_field->default_value &&
- sql_field->default_value->expr->basic_const_item() &&
- (!sql_field->field ||
- sql_field->field->default_value != sql_field->default_value) &&
- save_cs != sql_field->default_value->expr->collation.collation &&
- (sql_field->sql_type == MYSQL_TYPE_VAR_STRING ||
- sql_field->sql_type == MYSQL_TYPE_STRING ||
- sql_field->sql_type == MYSQL_TYPE_SET ||
- sql_field->sql_type == MYSQL_TYPE_TINY_BLOB ||
- sql_field->sql_type == MYSQL_TYPE_MEDIUM_BLOB ||
- sql_field->sql_type == MYSQL_TYPE_LONG_BLOB ||
- sql_field->sql_type == MYSQL_TYPE_BLOB ||
- sql_field->sql_type == MYSQL_TYPE_ENUM))
- {
- Item *item;
- if (!(item= sql_field->default_value->expr->
- safe_charset_converter(thd, save_cs)))
- {
- /* Could not convert */
- my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
- DBUG_RETURN(TRUE);
- }
- /* Fix for prepare statement */
- thd->change_item_tree(&sql_field->default_value->expr, item);
- }
+ !(sql_field->charset= find_bin_collation(sql_field->charset)))
+ DBUG_RETURN(true);
/* Virtual fields are always NULL */
if (sql_field->vcol_info)
sql_field->flags&= ~NOT_NULL_FLAG;
- if (sql_field->default_value &&
- sql_field->default_value->expr->basic_const_item() &&
- (sql_field->sql_type == MYSQL_TYPE_SET ||
- sql_field->sql_type == MYSQL_TYPE_ENUM))
- {
- StringBuffer<MAX_FIELD_WIDTH> str;
- String *def= sql_field->default_value->expr->val_str(&str);
- bool not_found;
- if (def == NULL) /* SQL "NULL" maps to NULL */
- {
- not_found= sql_field->flags & NOT_NULL_FLAG;
- }
- else
- {
- not_found= false;
- if (sql_field->sql_type == MYSQL_TYPE_SET)
- {
- char *not_used;
- uint not_used2;
- find_set(sql_field->interval, def->ptr(), def->length(),
- sql_field->charset, &not_used, &not_used2, &not_found);
- }
- else /* MYSQL_TYPE_ENUM */
- {
- def->length(sql_field->charset->cset->lengthsp(sql_field->charset,
- def->ptr(), def->length()));
- not_found= !find_type2(sql_field->interval, def->ptr(),
- def->length(), sql_field->charset);
- }
- }
+ if (sql_field->prepare_stage1(thd, thd->mem_root,
+ file, file->ha_table_flags()))
+ DBUG_RETURN(true);
- if (not_found)
- {
- my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
- DBUG_RETURN(TRUE);
- }
- }
+ if (sql_field->real_field_type() == MYSQL_TYPE_BIT &&
+ file->ha_table_flags() & HA_CAN_BIT_FIELD)
+ total_uneven_bit_length+= sql_field->length & 7;
if (!(sql_field->flags & NOT_NULL_FLAG))
null_fields++;
- if (check_column_name(sql_field->field_name))
+ if (check_column_name(sql_field->field_name.str))
{
- my_error(ER_WRONG_COLUMN_NAME, MYF(0), sql_field->field_name);
+ my_error(ER_WRONG_COLUMN_NAME, MYF(0), sql_field->field_name.str);
DBUG_RETURN(TRUE);
}
/* Check if we have used the same field name before */
for (dup_no=0; (dup_field=it2++) != sql_field; dup_no++)
{
- if (my_strcasecmp(system_charset_info,
- sql_field->field_name,
- dup_field->field_name) == 0)
+ if (lex_string_cmp(system_charset_info,
+ &sql_field->field_name,
+ &dup_field->field_name) == 0)
{
/*
If this was a CREATE ... SELECT statement, accept a field
redefinition if we are changing a field in the SELECT part
*/
- if (field_no < select_field_pos || dup_no >= select_field_pos)
+ if (field_no < select_field_pos || dup_no >= select_field_pos ||
+ dup_field->invisible >= INVISIBLE_SYSTEM)
{
- my_error(ER_DUP_FIELDNAME, MYF(0), sql_field->field_name);
+ my_error(ER_DUP_FIELDNAME, MYF(0), sql_field->field_name.str);
DBUG_RETURN(TRUE);
}
else
@@ -3481,34 +3492,10 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
If we are replacing a BIT field, revert the increment
of total_uneven_bit_length that was done above.
*/
- if (sql_field->sql_type == MYSQL_TYPE_BIT &&
+ if (sql_field->real_field_type() == MYSQL_TYPE_BIT &&
file->ha_table_flags() & HA_CAN_BIT_FIELD)
total_uneven_bit_length-= sql_field->length & 7;
- sql_field->default_value= dup_field->default_value;
- sql_field->sql_type= dup_field->sql_type;
-
- /*
- If we are replacing a field with a BIT field, we need
- to initialize pack_flag. Note that we do not need to
- increment total_uneven_bit_length here as this dup_field
- has already been processed.
- */
- if (sql_field->sql_type == MYSQL_TYPE_BIT)
- {
- sql_field->pack_flag= FIELDFLAG_NUMBER;
- if (!(file->ha_table_flags() & HA_CAN_BIT_FIELD))
- sql_field->pack_flag|= FIELDFLAG_TREAT_BIT_AS_CHAR;
- }
-
- sql_field->charset= (dup_field->charset ?
- dup_field->charset :
- create_info->default_table_charset);
- sql_field->length= dup_field->char_length;
- sql_field->pack_length= dup_field->pack_length;
- sql_field->key_length= dup_field->key_length;
- sql_field->decimals= dup_field->decimals;
- sql_field->unireg_check= dup_field->unireg_check;
/*
We're making one field from two, the result field will have
dup_field->flags as flags. If we've incremented null_fields
@@ -3516,10 +3503,10 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
*/
if (!(sql_field->flags & NOT_NULL_FLAG))
null_fields--;
- sql_field->flags= dup_field->flags;
- sql_field->create_length_to_internal_length();
- sql_field->interval= dup_field->interval;
- sql_field->vcol_info= dup_field->vcol_info;
+
+ if (sql_field->redefine_stage1(dup_field, file, create_info))
+ DBUG_RETURN(true);
+
it2.remove(); // Remove first (create) definition
select_field_pos--;
break;
@@ -3528,7 +3515,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
}
/* Don't pack rows in old tables if the user has requested this */
if ((sql_field->flags & BLOB_FLAG) ||
- (sql_field->sql_type == MYSQL_TYPE_VARCHAR &&
+ (sql_field->real_field_type() == MYSQL_TYPE_VARCHAR &&
create_info->row_type != ROW_TYPE_FIXED))
(*db_options)|= HA_OPTION_PACK_RECORD;
it2.rewind();
@@ -3542,11 +3529,9 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
while ((sql_field=it++))
{
DBUG_ASSERT(sql_field->charset != 0);
-
- if (prepare_create_field(sql_field, &blob_columns,
- file->ha_table_flags()))
+ if (sql_field->prepare_stage2(file, file->ha_table_flags()))
DBUG_RETURN(TRUE);
- if (sql_field->sql_type == MYSQL_TYPE_VARCHAR)
+ if (sql_field->real_field_type() == MYSQL_TYPE_VARCHAR)
create_info->varchar= TRUE;
sql_field->offset= record_offset;
if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
@@ -3563,8 +3548,12 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
*/
if (sql_field->stored_in_db())
record_offset+= sql_field->pack_length;
+ if (sql_field->flags & VERS_SYSTEM_FIELD)
+ continue;
}
- /* Update virtual fields' offset*/
+ /* Update virtual fields' offset and give error if
+ All fields are invisible */
+ bool is_all_invisible= true;
it.rewind();
while ((sql_field=it++))
{
@@ -3573,6 +3562,13 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
sql_field->offset= record_offset;
record_offset+= sql_field->pack_length;
}
+ if (sql_field->invisible == VISIBLE)
+ is_all_invisible= false;
+ }
+ if (is_all_invisible)
+ {
+ my_error(ER_TABLE_MUST_HAVE_COLUMNS, MYF(0));
+ DBUG_RETURN(TRUE);
}
if (auto_increment > 1)
{
@@ -3586,12 +3582,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
DBUG_RETURN(TRUE);
}
- if (blob_columns && (file->ha_table_flags() & HA_NO_BLOBS))
- {
- my_error(ER_TABLE_CANT_HANDLE_BLOB, MYF(0), file->table_type());
- DBUG_RETURN(TRUE);
- }
-
/*
CREATE TABLE[with auto_increment column] SELECT is unsafe as the rows
inserted in the created table depends on the order of the rows fetched
@@ -3622,9 +3612,9 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
if (key->type == Key::FOREIGN_KEY)
{
fk_key_count++;
- if (((Foreign_key *)key)->validate(alter_info->create_list))
- DBUG_RETURN(TRUE);
Foreign_key *fk_key= (Foreign_key*) key;
+ if (fk_key->validate(alter_info->create_list))
+ DBUG_RETURN(TRUE);
if (fk_key->ref_columns.elements &&
fk_key->ref_columns.elements != fk_key->columns.elements)
{
@@ -3685,6 +3675,17 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name.str);
DBUG_RETURN(TRUE);
}
+ if (key->type == Key::PRIMARY && key->name.str &&
+ my_strcasecmp(system_charset_info, key->name.str, primary_key_name) != 0)
+ {
+ bool sav_abort_on_warning= thd->abort_on_warning;
+ thd->abort_on_warning= FALSE; /* Don't make an error out of this. */
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_WRONG_NAME_FOR_INDEX,
+ "Name '%-.100s' ignored for PRIMARY key.",
+ key->name.str);
+ thd->abort_on_warning= sav_abort_on_warning;
+ }
}
tmp=file->max_keys();
if (*key_count > tmp)
@@ -3813,7 +3814,17 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
key->key_create_info.block_size :
create_info->key_block_size);
- if (key_info->block_size)
+ /*
+ Remember block_size for the future if the block size was given
+ either for key or table and it was given for the key during
+ create/alter table or we have an active key_block_size for the
+ table.
+ The idea is that table specific key_block_size > 0 will only affect
+ new keys and old keys will remember their original value.
+ */
+ if (key_info->block_size &&
+ ((key->key_create_info.flags & HA_USES_BLOCK_SIZE) ||
+ create_info->key_block_size))
key_info->flags|= HA_USES_BLOCK_SIZE;
List_iterator<Key_part_spec> cols(key->columns), cols2(key->columns);
@@ -3825,29 +3836,47 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
it.rewind();
field=0;
while ((sql_field=it++) &&
- my_strcasecmp(system_charset_info,
- column->field_name.str,
- sql_field->field_name))
+ lex_string_cmp(system_charset_info,
+ &column->field_name,
+ &sql_field->field_name))
field++;
+ /*
+ Either field is not present or field visibility is > INVISIBLE_USER
+ */
if (!sql_field)
{
my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name.str);
DBUG_RETURN(TRUE);
}
+ if (sql_field->invisible > INVISIBLE_USER &&
+ !(sql_field->flags & VERS_SYSTEM_FIELD) &&
+ !key->invisible && DBUG_EVALUATE_IF("test_invisible_index", 0, 1))
+ {
+ my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name.str);
+ DBUG_RETURN(TRUE);
+ }
while ((dup_column= cols2++) != column)
{
- if (!my_strcasecmp(system_charset_info,
- column->field_name.str, dup_column->field_name.str))
+ if (!lex_string_cmp(system_charset_info,
+ &column->field_name, &dup_column->field_name))
{
my_error(ER_DUP_FIELDNAME, MYF(0), column->field_name.str);
DBUG_RETURN(TRUE);
}
}
+
+ if (sql_field->compression_method())
+ {
+ my_error(ER_COMPRESSED_COLUMN_USED_AS_KEY, MYF(0),
+ column->field_name.str);
+ DBUG_RETURN(TRUE);
+ }
+
cols2.rewind();
if (key->type == Key::FULLTEXT)
{
- if ((sql_field->sql_type != MYSQL_TYPE_STRING &&
- sql_field->sql_type != MYSQL_TYPE_VARCHAR &&
+ 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
@@ -3925,7 +3954,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
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,
+ check_expression(sql_field->vcol_info, &sql_field->field_name,
VCOL_GENERATED_STORED);
DBUG_ASSERT(thd->is_error());
DBUG_RETURN(TRUE);
@@ -3973,7 +4002,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
if (f_is_blob(sql_field->pack_flag))
{
key_part_length= MY_MIN(column->length,
- blob_length_by_type(sql_field->sql_type)
+ blob_length_by_type(sql_field->real_field_type())
* sql_field->charset->mbmaxlen);
if (key_part_length > max_key_length ||
key_part_length > file->max_key_part_length())
@@ -4003,7 +4032,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
// is prefix length bigger than field length?
(column->length > key_part_length ||
// can the field have a partial key?
- !Field::type_can_have_key_part (sql_field->sql_type) ||
+ !sql_field->type_handler()->type_can_have_key_part() ||
// a packed field can't be used in a partial key
f_is_packed(sql_field->pack_flag) ||
// does the storage engine allow prefixed search?
@@ -4047,12 +4076,12 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
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->sql_type == MYSQL_TYPE_STRING ||
- sql_field->sql_type == MYSQL_TYPE_VARCHAR ||
+ (sql_field->real_field_type() == MYSQL_TYPE_STRING ||
+ sql_field->real_field_type() == MYSQL_TYPE_VARCHAR ||
f_is_blob(sql_field->pack_flag))))
{
if ((column_nr == 0 && f_is_blob(sql_field->pack_flag)) ||
- sql_field->sql_type == MYSQL_TYPE_VARCHAR)
+ 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;
@@ -4079,19 +4108,20 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
primary_key=1;
}
else if (!(key_name= key->name.str))
- key_name=make_unique_key_name(thd, sql_field->field_name,
+ key_name=make_unique_key_name(thd, sql_field->field_name.str,
*key_info_buffer, key_info);
if (check_if_keyname_exists(key_name, *key_info_buffer, key_info))
{
my_error(ER_DUP_KEYNAME, MYF(0), key_name);
DBUG_RETURN(TRUE);
}
- key_info->name=(char*) key_name;
+ key_info->name.str= (char*) key_name;
+ key_info->name.length= strlen(key_name);
}
}
- if (!key_info->name || check_column_name(key_info->name))
+ if (!key_info->name.str || check_column_name(key_info->name.str))
{
- my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key_info->name);
+ my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key_info->name.str);
DBUG_RETURN(TRUE);
}
if (key->type == Key::UNIQUE && !(key_info->flags & HA_NULL_PART_KEY))
@@ -4104,8 +4134,9 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
}
if (validate_comment_length(thd, &key->key_create_info.comment,
- INDEX_COMMENT_MAXLEN, ER_TOO_LONG_INDEX_COMMENT,
- key_info->name))
+ INDEX_COMMENT_MAXLEN,
+ ER_TOO_LONG_INDEX_COMMENT,
+ key_info->name.str))
DBUG_RETURN(TRUE);
key_info->comment.length= key->key_create_info.comment.length;
@@ -4122,7 +4153,9 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
}
if (!unique_key && !primary_key &&
- (file->ha_table_flags() & HA_REQUIRE_PRIMARY_KEY))
+ ((file->ha_table_flags() & HA_REQUIRE_PRIMARY_KEY) ||
+ ((file->ha_table_flags() & HA_WANTS_PRIMARY_KEY) &&
+ !create_info->sequence)))
{
my_message(ER_REQUIRES_PRIMARY_KEY, ER_THD(thd, ER_REQUIRES_PRIMARY_KEY),
MYF(0));
@@ -4152,8 +4185,9 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
if (!sql_field->default_value &&
!sql_field->has_default_function() &&
(sql_field->flags & NOT_NULL_FLAG) &&
- (!is_timestamp_type(sql_field->sql_type) ||
- opt_explicit_defaults_for_timestamp))
+ (!sql_field->is_timestamp_type() ||
+ opt_explicit_defaults_for_timestamp)&&
+ !sql_field->vers_sys_field())
{
sql_field->flags|= NO_DEFAULT_VALUE_FLAG;
sql_field->pack_flag|= FIELDFLAG_NO_DEFAULT;
@@ -4161,7 +4195,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
if (thd->variables.sql_mode & MODE_NO_ZERO_DATE &&
!sql_field->default_value && !sql_field->vcol_info &&
- is_timestamp_type(sql_field->sql_type) &&
+ !sql_field->vers_sys_field() &&
+ sql_field->is_timestamp_type() &&
!opt_explicit_defaults_for_timestamp &&
(sql_field->flags & NOT_NULL_FLAG) &&
(type == Field::NONE || type == Field::TIMESTAMP_UN_FIELD))
@@ -4180,7 +4215,15 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
'column_name TIMESTAMP DEFAULT 0'.
*/
- my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
+ my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name.str);
+ DBUG_RETURN(TRUE);
+ }
+ if (sql_field->invisible == INVISIBLE_USER &&
+ sql_field->flags & NOT_NULL_FLAG &&
+ sql_field->flags & NO_DEFAULT_VALUE_FLAG)
+ {
+ my_error(ER_INVISIBLE_NOT_NULL_WITHOUT_DEFAULT, MYF(0),
+ sql_field->field_name.str);
DBUG_RETURN(TRUE);
}
}
@@ -4202,9 +4245,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
const Virtual_column_info *dup_check;
while ((dup_check= dup_it++) && dup_check != check)
{
- if (check->name.length == dup_check->name.length &&
- my_strcasecmp(system_charset_info,
- check->name.str, dup_check->name.str) == 0)
+ if (!lex_string_cmp(system_charset_info,
+ &check->name, &dup_check->name))
{
my_error(ER_DUP_CONSTRAINT_NAME, MYF(0), "CHECK", check->name.str);
DBUG_RETURN(TRUE);
@@ -4239,7 +4281,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
my_error(ER_TOO_LONG_IDENT, MYF(0), check->name.str);
DBUG_RETURN(TRUE);
}
- if (check_expression(check, check->name.str, VCOL_CHECK_TABLE))
+ if (check_expression(check, &check->name, VCOL_CHECK_TABLE))
DBUG_RETURN(TRUE);
}
}
@@ -4282,11 +4324,10 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
@retval true Error found
@retval false On Success
*/
-bool validate_comment_length(THD *thd, LEX_STRING *comment, size_t max_len,
+bool validate_comment_length(THD *thd, LEX_CSTRING *comment, size_t max_len,
uint err_code, const char *name)
{
DBUG_ENTER("validate_comment_length");
-
if (comment->length == 0)
DBUG_RETURN(false);
@@ -4341,7 +4382,8 @@ bool validate_comment_length(THD *thd, LEX_STRING *comment, size_t max_len,
*/
static void set_table_default_charset(THD *thd,
- HA_CREATE_INFO *create_info, char *db)
+ HA_CREATE_INFO *create_info,
+ const LEX_CSTRING *db)
{
/*
If the table character set was not given explicitly,
@@ -4352,7 +4394,7 @@ static void set_table_default_charset(THD *thd,
{
Schema_specification_st db_info;
- load_db_opt_by_name(thd, db, &db_info);
+ load_db_opt_by_name(thd, db->str, &db_info);
create_info->default_table_charset= db_info.default_table_charset;
}
@@ -4364,7 +4406,6 @@ static void set_table_default_charset(THD *thd,
SYNOPSIS
prepare_blob_field()
- sql_field Field to check
RETURN
0 ok
@@ -4372,45 +4413,42 @@ static void set_table_default_charset(THD *thd,
In this case the error is given
*/
-static bool prepare_blob_field(THD *thd, Column_definition *sql_field)
+bool Column_definition::prepare_blob_field(THD *thd)
{
- DBUG_ENTER("prepare_blob_field");
+ DBUG_ENTER("Column_definition::prepare_blob_field");
- if (sql_field->length > MAX_FIELD_VARCHARLENGTH &&
- !(sql_field->flags & BLOB_FLAG))
+ if (length > MAX_FIELD_VARCHARLENGTH && !(flags & BLOB_FLAG))
{
/* Convert long VARCHAR columns to TEXT or BLOB */
char warn_buff[MYSQL_ERRMSG_SIZE];
if (thd->is_strict_mode())
{
- my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), sql_field->field_name,
- static_cast<ulong>(MAX_FIELD_VARCHARLENGTH /
- sql_field->charset->mbmaxlen));
+ my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), field_name.str,
+ static_cast<ulong>(MAX_FIELD_VARCHARLENGTH / charset->mbmaxlen));
DBUG_RETURN(1);
}
- sql_field->sql_type= MYSQL_TYPE_BLOB;
- sql_field->flags|= BLOB_FLAG;
+ set_handler(&type_handler_blob);
+ flags|= BLOB_FLAG;
my_snprintf(warn_buff, sizeof(warn_buff), ER_THD(thd, ER_AUTO_CONVERT),
- sql_field->field_name,
- (sql_field->charset == &my_charset_bin) ? "VARBINARY" :
- "VARCHAR",
- (sql_field->charset == &my_charset_bin) ? "BLOB" : "TEXT");
+ field_name.str,
+ (charset == &my_charset_bin) ? "VARBINARY" : "VARCHAR",
+ (charset == &my_charset_bin) ? "BLOB" : "TEXT");
push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_AUTO_CONVERT,
warn_buff);
}
- if ((sql_field->flags & BLOB_FLAG) && sql_field->length)
+ if ((flags & BLOB_FLAG) && length)
{
- if (sql_field->sql_type == FIELD_TYPE_BLOB ||
- sql_field->sql_type == FIELD_TYPE_TINY_BLOB ||
- sql_field->sql_type == FIELD_TYPE_MEDIUM_BLOB)
+ if (real_field_type() == FIELD_TYPE_BLOB ||
+ real_field_type() == FIELD_TYPE_TINY_BLOB ||
+ real_field_type() == FIELD_TYPE_MEDIUM_BLOB)
{
/* The user has given a length to the blob column */
- sql_field->sql_type= get_blob_type_from_length((ulong)sql_field->length);
- sql_field->pack_length= calc_pack_length(sql_field->sql_type, 0);
+ set_handler(Type_handler::blob_type_handler((uint) length));
+ pack_length= type_handler()->calc_pack_length(0);
}
- sql_field->length= 0;
+ length= 0;
}
DBUG_RETURN(0);
}
@@ -4423,52 +4461,64 @@ static bool prepare_blob_field(THD *thd, Column_definition *sql_field)
SYNOPSIS
sp_prepare_create_field()
- thd Thread object
- sql_field Field to prepare
+ thd Thread object
+ mem_root Memory root to allocate components on (e.g. interval)
DESCRIPTION
Prepares the field structures for field creation.
*/
-void sp_prepare_create_field(THD *thd, Column_definition *sql_field)
+bool Column_definition::sp_prepare_create_field(THD *thd, MEM_ROOT *mem_root)
{
- if (sql_field->sql_type == MYSQL_TYPE_SET ||
- sql_field->sql_type == MYSQL_TYPE_ENUM)
+ return prepare_stage1(thd, mem_root, NULL, HA_CAN_GEOMETRY) ||
+ prepare_stage2(NULL, HA_CAN_GEOMETRY);
+}
+
+
+static bool vers_prepare_keys(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);
+
+ List_iterator<Key> key_it(alter_info->key_list);
+ Key *key= NULL;
+ while ((key=key_it++))
{
- uint32 field_length, dummy;
- if (sql_field->sql_type == MYSQL_TYPE_SET)
- {
- calculate_interval_lengths(sql_field->charset,
- sql_field->interval, &dummy,
- &field_length);
- sql_field->length= field_length +
- (sql_field->interval->count - 1);
- }
- else /* MYSQL_TYPE_ENUM */
+ 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++))
{
- calculate_interval_lengths(sql_field->charset,
- sql_field->interval,
- &field_length, &dummy);
- sql_field->length= field_length;
+ if (!my_strcasecmp(system_charset_info,
+ row_start_field,
+ key_part->field_name.str) ||
+
+ !my_strcasecmp(system_charset_info,
+ row_end_field,
+ key_part->field_name.str))
+ break;
}
- set_if_smaller(sql_field->length, MAX_FIELD_WIDTH-1);
- }
+ if (key_part)
+ continue; // Key already contains Sys_start or Sys_end
- if (sql_field->sql_type == MYSQL_TYPE_BIT)
- {
- sql_field->pack_flag= FIELDFLAG_NUMBER |
- FIELDFLAG_TREAT_BIT_AS_CHAR;
+ Key_part_spec *key_part_sys_end_col=
+ new (thd->mem_root) Key_part_spec(&create_info->vers_info.as_row.end, 0);
+ key->columns.push_back(key_part_sys_end_col);
}
- sql_field->create_length_to_internal_length();
- DBUG_ASSERT(sql_field->default_value == 0);
- /* Can't go wrong as sql_field->def is not defined */
- (void) prepare_blob_field(thd, sql_field);
-}
+ return false;
+}
handler *mysql_create_frm_image(THD *thd,
- const char *db, const char *table_name,
+ const LEX_CSTRING *db, const LEX_CSTRING *table_name,
HA_CREATE_INFO *create_info,
Alter_info *alter_info, int create_table_mode,
KEY **key_info,
@@ -4485,16 +4535,14 @@ handler *mysql_create_frm_image(THD *thd,
DBUG_RETURN(NULL);
}
- set_table_default_charset(thd, create_info, (char*) db);
+ set_table_default_charset(thd, create_info, db);
db_options= create_info->table_options_with_row_type();
- if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root,
- create_info->db_type)))
- {
- mem_alloc_error(sizeof(handler));
+ if (unlikely(!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root,
+ create_info->db_type))))
DBUG_RETURN(NULL);
- }
+
#ifdef WITH_PARTITION_STORAGE_ENGINE
partition_info *part_info= thd->work_part_info;
@@ -4507,11 +4555,9 @@ handler *mysql_create_frm_image(THD *thd,
object with the default settings.
*/
thd->work_part_info= part_info= new partition_info();
- if (!part_info)
- {
- mem_alloc_error(sizeof(partition_info));
+ if (unlikely(!part_info))
goto err;
- }
+
file->set_auto_partitions(part_info);
part_info->default_engine_type= create_info->db_type;
part_info->is_auto_partitioned= TRUE;
@@ -4537,15 +4583,16 @@ handler *mysql_create_frm_image(THD *thd,
{
if (part_elem->part_comment)
{
- LEX_STRING comment= {
- part_elem->part_comment, strlen(part_elem->part_comment)
+ LEX_CSTRING comment= { part_elem->part_comment,
+ strlen(part_elem->part_comment)
};
if (validate_comment_length(thd, &comment,
TABLE_PARTITION_COMMENT_MAXLEN,
ER_TOO_LONG_TABLE_PARTITION_COMMENT,
part_elem->partition_name))
DBUG_RETURN(NULL);
- part_elem->part_comment[comment.length]= '\0';
+ /* cut comment length. Safe to do in all cases */
+ ((char*)part_elem->part_comment)[comment.length]= '\0';
}
if (part_elem->subpartitions.elements)
{
@@ -4555,7 +4602,7 @@ handler *mysql_create_frm_image(THD *thd,
{
if (subpart_elem->part_comment)
{
- LEX_STRING comment= {
+ LEX_CSTRING comment= {
subpart_elem->part_comment, strlen(subpart_elem->part_comment)
};
if (validate_comment_length(thd, &comment,
@@ -4563,7 +4610,8 @@ handler *mysql_create_frm_image(THD *thd,
ER_TOO_LONG_TABLE_PARTITION_COMMENT,
subpart_elem->partition_name))
DBUG_RETURN(NULL);
- subpart_elem->part_comment[comment.length]= '\0';
+ /* cut comment length. Safe to do in all cases */
+ ((char*)subpart_elem->part_comment)[comment.length]= '\0';
}
}
}
@@ -4606,6 +4654,12 @@ handler *mysql_create_frm_image(THD *thd,
goto err;
part_info->default_engine_type= engine_type;
+ if (part_info->vers_info && !create_info->versioned())
+ {
+ my_error(ER_VERS_NOT_VERSIONED, MYF(0), table_name->str);
+ goto err;
+ }
+
/*
We reverse the partitioning parser and generate a standard format
for syntax stored in frm file.
@@ -4617,7 +4671,10 @@ handler *mysql_create_frm_image(THD *thd,
part_info->part_info_string= part_syntax_buf;
part_info->part_info_len= syntax_len;
if ((!(engine_type->partition_flags &&
- engine_type->partition_flags() & HA_CAN_PARTITION)) ||
+ ((engine_type->partition_flags() & HA_CAN_PARTITION) ||
+ (part_info->part_type == VERSIONING_PARTITION &&
+ engine_type->partition_flags() & HA_ONLY_VERS_PARTITION))
+ )) ||
create_info->db_type == partition_hton)
{
/*
@@ -4668,12 +4725,9 @@ handler *mysql_create_frm_image(THD *thd,
engines in partition clauses.
*/
delete file;
- if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root,
- engine_type)))
- {
- mem_alloc_error(sizeof(handler));
+ if (unlikely(!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root,
+ engine_type))))
DBUG_RETURN(NULL);
- }
}
}
/*
@@ -4698,6 +4752,13 @@ handler *mysql_create_frm_image(THD *thd,
}
#endif
+ if (create_info->versioned())
+ {
+ if(vers_prepare_keys(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))
@@ -4748,13 +4809,14 @@ err:
@retval 0 OK
@retval 1 error
- @retval -1 table existed but IF EXISTS was used
+ @retval -1 table existed but IF NOT EXISTS was used
*/
static
int create_table_impl(THD *thd,
- const char *orig_db, const char *orig_table_name,
- const char *db, const char *table_name,
+ const LEX_CSTRING *orig_db,
+ const LEX_CSTRING *orig_table_name,
+ const LEX_CSTRING *db, const LEX_CSTRING *table_name,
const char *path,
const DDL_options_st options,
HA_CREATE_INFO *create_info,
@@ -4765,14 +4827,14 @@ int create_table_impl(THD *thd,
uint *key_count,
LEX_CUSTRING *frm)
{
- const char *alias;
+ LEX_CSTRING *alias;
handler *file= 0;
int error= 1;
bool frm_only= create_table_mode == C_ALTER_TABLE_FRM_ONLY;
bool internal_tmp_table= create_table_mode == C_ALTER_TABLE || frm_only;
DBUG_ENTER("mysql_create_table_no_lock");
DBUG_PRINT("enter", ("db: '%s' table: '%s' tmp: %d path: %s",
- db, table_name, internal_tmp_table, path));
+ db->str, table_name->str, internal_tmp_table, path));
if (fix_constraints_names(thd, &alter_info->check_constraint_list))
DBUG_RETURN(1);
@@ -4792,12 +4854,16 @@ int create_table_impl(THD *thd,
create_info->data_file_name= create_info->index_file_name= 0;
}
else
- if (error_if_data_home_dir(create_info->data_file_name, "DATA DIRECTORY") ||
- error_if_data_home_dir(create_info->index_file_name, "INDEX DIRECTORY")||
- check_partition_dirs(thd->lex->part_info))
- goto err;
+ {
+ if (unlikely(error_if_data_home_dir(create_info->data_file_name,
+ "DATA DIRECTORY")) ||
+ unlikely(error_if_data_home_dir(create_info->index_file_name,
+ "INDEX DIRECTORY")) ||
+ unlikely(check_partition_dirs(thd->lex->part_info)))
+ goto err;
+ }
- alias= table_case_name(create_info, table_name);
+ alias= const_cast<LEX_CSTRING*>(table_case_name(create_info, table_name));
/* Check if table exists */
if (create_info->tmp_table())
@@ -4806,7 +4872,7 @@ int create_table_impl(THD *thd,
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, table_name);
+ TABLE *tmp_table= thd->find_temporary_table(db->str, table_name->str);
if (tmp_table)
{
@@ -4824,7 +4890,7 @@ int create_table_impl(THD *thd,
goto warn;
else
{
- my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias);
+ my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias->str);
goto err;
}
/*
@@ -4845,14 +4911,10 @@ int create_table_impl(THD *thd,
{
if (options.or_replace())
{
- LEX_STRING db_name= {(char *) db, strlen(db)};
- LEX_STRING tab_name= {(char *) table_name, strlen(table_name)};
- (void) delete_statistics_for_table(thd, &db_name, &tab_name);
+ (void) delete_statistics_for_table(thd, db, table_name);
TABLE_LIST table_list;
- table_list.init_one_table(db, strlen(db), table_name,
- strlen(table_name), table_name,
- TL_WRITE_ALLOW_WRITE);
+ table_list.init_one_table(db, table_name, 0, TL_WRITE_ALLOW_WRITE);
table_list.table= create_info->table;
if (check_if_log_table(&table_list, TRUE, "CREATE OR REPLACE"))
@@ -4864,7 +4926,7 @@ int create_table_impl(THD *thd,
*/
(void) trans_rollback_stmt(thd);
/* Remove normal table without logging. Keep tables locked */
- if (mysql_rm_table_no_locks(thd, &table_list, 0, 0, 0, 1, 1))
+ if (mysql_rm_table_no_locks(thd, &table_list, 0, 0, 0, 0, 1, 1))
goto err;
/*
@@ -4887,7 +4949,7 @@ int create_table_impl(THD *thd,
goto warn;
else
{
- my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);
+ my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name->str);
goto err;
}
}
@@ -4895,7 +4957,7 @@ int create_table_impl(THD *thd,
THD_STAGE_INFO(thd, stage_creating_table);
- if (check_engine(thd, orig_db, orig_table_name, create_info))
+ if (check_engine(thd, orig_db->str, orig_table_name->str, create_info))
goto err;
if (create_table_mode == C_ASSISTED_DISCOVERY)
@@ -4915,7 +4977,7 @@ int create_table_impl(THD *thd,
goto err;
}
- init_tmp_table_share(thd, &share, db, 0, table_name, path);
+ init_tmp_table_share(thd, &share, db->str, 0, table_name->str, path);
/* prepare everything for discovery */
share.field= &no_fields;
@@ -4957,7 +5019,7 @@ int create_table_impl(THD *thd,
*/
if (!file || thd->is_error())
goto err;
- if (rea_create_table(thd, frm, path, db, table_name, create_info,
+ if (rea_create_table(thd, frm, path, db->str, table_name->str, create_info,
file, frm_only))
goto err;
}
@@ -4966,7 +5028,9 @@ int create_table_impl(THD *thd,
if (!frm_only && create_info->tmp_table())
{
TABLE *table= thd->create_and_open_tmp_table(create_info->db_type, frm,
- path, db, table_name, true);
+ path, db->str,
+ table_name->str, true,
+ false);
if (!table)
{
@@ -4996,11 +5060,11 @@ int create_table_impl(THD *thd,
TABLE table;
TABLE_SHARE share;
- init_tmp_table_share(thd, &share, db, 0, table_name, path);
+ init_tmp_table_share(thd, &share, db->str, 0, table_name->str, path);
bool result= (open_table_def(thd, &share, GTS_TABLE) ||
- open_table_from_share(thd, &share, "", 0, (uint) READ_ALL,
- 0, &table, true));
+ open_table_from_share(thd, &share, &empty_clex_str, 0,
+ (uint) READ_ALL, 0, &table, true));
if (!result)
(void) closefrm(&table);
@@ -5016,7 +5080,7 @@ int create_table_impl(THD *thd,
}
}
#endif
-
+
error= 0;
err:
THD_STAGE_INFO(thd, stage_after_create);
@@ -5029,20 +5093,28 @@ warn:
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_TABLE_EXISTS_ERROR,
ER_THD(thd, ER_TABLE_EXISTS_ERROR),
- alias);
+ alias->str);
goto err;
}
/**
Simple wrapper around create_table_impl() to be used
in various version of CREATE TABLE statement.
+
+ @result
+ 1 unspefied error
+ 2 error; Don't log create statement
+ 0 ok
+ -1 Table was used with IF NOT EXISTS and table existed (warning, not error)
*/
int mysql_create_table_no_lock(THD *thd,
- const char *db, const char *table_name,
- Table_specification_st *create_info,
- Alter_info *alter_info, bool *is_trans,
- int create_table_mode)
+ const LEX_CSTRING *db,
+ const LEX_CSTRING *table_name,
+ Table_specification_st *create_info,
+ Alter_info *alter_info, bool *is_trans,
+ int create_table_mode,
+ TABLE_LIST *table_list)
{
KEY *not_used_1;
uint not_used_2;
@@ -5055,9 +5127,8 @@ int mysql_create_table_no_lock(THD *thd,
else
{
int length;
- const char *alias= table_case_name(create_info, table_name);
- length= build_table_filename(path, sizeof(path) - 1, db, alias,
- "", 0);
+ const LEX_CSTRING *alias= table_case_name(create_info, table_name);
+ length= build_table_filename(path, sizeof(path) - 1, db->str, alias->str, "", 0);
// Check if we hit FN_REFLEN bytes along with file extension.
if (length+reg_ext_length > FN_REFLEN)
{
@@ -5072,6 +5143,35 @@ int mysql_create_table_no_lock(THD *thd,
alter_info, create_table_mode,
is_trans, &not_used_1, &not_used_2, &frm);
my_free(const_cast<uchar*>(frm.str));
+
+ if (!res && create_info->sequence)
+ {
+ /* Set create_info.table if temporary table */
+ if (create_info->tmp_table())
+ table_list->table= create_info->table;
+ else
+ table_list->table= 0;
+ res= sequence_insert(thd, thd->lex, table_list);
+ if (res)
+ {
+ DBUG_ASSERT(thd->is_error());
+ /* Drop the table as it wasn't completely done */
+ if (!mysql_rm_table_no_locks(thd, table_list, 1,
+ create_info->tmp_table(),
+ false, true /* Sequence*/,
+ true /* Don't log_query */,
+ true /* Don't free locks */ ))
+ {
+ /*
+ From the user point of view, the table creation failed
+ We return 2 to indicate that this statement doesn't have
+ to be logged.
+ */
+ res= 2;
+ }
+ }
+ }
+
return res;
}
@@ -5089,8 +5189,6 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table,
Table_specification_st *create_info,
Alter_info *alter_info)
{
- const char *db= create_table->db;
- const char *table_name= create_table->table_name;
bool is_trans= FALSE;
bool result;
int create_table_mode;
@@ -5132,8 +5230,11 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table,
if (!opt_explicit_defaults_for_timestamp)
promote_first_timestamp_column(&alter_info->create_list);
- if (mysql_create_table_no_lock(thd, db, table_name, create_info, alter_info,
- &is_trans, create_table_mode) > 0)
+ if (mysql_create_table_no_lock(thd, &create_table->db,
+ &create_table->table_name, create_info,
+ alter_info,
+ &is_trans, create_table_mode,
+ create_table) > 0)
{
result= 1;
goto err;
@@ -5146,6 +5247,7 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table,
if (thd->locked_tables_mode && pos_in_locked_tables &&
create_info->or_replace())
{
+ DBUG_ASSERT(thd->variables.option_bits & OPTION_TABLE_LOCK);
/*
Add back the deleted table and re-created table as a locked table
This should always work as we have a meta lock on the table.
@@ -5155,6 +5257,7 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table,
{
thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
result= 1;
+ goto err;
}
else
{
@@ -5164,17 +5267,22 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table,
}
err:
- /* In RBR we don't need to log CREATE TEMPORARY TABLE */
- if (!result && thd->is_current_stmt_binlog_format_row() && create_info->tmp_table())
+ /* In RBR or readonly server we don't need to log CREATE TEMPORARY TABLE */
+ if (!result && create_info->tmp_table() &&
+ (thd->is_current_stmt_binlog_format_row() || (opt_readonly && !thd->slave_thread)))
+ {
+ /* Note that table->s->table_creation_was_logged is not set! */
DBUG_RETURN(result);
+ }
if (create_info->tmp_table())
thd->transaction.stmt.mark_created_temp_table();
/* Write log if no error or if we already deleted a table */
- if (!result || thd->log_current_statement)
+ if (likely(!result) || thd->log_current_statement)
{
- if (result && create_info->table_was_deleted && pos_in_locked_tables)
+ if (unlikely(result) && create_info->table_was_deleted &&
+ pos_in_locked_tables)
{
/*
Possible locked table was dropped. We should remove meta data locks
@@ -5182,16 +5290,18 @@ err:
*/
thd->locked_tables_list.unlock_locked_table(thd, mdl_ticket);
}
- else if (!result && create_info->tmp_table() && create_info->table)
+ else if (likely(!result) && create_info->table)
{
/*
- Remember that tmp table creation was logged so that we know if
+ Remember that table creation was logged so that we know if
we should log a delete of it.
+ If create_info->table was not set, it's a normal table and
+ table_creation_was_logged will be set when the share is created.
*/
create_info->table->s->table_creation_was_logged= 1;
}
- if (write_bin_log(thd, result ? FALSE : TRUE, thd->query(),
- thd->query_length(), is_trans))
+ if (unlikely(write_bin_log(thd, result ? FALSE : TRUE, thd->query(),
+ thd->query_length(), is_trans)))
result= 1;
}
DBUG_RETURN(result);
@@ -5200,17 +5310,36 @@ err:
/*
** Give the key name after the first field with an optional '_#' after
+ @returns
+ 0 if keyname does not exists
+ [1..) index + 1 of duplicate key name
**/
-static bool
+static int
check_if_keyname_exists(const char *name, KEY *start, KEY *end)
{
- for (KEY *key=start ; key != end ; key++)
- if (!my_strcasecmp(system_charset_info,name,key->name))
- return 1;
+ uint i= 1;
+ for (KEY *key=start; key != end ; key++, i++)
+ if (!my_strcasecmp(system_charset_info, name, key->name.str))
+ return i;
return 0;
}
+/**
+ Returns 1 if field name exists otherwise 0
+*/
+static bool
+check_if_field_name_exists(const char *name, List<Create_field> * fields)
+{
+ Create_field *fld;
+ List_iterator<Create_field>it(*fields);
+ while ((fld = it++))
+ {
+ if (!my_strcasecmp(system_charset_info, fld->field_name.str, name))
+ return 1;
+ }
+ return 0;
+}
static char *
make_unique_key_name(THD *thd, const char *field_name,KEY *start,KEY *end)
@@ -5240,7 +5369,7 @@ make_unique_key_name(THD *thd, const char *field_name,KEY *start,KEY *end)
Make an unique name for constraints without a name
*/
-static bool make_unique_constraint_name(THD *thd, LEX_STRING *name,
+static bool make_unique_constraint_name(THD *thd, LEX_CSTRING *name,
List<Virtual_column_info> *vcol,
uint *nr)
{
@@ -5269,11 +5398,45 @@ static bool make_unique_constraint_name(THD *thd, LEX_STRING *name,
return FALSE;
}
+/**
+ INVISIBLE_FULL are internally created. They are completely invisible
+ to Alter command (Opposite of SYSTEM_INVISIBLE which throws an
+ error when same name column is added by Alter). So in the case of when
+ user added a same column name as of INVISIBLE_FULL , we change
+ INVISIBLE_FULL column name.
+*/
+static const
+char * make_unique_invisible_field_name(THD *thd, const char *field_name,
+ List<Create_field> *fields)
+{
+ if (!check_if_field_name_exists(field_name, fields))
+ return field_name;
+ char buff[MAX_FIELD_NAME], *buff_end;
+ buff_end= strmake_buf(buff, field_name);
+ if (buff_end - buff < 5)
+ return NULL; // Should not happen
+
+ for (uint i=1 ; i < 10000; i++)
+ {
+ char *real_end= int10_to_str(i, buff_end, 10);
+ if (check_if_field_name_exists(buff, fields))
+ continue;
+ return (const char *)thd->strmake(buff, real_end - buff);
+ }
+ return NULL; //Should not happen
+}
/****************************************************************************
** Alter a table definition
****************************************************************************/
+bool operator!=(const MYSQL_TIME &lhs, const MYSQL_TIME &rhs)
+{
+ return lhs.year != rhs.year || lhs.month != rhs.month || lhs.day != rhs.day ||
+ lhs.hour != rhs.hour || lhs.minute != rhs.minute ||
+ lhs.second_part != rhs.second_part || lhs.neg != rhs.neg ||
+ lhs.time_type != rhs.time_type;
+}
/**
Rename a table.
@@ -5296,9 +5459,9 @@ static bool make_unique_constraint_name(THD *thd, LEX_STRING *name,
*/
bool
-mysql_rename_table(handlerton *base, const char *old_db,
- const char *old_name, const char *new_db,
- const char *new_name, uint flags)
+mysql_rename_table(handlerton *base, const LEX_CSTRING *old_db,
+ const LEX_CSTRING *old_name, const LEX_CSTRING *new_db,
+ const LEX_CSTRING *new_name, uint flags)
{
THD *thd= current_thd;
char from[FN_REFLEN + 1], to[FN_REFLEN + 1],
@@ -5312,7 +5475,7 @@ mysql_rename_table(handlerton *base, const char *old_db,
DBUG_ENTER("mysql_rename_table");
DBUG_ASSERT(base);
DBUG_PRINT("enter", ("old: '%s'.'%s' new: '%s'.'%s'",
- old_db, old_name, new_db, new_name));
+ old_db->str, old_name->str, new_db->str, new_name->str));
// Temporarily disable foreign key checks
if (flags & NO_FK_CHECKS)
@@ -5320,9 +5483,9 @@ mysql_rename_table(handlerton *base, const char *old_db,
file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root, base);
- build_table_filename(from, sizeof(from) - 1, old_db, old_name, "",
+ 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, new_name, "",
+ 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)
@@ -5339,18 +5502,18 @@ mysql_rename_table(handlerton *base, const char *old_db,
if (lower_case_table_names == 2 && file &&
!(file->ha_table_flags() & HA_FILE_BASED))
{
- strmov(tmp_name, old_name);
+ strmov(tmp_name, old_name->str);
my_casedn_str(files_charset_info, tmp_name);
- strmov(tmp_db_name, old_db);
+ strmov(tmp_db_name, old_db->str);
my_casedn_str(files_charset_info, tmp_db_name);
build_table_filename(lc_from, sizeof(lc_from) - 1, tmp_db_name, tmp_name,
"", flags & FN_FROM_IS_TMP);
from_base= lc_from;
- strmov(tmp_name, new_name);
+ strmov(tmp_name, new_name->str);
my_casedn_str(files_charset_info, tmp_name);
- strmov(tmp_db_name, new_db);
+ strmov(tmp_db_name, new_db->str);
my_casedn_str(files_charset_info, tmp_db_name);
build_table_filename(lc_to, sizeof(lc_to) - 1, tmp_db_name, tmp_name, "",
@@ -5364,9 +5527,9 @@ mysql_rename_table(handlerton *base, const char *old_db,
error= my_errno;
(void) file->ha_create_partitioning_metadata(to, from, CHF_RENAME_FLAG);
}
- else if (!file || !(error=file->ha_rename_table(from_base, to_base)))
+ else if (!file || likely(!(error=file->ha_rename_table(from_base, to_base))))
{
- if (!(flags & NO_FRM_RENAME) && rename_file_ext(from,to,reg_ext))
+ if (!(flags & NO_FRM_RENAME) && unlikely(rename_file_ext(from,to,reg_ext)))
{
error=my_errno;
if (file)
@@ -5379,12 +5542,14 @@ mysql_rename_table(handlerton *base, const char *old_db,
}
}
delete file;
+
if (error == HA_ERR_WRONG_COMMAND)
my_error(ER_NOT_SUPPORTED_YET, MYF(0), "ALTER TABLE");
else if (error == ENOTDIR)
- my_error(ER_BAD_DB_ERROR, MYF(0), new_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);
@@ -5395,8 +5560,8 @@ mysql_rename_table(handlerton *base, const char *old_db,
if (likely(error == 0))
{
PSI_CALL_drop_table_share(flags & FN_FROM_IS_TMP,
- old_db, strlen(old_db),
- old_name, strlen(old_name));
+ old_db->str, (uint)old_db->length,
+ old_name->str, (uint)old_name->length);
}
// Restore options bits to the original value
@@ -5429,7 +5594,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
TABLE_LIST *pos_in_locked_tables= 0;
Alter_info local_alter_info;
Alter_table_ctx local_alter_ctx; // Not used
- bool res= TRUE;
+ int res= 1;
bool is_trans= FALSE;
bool do_logging= FALSE;
uint not_used;
@@ -5510,7 +5675,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
local_create_info.max_rows= 0;
/* Replace type of source table with one specified in the statement. */
local_create_info.options&= ~HA_LEX_CREATE_TMP_TABLE;
- local_create_info.options|= create_info->tmp_table();
+ local_create_info.options|= create_info->options;
/* Reset auto-increment counter for the new table. */
local_create_info.auto_increment_value= 0;
/*
@@ -5519,14 +5684,22 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
*/
local_create_info.data_file_name= local_create_info.index_file_name= NULL;
+ if (src_table->table->versioned() &&
+ local_create_info.vers_info.fix_create_like(local_alter_info, local_create_info,
+ *src_table, *table))
+ {
+ goto err;
+ }
+
/* The following is needed only in case of lock tables */
if ((local_create_info.table= thd->lex->query_tables->table))
pos_in_locked_tables= local_create_info.table->pos_in_locked_tables;
res= ((create_res=
- mysql_create_table_no_lock(thd, table->db, table->table_name,
+ mysql_create_table_no_lock(thd, &table->db, &table->table_name,
&local_create_info, &local_alter_info,
- &is_trans, C_ORDINARY_CREATE)) > 0);
+ &is_trans, C_ORDINARY_CREATE,
+ table)) > 0);
/* Remember to log if we deleted something */
do_logging= thd->log_current_statement;
if (res)
@@ -5566,8 +5739,8 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
non-temporary table.
*/
DBUG_ASSERT((create_info->tmp_table()) ||
- thd->mdl_context.is_lock_owner(MDL_key::TABLE, table->db,
- table->table_name,
+ thd->mdl_context.is_lock_owner(MDL_key::TABLE, table->db.str,
+ table->table_name.str,
MDL_EXCLUSIVE));
}
@@ -5724,16 +5897,17 @@ err:
Table was not deleted. Original table was deleted.
We have to log it.
*/
- log_drop_table(thd, table->db, table->db_length,
- table->table_name, table->table_name_length,
- create_info->tmp_table());
+ log_drop_table(thd, &table->db, &table->table_name, create_info->tmp_table());
}
- else if (write_bin_log(thd, res ? FALSE : TRUE, thd->query(),
- thd->query_length(), is_trans))
+ else if (res != 2) // Table was not dropped
+ {
+ if (write_bin_log(thd, res ? FALSE : TRUE, thd->query(),
+ thd->query_length(), is_trans))
res= 1;
+ }
}
- DBUG_RETURN(res);
+ DBUG_RETURN(res != 0);
}
@@ -5767,7 +5941,7 @@ int mysql_discard_or_import_tablespace(THD *thd,
table_list->mdl_request.set_type(MDL_EXCLUSIVE);
table_list->lock_type= TL_WRITE;
/* Do not open views. */
- table_list->required_type= FRMTYPE_TABLE;
+ table_list->required_type= TABLE_TYPE_NORMAL;
if (open_and_lock_tables(thd, table_list, FALSE, 0,
&alter_prelocking_strategy))
@@ -5780,7 +5954,7 @@ int mysql_discard_or_import_tablespace(THD *thd,
THD_STAGE_INFO(thd, stage_end);
- if (error)
+ if (unlikely(error))
goto err;
/*
@@ -5791,16 +5965,15 @@ int mysql_discard_or_import_tablespace(THD *thd,
/* The ALTER TABLE is always in its own transaction */
error= trans_commit_stmt(thd);
- if (trans_commit_implicit(thd))
+ if (unlikely(trans_commit_implicit(thd)))
error=1;
- if (error)
- goto err;
- error= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
+ if (likely(!error))
+ error= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
err:
thd->tablespace_op=FALSE;
- if (error == 0)
+ if (likely(error == 0))
{
my_ok(thd);
DBUG_RETURN(0);
@@ -5866,7 +6039,7 @@ handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info)
while ((sql_field=it++))
{
- if (!sql_field->create_if_not_exists || sql_field->change)
+ if (!sql_field->create_if_not_exists || sql_field->change.str)
continue;
/*
If there is a field with the same name in the table already,
@@ -5874,8 +6047,9 @@ handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info)
*/
for (f_ptr=table->field; *f_ptr; f_ptr++)
{
- if (my_strcasecmp(system_charset_info,
- sql_field->field_name, (*f_ptr)->field_name) == 0)
+ if (lex_string_cmp(system_charset_info,
+ &sql_field->field_name,
+ &(*f_ptr)->field_name) == 0)
goto drop_create_field;
}
{
@@ -5887,8 +6061,9 @@ handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info)
Create_field *chk_field;
while ((chk_field= chk_it++) && chk_field != sql_field)
{
- if (my_strcasecmp(system_charset_info,
- sql_field->field_name, chk_field->field_name) == 0)
+ if (lex_string_cmp(system_charset_info,
+ &sql_field->field_name,
+ &chk_field->field_name) == 0)
goto drop_create_field;
}
}
@@ -5896,14 +6071,13 @@ handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info)
drop_create_field:
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_DUP_FIELDNAME, ER_THD(thd, ER_DUP_FIELDNAME),
- sql_field->field_name);
+ sql_field->field_name.str);
it.remove();
if (alter_info->create_list.is_empty())
{
- alter_info->flags&= ~Alter_info::ALTER_ADD_COLUMN;
+ alter_info->flags&= ~ALTER_PARSER_ADD_COLUMN;
if (alter_info->key_list.is_empty())
- alter_info->flags&= ~(Alter_info::ALTER_ADD_INDEX |
- Alter_info::ADD_FOREIGN_KEY);
+ alter_info->flags&= ~(ALTER_ADD_INDEX | ALTER_ADD_FOREIGN_KEY);
}
}
}
@@ -5915,7 +6089,7 @@ drop_create_field:
while ((sql_field=it++))
{
- if (!sql_field->create_if_not_exists || !sql_field->change)
+ if (!sql_field->create_if_not_exists || !sql_field->change.str)
continue;
/*
If there is NO field with the same name in the table already,
@@ -5923,25 +6097,59 @@ drop_create_field:
*/
for (f_ptr=table->field; *f_ptr; f_ptr++)
{
- if (my_strcasecmp(system_charset_info,
- sql_field->change, (*f_ptr)->field_name) == 0)
+ if (lex_string_cmp(system_charset_info,
+ &sql_field->change,
+ &(*f_ptr)->field_name) == 0)
{
break;
}
}
- if (*f_ptr == NULL)
+ if (unlikely(*f_ptr == NULL))
{
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_BAD_FIELD_ERROR,
ER_THD(thd, ER_BAD_FIELD_ERROR),
- sql_field->change, table->s->table_name.str);
+ sql_field->change.str, table->s->table_name.str);
it.remove();
if (alter_info->create_list.is_empty())
{
- alter_info->flags&= ~(Alter_info::ALTER_ADD_COLUMN |
- Alter_info::ALTER_CHANGE_COLUMN);
+ alter_info->flags&= ~(ALTER_PARSER_ADD_COLUMN | ALTER_CHANGE_COLUMN);
if (alter_info->key_list.is_empty())
- alter_info->flags&= ~Alter_info::ALTER_ADD_INDEX;
+ alter_info->flags&= ~ALTER_ADD_INDEX;
+ }
+ }
+ }
+ }
+
+ /* Handle ALTER COLUMN IF EXISTS SET/DROP DEFAULT. */
+ {
+ List_iterator<Alter_column> it(alter_info->alter_list);
+ Alter_column *acol;
+
+ while ((acol=it++))
+ {
+ if (!acol->alter_if_exists)
+ continue;
+ /*
+ If there is NO field with the same name in the table already,
+ remove the acol from the list.
+ */
+ for (f_ptr=table->field; *f_ptr; f_ptr++)
+ {
+ if (my_strcasecmp(system_charset_info,
+ acol->name, (*f_ptr)->field_name.str) == 0)
+ break;
+ }
+ if (unlikely(*f_ptr == NULL))
+ {
+ 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);
+ it.remove();
+ if (alter_info->alter_list.is_empty())
+ {
+ alter_info->flags&= ~(ALTER_CHANGE_COLUMN_DEFAULT);
}
}
}
@@ -5958,13 +6166,13 @@ drop_create_field:
ulonglong cur_flag= 0;
switch (drop->type) {
case Alter_drop::COLUMN:
- cur_flag= Alter_info::ALTER_DROP_COLUMN;
+ cur_flag= ALTER_PARSER_DROP_COLUMN;
break;
case Alter_drop::FOREIGN_KEY:
- cur_flag= Alter_info::DROP_FOREIGN_KEY;
+ cur_flag= ALTER_DROP_FOREIGN_KEY;
break;
case Alter_drop::KEY:
- cur_flag= Alter_info::ALTER_DROP_INDEX;
+ cur_flag= ALTER_DROP_INDEX;
break;
default:
break;
@@ -5984,7 +6192,7 @@ drop_create_field:
for (f_ptr=table->field; *f_ptr; f_ptr++)
{
if (my_strcasecmp(system_charset_info,
- drop->name, (*f_ptr)->field_name) == 0)
+ drop->name, (*f_ptr)->field_name.str) == 0)
{
remove_drop= FALSE;
break;
@@ -5993,7 +6201,9 @@ drop_create_field:
}
else if (drop->type == Alter_drop::CHECK_CONSTRAINT)
{
- for (uint i=table->s->field_check_constraints; i < table->s->table_check_constraints; i++)
+ for (uint i=table->s->field_check_constraints;
+ i < table->s->table_check_constraints;
+ i++)
{
if (my_strcasecmp(system_charset_info, drop->name,
table->check_constraints[i]->name.str) == 0)
@@ -6011,7 +6221,8 @@ drop_create_field:
for (n_key=0; n_key < table->s->keys; n_key++)
{
if (my_strcasecmp(system_charset_info,
- drop->name, table->key_info[n_key].name) == 0)
+ drop->name,
+ table->key_info[n_key].name.str) == 0)
{
remove_drop= FALSE;
break;
@@ -6067,9 +6278,9 @@ drop_create_field:
left_flags|= cur_flag;
}
/* Reset state to what's left in drop list */
- alter_info->flags&= ~(Alter_info::ALTER_DROP_COLUMN |
- Alter_info::ALTER_DROP_INDEX |
- Alter_info::DROP_FOREIGN_KEY);
+ alter_info->flags&= ~(ALTER_PARSER_DROP_COLUMN |
+ ALTER_DROP_INDEX |
+ ALTER_DROP_FOREIGN_KEY);
alter_info->flags|= left_flags;
}
@@ -6089,7 +6300,7 @@ drop_create_field:
bool dup_primary_key=
key->type == Key::PRIMARY &&
table->s->primary_key != MAX_KEY &&
- (keyname= table->s->key_info[table->s->primary_key].name) &&
+ (keyname= table->s->key_info[table->s->primary_key].name.str) &&
my_strcasecmp(system_charset_info, keyname, primary_key_name) == 0;
if (dup_primary_key)
goto remove_key;
@@ -6115,7 +6326,7 @@ drop_create_field:
for (n_key=0; n_key < table->s->keys; n_key++)
{
if (my_strcasecmp(system_charset_info,
- keyname, table->key_info[n_key].name) == 0)
+ keyname, table->key_info[n_key].name.str) == 0)
{
goto remove_key;
}
@@ -6170,8 +6381,7 @@ remove_key:
key_it.remove();
}
if (alter_info->key_list.is_empty())
- alter_info->flags&= ~(Alter_info::ALTER_ADD_INDEX |
- Alter_info::ADD_FOREIGN_KEY);
+ alter_info->flags&= ~(ALTER_ADD_INDEX | ALTER_ADD_FOREIGN_KEY);
}
else
{
@@ -6182,20 +6392,20 @@ remove_key:
if (ad != NULL)
{
// Adding the index into the drop list for replacing
- alter_info->flags |= Alter_info::ALTER_DROP_INDEX;
+ alter_info->flags |= ALTER_DROP_INDEX;
alter_info->drop_list.push_back(ad, thd->mem_root);
}
}
}
}
-
+
#ifdef WITH_PARTITION_STORAGE_ENGINE
partition_info *tab_part_info= table->part_info;
thd->work_part_info= thd->lex->part_info;
if (tab_part_info)
{
/* ALTER TABLE ADD PARTITION IF NOT EXISTS */
- if ((alter_info->flags & Alter_info::ALTER_ADD_PARTITION) &&
+ if ((alter_info->partition_flags & ALTER_PARTITION_ADD) &&
thd->lex->create_info.if_not_exists())
{
partition_info *alt_part_info= thd->lex->part_info;
@@ -6211,7 +6421,7 @@ remove_key:
ER_SAME_NAME_PARTITION,
ER_THD(thd, ER_SAME_NAME_PARTITION),
pe->partition_name);
- alter_info->flags&= ~Alter_info::ALTER_ADD_PARTITION;
+ alter_info->partition_flags&= ~ALTER_PARTITION_ADD;
thd->work_part_info= NULL;
break;
}
@@ -6219,11 +6429,11 @@ remove_key:
}
}
/* ALTER TABLE DROP PARTITION IF EXISTS */
- if ((alter_info->flags & Alter_info::ALTER_DROP_PARTITION) &&
+ if ((alter_info->partition_flags & ALTER_PARTITION_DROP) &&
thd->lex->if_exists())
{
- List_iterator<char> names_it(alter_info->partition_names);
- char *name;
+ List_iterator<const char> names_it(alter_info->partition_names);
+ const char *name;
while ((name= names_it++))
{
@@ -6245,7 +6455,7 @@ remove_key:
}
}
if (alter_info->partition_names.elements == 0)
- alter_info->flags&= ~Alter_info::ALTER_DROP_PARTITION;
+ alter_info->partition_flags&= ~ALTER_PARTITION_DROP;
}
}
#endif /*WITH_PARTITION_STORAGE_ENGINE*/
@@ -6268,15 +6478,15 @@ remove_key:
{
Virtual_column_info *dup= table->check_constraints[c];
if (dup->name.length == check->name.length &&
- my_strcasecmp(system_charset_info,
- check->name.str, dup->name.str) == 0)
+ lex_string_cmp(system_charset_info,
+ &check->name, &dup->name) == 0)
{
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_DUP_CONSTRAINT_NAME, ER_THD(thd, ER_DUP_CONSTRAINT_NAME),
"CHECK", check->name.str);
it.remove();
if (alter_info->check_constraint_list.elements == 0)
- alter_info->flags&= ~Alter_info::ALTER_ADD_CHECK_CONSTRAINT;
+ alter_info->flags&= ~ALTER_ADD_CHECK_CONSTRAINT;
break;
}
@@ -6400,6 +6610,7 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar,
KEY_PART_INFO *end;
Alter_info *alter_info= ha_alter_info->alter_info;
DBUG_ENTER("fill_alter_inplace_info");
+ DBUG_PRINT("info", ("alter_info->flags: %llu", alter_info->flags));
/* Allocate result buffers. */
if (! (ha_alter_info->index_drop_buffer=
@@ -6410,53 +6621,40 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar,
DBUG_RETURN(true);
/*
+ Copy parser flags, but remove some flags that handlers doesn't
+ need to care about (old engines may not ignore these parser flags).
+ ALTER_RENAME_COLUMN is replaced by ALTER_COLUMN_NAME.
+ ALTER_CHANGE_COLUMN_DEFAULT is replaced by ALTER_CHANGE_COLUMN
+ ALTER_PARSE_ADD_COLUMN, ALTER_PARSE_DROP_COLUMN, ALTER_ADD_INDEX and
+ ALTER_DROP_INDEX are replaced with versions that have higher granuality.
+ */
+
+ alter_table_operations flags_to_remove=
+ ALTER_ADD_INDEX | ALTER_DROP_INDEX | ALTER_PARSER_ADD_COLUMN |
+ ALTER_PARSER_DROP_COLUMN | ALTER_COLUMN_ORDER | ALTER_RENAME_COLUMN |
+ ALTER_CHANGE_COLUMN;
+
+ if (!table->file->native_versioned())
+ flags_to_remove|= ALTER_COLUMN_UNVERSIONED;
+
+ ha_alter_info->handler_flags|= (alter_info->flags & ~flags_to_remove);
+ /*
Comparing new and old default values of column is cumbersome.
So instead of using such a comparison for detecting if default
has really changed we rely on flags set by parser to get an
approximate value for storage engine flag.
*/
- if (alter_info->flags & (Alter_info::ALTER_CHANGE_COLUMN |
- Alter_info::ALTER_CHANGE_COLUMN_DEFAULT))
- ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_DEFAULT;
- if (alter_info->flags & Alter_info::ADD_FOREIGN_KEY)
- ha_alter_info->handler_flags|= Alter_inplace_info::ADD_FOREIGN_KEY;
- if (alter_info->flags & Alter_info::DROP_FOREIGN_KEY)
- ha_alter_info->handler_flags|= Alter_inplace_info::DROP_FOREIGN_KEY;
- if (alter_info->flags & Alter_info::ALTER_OPTIONS)
- ha_alter_info->handler_flags|= Alter_inplace_info::CHANGE_CREATE_OPTION;
- if (alter_info->flags & Alter_info::ALTER_RENAME)
- ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_RENAME;
- /* Check partition changes */
- if (alter_info->flags & Alter_info::ALTER_ADD_PARTITION)
- ha_alter_info->handler_flags|= Alter_inplace_info::ADD_PARTITION;
- if (alter_info->flags & Alter_info::ALTER_DROP_PARTITION)
- ha_alter_info->handler_flags|= Alter_inplace_info::DROP_PARTITION;
- if (alter_info->flags & Alter_info::ALTER_PARTITION)
- ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_PARTITION;
- if (alter_info->flags & Alter_info::ALTER_COALESCE_PARTITION)
- ha_alter_info->handler_flags|= Alter_inplace_info::COALESCE_PARTITION;
- if (alter_info->flags & Alter_info::ALTER_REORGANIZE_PARTITION)
- ha_alter_info->handler_flags|= Alter_inplace_info::REORGANIZE_PARTITION;
- if (alter_info->flags & Alter_info::ALTER_TABLE_REORG)
- ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_TABLE_REORG;
- if (alter_info->flags & Alter_info::ALTER_REMOVE_PARTITIONING)
- ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_REMOVE_PARTITIONING;
- if (alter_info->flags & Alter_info::ALTER_ALL_PARTITION)
- ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_ALL_PARTITION;
- /* Check for: ALTER TABLE FORCE, ALTER TABLE ENGINE and OPTIMIZE TABLE. */
- if (alter_info->flags & Alter_info::ALTER_RECREATE)
- ha_alter_info->handler_flags|= Alter_inplace_info::RECREATE_TABLE;
- if (alter_info->flags & Alter_info::ALTER_ADD_CHECK_CONSTRAINT)
- ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_ADD_CHECK_CONSTRAINT;
- if (alter_info->flags & Alter_info::ALTER_DROP_CHECK_CONSTRAINT)
- ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_DROP_CHECK_CONSTRAINT;
+ if (alter_info->flags & ALTER_CHANGE_COLUMN)
+ ha_alter_info->handler_flags|= ALTER_COLUMN_DEFAULT;
/*
If we altering table with old VARCHAR fields we will be automatically
upgrading VARCHAR column types.
*/
if (table->s->frm_version < FRM_VER_TRUE_VARCHAR && varchar)
- ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_STORED_COLUMN_TYPE;
+ ha_alter_info->handler_flags|= ALTER_STORED_COLUMN_TYPE;
+
+ DBUG_PRINT("info", ("handler_flags: %llu", ha_alter_info->handler_flags));
/*
Go through fields in old version of table and detect changes to them.
@@ -6502,11 +6700,9 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar,
case IS_EQUAL_NO:
/* New column type is incompatible with old one. */
if (field->stored_in_db())
- ha_alter_info->handler_flags|=
- Alter_inplace_info::ALTER_STORED_COLUMN_TYPE;
+ ha_alter_info->handler_flags|= ALTER_STORED_COLUMN_TYPE;
else
- ha_alter_info->handler_flags|=
- Alter_inplace_info::ALTER_VIRTUAL_COLUMN_TYPE;
+ ha_alter_info->handler_flags|= ALTER_VIRTUAL_COLUMN_TYPE;
if (table->s->tmp_table == NO_TMP_TABLE)
{
delete_statistics_for_column(thd, table, field);
@@ -6545,38 +6741,35 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar,
be carried out by simply updating data dictionary without changing
actual data (for example, VARCHAR(300) is changed to VARCHAR(400)).
*/
- ha_alter_info->handler_flags|= Alter_inplace_info::
- ALTER_COLUMN_EQUAL_PACK_LENGTH;
+ ha_alter_info->handler_flags|= ALTER_COLUMN_EQUAL_PACK_LENGTH;
break;
default:
DBUG_ASSERT(0);
/* Safety. */
- ha_alter_info->handler_flags|=
- Alter_inplace_info::ALTER_STORED_COLUMN_TYPE;
+ ha_alter_info->handler_flags|= ALTER_STORED_COLUMN_TYPE;
}
if (field->vcol_info || new_field->vcol_info)
{
/* base <-> virtual or stored <-> virtual */
if (field->stored_in_db() != new_field->stored_in_db())
- ha_alter_info->handler_flags|=
- Alter_inplace_info::ALTER_STORED_COLUMN_TYPE |
- Alter_inplace_info::ALTER_VIRTUAL_COLUMN_TYPE;
+ ha_alter_info->handler_flags|= ( ALTER_STORED_COLUMN_TYPE |
+ ALTER_VIRTUAL_COLUMN_TYPE);
if (field->vcol_info && new_field->vcol_info)
{
bool value_changes= is_equal == IS_EQUAL_NO;
- Alter_inplace_info::HA_ALTER_FLAGS alter_expr;
+ alter_table_operations alter_expr;
if (field->stored_in_db())
- alter_expr= Alter_inplace_info::ALTER_STORED_GCOL_EXPR;
+ alter_expr= ALTER_STORED_GCOL_EXPR;
else
- alter_expr= Alter_inplace_info::ALTER_VIRTUAL_GCOL_EXPR;
+ alter_expr= ALTER_VIRTUAL_GCOL_EXPR;
if (!field->vcol_info->is_equal(new_field->vcol_info))
{
ha_alter_info->handler_flags|= alter_expr;
value_changes= true;
}
- if ((ha_alter_info->handler_flags & Alter_inplace_info::ALTER_COLUMN_DEFAULT)
+ if ((ha_alter_info->handler_flags & ALTER_COLUMN_DEFAULT)
&& !(ha_alter_info->handler_flags & alter_expr))
{ /*
a DEFAULT value of a some column was changed. see if this vcol
@@ -6592,28 +6785,26 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar,
}
if (field->vcol_info->is_in_partitioning_expr() ||
- field->flags & PART_KEY_FLAG)
+ field->flags & PART_KEY_FLAG || field->stored_in_db())
{
if (value_changes)
- ha_alter_info->handler_flags|=
- Alter_inplace_info::ALTER_COLUMN_VCOL;
+ ha_alter_info->handler_flags|= ALTER_COLUMN_VCOL;
else
maybe_alter_vcol= true;
}
}
else /* base <-> stored */
- ha_alter_info->handler_flags|=
- Alter_inplace_info::ALTER_STORED_COLUMN_TYPE;
+ ha_alter_info->handler_flags|= ALTER_STORED_COLUMN_TYPE;
}
/* Check if field was renamed */
- if (my_strcasecmp(system_charset_info, field->field_name,
- new_field->field_name))
+ if (lex_string_cmp(system_charset_info, &field->field_name,
+ &new_field->field_name))
{
field->flags|= FIELD_IS_RENAMED;
- ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_NAME;
+ ha_alter_info->handler_flags|= ALTER_COLUMN_NAME;
rename_column_in_stat_tables(thd, table, field,
- new_field->field_name);
+ new_field->field_name.str);
}
/* Check that NULL behavior is same for old and new fields */
@@ -6621,11 +6812,9 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar,
(uint) (field->flags & NOT_NULL_FLAG))
{
if (new_field->flags & NOT_NULL_FLAG)
- ha_alter_info->handler_flags|=
- Alter_inplace_info::ALTER_COLUMN_NOT_NULLABLE;
+ ha_alter_info->handler_flags|= ALTER_COLUMN_NOT_NULLABLE;
else
- ha_alter_info->handler_flags|=
- Alter_inplace_info::ALTER_COLUMN_NULLABLE;
+ ha_alter_info->handler_flags|= ALTER_COLUMN_NULLABLE;
}
/*
@@ -6639,30 +6828,26 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar,
if (field->stored_in_db())
{
if (field_stored_index != new_field_stored_index)
- ha_alter_info->handler_flags|=
- Alter_inplace_info::ALTER_STORED_COLUMN_ORDER;
+ ha_alter_info->handler_flags|= ALTER_STORED_COLUMN_ORDER;
}
else
{
if (field->field_index != new_field_index)
- ha_alter_info->handler_flags|=
- Alter_inplace_info::ALTER_VIRTUAL_COLUMN_ORDER;
+ ha_alter_info->handler_flags|= ALTER_VIRTUAL_COLUMN_ORDER;
}
/* Detect changes in storage type of column */
if (new_field->field_storage_type() != field->field_storage_type())
- ha_alter_info->handler_flags|=
- Alter_inplace_info::ALTER_COLUMN_STORAGE_TYPE;
+ ha_alter_info->handler_flags|= ALTER_COLUMN_STORAGE_TYPE;
/* Detect changes in column format of column */
if (new_field->column_format() != field->column_format())
- ha_alter_info->handler_flags|=
- Alter_inplace_info::ALTER_COLUMN_COLUMN_FORMAT;
+ ha_alter_info->handler_flags|= ALTER_COLUMN_COLUMN_FORMAT;
if (engine_options_differ(field->option_struct, new_field->option_struct,
table->file->ht->field_options))
{
- ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_OPTION;
+ ha_alter_info->handler_flags|= ALTER_COLUMN_OPTION;
ha_alter_info->create_info->fields_option_struct[f_ptr - table->field]=
new_field->option_struct;
}
@@ -6673,9 +6858,9 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar,
// Field is not present in new version of table and therefore was dropped.
field->flags|= FIELD_IS_DROPPED;
if (field->stored_in_db())
- ha_alter_info->handler_flags|= Alter_inplace_info::DROP_STORED_COLUMN;
+ ha_alter_info->handler_flags|= ALTER_DROP_STORED_COLUMN;
else
- ha_alter_info->handler_flags|= Alter_inplace_info::DROP_VIRTUAL_COLUMN;
+ ha_alter_info->handler_flags|= ALTER_DROP_VIRTUAL_COLUMN;
}
}
@@ -6687,12 +6872,11 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar,
(FIXME), so let's just say that a vcol *might* be affected if any other
column was altered.
*/
- if (ha_alter_info->handler_flags &
- ( Alter_inplace_info::ALTER_STORED_COLUMN_TYPE
- | Alter_inplace_info::ALTER_VIRTUAL_COLUMN_TYPE
- | Alter_inplace_info::ALTER_COLUMN_NOT_NULLABLE
- | Alter_inplace_info::ALTER_COLUMN_OPTION ))
- ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_VCOL;
+ if (ha_alter_info->handler_flags & (ALTER_STORED_COLUMN_TYPE |
+ ALTER_VIRTUAL_COLUMN_TYPE |
+ ALTER_COLUMN_NOT_NULLABLE |
+ ALTER_COLUMN_OPTION))
+ ha_alter_info->handler_flags|= ALTER_COLUMN_VCOL;
}
new_field_it.init(alter_info->create_list);
@@ -6702,15 +6886,14 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar,
{
// Field is not present in old version of table and therefore was added.
if (new_field->vcol_info)
+ {
if (new_field->stored_in_db())
- ha_alter_info->handler_flags|=
- Alter_inplace_info::ADD_STORED_GENERATED_COLUMN;
+ ha_alter_info->handler_flags|= ALTER_ADD_STORED_GENERATED_COLUMN;
else
- ha_alter_info->handler_flags|=
- Alter_inplace_info::ADD_VIRTUAL_COLUMN;
+ ha_alter_info->handler_flags|= ALTER_ADD_VIRTUAL_COLUMN;
+ }
else
- ha_alter_info->handler_flags|=
- Alter_inplace_info::ADD_STORED_BASE_COLUMN;
+ ha_alter_info->handler_flags|= ALTER_ADD_STORED_BASE_COLUMN;
}
}
@@ -6729,7 +6912,7 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar,
*/
const KEY* const new_pk= (ha_alter_info->key_count > 0 &&
(!my_strcasecmp(system_charset_info,
- ha_alter_info->key_info_buffer->name,
+ ha_alter_info->key_info_buffer->name.str,
primary_key_name) ||
is_candidate_key(ha_alter_info->key_info_buffer))) ?
ha_alter_info->key_info_buffer : NULL;
@@ -6751,7 +6934,8 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar,
new_key < new_key_end;
new_key++)
{
- if (! strcmp(table_key->name, new_key->name))
+ if (!lex_string_cmp(system_charset_info, &table_key->name,
+ &new_key->name))
break;
}
if (new_key >= new_key_end)
@@ -6760,7 +6944,7 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar,
ha_alter_info->index_drop_buffer
[ha_alter_info->index_drop_count++]=
table_key;
- DBUG_PRINT("info", ("index dropped: '%s'", table_key->name));
+ DBUG_PRINT("info", ("index dropped: '%s'", table_key->name.str));
continue;
}
@@ -6812,8 +6996,7 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar,
(key_part->field->is_equal((Create_field*) new_field)
== IS_EQUAL_PACK_LENGTH))
{
- ha_alter_info->handler_flags |=
- Alter_inplace_info::ALTER_COLUMN_INDEX_LENGTH;
+ ha_alter_info->handler_flags |= ALTER_COLUMN_INDEX_LENGTH;
}
else if (key_part->length != new_part->length)
goto index_changed;
@@ -6855,7 +7038,7 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar,
[ha_alter_info->index_add_count++]=
(uint)(new_key - ha_alter_info->key_info_buffer);
/* Mark all old fields which are used in newly created index. */
- DBUG_PRINT("info", ("index changed: '%s'", table_key->name));
+ DBUG_PRINT("info", ("index changed: '%s'", table_key->name.str));
}
/*end of for (; table_key < table_key_end;) */
@@ -6869,7 +7052,8 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar,
/* Search an old key with the same name. */
for (table_key= table->key_info; table_key < table_key_end; table_key++)
{
- if (! strcmp(table_key->name, new_key->name))
+ if (!lex_string_cmp(system_charset_info, &table_key->name,
+ &new_key->name))
break;
}
if (table_key >= table_key_end)
@@ -6878,7 +7062,7 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar,
ha_alter_info->index_add_buffer
[ha_alter_info->index_add_count++]=
(uint)(new_key - ha_alter_info->key_info_buffer);
- DBUG_PRINT("info", ("index added: '%s'", new_key->name));
+ DBUG_PRINT("info", ("index added: '%s'", new_key->name.str));
}
else
ha_alter_info->create_info->indexes_option_struct[table_key - table->key_info]=
@@ -6908,12 +7092,12 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar,
if (table_key->flags & HA_NOSAME)
{
if (table_key == old_pk)
- ha_alter_info->handler_flags|= Alter_inplace_info::DROP_PK_INDEX;
+ ha_alter_info->handler_flags|= ALTER_DROP_PK_INDEX;
else
- ha_alter_info->handler_flags|= Alter_inplace_info::DROP_UNIQUE_INDEX;
+ ha_alter_info->handler_flags|= ALTER_DROP_UNIQUE_INDEX;
}
else
- ha_alter_info->handler_flags|= Alter_inplace_info::DROP_INDEX;
+ ha_alter_info->handler_flags|= ALTER_DROP_NON_UNIQUE_NON_PRIM_INDEX;
}
/* Now figure out what kind of indexes we are adding. */
@@ -6924,14 +7108,15 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar,
if (new_key->flags & HA_NOSAME)
{
if (new_key == new_pk)
- ha_alter_info->handler_flags|= Alter_inplace_info::ADD_PK_INDEX;
+ ha_alter_info->handler_flags|= ALTER_ADD_PK_INDEX;
else
- ha_alter_info->handler_flags|= Alter_inplace_info::ADD_UNIQUE_INDEX;
+ ha_alter_info->handler_flags|= ALTER_ADD_UNIQUE_INDEX;
}
else
- ha_alter_info->handler_flags|= Alter_inplace_info::ADD_INDEX;
+ ha_alter_info->handler_flags|= ALTER_ADD_NON_UNIQUE_NON_PRIM_INDEX;
}
+ DBUG_PRINT("exit", ("handler_flags: %llu", ha_alter_info->handler_flags));
DBUG_RETURN(false);
}
@@ -7057,14 +7242,14 @@ bool mysql_compare_tables(TABLE *table,
if (create_info->row_type == ROW_TYPE_DYNAMIC ||
create_info->row_type == ROW_TYPE_PAGE ||
(tmp_new_field->flags & BLOB_FLAG) ||
- (tmp_new_field->sql_type == MYSQL_TYPE_VARCHAR &&
+ (tmp_new_field->real_field_type() == MYSQL_TYPE_VARCHAR &&
create_info->row_type != ROW_TYPE_FIXED))
create_info->table_options|= HA_OPTION_PACK_RECORD;
/* Check if field was renamed */
- if (my_strcasecmp(system_charset_info,
- field->field_name,
- tmp_new_field->field_name))
+ if (lex_string_cmp(system_charset_info,
+ &field->field_name,
+ &tmp_new_field->field_name))
DBUG_RETURN(false);
/* Evaluate changes bitmap and send to check_if_incompatible_data() */
@@ -7091,7 +7276,8 @@ bool mysql_compare_tables(TABLE *table,
/* Search a key with the same name. */
for (new_key= key_info_buffer; new_key < new_key_end; new_key++)
{
- if (! strcmp(table_key->name, new_key->name))
+ if (!lex_string_cmp(system_charset_info, &table_key->name,
+ &new_key->name))
break;
}
if (new_key >= new_key_end)
@@ -7130,7 +7316,8 @@ bool mysql_compare_tables(TABLE *table,
/* Search a key with the same name. */
for (table_key= table->key_info; table_key < table_key_end; table_key++)
{
- if (! strcmp(table_key->name, new_key->name))
+ if (!lex_string_cmp(system_charset_info, &table_key->name,
+ &new_key->name))
break;
}
if (table_key >= table_key_end)
@@ -7179,18 +7366,20 @@ bool alter_table_manage_keys(TABLE *table, int indexes_were_disabled,
error= table->file->ha_disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
}
- if (error == HA_ERR_WRONG_COMMAND)
+ if (unlikely(error))
{
- THD *thd= table->in_use;
- push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
- ER_ILLEGAL_HA, ER_THD(thd, ER_ILLEGAL_HA),
- table->file->table_type(),
- table->s->db.str, table->s->table_name.str);
- error= 0;
+ if (error == HA_ERR_WRONG_COMMAND)
+ {
+ THD *thd= table->in_use;
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_ILLEGAL_HA, ER_THD(thd, ER_ILLEGAL_HA),
+ table->file->table_type(),
+ table->s->db.str, table->s->table_name.str);
+ error= 0;
+ }
+ else
+ table->file->print_error(error, MYF(0));
}
- else if (error)
- table->file->print_error(error, MYF(0));
-
DBUG_RETURN(error);
}
@@ -7231,8 +7420,7 @@ static bool is_inplace_alter_impossible(TABLE *table,
not supported for in-place in combination with other operations.
Alone, it will be done by simple_rename_or_index_change().
*/
- if (alter_info->flags & (Alter_info::ALTER_ORDER |
- Alter_info::ALTER_KEYS_ONOFF))
+ if (alter_info->flags & (ALTER_ORDER | ALTER_KEYS_ONOFF))
DBUG_RETURN(true);
/*
@@ -7329,11 +7517,14 @@ static bool mysql_inplace_alter_table(THD *thd,
exclusive lock is required for duration of the whole statement.
*/
if (inplace_supported == HA_ALTER_INPLACE_EXCLUSIVE_LOCK ||
- ((inplace_supported == HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE ||
- inplace_supported == HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE) &&
+ ((inplace_supported == HA_ALTER_INPLACE_COPY_NO_LOCK ||
+ inplace_supported == HA_ALTER_INPLACE_COPY_LOCK ||
+ inplace_supported == HA_ALTER_INPLACE_NOCOPY_NO_LOCK ||
+ inplace_supported == HA_ALTER_INPLACE_NOCOPY_LOCK ||
+ inplace_supported == HA_ALTER_INPLACE_INSTANT) &&
(thd->locked_tables_mode == LTM_LOCK_TABLES ||
thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES)) ||
- alter_info->requested_lock == Alter_info::ALTER_TABLE_LOCK_EXCLUSIVE)
+ alter_info->requested_lock == Alter_info::ALTER_TABLE_LOCK_EXCLUSIVE)
{
if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
goto cleanup;
@@ -7354,8 +7545,11 @@ static bool mysql_inplace_alter_table(THD *thd,
*/
reopen_tables= true;
}
- else if (inplace_supported == HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE ||
- inplace_supported == HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE)
+ else if (inplace_supported == HA_ALTER_INPLACE_COPY_LOCK ||
+ inplace_supported == HA_ALTER_INPLACE_COPY_NO_LOCK ||
+ inplace_supported == HA_ALTER_INPLACE_NOCOPY_LOCK ||
+ inplace_supported == HA_ALTER_INPLACE_NOCOPY_NO_LOCK ||
+ inplace_supported == HA_ALTER_INPLACE_INSTANT)
{
/*
Storage engine has requested exclusive lock only for prepare phase
@@ -7400,7 +7594,9 @@ static bool mysql_inplace_alter_table(THD *thd,
DBUG_ASSERT(0);
// fall through
case HA_ALTER_INPLACE_NO_LOCK:
- case HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE:
+ case HA_ALTER_INPLACE_INSTANT:
+ case HA_ALTER_INPLACE_COPY_NO_LOCK:
+ case HA_ALTER_INPLACE_NOCOPY_NO_LOCK:
switch (alter_info->requested_lock) {
case Alter_info::ALTER_TABLE_LOCK_DEFAULT:
case Alter_info::ALTER_TABLE_LOCK_NONE:
@@ -7412,8 +7608,9 @@ static bool mysql_inplace_alter_table(THD *thd,
}
break;
case HA_ALTER_INPLACE_EXCLUSIVE_LOCK:
- case HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE:
case HA_ALTER_INPLACE_SHARED_LOCK:
+ case HA_ALTER_INPLACE_COPY_LOCK:
+ case HA_ALTER_INPLACE_NOCOPY_LOCK:
break;
}
@@ -7428,19 +7625,23 @@ static bool mysql_inplace_alter_table(THD *thd,
necessary only for prepare phase (unless we are not under LOCK TABLES) and
user has not explicitly requested exclusive lock.
*/
- if ((inplace_supported == HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE ||
- inplace_supported == HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE) &&
+ if ((inplace_supported == HA_ALTER_INPLACE_COPY_NO_LOCK ||
+ inplace_supported == HA_ALTER_INPLACE_COPY_LOCK ||
+ inplace_supported == HA_ALTER_INPLACE_NOCOPY_LOCK ||
+ inplace_supported == HA_ALTER_INPLACE_NOCOPY_NO_LOCK) &&
!(thd->locked_tables_mode == LTM_LOCK_TABLES ||
thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES) &&
(alter_info->requested_lock != Alter_info::ALTER_TABLE_LOCK_EXCLUSIVE))
{
/* If storage engine or user requested shared lock downgrade to SNW. */
- if (inplace_supported == HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE ||
+ if (inplace_supported == HA_ALTER_INPLACE_COPY_LOCK ||
+ inplace_supported == HA_ALTER_INPLACE_NOCOPY_LOCK ||
alter_info->requested_lock == Alter_info::ALTER_TABLE_LOCK_SHARED)
table->mdl_ticket->downgrade_lock(MDL_SHARED_NO_WRITE);
else
{
- DBUG_ASSERT(inplace_supported == HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE);
+ DBUG_ASSERT(inplace_supported == HA_ALTER_INPLACE_COPY_NO_LOCK ||
+ inplace_supported == HA_ALTER_INPLACE_NOCOPY_NO_LOCK);
table->mdl_ticket->downgrade_lock(MDL_SHARED_UPGRADABLE);
}
}
@@ -7477,11 +7678,34 @@ static bool mysql_inplace_alter_table(THD *thd,
DEBUG_SYNC(thd, "alter_table_inplace_before_commit");
THD_STAGE_INFO(thd, stage_alter_inplace_commit);
- if (table->file->ha_commit_inplace_alter_table(altered_table,
- ha_alter_info,
- true))
{
- goto rollback;
+ TR_table trt(thd, true);
+ if (trt != *table_list && table->file->ht->prepare_commit_versioned)
+ {
+ ulonglong trx_start_id= 0;
+ ulonglong trx_end_id= table->file->ht->prepare_commit_versioned(thd, &trx_start_id);
+ if (trx_end_id)
+ {
+ if (!TR_table::use_transaction_registry)
+ {
+ my_error(ER_VERS_TRT_IS_DISABLED, MYF(0));
+ goto rollback;
+ }
+ if (trt.update(trx_start_id, trx_end_id))
+ {
+ goto rollback;
+ }
+ }
+ }
+
+ if (table->file->ha_commit_inplace_alter_table(altered_table,
+ ha_alter_info,
+ true))
+ {
+ goto rollback;
+ }
+
+ thd->drop_temporary_table(altered_table, NULL, false);
}
close_all_tables_for_name(thd, table->s,
@@ -7491,8 +7715,6 @@ static bool mysql_inplace_alter_table(THD *thd,
NULL);
table_list->table= table= NULL;
- thd->drop_temporary_table(altered_table, NULL, false);
-
/*
Replace the old .FRM with the new .FRM, but keep the old name for now.
Rename to the new name (if needed) will be handled separately below.
@@ -7501,14 +7723,14 @@ static bool mysql_inplace_alter_table(THD *thd,
errors in some val_*() methoids and bring some single place to
such error interception).
*/
- if (mysql_rename_table(db_type, alter_ctx->new_db, alter_ctx->tmp_name,
- alter_ctx->db, alter_ctx->alias,
+ if (mysql_rename_table(db_type, &alter_ctx->new_db, &alter_ctx->tmp_name,
+ &alter_ctx->db, &alter_ctx->alias,
FN_FROM_IS_TMP | NO_HA_TABLE) ||
thd->is_error())
{
// Since changes were done in-place, we can't revert them.
(void) quick_rm_table(thd, db_type,
- alter_ctx->new_db, alter_ctx->tmp_name,
+ &alter_ctx->new_db, &alter_ctx->tmp_name,
FN_IS_TMP | NO_HA_TABLE);
DBUG_RETURN(true);
}
@@ -7536,10 +7758,10 @@ static bool mysql_inplace_alter_table(THD *thd,
{
// Remove TABLE and TABLE_SHARE for old name from TDC.
tdc_remove_table(thd, TDC_RT_REMOVE_ALL,
- alter_ctx->db, alter_ctx->table_name, false);
+ alter_ctx->db.str, alter_ctx->table_name.str, false);
- if (mysql_rename_table(db_type, alter_ctx->db, alter_ctx->table_name,
- alter_ctx->new_db, alter_ctx->new_alias, 0))
+ if (mysql_rename_table(db_type, &alter_ctx->db, &alter_ctx->table_name,
+ &alter_ctx->new_db, &alter_ctx->new_alias, 0))
{
/*
If the rename fails we will still have a working table
@@ -7548,23 +7770,23 @@ static bool mysql_inplace_alter_table(THD *thd,
DBUG_RETURN(true);
}
if (Table_triggers_list::change_table_name(thd,
- alter_ctx->db,
- alter_ctx->alias,
- alter_ctx->table_name,
- alter_ctx->new_db,
- alter_ctx->new_alias))
+ &alter_ctx->db,
+ &alter_ctx->alias,
+ &alter_ctx->table_name,
+ &alter_ctx->new_db,
+ &alter_ctx->new_alias))
{
/*
If the rename of trigger files fails, try to rename the table
back so we at least have matching table and trigger files.
*/
(void) mysql_rename_table(db_type,
- alter_ctx->new_db, alter_ctx->new_alias,
- alter_ctx->db, alter_ctx->alias, NO_FK_CHECKS);
+ &alter_ctx->new_db, &alter_ctx->new_alias,
+ &alter_ctx->db, &alter_ctx->alias, NO_FK_CHECKS);
DBUG_RETURN(true);
}
- rename_table_in_stat_tables(thd, alter_ctx->db,alter_ctx->alias,
- alter_ctx->new_db, alter_ctx->new_alias);
+ rename_table_in_stat_tables(thd, &alter_ctx->db, &alter_ctx->alias,
+ &alter_ctx->new_db, &alter_ctx->new_alias);
}
DBUG_RETURN(false);
@@ -7588,8 +7810,8 @@ static bool mysql_inplace_alter_table(THD *thd,
}
thd->drop_temporary_table(altered_table, NULL, false);
// Delete temporary .frm/.par
- (void) quick_rm_table(thd, create_info->db_type, alter_ctx->new_db,
- alter_ctx->tmp_name, FN_IS_TMP | NO_HA_TABLE);
+ (void) quick_rm_table(thd, create_info->db_type, &alter_ctx->new_db,
+ &alter_ctx->tmp_name, FN_IS_TMP | NO_HA_TABLE);
DBUG_RETURN(true);
}
@@ -7614,7 +7836,7 @@ blob_length_by_type(enum_field_types type)
case MYSQL_TYPE_MEDIUM_BLOB:
return 16777215;
case MYSQL_TYPE_LONG_BLOB:
- return 4294967295U;
+ return (uint) UINT_MAX32;
default:
DBUG_ASSERT(0); // we should never go here
return 0;
@@ -7622,6 +7844,16 @@ blob_length_by_type(enum_field_types type)
}
+static inline
+void append_drop_column(THD *thd, String *str, Field *field)
+{
+ if (str->length())
+ str->append(STRING_WITH_LEN(", "));
+ str->append(STRING_WITH_LEN("DROP COLUMN "));
+ append_identifier(thd, str, &field->field_name);
+}
+
+
/**
Prepare column and key definitions for CREATE TABLE in ALTER TABLE.
@@ -7686,10 +7918,11 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
uint db_create_options= (table->s->db_create_options
& ~(HA_OPTION_PACK_RECORD));
Item::func_processor_rename column_rename_param;
- uint used_fields;
+ uint used_fields, dropped_sys_vers_fields= 0;
KEY *key_info=table->key_info;
bool rc= TRUE;
bool modified_primary_key= FALSE;
+ bool vers_system_invisible= false;
Create_field *def;
Field **f_ptr,*field;
MY_BITMAP *dropped_fields= NULL; // if it's NULL - no dropped fields
@@ -7735,10 +7968,11 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
if (!(used_fields & HA_CREATE_USED_CONNECTION))
create_info->connect_string= table->s->connect_string;
- column_rename_param.db_name.str= table->s->db.str;
- column_rename_param.db_name.length= table->s->db.length;
- column_rename_param.table_name.str= table->s->table_name.str;
- column_rename_param.table_name.length= table->s->table_name.length;
+ if (!(used_fields & HA_CREATE_USED_SEQUENCE))
+ create_info->sequence= table->s->table_type == TABLE_TYPE_SEQUENCE;
+
+ column_rename_param.db_name= table->s->db;
+ column_rename_param.table_name= table->s->table_name;
if (column_rename_param.fields.copy(&alter_info->create_list, thd->mem_root))
DBUG_RETURN(1); // OOM
@@ -7759,6 +7993,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
bitmap_clear_all(&table->tmp_set);
for (f_ptr=table->field ; (field= *f_ptr) ; f_ptr++)
{
+ if (field->invisible == INVISIBLE_FULL)
+ continue;
Alter_drop *drop;
if (field->type() == MYSQL_TYPE_VARCHAR)
create_info->varchar= TRUE;
@@ -7767,10 +8003,18 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
while ((drop=drop_it++))
{
if (drop->type == Alter_drop::COLUMN &&
- !my_strcasecmp(system_charset_info, field->field_name, drop->name))
- break;
+ !my_strcasecmp(system_charset_info,field->field_name.str, drop->name))
+ break;
}
- if (drop)
+ /*
+ DROP COLULMN xxx
+ 1. it does not see INVISIBLE_SYSTEM columns
+ 2. otherwise, normally a column is dropped
+ 3. unless it's a system versioning column (but see below).
+ */
+ if (drop && field->invisible < INVISIBLE_SYSTEM &&
+ !(field->flags & VERS_SYSTEM_FIELD &&
+ !(alter_info->flags & ALTER_DROP_SYSTEM_VERSIONING)))
{
/* Reset auto_increment value if it was dropped */
if (MTYP_TYPENR(field->unireg_check) == Field::NEXT_NUMBER &&
@@ -7781,17 +8025,32 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
}
if (table->s->tmp_table == NO_TMP_TABLE)
(void) delete_statistics_for_column(thd, table, field);
+ dropped_sys_vers_fields|= field->flags;
drop_it.remove();
dropped_fields= &table->tmp_set;
bitmap_set_bit(dropped_fields, field->field_index);
continue;
}
+ if (field->invisible == INVISIBLE_SYSTEM &&
+ field->flags & VERS_SYSTEM_FIELD)
+ {
+ vers_system_invisible= true;
+ }
+ /* invisible versioning column is dropped automatically on DROP SYSTEM VERSIONING */
+ if (!drop && field->invisible >= INVISIBLE_SYSTEM &&
+ field->flags & VERS_SYSTEM_FIELD &&
+ alter_info->flags & ALTER_DROP_SYSTEM_VERSIONING)
+ {
+ if (table->s->tmp_table == NO_TMP_TABLE)
+ (void) delete_statistics_for_column(thd, table, field);
+ 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_info::ALTER_RENAME_COLUMN)
+ if (alter_info->flags & ALTER_RENAME_COLUMN)
{
if (field->vcol_info)
field->vcol_info->expr->walk(&Item::rename_fields_processor, 1,
@@ -7810,11 +8069,12 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
def_it.rewind();
while ((def=def_it++))
{
- if (def->change &&
- !my_strcasecmp(system_charset_info,field->field_name, def->change))
+ if (def->change.str &&
+ !lex_string_cmp(system_charset_info, &field->field_name,
+ &def->change))
break;
}
- if (def)
+ if (def && field->invisible < INVISIBLE_SYSTEM)
{ // Field is changed
def->field=field;
/*
@@ -7828,7 +8088,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
my_error(ER_UNSUPPORTED_ACTION_ON_GENERATED_COLUMN, MYF(0));
goto err;
}
- if (!def->after)
+ if (!def->after.str)
{
/*
If this ALTER TABLE doesn't have an AFTER clause for the modified
@@ -7840,6 +8100,31 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
def_it.remove();
}
}
+ else if (alter_info->flags & ALTER_DROP_SYSTEM_VERSIONING &&
+ field->flags & VERS_SYSTEM_FIELD &&
+ field->invisible < INVISIBLE_SYSTEM)
+ {
+ StringBuffer<NAME_LEN*3> tmp;
+ append_drop_column(thd, &tmp, field);
+ my_error(ER_MISSING, MYF(0), table->s->table_name.str, tmp.c_ptr());
+ goto err;
+ }
+ else if (drop && field->invisible < INVISIBLE_SYSTEM &&
+ field->flags & VERS_SYSTEM_FIELD &&
+ !(alter_info->flags & ALTER_DROP_SYSTEM_VERSIONING))
+ {
+ /* "dropping" a versioning field only hides it from the user */
+ def= new (thd->mem_root) Create_field(thd, field, field);
+ def->invisible= INVISIBLE_SYSTEM;
+ alter_info->flags|= ALTER_CHANGE_COLUMN;
+ if (field->flags & VERS_SYS_START_FLAG)
+ create_info->vers_info.as_row.start= def->field_name= Vers_parse_info::default_start;
+ else
+ create_info->vers_info.as_row.end= def->field_name= Vers_parse_info::default_end;
+ new_create_list.push_back(def, thd->mem_root);
+ dropped_sys_vers_fields|= field->flags;
+ drop_it.remove();
+ }
else
{
/*
@@ -7852,7 +8137,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
Alter_column *alter;
while ((alter=alter_it++))
{
- if (!my_strcasecmp(system_charset_info,field->field_name, alter->name))
+ if (!my_strcasecmp(system_charset_info,field->field_name.str,
+ alter->name))
break;
}
if (alter)
@@ -7865,27 +8151,48 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
}
}
}
+ dropped_sys_vers_fields &= VERS_SYSTEM_FIELD;
+ if ((dropped_sys_vers_fields ||
+ alter_info->flags & ALTER_DROP_PERIOD) &&
+ dropped_sys_vers_fields != VERS_SYSTEM_FIELD &&
+ !vers_system_invisible)
+ {
+ StringBuffer<NAME_LEN*3> tmp;
+ if (!(dropped_sys_vers_fields & VERS_SYS_START_FLAG))
+ append_drop_column(thd, &tmp, table->vers_start_field());
+ if (!(dropped_sys_vers_fields & VERS_SYS_END_FLAG))
+ append_drop_column(thd, &tmp, table->vers_end_field());
+ my_error(ER_MISSING, MYF(0), table->s->table_name.str, tmp.c_ptr());
+ goto err;
+ }
+ else if (alter_info->flags & ALTER_DROP_PERIOD && vers_system_invisible)
+ {
+ my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0), "PERIOD FOR SYSTEM_TIME on", table->s->table_name.str);
+ goto err;
+ }
+ alter_info->flags &= ~(ALTER_DROP_PERIOD | ALTER_ADD_PERIOD);
def_it.rewind();
while ((def=def_it++)) // Add new columns
{
- if (def->change && ! def->field)
+ Create_field *find;
+ if (def->change.str && ! def->field)
{
/*
Check if there is modify for newly added field.
*/
- Create_field *find;
find_it.rewind();
while((find=find_it++))
{
- if (!my_strcasecmp(system_charset_info,find->field_name, def->field_name))
+ if (!my_strcasecmp(system_charset_info,find->field_name.str,
+ def->field_name.str))
break;
}
- if (find && !find->field)
+ if (likely(find && !find->field))
find_it.remove();
else
{
- my_error(ER_BAD_FIELD_ERROR, MYF(0), def->change,
+ my_error(ER_BAD_FIELD_ERROR, MYF(0), def->change.str,
table->s->table_name.str);
goto err;
}
@@ -7897,10 +8204,10 @@ 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->sql_type == MYSQL_TYPE_DATE ||
- def->sql_type == MYSQL_TYPE_NEWDATE ||
- def->sql_type == MYSQL_TYPE_DATETIME ||
- def->sql_type == MYSQL_TYPE_DATETIME2) &&
+ 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)
@@ -7908,12 +8215,11 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
alter_ctx->datetime_field= def;
alter_ctx->error_if_not_empty= TRUE;
}
- if (!def->after)
+ if (!def->after.str)
new_create_list.push_back(def, thd->mem_root);
else
{
- Create_field *find;
- if (def->change)
+ if (def->change.str)
{
find_it.rewind();
/*
@@ -7936,19 +8242,21 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
}
}
}
- if (def->after == first_keyword)
+ if (def->after.str == first_keyword)
new_create_list.push_front(def, thd->mem_root);
else
{
find_it.rewind();
while ((find=find_it++))
{
- if (!my_strcasecmp(system_charset_info, def->after, find->field_name))
+ if (!lex_string_cmp(system_charset_info, &def->after,
+ &find->field_name))
break;
}
- if (!find)
+ if (unlikely(!find))
{
- my_error(ER_BAD_FIELD_ERROR, MYF(0), def->after, table->s->table_name.str);
+ my_error(ER_BAD_FIELD_ERROR, MYF(0), def->after.str,
+ table->s->table_name.str);
goto err;
}
find_it.after(def); // Put column after this
@@ -7961,7 +8269,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
Alter_column *alter;
while ((alter=alter_it++))
{
- if (!my_strcasecmp(system_charset_info,def->field_name, alter->name))
+ if (!my_strcasecmp(system_charset_info,def->field_name.str,
+ alter->name))
break;
}
if (alter)
@@ -7973,13 +8282,13 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
alter_it.remove();
}
}
- if (alter_info->alter_list.elements)
+ 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);
goto err;
}
- if (!new_create_list.elements)
+ if (unlikely(!new_create_list.elements))
{
my_message(ER_CANT_REMOVE_ALL_FIELDS,
ER_THD(thd, ER_CANT_REMOVE_ALL_FIELDS),
@@ -7991,10 +8300,12 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
Collect all keys which isn't in drop list. Add only those
for which some fields exists.
*/
-
+
for (uint i=0 ; i < table->s->keys ; i++,key_info++)
{
- char *key_name= key_info->name;
+ if (key_info->flags & HA_INVISIBLE_KEY)
+ continue;
+ const char *key_name= key_info->name.str;
Alter_drop *drop;
drop_it.rewind();
while ((drop=drop_it++))
@@ -8030,23 +8341,24 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
bool delete_index_stat= FALSE;
for (uint j=0 ; j < key_info->user_defined_key_parts ; j++,key_part++)
{
- if (!key_part->field)
+ Field *kfield= key_part->field;
+ if (!kfield)
continue; // Wrong field (from UNIREG)
- const char *key_part_name=key_part->field->field_name;
+ const char *key_part_name=kfield->field_name.str;
Create_field *cfield;
uint key_part_length;
field_it.rewind();
while ((cfield=field_it++))
{
- if (cfield->change)
+ if (cfield->change.str)
{
if (!my_strcasecmp(system_charset_info, key_part_name,
- cfield->change))
+ cfield->change.str))
break;
}
else if (!my_strcasecmp(system_charset_info,
- key_part_name, cfield->field_name))
+ key_part_name, cfield->field_name.str))
break;
}
if (!cfield)
@@ -8054,7 +8366,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
if (table->s->primary_key == i)
modified_primary_key= TRUE;
delete_index_stat= TRUE;
- dropped_key_part= key_part_name;
+ if (!(kfield->flags & VERS_SYSTEM_FIELD))
+ dropped_key_part= key_part_name;
continue; // Field is removed
}
key_part_length= key_part->length;
@@ -8080,22 +8393,22 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
- data type maximum length is 255.
- key_part_length is 1016 (=254*4, where 4 is mbmaxlen)
*/
- if (!Field::type_can_have_key_part(cfield->field->type()) ||
- !Field::type_can_have_key_part(cfield->sql_type) ||
+ if (!cfield->field->type_handler()->type_can_have_key_part() ||
+ !cfield->type_handler()->type_can_have_key_part() ||
/* spatial keys can't have sub-key length */
(key_info->flags & HA_SPATIAL) ||
(cfield->field->field_length == key_part_length &&
!f_is_blob(key_part->key_type)) ||
- (cfield->length && (((cfield->sql_type >= MYSQL_TYPE_TINY_BLOB &&
- cfield->sql_type <= MYSQL_TYPE_BLOB) ?
- blob_length_by_type(cfield->sql_type) :
- cfield->length) <
- key_part_length / key_part->field->charset()->mbmaxlen)))
+ (cfield->length &&
+ (((cfield->real_field_type() >= MYSQL_TYPE_TINY_BLOB &&
+ cfield->real_field_type() <= MYSQL_TYPE_BLOB) ?
+ blob_length_by_type(cfield->real_field_type()) :
+ cfield->length) <
+ key_part_length / kfield->charset()->mbmaxlen)))
key_part_length= 0; // Use whole field
}
- key_part_length /= key_part->field->charset()->mbmaxlen;
- key_parts.push_back(new Key_part_spec(cfield->field_name,
- strlen(cfield->field_name),
+ key_part_length /= kfield->charset()->mbmaxlen;
+ key_parts.push_back(new Key_part_spec(&cfield->field_name,
key_part_length),
thd->mem_root);
}
@@ -8113,11 +8426,15 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
KEY_CREATE_INFO key_create_info;
Key *key;
enum Key::Keytype key_type;
+ LEX_CSTRING tmp_name;
bzero((char*) &key_create_info, sizeof(key_create_info));
-
key_create_info.algorithm= key_info->algorithm;
- if (key_info->flags & HA_USES_BLOCK_SIZE)
- key_create_info.block_size= key_info->block_size;
+ /*
+ We copy block size directly as some engines, like Area, sets this
+ automatically
+ */
+ key_create_info.block_size= key_info->block_size;
+ key_create_info.flags= key_info->flags; // HA_USE_BLOCK_SIZE
if (key_info->flags & HA_USES_PARSER)
key_create_info.parser_name= *plugin_name(key_info->parser);
if (key_info->flags & HA_USES_COMMENT)
@@ -8148,10 +8465,11 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
else
key_type= Key::MULTIPLE;
- key= new Key(key_type, key_name, strlen(key_name),
- &key_create_info,
+ tmp_name.str= key_name;
+ tmp_name.length= strlen(key_name);
+ key= new 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_parts, key_info->option_list, DDL_options());
new_key_list.push_back(key, thd->mem_root);
}
}
@@ -8205,13 +8523,13 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
bitmap_intersect(table->read_set, dropped_fields);
uint field_nr= bitmap_get_first_set(table->read_set);
my_error(ER_BAD_FIELD_ERROR, MYF(0),
- table->field[field_nr]->field_name, "CHECK");
+ table->field[field_nr]->field_name.str, "CHECK");
goto err;
}
}
if (!drop)
{
- if (alter_info->flags & Alter_info::ALTER_RENAME_COLUMN)
+ if (alter_info->flags & ALTER_RENAME_COLUMN)
{
check->expr->walk(&Item::rename_fields_processor, 1,
&column_rename_param);
@@ -8331,7 +8649,7 @@ static Create_field *get_field_by_old_name(Alter_info *alter_info,
{
if (new_field->field &&
(my_strcasecmp(system_charset_info,
- new_field->field->field_name,
+ new_field->field->field_name.str,
old_name) == 0))
break;
}
@@ -8371,11 +8689,11 @@ enum fk_column_change_type
static enum fk_column_change_type
fk_check_column_changes(THD *thd, Alter_info *alter_info,
- List<LEX_STRING> &fk_columns,
+ List<LEX_CSTRING> &fk_columns,
const char **bad_column_name)
{
- List_iterator_fast<LEX_STRING> column_it(fk_columns);
- LEX_STRING *column;
+ List_iterator_fast<LEX_CSTRING> column_it(fk_columns);
+ LEX_CSTRING *column;
*bad_column_name= NULL;
@@ -8387,8 +8705,8 @@ fk_check_column_changes(THD *thd, Alter_info *alter_info,
{
Field *old_field= new_field->field;
- if (my_strcasecmp(system_charset_info, old_field->field_name,
- new_field->field_name))
+ if (lex_string_cmp(system_charset_info, &old_field->field_name,
+ &new_field->field_name))
{
/*
Copy algorithm doesn't support proper renaming of columns in
@@ -8474,7 +8792,7 @@ static bool fk_prepare_copy_alter_table(THD *thd, TABLE *table,
table->file->get_parent_foreign_key_list(thd, &fk_parent_key_list);
/* OOM when building list. */
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
DBUG_RETURN(true);
/*
@@ -8501,10 +8819,10 @@ static bool fk_prepare_copy_alter_table(THD *thd, TABLE *table,
if ((drop->type == Alter_drop::FOREIGN_KEY) &&
(my_strcasecmp(system_charset_info, f_key->foreign_id->str,
drop->name) == 0) &&
- (my_strcasecmp(table_alias_charset, f_key->foreign_db->str,
- table->s->db.str) == 0) &&
- (my_strcasecmp(table_alias_charset, f_key->foreign_table->str,
- table->s->table_name.str) == 0))
+ (lex_string_cmp(table_alias_charset, f_key->foreign_db,
+ &table->s->db) == 0) &&
+ (lex_string_cmp(table_alias_charset, f_key->foreign_table,
+ &table->s->table_name) == 0))
fk_parent_key_it.remove();
}
}
@@ -8552,11 +8870,11 @@ static bool fk_prepare_copy_alter_table(THD *thd, TABLE *table,
case FK_COLUMN_DROPPED:
{
StringBuffer<NAME_LEN*2+2> buff(system_charset_info);
- LEX_STRING *db= f_key->foreign_db, *tbl= f_key->foreign_table;
+ LEX_CSTRING *db= f_key->foreign_db, *tbl= f_key->foreign_table;
- append_identifier(thd, &buff, db->str, db->length);
+ append_identifier(thd, &buff, db);
buff.append('.');
- append_identifier(thd, &buff, tbl->str,tbl->length);
+ append_identifier(thd, &buff, tbl);
my_error(ER_FK_COLUMN_CANNOT_DROP_CHILD, MYF(0), bad_column_name,
f_key->foreign_id->str, buff.c_ptr());
DBUG_RETURN(true);
@@ -8569,7 +8887,7 @@ static bool fk_prepare_copy_alter_table(THD *thd, TABLE *table,
table->file->get_foreign_key_list(thd, &fk_child_key_list);
/* OOM when building list. */
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
DBUG_RETURN(true);
/*
@@ -8637,7 +8955,7 @@ static bool fk_prepare_copy_alter_table(THD *thd, TABLE *table,
table is not locked yet (it's a temporary table). So, we have to
lock FK parents explicitly.
*/
- if (alter_info->flags & Alter_info::ADD_FOREIGN_KEY)
+ if (alter_info->flags & ALTER_ADD_FOREIGN_KEY)
{
List_iterator<Key> fk_list_it(alter_info->key_list);
@@ -8649,7 +8967,9 @@ static bool fk_prepare_copy_alter_table(THD *thd, TABLE *table,
Foreign_key *fk= static_cast<Foreign_key*>(key);
char dbuf[NAME_LEN];
char tbuf[NAME_LEN];
- const char *ref_db= fk->ref_db.str ? fk->ref_db.str : alter_ctx->new_db;
+ const char *ref_db= (fk->ref_db.str ?
+ fk->ref_db.str :
+ alter_ctx->new_db.str);
const char *ref_table= fk->ref_table.str;
MDL_request mdl_request;
@@ -8707,7 +9027,7 @@ simple_tmp_rename_or_index_change(THD *thd, TABLE_LIST *table_list,
keys_onoff);
}
- if (!error && alter_ctx->is_table_renamed())
+ if (likely(!error) && alter_ctx->is_table_renamed())
{
THD_STAGE_INFO(thd, stage_rename);
@@ -8716,24 +9036,21 @@ simple_tmp_rename_or_index_change(THD *thd, TABLE_LIST *table_list,
back to the original name (unlike the case for non-temporary tables),
as it was an allocation error and the table was not renamed.
*/
- error= thd->rename_temporary_table(table, alter_ctx->new_db,
- alter_ctx->new_alias);
+ error= thd->rename_temporary_table(table, &alter_ctx->new_db,
+ &alter_ctx->new_alias);
}
- if (!error)
+ if (likely(!error))
{
- int res= 0;
/*
We do not replicate alter table statement on temporary tables under
ROW-based replication.
*/
if (!thd->is_current_stmt_binlog_format_row())
{
- res= write_bin_log(thd, true, thd->query(), thd->query_length());
+ error= write_bin_log(thd, true, thd->query(), thd->query_length()) != 0;
}
- if (res != 0)
- error= true;
- else
+ if (likely(!error))
my_ok(thd);
}
@@ -8782,7 +9099,7 @@ simple_rename_or_index_change(THD *thd, TABLE_LIST *table_list,
keys_onoff);
}
- if (!error && alter_ctx->is_table_renamed())
+ if (likely(!error) && alter_ctx->is_table_renamed())
{
THD_STAGE_INFO(thd, stage_rename);
handlerton *old_db_type= table->s->db_type();
@@ -8796,40 +9113,37 @@ simple_rename_or_index_change(THD *thd, TABLE_LIST *table_list,
*/
if (wait_while_table_is_used(thd, table, extra_func))
DBUG_RETURN(true);
- close_all_tables_for_name(thd, table->s, HA_EXTRA_PREPARE_FOR_RENAME, NULL);
-
- LEX_STRING old_db_name= { alter_ctx->db, strlen(alter_ctx->db) };
- LEX_STRING old_table_name=
- { alter_ctx->table_name, strlen(alter_ctx->table_name) };
- LEX_STRING new_db_name= { alter_ctx->new_db, strlen(alter_ctx->new_db) };
- LEX_STRING new_table_name=
- { alter_ctx->new_alias, strlen(alter_ctx->new_alias) };
- (void) rename_table_in_stat_tables(thd, &old_db_name, &old_table_name,
- &new_db_name, &new_table_name);
-
- if (mysql_rename_table(old_db_type, alter_ctx->db, alter_ctx->table_name,
- alter_ctx->new_db, alter_ctx->new_alias, 0))
+ close_all_tables_for_name(thd, table->s, HA_EXTRA_PREPARE_FOR_RENAME,
+ NULL);
+
+ (void) rename_table_in_stat_tables(thd, &alter_ctx->db,
+ &alter_ctx->table_name,
+ &alter_ctx->new_db,
+ &alter_ctx->new_alias);
+
+ if (mysql_rename_table(old_db_type, &alter_ctx->db, &alter_ctx->table_name,
+ &alter_ctx->new_db, &alter_ctx->new_alias, 0))
error= -1;
else if (Table_triggers_list::change_table_name(thd,
- alter_ctx->db,
- alter_ctx->alias,
- alter_ctx->table_name,
- alter_ctx->new_db,
- alter_ctx->new_alias))
+ &alter_ctx->db,
+ &alter_ctx->alias,
+ &alter_ctx->table_name,
+ &alter_ctx->new_db,
+ &alter_ctx->new_alias))
{
(void) mysql_rename_table(old_db_type,
- alter_ctx->new_db, alter_ctx->new_alias,
- alter_ctx->db, alter_ctx->table_name,
+ &alter_ctx->new_db, &alter_ctx->new_alias,
+ &alter_ctx->db, &alter_ctx->table_name,
NO_FK_CHECKS);
error= -1;
}
}
- if (!error)
+ if (likely(!error))
{
error= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
- if (!error)
+ if (likely(!error))
my_ok(thd);
}
table_list->table= NULL; // For query cache
@@ -8890,7 +9204,8 @@ simple_rename_or_index_change(THD *thd, TABLE_LIST *table_list,
based on information about the table changes from fill_alter_inplace_info().
*/
-bool mysql_alter_table(THD *thd,char *new_db, char *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,
@@ -8920,6 +9235,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
(!create_info->db_type || /* unknown engine */
!(create_info->db_type->flags & HTON_SUPPORT_LOG_TABLES)))
{
+ unsupported:
my_error(ER_UNSUPORTED_LOG_ENGINE, MYF(0),
hton_name(create_info->db_type)->str);
DBUG_RETURN(true);
@@ -8927,13 +9243,10 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
if (create_info->db_type == maria_hton &&
create_info->transactional != HA_CHOICE_NO)
- {
- my_error(ER_TRANSACTIONAL_ARIA_LOG_ENGINE, MYF(0));
- DBUG_RETURN(true);
- }
+ goto unsupported;
#ifdef WITH_PARTITION_STORAGE_ENGINE
- if (alter_info->flags & Alter_info::ALTER_PARTITION)
+ if (alter_info->partition_flags & ALTER_PARTITION_INFO)
{
my_error(ER_WRONG_USAGE, MYF(0), "PARTITION", "log table");
DBUG_RETURN(true);
@@ -8941,14 +9254,14 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
#endif
}
- THD_STAGE_INFO(thd, stage_init);
+ THD_STAGE_INFO(thd, stage_init_update);
/*
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
has been already processed.
*/
- table_list->required_type= FRMTYPE_TABLE;
+ table_list->required_type= TABLE_TYPE_NORMAL;
Alter_table_prelocking_strategy alter_prelocking_strategy;
@@ -8960,6 +9273,31 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
&alter_prelocking_strategy);
thd->open_options&= ~HA_OPEN_FOR_ALTER;
+ TABLE *table= table_list->table;
+ bool versioned= table && table->versioned();
+
+ if (versioned)
+ {
+ if (handlerton *hton1= create_info->db_type)
+ {
+ handlerton *hton2= table->file->partition_ht();
+ if (hton1 != hton2 &&
+ (ha_check_storage_engine_flag(hton1, HTON_NATIVE_SYS_VERSIONING) ||
+ ha_check_storage_engine_flag(hton2, HTON_NATIVE_SYS_VERSIONING)))
+ {
+ my_error(ER_VERS_ALTER_ENGINE_PROHIBITED, MYF(0), table_list->db.str,
+ table_list->table_name.str);
+ DBUG_RETURN(true);
+ }
+ }
+ if (alter_info->vers_prohibited(thd))
+ {
+ my_error(ER_VERS_ALTER_NOT_ALLOWED, MYF(0),
+ table_list->db.str, table_list->table_name.str);
+ DBUG_RETURN(true);
+ }
+ }
+
DEBUG_SYNC(thd, "alter_opened_table");
#ifdef WITH_WSREP
@@ -8973,10 +9311,9 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
};);
#endif // WITH_WSREP
- if (error)
+ if (unlikely(error))
DBUG_RETURN(true);
- TABLE *table= table_list->table;
table->use_all_columns();
MDL_ticket *mdl_ticket= table->mdl_ticket;
@@ -9009,9 +9346,9 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
If such table exists, there must be a corresponding TABLE_SHARE in
THD::all_temp_tables list.
*/
- if (thd->find_tmp_table_share(alter_ctx.new_db, alter_ctx.new_name))
+ if (thd->find_tmp_table_share(alter_ctx.new_db.str, alter_ctx.new_name.str))
{
- my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alter_ctx.new_alias);
+ my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alter_ctx.new_alias.str);
DBUG_RETURN(true);
}
}
@@ -9021,7 +9358,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
MDL_request target_db_mdl_request;
target_mdl_request.init(MDL_key::TABLE,
- alter_ctx.new_db, alter_ctx.new_name,
+ alter_ctx.new_db.str, alter_ctx.new_name.str,
MDL_EXCLUSIVE, MDL_TRANSACTION);
mdl_requests.push_front(&target_mdl_request);
@@ -9032,7 +9369,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
*/
if (alter_ctx.is_database_changed())
{
- target_db_mdl_request.init(MDL_key::SCHEMA, alter_ctx.new_db, "",
+ target_db_mdl_request.init(MDL_key::SCHEMA, alter_ctx.new_db.str, "",
MDL_INTENTION_EXCLUSIVE,
MDL_TRANSACTION);
mdl_requests.push_front(&target_db_mdl_request);
@@ -9055,10 +9392,10 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
Table maybe does not exist, but we got an exclusive lock
on the name, now we can safely try to find out for sure.
*/
- if (ha_table_exists(thd, alter_ctx.new_db, alter_ctx.new_name, 0))
+ if (ha_table_exists(thd, &alter_ctx.new_db, &alter_ctx.new_name))
{
/* Table will be closed in do_command() */
- my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alter_ctx.new_alias);
+ my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alter_ctx.new_alias.str);
DBUG_RETURN(true);
}
}
@@ -9084,11 +9421,16 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
create_info->db_type= table->s->db_type();
}
- if (check_engine(thd, alter_ctx.new_db, alter_ctx.new_name, create_info))
+ if (check_engine(thd, alter_ctx.new_db.str, alter_ctx.new_name.str, create_info))
DBUG_RETURN(true);
+ if (create_info->vers_info.fix_alter_info(thd, alter_info, create_info, table))
+ {
+ DBUG_RETURN(true);
+ }
+
if ((create_info->db_type != table->s->db_type() ||
- alter_info->flags & Alter_info::ALTER_PARTITION) &&
+ (alter_info->partition_flags & ALTER_PARTITION_INFO)) &&
!table->file->can_switch_engines())
{
my_error(ER_ROW_IS_REFERENCED, MYF(0));
@@ -9103,8 +9445,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
create_info->db_type is set here, check to parent table access is delayed
till this point for the alter operation.
*/
- if ((alter_info->flags & Alter_info::ADD_FOREIGN_KEY) &&
- check_fk_parent_table_access(thd, create_info, alter_info, new_db))
+ if ((alter_info->flags & ALTER_ADD_FOREIGN_KEY) &&
+ check_fk_parent_table_access(thd, create_info, alter_info, new_db->str))
DBUG_RETURN(true);
/*
@@ -9130,7 +9472,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
{
DBUG_PRINT("info", ("doesn't support alter"));
my_error(ER_ILLEGAL_HA, MYF(0), hton_name(table->s->db_type())->str,
- alter_ctx.db, alter_ctx.table_name);
+ alter_ctx.db.str, alter_ctx.table_name.str);
DBUG_RETURN(true);
}
@@ -9139,7 +9481,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
{
DBUG_PRINT("info", ("doesn't support alter"));
my_error(ER_ILLEGAL_HA, MYF(0), hton_name(create_info->db_type)->str,
- alter_ctx.new_db, alter_ctx.new_name);
+ alter_ctx.new_db.str, alter_ctx.new_name.str);
DBUG_RETURN(true);
}
@@ -9148,7 +9490,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
THD_STAGE_INFO(thd, stage_setup);
- if (alter_info->flags & Alter_info::ALTER_DROP_CHECK_CONSTRAINT)
+ if (alter_info->flags & ALTER_DROP_CHECK_CONSTRAINT)
{
/*
ALTER TABLE DROP CONSTRAINT
@@ -9161,7 +9503,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
List <FOREIGN_KEY_INFO> fk_child_key_list;
table->file->get_foreign_key_list(thd, &fk_child_key_list);
- alter_info->flags&= ~Alter_info::ALTER_DROP_CHECK_CONSTRAINT;
+ alter_info->flags&= ~ALTER_DROP_CHECK_CONSTRAINT;
while ((drop= drop_it++))
{
@@ -9178,7 +9520,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
drop->name) == 0)
{
drop->type= Alter_drop::FOREIGN_KEY;
- alter_info->flags|= Alter_info::DROP_FOREIGN_KEY;
+ alter_info->flags|= ALTER_DROP_FOREIGN_KEY;
goto do_continue;
}
}
@@ -9192,16 +9534,16 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
{
if ((table->key_info[n_key].flags & HA_NOSAME) &&
my_strcasecmp(system_charset_info,
- drop->name, table->key_info[n_key].name) == 0)
+ drop->name, table->key_info[n_key].name.str) == 0) // Merge todo: review '.str'
{
drop->type= Alter_drop::KEY;
- alter_info->flags|= Alter_info::ALTER_DROP_INDEX;
+ alter_info->flags|= ALTER_DROP_INDEX;
goto do_continue;
}
}
}
}
- alter_info->flags|= Alter_info::ALTER_DROP_CHECK_CONSTRAINT;
+ alter_info->flags|= ALTER_DROP_CHECK_CONSTRAINT;
do_continue:;
}
}
@@ -9216,12 +9558,12 @@ do_continue:;
ALTER can become NOOP after handling
the IF (NOT) EXISTS options.
*/
- if (alter_info->flags == 0)
+ if (alter_info->flags == 0 && alter_info->partition_flags == 0)
{
- my_snprintf(alter_ctx.tmp_name, sizeof(alter_ctx.tmp_name),
+ my_snprintf(alter_ctx.tmp_buff, sizeof(alter_ctx.tmp_buff),
ER_THD(thd, ER_INSERT_INFO), 0L, 0L,
thd->get_stmt_da()->current_statement_warn_count());
- my_ok(thd, 0L, 0L, alter_ctx.tmp_name);
+ 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 ||
@@ -9238,9 +9580,9 @@ do_continue:;
Test if we are only doing RENAME or KEYS ON/OFF. This works
as we are testing if flags == 0 above.
*/
- if (!(alter_info->flags & ~(Alter_info::ALTER_RENAME |
- Alter_info::ALTER_KEYS_ONOFF)) &&
- alter_info->requested_algorithm !=
+ if (!(alter_info->flags & ~(ALTER_RENAME | ALTER_KEYS_ONOFF)) &&
+ alter_info->partition_flags == 0 &&
+ alter_info->algorithm(thd) !=
Alter_info::ALTER_TABLE_ALGORITHM_COPY) // No need to touch frm.
{
bool res;
@@ -9289,7 +9631,13 @@ do_continue:;
DBUG_RETURN(true);
}
- set_table_default_charset(thd, create_info, alter_ctx.db);
+ if (create_info->vers_check_system_fields(thd, alter_info,
+ table->s->table_name, table->s->db))
+ {
+ DBUG_RETURN(true);
+ }
+
+ set_table_default_charset(thd, create_info, &alter_ctx.db);
if (!opt_explicit_defaults_for_timestamp)
promote_first_timestamp_column(&alter_info->create_list);
@@ -9300,7 +9648,7 @@ do_continue:;
/*
ALGORITHM and LOCK clauses are generally not allowed by the
parser for operations related to partitioning.
- The exceptions are ALTER_PARTITION and ALTER_REMOVE_PARTITIONING.
+ The exceptions are ALTER_PARTITION_INFO and ALTER_PARTITION_REMOVE.
For consistency, we report ER_ALTER_OPERATION_NOT_SUPPORTED here.
*/
if (alter_info->requested_lock !=
@@ -9312,7 +9660,7 @@ do_continue:;
"LOCK=DEFAULT");
DBUG_RETURN(true);
}
- else if (alter_info->requested_algorithm !=
+ else if (alter_info->algorithm(thd) !=
Alter_info::ALTER_TABLE_ALGORITHM_DEFAULT)
{
my_error(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON, MYF(0),
@@ -9326,9 +9674,9 @@ do_continue:;
Upgrade from MDL_SHARED_UPGRADABLE to MDL_SHARED_NO_WRITE.
Afterwards it's safe to take the table level lock.
*/
- if (thd->mdl_context.upgrade_shared_lock(mdl_ticket, MDL_SHARED_NO_WRITE,
- thd->variables.lock_wait_timeout)
- || lock_tables(thd, table_list, alter_ctx.tables_opened, 0))
+ if ((thd->mdl_context.upgrade_shared_lock(mdl_ticket, MDL_SHARED_NO_WRITE,
+ thd->variables.lock_wait_timeout)) ||
+ lock_tables(thd, table_list, alter_ctx.tables_opened, 0))
{
DBUG_RETURN(true);
}
@@ -9336,8 +9684,8 @@ do_continue:;
// In-place execution of ALTER TABLE for partitioning.
DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info,
create_info, table_list,
- alter_ctx.db,
- alter_ctx.table_name));
+ &alter_ctx.db,
+ &alter_ctx.table_name));
}
#endif
@@ -9351,21 +9699,22 @@ do_continue:;
supports auto-partitioning as such engines can do some changes
using in-place API.
*/
- if ((thd->variables.old_alter_table &&
- alter_info->requested_algorithm !=
+ if ((thd->variables.alter_algorithm == Alter_info::ALTER_TABLE_ALGORITHM_COPY &&
+ alter_info->algorithm(thd) !=
Alter_info::ALTER_TABLE_ALGORITHM_INPLACE)
|| is_inplace_alter_impossible(table, create_info, alter_info)
|| IF_PARTITIONING((partition_changed &&
!(table->s->db_type()->partition_flags() & HA_USE_AUTO_PARTITION)), 0))
{
- if (alter_info->requested_algorithm ==
+ if (alter_info->algorithm(thd) ==
Alter_info::ALTER_TABLE_ALGORITHM_INPLACE)
{
my_error(ER_ALTER_OPERATION_NOT_SUPPORTED, MYF(0),
"ALGORITHM=INPLACE", "ALGORITHM=COPY");
DBUG_RETURN(true);
}
- alter_info->requested_algorithm= Alter_info::ALTER_TABLE_ALGORITHM_COPY;
+ alter_info->set_requested_algorithm(
+ Alter_info::ALTER_TABLE_ALGORITHM_COPY);
}
/*
@@ -9375,7 +9724,7 @@ do_continue:;
*/
if (create_info->db_type == table->s->db_type() &&
create_info->used_fields & HA_CREATE_USED_ENGINE)
- alter_info->flags|= Alter_info::ALTER_RECREATE;
+ alter_info->flags|= ALTER_RECREATE;
/*
If the old table had partitions and we are doing ALTER TABLE ...
@@ -9423,7 +9772,7 @@ do_continue:;
if (create_info->index_file_name)
{
/* Fix index_file_name to have 'tmp_name' as basename */
- strmov(index_file, alter_ctx.tmp_name);
+ strmov(index_file, alter_ctx.tmp_name.str);
create_info->index_file_name=fn_same(index_file,
create_info->index_file_name,
1);
@@ -9431,7 +9780,7 @@ do_continue:;
if (create_info->data_file_name)
{
/* Fix data_file_name to have 'tmp_name' as basename */
- strmov(data_file, alter_ctx.tmp_name);
+ strmov(data_file, alter_ctx.tmp_name.str);
create_info->data_file_name=fn_same(data_file,
create_info->data_file_name,
1);
@@ -9468,15 +9817,15 @@ do_continue:;
tmp_disable_binlog(thd);
create_info->options|=HA_CREATE_TMP_ALTER;
error= create_table_impl(thd,
- alter_ctx.db, alter_ctx.table_name,
- alter_ctx.new_db, alter_ctx.tmp_name,
+ &alter_ctx.db, &alter_ctx.table_name,
+ &alter_ctx.new_db, &alter_ctx.tmp_name,
alter_ctx.get_tmp_path(),
thd->lex->create_info, create_info, alter_info,
C_ALTER_TABLE_FRM_ONLY, NULL,
&key_info, &key_count, &frm);
reenable_binlog(thd);
thd->abort_on_warning= false;
- if (error)
+ if (unlikely(error))
{
my_free(const_cast<uchar*>(frm.str));
DBUG_RETURN(true);
@@ -9485,12 +9834,12 @@ do_continue:;
/* Remember that we have not created table in storage engine yet. */
bool no_ha_table= true;
- if (alter_info->requested_algorithm != Alter_info::ALTER_TABLE_ALGORITHM_COPY)
+ if (alter_info->algorithm(thd) != Alter_info::ALTER_TABLE_ALGORITHM_COPY)
{
Alter_inplace_info ha_alter_info(create_info, alter_info,
key_info, key_count,
IF_PARTITIONING(thd->work_part_info, NULL),
- ignore);
+ ignore, alter_ctx.error_if_not_empty);
TABLE *altered_table= NULL;
bool use_inplace= true;
@@ -9498,7 +9847,15 @@ do_continue:;
if (fill_alter_inplace_info(thd, table, varchar, &ha_alter_info))
goto err_new_table_cleanup;
- if (ha_alter_info.handler_flags == 0)
+ /*
+ We can ignore ALTER_COLUMN_ORDER and instead check
+ ALTER_STORED_COLUMN_ORDER & ALTER_VIRTUAL_COLUMN_ORDER. This
+ is ok as ALTER_COLUMN_ORDER may be wrong if we use AFTER last_field
+ ALTER_COLUMN_NAME is set if field really was renamed.
+ */
+
+ if (!(ha_alter_info.handler_flags &
+ ~(ALTER_COLUMN_ORDER | ALTER_RENAME_COLUMN)))
{
/*
No-op ALTER, no need to call handler API functions.
@@ -9527,8 +9884,9 @@ do_continue:;
if (!(altered_table=
thd->create_and_open_tmp_table(new_db_type, &frm,
alter_ctx.get_tmp_path(),
- alter_ctx.new_db, alter_ctx.tmp_name,
- false)))
+ alter_ctx.new_db.str,
+ alter_ctx.new_name.str,
+ false, true)))
goto err_new_table_cleanup;
/* Set markers for fields in TABLE object for altered table. */
@@ -9543,85 +9901,52 @@ do_continue:;
&altered_table->s->all_set);
restore_record(altered_table, s->default_values); // Create empty record
/* Check that we can call default functions with default field values */
+ thd->count_cuted_fields= CHECK_FIELD_EXPRESSION;
altered_table->reset_default_fields();
if (altered_table->default_field &&
altered_table->update_default_fields(true))
goto err_new_table_cleanup;
+ thd->count_cuted_fields= CHECK_FIELD_IGNORE;
+ if (alter_info->requested_lock == Alter_info::ALTER_TABLE_LOCK_NONE)
+ ha_alter_info.online= true;
// Ask storage engine whether to use copy or in-place
enum_alter_inplace_result inplace_supported=
table->file->check_if_supported_inplace_alter(altered_table,
&ha_alter_info);
- switch (inplace_supported) {
- case HA_ALTER_INPLACE_EXCLUSIVE_LOCK:
- // If SHARED lock and no particular algorithm was requested, use COPY.
- if (alter_info->requested_lock ==
- Alter_info::ALTER_TABLE_LOCK_SHARED &&
- alter_info->requested_algorithm ==
- Alter_info::ALTER_TABLE_ALGORITHM_DEFAULT)
- {
- use_inplace= false;
- }
- // Otherwise, if weaker lock was requested, report errror.
- else if (alter_info->requested_lock ==
- Alter_info::ALTER_TABLE_LOCK_NONE ||
- alter_info->requested_lock ==
- Alter_info::ALTER_TABLE_LOCK_SHARED)
- {
- ha_alter_info.report_unsupported_error("LOCK=NONE/SHARED",
- "LOCK=EXCLUSIVE");
- thd->drop_temporary_table(altered_table, NULL, false);
- goto err_new_table_cleanup;
- }
- break;
- case HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE:
- case HA_ALTER_INPLACE_SHARED_LOCK:
- // If weaker lock was requested, report errror.
- if (alter_info->requested_lock ==
- Alter_info::ALTER_TABLE_LOCK_NONE)
- {
- ha_alter_info.report_unsupported_error("LOCK=NONE", "LOCK=SHARED");
- thd->drop_temporary_table(altered_table, NULL, false);
- goto err_new_table_cleanup;
- }
- break;
- case HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE:
- case HA_ALTER_INPLACE_NO_LOCK:
- break;
- case HA_ALTER_INPLACE_NOT_SUPPORTED:
- // If INPLACE was requested, report error.
- if (alter_info->requested_algorithm ==
- Alter_info::ALTER_TABLE_ALGORITHM_INPLACE)
- {
- ha_alter_info.report_unsupported_error("ALGORITHM=INPLACE",
- "ALGORITHM=COPY");
- thd->drop_temporary_table(altered_table, NULL, false);
- goto err_new_table_cleanup;
- }
- // COPY with LOCK=NONE is not supported, no point in trying.
- if (alter_info->requested_lock ==
- Alter_info::ALTER_TABLE_LOCK_NONE)
- {
- ha_alter_info.report_unsupported_error("LOCK=NONE", "LOCK=SHARED");
- thd->drop_temporary_table(altered_table, NULL, false);
- goto err_new_table_cleanup;
- }
- // Otherwise use COPY
- use_inplace= false;
- break;
- case HA_ALTER_ERROR:
- default:
+ if (alter_info->supports_algorithm(thd, inplace_supported, &ha_alter_info) ||
+ alter_info->supports_lock(thd, inplace_supported, &ha_alter_info))
+ {
thd->drop_temporary_table(altered_table, NULL, false);
goto err_new_table_cleanup;
}
+ // If SHARED lock and no particular algorithm was requested, use COPY.
+ if (inplace_supported == HA_ALTER_INPLACE_EXCLUSIVE_LOCK &&
+ alter_info->requested_lock == Alter_info::ALTER_TABLE_LOCK_SHARED &&
+ alter_info->algorithm(thd) ==
+ Alter_info::ALTER_TABLE_ALGORITHM_DEFAULT &&
+ thd->variables.alter_algorithm ==
+ Alter_info::ALTER_TABLE_ALGORITHM_DEFAULT)
+ use_inplace= false;
+
+ if (inplace_supported == HA_ALTER_INPLACE_NOT_SUPPORTED)
+ use_inplace= false;
+
if (use_inplace)
{
table->s->frm_image= &frm;
+ enum_check_fields save_count_cuted_fields= thd->count_cuted_fields;
+ /*
+ Set the truncated column values of thd as warning
+ for alter table.
+ */
+ thd->count_cuted_fields = CHECK_FIELD_WARN;
int res= mysql_inplace_alter_table(thd, table_list, table, altered_table,
&ha_alter_info, inplace_supported,
&target_mdl_request, &alter_ctx);
+ thd->count_cuted_fields= save_count_cuted_fields;
my_free(const_cast<uchar*>(frm.str));
if (res)
@@ -9673,11 +9998,12 @@ do_continue:;
thd->close_unused_temporary_table_instances(table_list);
// It's now safe to take the table level lock.
- if (lock_tables(thd, table_list, alter_ctx.tables_opened, 0))
+ if (lock_tables(thd, table_list, alter_ctx.tables_opened,
+ MYSQL_LOCK_USE_MALLOC))
goto err_new_table_cleanup;
if (ha_create_table(thd, alter_ctx.get_tmp_path(),
- alter_ctx.new_db, alter_ctx.new_name,
+ alter_ctx.new_db.str, alter_ctx.new_name.str,
create_info, &frm))
goto err_new_table_cleanup;
@@ -9687,7 +10013,9 @@ do_continue:;
new_table=
thd->create_and_open_tmp_table(new_db_type, &frm, alter_ctx.get_tmp_path(),
- alter_ctx.new_db, alter_ctx.new_name, true);
+ alter_ctx.new_db.str,
+ alter_ctx.new_name.str,
+ true, true);
if (!new_table)
goto err_new_table_cleanup;
@@ -9724,7 +10052,9 @@ do_continue:;
order_num, order, &copied, &deleted,
alter_info->keys_onoff,
&alter_ctx))
+ {
goto err_new_table_cleanup;
+ }
}
else
{
@@ -9764,8 +10094,8 @@ do_continue:;
/* Remove link to old table and rename the new one */
thd->drop_temporary_table(table, NULL, true);
/* Should pass the 'new_name' as we store table name in the cache */
- if (thd->rename_temporary_table(new_table, alter_ctx.new_db,
- alter_ctx.new_name))
+ if (thd->rename_temporary_table(new_table, &alter_ctx.new_db,
+ &alter_ctx.new_name))
goto err_new_table_cleanup;
/* We don't replicate alter table statement on temporary tables */
if (!thd->is_current_stmt_binlog_format_row() &&
@@ -9819,32 +10149,36 @@ do_continue:;
Rename the old table to temporary name to have a backup in case
anything goes wrong while renaming the new table.
*/
- char backup_name[32];
- my_snprintf(backup_name, sizeof(backup_name), "%s2-%lx-%lx", tmp_file_prefix,
- current_pid, thd->thread_id);
+ 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);
- if (mysql_rename_table(old_db_type, alter_ctx.db, alter_ctx.table_name,
- alter_ctx.db, backup_name, FN_TO_IS_TMP))
+ 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))
{
// 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);
+ (void) quick_rm_table(thd, new_db_type, &alter_ctx.new_db,
+ &alter_ctx.tmp_name, FN_IS_TMP);
goto err_with_mdl;
}
// Rename the new table to the correct name.
- if (mysql_rename_table(new_db_type, alter_ctx.new_db, alter_ctx.tmp_name,
- alter_ctx.new_db, alter_ctx.new_alias,
+ if (mysql_rename_table(new_db_type, &alter_ctx.new_db, &alter_ctx.tmp_name,
+ &alter_ctx.new_db, &alter_ctx.new_alias,
FN_FROM_IS_TMP))
{
// Rename failed, delete the temporary table.
- (void) quick_rm_table(thd, new_db_type, alter_ctx.new_db,
- alter_ctx.tmp_name, FN_IS_TMP);
+ (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,
+ (void) mysql_rename_table(old_db_type, &alter_ctx.db, &backup_name,
+ &alter_ctx.db, &alter_ctx.alias,
FN_FROM_IS_TMP | NO_FK_CHECKS);
goto err_with_mdl;
}
@@ -9853,27 +10187,27 @@ do_continue:;
if (alter_ctx.is_table_renamed())
{
if (Table_triggers_list::change_table_name(thd,
- alter_ctx.db,
- alter_ctx.alias,
- alter_ctx.table_name,
- alter_ctx.new_db,
- alter_ctx.new_alias))
+ &alter_ctx.db,
+ &alter_ctx.alias,
+ &alter_ctx.table_name,
+ &alter_ctx.new_db,
+ &alter_ctx.new_alias))
{
// Rename succeeded, delete the new table.
(void) quick_rm_table(thd, new_db_type,
- alter_ctx.new_db, alter_ctx.new_alias, 0);
+ &alter_ctx.new_db, &alter_ctx.new_alias, 0);
// Restore the backup of the original table to the old name.
- (void) mysql_rename_table(old_db_type, alter_ctx.db, backup_name,
- alter_ctx.db, alter_ctx.alias,
+ (void) mysql_rename_table(old_db_type, &alter_ctx.db, &backup_name,
+ &alter_ctx.db, &alter_ctx.alias,
FN_FROM_IS_TMP | NO_FK_CHECKS);
goto err_with_mdl;
}
- rename_table_in_stat_tables(thd, alter_ctx.db,alter_ctx.alias,
- alter_ctx.new_db, alter_ctx.new_alias);
+ rename_table_in_stat_tables(thd, &alter_ctx.db, &alter_ctx.alias,
+ &alter_ctx.new_db, &alter_ctx.new_alias);
}
// 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))
+ if (quick_rm_table(thd, old_db_type, &alter_ctx.db, &backup_name, FN_IS_TMP))
{
/*
The fact that deletion of the backup failed is not critical
@@ -9911,11 +10245,11 @@ end_inplace:
}
end_temporary:
- my_snprintf(alter_ctx.tmp_name, sizeof(alter_ctx.tmp_name),
+ my_snprintf(alter_ctx.tmp_buff, sizeof(alter_ctx.tmp_buff),
ER_THD(thd, ER_INSERT_INFO),
(ulong) (copied + deleted), (ulong) deleted,
(ulong) thd->get_stmt_da()->current_statement_warn_count());
- my_ok(thd, copied + deleted, 0L, alter_ctx.tmp_name);
+ my_ok(thd, copied + deleted, 0L, alter_ctx.tmp_buff);
DEBUG_SYNC(thd, "alter_table_inplace_trans_commit");
DBUG_RETURN(false);
@@ -9927,12 +10261,12 @@ err_new_table_cleanup:
the table to be altered isn't empty.
Report error here.
*/
- if (alter_ctx.error_if_not_empty &&
- thd->get_stmt_da()->current_row_for_warning())
+ if (unlikely(alter_ctx.error_if_not_empty &&
+ thd->get_stmt_da()->current_row_for_warning()))
{
const char *f_val= 0;
enum enum_mysql_timestamp_type t_type= MYSQL_TIMESTAMP_DATE;
- switch (alter_ctx.datetime_field->sql_type)
+ switch (alter_ctx.datetime_field->real_field_type())
{
case MYSQL_TYPE_DATE:
case MYSQL_TYPE_NEWDATE:
@@ -9952,9 +10286,9 @@ err_new_table_cleanup:
thd->abort_on_warning= true;
make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN,
f_val, strlength(f_val), t_type,
- alter_ctx.new_db,
- alter_ctx.new_name,
- alter_ctx.datetime_field->field_name);
+ 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;
}
@@ -9964,11 +10298,10 @@ err_new_table_cleanup:
}
else
(void) quick_rm_table(thd, new_db_type,
- alter_ctx.new_db, alter_ctx.tmp_name,
+ &alter_ctx.new_db, &alter_ctx.tmp_name,
(FN_IS_TMP | (no_ha_table ? NO_HA_TABLE : 0)),
alter_ctx.get_tmp_path());
-
DBUG_RETURN(true);
err_with_mdl_after_alter:
@@ -9986,7 +10319,8 @@ err_with_mdl:
tables and release the exclusive metadata lock.
*/
thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
- thd->mdl_context.release_all_locks_for_name(mdl_ticket);
+ if (!table_list->table)
+ thd->mdl_context.release_all_locks_for_name(mdl_ticket);
DBUG_RETURN(true);
}
@@ -10066,13 +10400,18 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
ulonglong prev_insert_id, time_to_report_progress;
Field **dfield_ptr= to->default_field;
uint save_to_s_default_fields= to->s->default_fields;
+ bool make_versioned= !from->versioned() && to->versioned();
+ bool make_unversioned= from->versioned() && !to->versioned();
+ bool keep_versioned= from->versioned() && to->versioned();
+ Field *to_row_start= NULL, *to_row_end= NULL, *from_row_end= NULL;
+ MYSQL_TIME query_start;
DBUG_ENTER("copy_data_between_tables");
/* Two or 3 stages; Sorting, copying data and update indexes */
thd_progress_init(thd, 2 + MY_TEST(order));
- if (!(copy= new Copy_field[to->s->fields]))
- DBUG_RETURN(-1); /* purecov: inspected */
+ if (!(copy= new (thd->mem_root) Copy_field[to->s->fields]))
+ DBUG_RETURN(-1);
if (mysql_trans_prepare_alter_copy_data(thd))
{
@@ -10100,6 +10439,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
thd->abort_on_warning= !ignore && thd->is_strict_mode();
from->file->info(HA_STATUS_VARIABLE);
+ to->file->extra(HA_EXTRA_PREPARE_FOR_ALTER_TABLE);
to->file->ha_start_bulk_insert(from->file->stats.records,
ignore ? 0 : HA_CREATE_UNIQUE_INDEX_BY_SORT);
List_iterator<Create_field> it(create);
@@ -10162,8 +10502,8 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
{
bzero((char *) &tables, sizeof(tables));
tables.table= from;
- tables.alias= tables.table_name= from->s->table_name.str;
- tables.db= from->s->db.str;
+ tables.alias= tables.table_name= from->s->table_name;
+ tables.db= from->s->db;
THD_STAGE_INFO(thd, stage_sorting);
Filesort_tracker dummy_tracker(false);
@@ -10180,6 +10520,17 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
thd_progress_next_stage(thd);
}
+ if (make_versioned)
+ {
+ query_start= thd->query_start_TIME();
+ to_row_start= to->vers_start_field();
+ to_row_end= to->vers_end_field();
+ }
+ else if (make_unversioned)
+ {
+ from_row_end= from->vers_end_field();
+ }
+
THD_STAGE_INFO(thd, stage_copy_to_tmp_table);
/* Tell handler that we have values for all columns in the to table */
to->use_all_columns();
@@ -10202,15 +10553,22 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
if (!ignore) /* for now, InnoDB needs the undo log for ALTER IGNORE */
to->file->extra(HA_EXTRA_BEGIN_ALTER_COPY);
- while (!(error=info.read_record(&info)))
+ while (likely(!(error= info.read_record())))
{
- if (thd->killed)
+ if (unlikely(thd->killed))
{
thd->send_kill_message();
error= 1;
break;
}
- if (++thd->progress.counter >= time_to_report_progress)
+
+ if (make_unversioned)
+ {
+ if (!from_row_end->is_max())
+ continue; // Drop history rows.
+ }
+
+ if (unlikely(++thd->progress.counter >= time_to_report_progress))
{
time_to_report_progress+= MY_HOW_OFTEN_TO_WRITE/10;
thd_progress_report(thd, thd->progress.counter,
@@ -10218,7 +10576,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
}
/* Return error if source table isn't empty. */
- if (alter_ctx->error_if_not_empty)
+ if (unlikely(alter_ctx->error_if_not_empty))
{
error= 1;
break;
@@ -10228,6 +10586,14 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
{
copy_ptr->do_copy(copy_ptr);
}
+
+ if (make_versioned)
+ {
+ to_row_start->set_notnull();
+ to_row_start->store_time(&query_start);
+ to_row_end->set_max();
+ }
+
prev_insert_id= to->file->next_insert_id;
if (to->default_field)
to->update_default_fields(ignore);
@@ -10237,11 +10603,14 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
/* This will set thd->is_error() if fatal failure */
if (to->verify_constraints(ignore) == VIEW_CHECK_SKIP)
continue;
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
{
error= 1;
break;
}
+ if (keep_versioned && to->versioned(VERS_TRX_ID))
+ to->vers_write= false;
+
if (to->next_number_field)
{
if (auto_increment_field_copied)
@@ -10249,9 +10618,9 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
else
to->next_number_field->reset();
}
- error=to->file->ha_write_row(to->record[0]);
+ error= to->file->ha_write_row(to->record[0]);
to->auto_increment_field_not_null= FALSE;
- if (error)
+ if (unlikely(error))
{
if (to->file->is_fatal_error(error, HA_CHECK_DUP))
{
@@ -10263,7 +10632,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
else
{
/* Duplicate key error. */
- if (alter_ctx->fk_error_if_delete_row)
+ if (unlikely(alter_ctx->fk_error_if_delete_row))
{
/*
We are trying to omit a row from the table which serves as parent
@@ -10319,7 +10688,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
/* We are going to drop the temporary table */
to->file->extra(HA_EXTRA_PREPARE_FOR_DROP);
}
- if (to->file->ha_end_bulk_insert() && error <= 0)
+ if (unlikely(to->file->ha_end_bulk_insert()) && error <= 0)
{
/* Give error, if not already given */
if (!thd->is_error())
@@ -10332,7 +10701,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
cleanup_done= 1;
to->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
- if (mysql_trans_commit_alter_copy_data(thd))
+ if (unlikely(mysql_trans_commit_alter_copy_data(thd)))
error= 1;
err:
@@ -10386,8 +10755,8 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list, bool table_copy)
HA_CREATE_INFO create_info;
Alter_info alter_info;
TABLE_LIST *next_table= table_list->next_global;
-
DBUG_ENTER("mysql_recreate_table");
+
/* Set lock type which is appropriate for ALTER TABLE. */
table_list->lock_type= TL_READ_NO_INSERT;
/* Same applies to MDL request. */
@@ -10399,13 +10768,13 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list, bool table_copy)
create_info.row_type=ROW_TYPE_NOT_USED;
create_info.default_table_charset=default_charset_info;
/* Force alter table to recreate table */
- alter_info.flags= (Alter_info::ALTER_CHANGE_COLUMN |
- Alter_info::ALTER_RECREATE);
+ alter_info.flags= (ALTER_CHANGE_COLUMN | ALTER_RECREATE);
if (table_copy)
- alter_info.requested_algorithm= Alter_info::ALTER_TABLE_ALGORITHM_COPY;
+ alter_info.set_requested_algorithm(
+ Alter_info::ALTER_TABLE_ALGORITHM_COPY);
- bool res= mysql_alter_table(thd, NullS, NullS, &create_info,
+ bool res= mysql_alter_table(thd, &null_clex_str, &null_clex_str, &create_info,
table_list, &alter_info, 0,
(ORDER *) 0, 0);
table_list->next_global= next_table;
@@ -10413,18 +10782,6 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list, bool table_copy)
}
-static void flush_checksum(ha_checksum *row_crc, uchar **checksum_start,
- size_t *checksum_length)
-{
- if (*checksum_start)
- {
- *row_crc= my_checksum(*row_crc, *checksum_start, *checksum_length);
- *checksum_start= NULL;
- *checksum_length= 0;
- }
-}
-
-
bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
HA_CHECK_OPT *check_opt)
{
@@ -10468,14 +10825,14 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
TABLE *t;
TABLE_LIST *save_next_global;
- strxmov(table_name, table->db ,".", table->table_name, NullS);
+ strxmov(table_name, table->db.str ,".", table->table_name.str, NullS);
/* Remember old 'next' pointer and break the list. */
save_next_global= table->next_global;
table->next_global= NULL;
table->lock_type= TL_READ;
/* Allow to open real tables only. */
- table->required_type= FRMTYPE_TABLE;
+ table->required_type= TABLE_TYPE_NORMAL;
if (thd->open_temporary_tables(table) ||
open_and_lock_tables(thd, table, FALSE, 0))
@@ -10501,98 +10858,31 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
if (!(check_opt->flags & T_EXTEND) &&
(((t->file->ha_table_flags() & HA_HAS_OLD_CHECKSUM) && thd->variables.old_mode) ||
((t->file->ha_table_flags() & HA_HAS_NEW_CHECKSUM) && !thd->variables.old_mode)))
- protocol->store((ulonglong)t->file->checksum());
+ {
+ if (t->file->info(HA_STATUS_VARIABLE))
+ protocol->store_null();
+ else
+ protocol->store((longlong)t->file->stats.checksum);
+ }
else if (check_opt->flags & T_QUICK)
protocol->store_null();
else
{
- /* calculating table's checksum */
- ha_checksum crc= 0;
- DBUG_ASSERT(t->s->last_null_bit_pos < 8);
- uchar null_mask= (t->s->last_null_bit_pos ?
- (256 - (1 << t->s->last_null_bit_pos)):
- 0);
-
- t->use_all_columns();
-
- if (t->file->ha_rnd_init(1))
- protocol->store_null();
- else
+ int error= t->file->calculate_checksum();
+ if (thd->killed)
{
- for (;;)
- {
- if (thd->killed)
- {
- /*
- we've been killed; let handler clean up, and remove the
- partial current row from the recordset (embedded lib)
- */
- t->file->ha_rnd_end();
- thd->protocol->remove_last_row();
- goto err;
- }
- ha_checksum row_crc= 0;
- int error= t->file->ha_rnd_next(t->record[0]);
- if (unlikely(error))
- {
- if (error == HA_ERR_RECORD_DELETED)
- continue;
- break;
- }
- if (t->s->null_bytes)
- {
- /* fix undefined null bits */
- t->record[0][t->s->null_bytes-1] |= null_mask;
- if (!(t->s->db_create_options & HA_OPTION_PACK_RECORD))
- t->record[0][0] |= 1;
-
- row_crc= my_checksum(row_crc, t->record[0], t->s->null_bytes);
- }
-
- uchar *checksum_start= NULL;
- size_t checksum_length= 0;
- for (uint i= 0; i < t->s->fields; i++ )
- {
- Field *f= t->field[i];
-
- if (! thd->variables.old_mode && f->is_real_null(0))
- {
- flush_checksum(&row_crc, &checksum_start, &checksum_length);
- continue;
- }
- /*
- BLOB and VARCHAR have pointers in their field, we must convert
- to string; GEOMETRY is implemented on top of BLOB.
- BIT may store its data among NULL bits, convert as well.
- */
- switch (f->type()) {
- case MYSQL_TYPE_BLOB:
- case MYSQL_TYPE_VARCHAR:
- case MYSQL_TYPE_GEOMETRY:
- case MYSQL_TYPE_BIT:
- {
- flush_checksum(&row_crc, &checksum_start, &checksum_length);
- String tmp;
- f->val_str(&tmp);
- row_crc= my_checksum(row_crc, (uchar*) tmp.ptr(),
- tmp.length());
- break;
- }
- default:
- if (!checksum_start)
- checksum_start= f->ptr;
- DBUG_ASSERT(checksum_start + checksum_length == f->ptr);
- checksum_length+= f->pack_length();
- break;
- }
- }
- flush_checksum(&row_crc, &checksum_start, &checksum_length);
-
- crc+= row_crc;
- }
- protocol->store((ulonglong)crc);
+ /*
+ we've been killed; let handler clean up, and remove the
+ partial current row from the recordset (embedded lib)
+ */
t->file->ha_rnd_end();
+ thd->protocol->remove_last_row();
+ goto err;
}
+ if (error)
+ protocol->store_null();
+ else
+ protocol->store((longlong)t->file->stats.checksum);
}
trans_rollback_stmt(thd);
close_thread_tables(thd);
@@ -10695,7 +10985,7 @@ bool check_engine(THD *thd, const char *db_name,
}
-bool Sql_cmd_create_table::execute(THD *thd)
+bool Sql_cmd_create_table_like::execute(THD *thd)
{
DBUG_ENTER("Sql_cmd_create_table::execute");
LEX *lex= thd->lex;
@@ -10725,7 +11015,7 @@ bool Sql_cmd_create_table::execute(THD *thd)
ER_WARN_USING_OTHER_HANDLER,
ER_THD(thd, ER_WARN_USING_OTHER_HANDLER),
hton_name(lex->create_info.db_type)->str,
- create_table->table_name);
+ create_table->table_name.str);
}
}
@@ -10750,7 +11040,7 @@ bool Sql_cmd_create_table::execute(THD *thd)
*/
Alter_info alter_info(lex->alter_info, thd->mem_root);
- if (thd->is_fatal_error)
+ if (unlikely(thd->is_fatal_error))
{
/* If out of memory when creating a copy of alter_info. */
res= 1;
@@ -10766,9 +11056,9 @@ bool Sql_cmd_create_table::execute(THD *thd)
/* Fix names if symlinked or relocated tables */
if (append_file_to_dir(thd, &create_info.data_file_name,
- create_table->table_name) ||
+ &create_table->table_name) ||
append_file_to_dir(thd, &create_info.index_file_name,
- create_table->table_name))
+ &create_table->table_name))
goto end_with_restore_list;
/*
@@ -10806,6 +11096,7 @@ bool Sql_cmd_create_table::execute(THD *thd)
}
#ifdef WITH_PARTITION_STORAGE_ENGINE
+ thd->work_part_info= 0;
{
partition_info *part_info= thd->lex->part_info;
if (part_info && !(part_info= part_info->get_clone(thd)))
@@ -10880,8 +11171,8 @@ bool Sql_cmd_create_table::execute(THD *thd)
*/
if (create_info.used_fields & HA_CREATE_USED_UNION)
{
- my_error(ER_WRONG_OBJECT, MYF(0), create_table->db,
- create_table->table_name, "BASE TABLE");
+ my_error(ER_WRONG_OBJECT, MYF(0), create_table->db.str,
+ create_table->table_name.str, "BASE TABLE");
res= 1;
goto end_with_restore_list;
}
@@ -10891,7 +11182,7 @@ bool Sql_cmd_create_table::execute(THD *thd)
thd->lex->create_info.options|= create_info.options;
res= open_and_lock_tables(thd, create_info, lex->query_tables, TRUE, 0);
thd->lex->create_info.options= save_thd_create_info_options;
- if (res)
+ if (unlikely(res))
{
/* Got error or warning. Set res to 1 if error */
if (!(res= thd->is_error()))
@@ -10962,18 +11253,24 @@ bool Sql_cmd_create_table::execute(THD *thd)
}
else
{
+ if (create_info.vers_fix_system_fields(thd, &alter_info, *create_table) ||
+ create_info.vers_check_system_fields(thd, &alter_info,
+ create_table->table_name,
+ create_table->db))
+ goto end_with_restore_list;
+
/*
In STATEMENT format, we probably have to replicate also temporary
tables, like mysql replication does. Also check if the requested
engine is allowed/supported.
*/
if (WSREP(thd) &&
- !check_engine(thd, create_table->db, create_table->table_name,
+ !check_engine(thd, create_table->db.str, create_table->table_name.str,
&create_info) &&
(!thd->is_current_stmt_binlog_format_row() ||
!create_info.tmp_table()))
{
- WSREP_TO_ISOLATION_BEGIN(create_table->db, create_table->table_name, NULL)
+ WSREP_TO_ISOLATION_BEGIN(create_table->db.str, create_table->table_name.str, NULL)
}
/* Regular CREATE TABLE */
res= mysql_create_table(thd, create_table, &create_info, &alter_info);
diff --git a/sql/sql_table.h b/sql/sql_table.h
index aec783d4449..0ccd1bfe827 100644
--- a/sql/sql_table.h
+++ b/sql/sql_table.h
@@ -17,8 +17,7 @@
#ifndef SQL_TABLE_INCLUDED
#define SQL_TABLE_INCLUDED
-#include "my_global.h" /* my_bool */
-#include "my_sys.h" // pthread_mutex_t
+#include <my_sys.h> // pthread_mutex_t
#include "m_string.h" // LEX_CUSTRING
class Alter_info;
@@ -142,10 +141,10 @@ 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;
-uint filename_to_tablename(const char *from, char *to, uint to_length,
+uint filename_to_tablename(const char *from, char *to, size_t to_length,
bool stay_quiet = false);
-uint tablename_to_filename(const char *from, char *to, uint to_length);
-uint check_n_cut_mysql50_prefix(const char *from, char *to, uint to_length);
+uint tablename_to_filename(const char *from, char *to, size_t to_length);
+uint check_n_cut_mysql50_prefix(const char *from, char *to, size_t to_length);
bool check_mysql50_prefix(const char *name);
uint build_table_filename(char *buff, size_t bufflen, const char *db,
const char *table, const char *ext, uint flags);
@@ -192,14 +191,15 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table,
#define C_ALTER_TABLE_FRM_ONLY -2
#define C_ASSISTED_DISCOVERY -3
-int mysql_create_table_no_lock(THD *thd, const char *db,
- const char *table_name,
+int mysql_create_table_no_lock(THD *thd, const LEX_CSTRING *db,
+ const LEX_CSTRING *table_name,
Table_specification_st *create_info,
Alter_info *alter_info, bool *is_trans,
- int create_table_mode);
+ int create_table_mode, TABLE_LIST *table);
handler *mysql_create_frm_image(THD *thd,
- const char *db, const char *table_name,
+ const LEX_CSTRING *db,
+ const LEX_CSTRING *table_name,
HA_CREATE_INFO *create_info,
Alter_info *alter_info,
int create_table_mode,
@@ -217,7 +217,7 @@ 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, char *new_db, char *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,
@@ -230,32 +230,29 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list, bool table_copy);
bool mysql_create_like_table(THD *thd, TABLE_LIST *table,
TABLE_LIST *src_table,
Table_specification_st *create_info);
-bool mysql_rename_table(handlerton *base, const char *old_db,
- const char * old_name, const char *new_db,
- const char * new_name, uint flags);
+bool mysql_rename_table(handlerton *base, const LEX_CSTRING *old_db,
+ const LEX_CSTRING *old_name, const LEX_CSTRING *new_db,
+ const LEX_CSTRING *new_name, uint flags);
bool mysql_backup_table(THD* thd, TABLE_LIST* table_list);
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, my_bool if_exists,
- my_bool drop_temporary);
+bool mysql_rm_table(THD *thd,TABLE_LIST *tables, bool if_exists,
+ bool drop_temporary, bool drop_sequence);
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 log_drop_table(THD *thd, const char *db_name, size_t db_name_length,
- const char *table_name, size_t table_name_length,
- bool temporary_table);
-bool quick_rm_table(THD *thd, handlerton *base, const char *db,
- const char *table_name, uint flags,
+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,
+ const LEX_CSTRING *table_name, uint flags,
const char *table_path=0);
void close_cached_table(THD *thd, TABLE *table);
void sp_prepare_create_field(THD *thd, Column_definition *sql_field);
-int prepare_create_field(Column_definition *sql_field,
- uint *blob_columns,
- ulonglong table_flags);
-CHARSET_INFO* get_sql_field_charset(Create_field *sql_field,
+CHARSET_INFO* get_sql_field_charset(Column_definition *sql_field,
HA_CREATE_INFO *create_info);
bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags);
int write_bin_log(THD *thd, bool clear_error,
diff --git a/sql/sql_tablespace.cc b/sql/sql_tablespace.cc
index 3e241e313a5..d912fabe8c8 100644
--- a/sql/sql_tablespace.cc
+++ b/sql/sql_tablespace.cc
@@ -15,7 +15,7 @@
/* drop and alter of tablespaces */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#include "sql_tablespace.h"
@@ -46,21 +46,16 @@ int mysql_alter_tablespace(THD *thd, st_alter_tablespace *ts_info)
if (hton->alter_tablespace)
{
- if ((error= hton->alter_tablespace(hton, thd, ts_info)))
+ if (unlikely((error= hton->alter_tablespace(hton, thd, ts_info))))
{
if (error == 1)
- {
DBUG_RETURN(1);
- }
if (error == HA_ADMIN_NOT_IMPLEMENTED)
- {
my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "");
- }
else
- {
my_error(error, MYF(0));
- }
+
DBUG_RETURN(error);
}
}
diff --git a/sql/sql_test.cc b/sql/sql_test.cc
index db52ce0aea1..6f144cbcf33 100644
--- a/sql/sql_test.cc
+++ b/sql/sql_test.cc
@@ -16,7 +16,7 @@
/* Write some debug info */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#include "sql_test.h"
@@ -137,7 +137,8 @@ void TEST_filesort(SORT_FIELD *sortorder,uint s_length)
out.append(*sortorder->field->table_name);
out.append('.');
}
- out.append(sortorder->field->field_name ? sortorder->field->field_name:
+ out.append(sortorder->field->field_name.str ?
+ sortorder->field->field_name.str :
"tmp_table_column");
}
else
@@ -238,11 +239,11 @@ static void print_keyuse(KEYUSE *keyuse)
keyuse->val->print(&str, QT_ORDINARY);
str.append('\0');
if (keyuse->is_for_hash_join())
- fieldname= keyuse->table->field[keyuse->keypart]->field_name;
+ fieldname= keyuse->table->field[keyuse->keypart]->field_name.str;
else if (keyuse->keypart == FT_KEYPART)
fieldname= "FT_KEYPART";
else
- fieldname= key_info->key_part[keyuse->keypart].field->field_name;
+ fieldname= key_info->key_part[keyuse->keypart].field->field_name.str;
ll2str(keyuse->used_tables, buf2, 16, 0);
fprintf(DBUG_FILE, "KEYUSE: %s.%s=%s optimize: %u used_tables: %s "
"ref_table_rows: %lu keypart_map: %0lx\n",
@@ -387,10 +388,10 @@ void print_sjm(SJ_MATERIALIZATION_INFO *sjm)
/*
Debugging help: force List<...>::elem function not be removed as unused.
*/
-Item* (List<Item>:: *dbug_list_item_elem_ptr)(int)= &List<Item>::elem;
-Item_equal* (List<Item_equal>:: *dbug_list_item_equal_elem_ptr)(int)=
+Item* (List<Item>:: *dbug_list_item_elem_ptr)(uint)= &List<Item>::elem;
+Item_equal* (List<Item_equal>:: *dbug_list_item_equal_elem_ptr)(uint)=
&List<Item_equal>::elem;
-TABLE_LIST* (List<TABLE_LIST>:: *dbug_list_table_list_elem_ptr)(int) =
+TABLE_LIST* (List<TABLE_LIST>:: *dbug_list_table_list_elem_ptr)(uint) =
&List<TABLE_LIST>::elem;
#endif
@@ -595,13 +596,13 @@ update: %10lu\n",
tmp.ha_update_count);
printf("\nTable status:\n\
Opened tables: %10lu\n\
-Open tables: %10lu\n\
-Open files: %10lu\n\
+Open tables: %10u\n\
+Open files: %10u\n\
Open streams: %10lu\n",
tmp.opened_tables,
- (ulong) tc_records(),
- (ulong) my_file_opened,
- (ulong) my_stream_opened);
+ tc_records(),
+ my_file_opened,
+ my_stream_opened);
#ifndef DONT_USE_THR_ALARM
ALARM_INFO alarm_info;
diff --git a/sql/sql_time.cc b/sql/sql_time.cc
index 7f5919007e8..a629e006c04 100644
--- a/sql/sql_time.cc
+++ b/sql/sql_time.cc
@@ -17,7 +17,7 @@
/* Functions to handle date and time */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_time.h"
#include "tztime.h" // struct Time_zone
#include "sql_class.h" // THD
@@ -36,34 +36,108 @@
Order of elements in 'interval_type_to_name' should correspond to
the order of elements in 'interval_type' enum
- See also interval_type, interval_names
+ See also interval_type, interval_names, append_interval
*/
-LEX_STRING interval_type_to_name[INTERVAL_LAST] = {
- { C_STRING_WITH_LEN("YEAR")},
- { C_STRING_WITH_LEN("QUARTER")},
- { C_STRING_WITH_LEN("MONTH")},
- { C_STRING_WITH_LEN("WEEK")},
- { C_STRING_WITH_LEN("DAY")},
- { C_STRING_WITH_LEN("HOUR")},
- { C_STRING_WITH_LEN("MINUTE")},
- { C_STRING_WITH_LEN("SECOND")},
- { C_STRING_WITH_LEN("MICROSECOND")},
- { C_STRING_WITH_LEN("YEAR_MONTH")},
- { C_STRING_WITH_LEN("DAY_HOUR")},
- { C_STRING_WITH_LEN("DAY_MINUTE")},
- { C_STRING_WITH_LEN("DAY_SECOND")},
- { C_STRING_WITH_LEN("HOUR_MINUTE")},
- { C_STRING_WITH_LEN("HOUR_SECOND")},
- { C_STRING_WITH_LEN("MINUTE_SECOND")},
- { C_STRING_WITH_LEN("DAY_MICROSECOND")},
- { C_STRING_WITH_LEN("HOUR_MICROSECOND")},
- { C_STRING_WITH_LEN("MINUTE_MICROSECOND")},
- { C_STRING_WITH_LEN("SECOND_MICROSECOND")}
-};
-
- /* Calc weekday from daynr */
- /* Returns 0 for monday, 1 for tuesday .... */
+LEX_CSTRING interval_type_to_name[INTERVAL_LAST] = {
+ { STRING_WITH_LEN("YEAR")},
+ { STRING_WITH_LEN("QUARTER")},
+ { STRING_WITH_LEN("MONTH")},
+ { STRING_WITH_LEN("WEEK")},
+ { STRING_WITH_LEN("DAY")},
+ { STRING_WITH_LEN("HOUR")},
+ { STRING_WITH_LEN("MINUTE")},
+ { STRING_WITH_LEN("SECOND")},
+ { STRING_WITH_LEN("MICROSECOND")},
+ { STRING_WITH_LEN("YEAR_MONTH")},
+ { STRING_WITH_LEN("DAY_HOUR")},
+ { STRING_WITH_LEN("DAY_MINUTE")},
+ { STRING_WITH_LEN("DAY_SECOND")},
+ { STRING_WITH_LEN("HOUR_MINUTE")},
+ { STRING_WITH_LEN("HOUR_SECOND")},
+ { STRING_WITH_LEN("MINUTE_SECOND")},
+ { STRING_WITH_LEN("DAY_MICROSECOND")},
+ { STRING_WITH_LEN("HOUR_MICROSECOND")},
+ { STRING_WITH_LEN("MINUTE_MICROSECOND")},
+ { STRING_WITH_LEN("SECOND_MICROSECOND")}
+};
+
+int append_interval(String *str, interval_type int_type, const INTERVAL &interval)
+{
+ char buf[64];
+ size_t len;
+ switch (int_type) {
+ case INTERVAL_YEAR:
+ len= my_snprintf(buf,sizeof(buf),"%u", interval.year);
+ break;
+ case INTERVAL_QUARTER:
+ case INTERVAL_MONTH:
+ len= my_snprintf(buf,sizeof(buf),"%u", interval.month);
+ int_type=INTERVAL_MONTH;
+ break;
+ case INTERVAL_WEEK:
+ case INTERVAL_DAY:
+ len= my_snprintf(buf,sizeof(buf),"%u", interval.day);
+ int_type=INTERVAL_DAY;
+ break;
+ case INTERVAL_HOUR:
+ len= my_snprintf(buf,sizeof(buf),"%u", interval.hour);
+ break;
+ case INTERVAL_MINUTE:
+ len= my_snprintf(buf,sizeof(buf),"%u", interval.minute);
+ break;
+ case INTERVAL_SECOND:
+ len= my_snprintf(buf,sizeof(buf),"%u", interval.second);
+ break;
+ case INTERVAL_MICROSECOND:
+ len= my_snprintf(buf,sizeof(buf),"%u", interval.second_part);
+ break;
+ case INTERVAL_YEAR_MONTH:
+ len= my_snprintf(buf,sizeof(buf),"%u-%02u", interval.day, interval.month);
+ break;
+ case INTERVAL_DAY_HOUR:
+ len= my_snprintf(buf,sizeof(buf),"%u %u", interval.day, interval.hour);
+ break;
+ case INTERVAL_DAY_MINUTE:
+ len= my_snprintf(buf,sizeof(buf),"%u %u:%02u", interval.day, interval.hour, interval.minute);
+ break;
+ case INTERVAL_DAY_SECOND:
+ len= my_snprintf(buf,sizeof(buf),"%u %u:%02u:%02u", interval.day, interval.hour, interval.minute, interval.second);
+ break;
+ case INTERVAL_HOUR_MINUTE:
+ len= my_snprintf(buf,sizeof(buf),"%u:%02u", interval.hour, interval.minute);
+ break;
+ case INTERVAL_HOUR_SECOND:
+ len= my_snprintf(buf,sizeof(buf),"%u:%02u:%02u", interval.hour, interval.minute, interval.second);
+ break;
+ case INTERVAL_MINUTE_SECOND:
+ len= my_snprintf(buf,sizeof(buf),"%u:%02u", interval.minute, interval.second);
+ break;
+ case INTERVAL_DAY_MICROSECOND:
+ len= my_snprintf(buf,sizeof(buf),"%u %u:%02u:%02u.%06u", interval.day, interval.hour, interval.minute, interval.second, interval.second_part);
+ break;
+ case INTERVAL_HOUR_MICROSECOND:
+ len= my_snprintf(buf,sizeof(buf),"%u:%02u:%02u.%06u", interval.hour, interval.minute, interval.second, interval.second_part);
+ break;
+ case INTERVAL_MINUTE_MICROSECOND:
+ len= my_snprintf(buf,sizeof(buf),"%u:%02u.%06u", interval.minute, interval.second, interval.second_part);
+ break;
+ case INTERVAL_SECOND_MICROSECOND:
+ len= my_snprintf(buf,sizeof(buf),"%u.%06u", interval.second, interval.second_part);
+ break;
+ default:
+ DBUG_ASSERT(0);
+ len= 0;
+ }
+ return str->append(buf, len) || str->append(' ') ||
+ str->append(interval_type_to_name + int_type);
+}
+
+
+/*
+ Calc weekday from daynr
+ Returns 0 for monday, 1 for tuesday ...
+*/
int calc_weekday(long daynr,bool sunday_first_day_of_week)
{
@@ -255,8 +329,8 @@ adjust_time_range_with_warn(MYSQL_TIME *ltime, uint dec)
*/
static uint
to_ascii(CHARSET_INFO *cs,
- const char *src, uint src_length,
- char *dst, uint dst_length)
+ const char *src, size_t src_length,
+ char *dst, size_t dst_length)
{
int cnvres;
@@ -279,7 +353,7 @@ to_ascii(CHARSET_INFO *cs,
/* Character set-aware version of str_to_time() */
bool
-str_to_time(CHARSET_INFO *cs, const char *str,uint length,
+str_to_time(CHARSET_INFO *cs, const char *str, size_t length,
MYSQL_TIME *l_time, ulonglong fuzzydate, MYSQL_TIME_STATUS *status)
{
char cnv[32];
@@ -293,7 +367,7 @@ str_to_time(CHARSET_INFO *cs, const char *str,uint length,
/* Character set-aware version of str_to_datetime() */
-bool str_to_datetime(CHARSET_INFO *cs, const char *str, uint length,
+bool str_to_datetime(CHARSET_INFO *cs, const char *str, size_t length,
MYSQL_TIME *l_time, ulonglong flags,
MYSQL_TIME_STATUS *status)
{
@@ -317,7 +391,7 @@ bool str_to_datetime(CHARSET_INFO *cs, const char *str, uint length,
bool
str_to_datetime_with_warn(CHARSET_INFO *cs,
- const char *str, uint length, MYSQL_TIME *l_time,
+ const char *str, size_t length, MYSQL_TIME *l_time,
ulonglong flags)
{
MYSQL_TIME_STATUS status;
@@ -357,19 +431,19 @@ static bool number_to_time_with_warn(bool neg, ulonglong nr, ulong sec_part,
{
int was_cut;
longlong res;
- enum_field_types f_type;
+ enum_mysql_timestamp_type ts_type;
bool have_warnings;
if (fuzzydate & TIME_TIME_ONLY)
{
fuzzydate= TIME_TIME_ONLY; // clear other flags
- f_type= MYSQL_TYPE_TIME;
+ ts_type= MYSQL_TIMESTAMP_TIME;
res= number_to_time(neg, nr, sec_part, ltime, &was_cut);
have_warnings= MYSQL_TIME_WARN_HAVE_WARNINGS(was_cut);
}
else
{
- f_type= MYSQL_TYPE_DATETIME;
+ ts_type= MYSQL_TIMESTAMP_DATETIME;
if (neg)
{
res= -1;
@@ -385,8 +459,7 @@ static bool number_to_time_with_warn(bool neg, ulonglong nr, ulong sec_part,
{
make_truncated_value_warning(current_thd,
Sql_condition::WARN_LEVEL_WARN, str,
- res < 0 ? MYSQL_TIMESTAMP_ERROR
- : mysql_type_to_time_type(f_type),
+ res < 0 ? MYSQL_TIMESTAMP_ERROR : ts_type,
s ? s->db.str : NULL,
s ? s->table_name.str : NULL, field_name);
}
@@ -479,6 +552,7 @@ void localtime_to_TIME(MYSQL_TIME *to, struct tm *from)
to->second= (int) from->tm_sec;
}
+
void calc_time_from_sec(MYSQL_TIME *to, long seconds, long microseconds)
{
long t_seconds;
@@ -762,7 +836,7 @@ DATE_TIME_FORMAT
!parse_date_time_format(format_type, format_str,
format_length, &tmp))
{
- tmp.format.str= (char*) format_str;
+ tmp.format.str= format_str;
tmp.format.length= format_length;
return date_time_format_copy((THD *)0, &tmp);
}
@@ -789,7 +863,8 @@ DATE_TIME_FORMAT
DATE_TIME_FORMAT *date_time_format_copy(THD *thd, DATE_TIME_FORMAT *format)
{
DATE_TIME_FORMAT *new_format;
- ulong length= sizeof(*format) + format->format.length + 1;
+ size_t length= sizeof(*format) + format->format.length + 1;
+ char *format_pos;
if (thd)
new_format= (DATE_TIME_FORMAT *) thd->alloc(length);
@@ -798,14 +873,13 @@ DATE_TIME_FORMAT *date_time_format_copy(THD *thd, DATE_TIME_FORMAT *format)
if (new_format)
{
/* Put format string after current pos */
- new_format->format.str= (char*) (new_format+1);
+ new_format->format.str= format_pos= (char*) (new_format+1);
memcpy((char*) new_format->positions, (char*) format->positions,
sizeof(format->positions));
new_format->time_separator= format->time_separator;
/* We make the string null terminated for easy printf in SHOW VARIABLES */
- memcpy((char*) new_format->format.str, format->format.str,
- format->format.length);
- new_format->format.str[format->format.length]= 0;
+ memcpy(format_pos, format->format.str, format->format.length);
+ format_pos[format->format.length]= 0;
new_format->format.length= format->format.length;
}
return new_format;
@@ -915,11 +989,11 @@ void make_truncated_value_warning(THD *thd,
#define GET_PART(X, N) X % N ## LL; X/= N ## LL
bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type,
- INTERVAL interval)
+ const INTERVAL &interval)
{
long period, sign;
- sign= (interval.neg == ltime->neg ? 1 : -1);
+ sign= (interval.neg == (bool)ltime->neg ? 1 : -1);
switch (int_type) {
case INTERVAL_SECOND:
@@ -1407,3 +1481,46 @@ bool datetime_to_time_with_warn(THD *thd, const MYSQL_TIME *dt,
int warnings= 0;
return check_time_range(tm, dec, &warnings);
}
+
+
+longlong pack_time(const MYSQL_TIME *my_time)
+{
+ return ((((((my_time->year * 13ULL +
+ my_time->month) * 32ULL +
+ my_time->day) * 24ULL +
+ my_time->hour) * 60ULL +
+ my_time->minute) * 60ULL +
+ my_time->second) * 1000000ULL +
+ my_time->second_part) * (my_time->neg ? -1 : 1);
+}
+
+#define get_one(WHERE, FACTOR) WHERE= (ulong)(packed % FACTOR); packed/= FACTOR
+
+void unpack_time(longlong packed, MYSQL_TIME *my_time,
+ enum_mysql_timestamp_type ts_type)
+{
+ if ((my_time->neg= packed < 0))
+ packed= -packed;
+ get_one(my_time->second_part, 1000000ULL);
+ get_one(my_time->second, 60U);
+ get_one(my_time->minute, 60U);
+ get_one(my_time->hour, 24U);
+ get_one(my_time->day, 32U);
+ get_one(my_time->month, 13U);
+ my_time->year= (uint)packed;
+ my_time->time_type= ts_type;
+ switch (ts_type) {
+ case MYSQL_TIMESTAMP_TIME:
+ my_time->hour+= (my_time->month * 32 + my_time->day) * 24;
+ my_time->month= my_time->day= 0;
+ break;
+ case MYSQL_TIMESTAMP_DATE:
+ my_time->hour= my_time->minute= my_time->second= my_time->second_part= 0;
+ break;
+ case MYSQL_TIMESTAMP_NONE:
+ case MYSQL_TIMESTAMP_ERROR:
+ DBUG_ASSERT(0);
+ case MYSQL_TIMESTAMP_DATETIME:
+ break;
+ }
+}
diff --git a/sql/sql_time.h b/sql/sql_time.h
index ebfb86cde61..a0eea359c6a 100644
--- a/sql/sql_time.h
+++ b/sql/sql_time.h
@@ -1,5 +1,5 @@
/* Copyright (c) 2006, 2010, Oracle and/or its affiliates.
- Copyright (c) 2011, 2016, MariaDB
+ Copyright (c) 2011, 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,7 +17,6 @@
#ifndef SQL_TIME_INCLUDED
#define SQL_TIME_INCLUDED
-#include "my_global.h" /* ulong */
#include "my_time.h"
#include "mysql_time.h" /* timestamp_type */
#include "sql_error.h" /* Sql_condition */
@@ -39,8 +38,7 @@ bool time_to_datetime(MYSQL_TIME *ltime);
void time_to_daytime_interval(MYSQL_TIME *l_time);
bool get_date_from_daynr(long daynr,uint *year, uint *month, uint *day);
my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, uint *error_code);
-bool str_to_datetime_with_warn(CHARSET_INFO *cs, const char *str,
- uint length, MYSQL_TIME *l_time,
+bool str_to_datetime_with_warn(CHARSET_INFO *cs, const char *str, size_t length, MYSQL_TIME *l_time,
ulonglong flags);
bool double_to_datetime_with_warn(double value, MYSQL_TIME *ltime,
ulonglong fuzzydate,
@@ -106,7 +104,7 @@ inline void datetime_to_date(MYSQL_TIME *ltime)
DBUG_ASSERT(ltime->time_type == MYSQL_TIMESTAMP_DATE ||
ltime->time_type == MYSQL_TIMESTAMP_DATETIME);
DBUG_ASSERT(ltime->neg == 0);
- ltime->hour= ltime->minute= ltime->second= ltime->second_part= 0;
+ ltime->second_part= ltime->hour= ltime->minute= ltime->second= 0;
ltime->time_type= MYSQL_TIMESTAMP_DATE;
}
inline void date_to_datetime(MYSQL_TIME *ltime)
@@ -125,7 +123,7 @@ void make_truncated_value_warning(THD *thd,
static inline void make_truncated_value_warning(THD *thd,
Sql_condition::enum_warning_level level, const char *str_val,
- uint str_length, timestamp_type time_type,
+ size_t str_length, timestamp_type time_type,
const char *db_name, const char *table_name,
const char *field_name)
{
@@ -145,9 +143,11 @@ bool my_TIME_to_str(const MYSQL_TIME *ltime, String *str, uint dec);
/* MYSQL_TIME operations */
bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type,
- INTERVAL interval);
+ const INTERVAL &interval);
bool calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2,
int l_sign, longlong *seconds_out, long *microseconds_out);
+int append_interval(String *str, interval_type int_type,
+ const INTERVAL &interval);
/**
Calculate time difference between two MYSQL_TIME values and
store the result as an out MYSQL_TIME value in MYSQL_TIMESTAMP_TIME format.
@@ -174,6 +174,7 @@ bool calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2,
int lsign, MYSQL_TIME *l_time3, ulonglong fuzzydate);
int my_time_compare(const MYSQL_TIME *a, const MYSQL_TIME *b);
void localtime_to_TIME(MYSQL_TIME *to, struct tm *from);
+
void calc_time_from_sec(MYSQL_TIME *to, long seconds, long microseconds);
uint calc_week(MYSQL_TIME *l_time, uint week_behaviour, uint *year);
@@ -182,12 +183,12 @@ bool parse_date_time_format(timestamp_type format_type,
const char *format, uint format_length,
DATE_TIME_FORMAT *date_time_format);
/* Character set-aware version of str_to_time() */
-bool str_to_time(CHARSET_INFO *cs, const char *str,uint length,
+bool str_to_time(CHARSET_INFO *cs, const char *str,size_t length,
MYSQL_TIME *l_time, ulonglong fuzzydate,
MYSQL_TIME_STATUS *status);
/* Character set-aware version of str_to_datetime() */
bool str_to_datetime(CHARSET_INFO *cs,
- const char *str, uint length,
+ const char *str, size_t length,
MYSQL_TIME *l_time, ulonglong flags,
MYSQL_TIME_STATUS *status);
@@ -206,7 +207,7 @@ extern DATE_TIME_FORMAT global_date_format;
extern DATE_TIME_FORMAT global_datetime_format;
extern DATE_TIME_FORMAT global_time_format;
extern KNOWN_DATE_TIME_FORMAT known_date_time_formats[];
-extern LEX_STRING interval_type_to_name[];
+extern LEX_CSTRING interval_type_to_name[];
static inline bool
non_zero_hhmmssuu(const MYSQL_TIME *ltime)
@@ -236,4 +237,8 @@ bool make_date_with_warn(MYSQL_TIME *ltime,
ulonglong fuzzy_date, timestamp_type ts_type);
bool adjust_time_range_with_warn(MYSQL_TIME *ltime, uint dec);
+longlong pack_time(const MYSQL_TIME *my_time);
+void unpack_time(longlong packed, MYSQL_TIME *my_time,
+ enum_mysql_timestamp_type ts_type);
+
#endif /* SQL_TIME_INCLUDED */
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index 52817ef65ae..247055d397c 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -17,7 +17,7 @@
#define MYSQL_LEX 1
-#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
#include "unireg.h"
#include "sp_head.h"
@@ -38,8 +38,9 @@
#include "debug_sync.h"
#endif /* WITH_WSREP */
-LEX_STRING *make_lex_string(LEX_STRING *lex_str, const char* str, uint length,
- MEM_ROOT *mem_root)
+LEX_CSTRING *make_lex_string(LEX_CSTRING *lex_str,
+ const char* str, size_t length,
+ MEM_ROOT *mem_root)
{
if (!(lex_str->str= strmake_root(mem_root, str, length)))
return 0;
@@ -60,9 +61,9 @@ public:
static Trigger_creation_ctx *create(THD *thd,
const char *db_name,
const char *table_name,
- const LEX_STRING *client_cs_name,
- const LEX_STRING *connection_cl_name,
- const LEX_STRING *db_cl_name);
+ const LEX_CSTRING *client_cs_name,
+ const LEX_CSTRING *connection_cl_name,
+ const LEX_CSTRING *db_cl_name);
Trigger_creation_ctx(CHARSET_INFO *client_cs,
CHARSET_INFO *connection_cl,
@@ -96,9 +97,9 @@ Trigger_creation_ctx *
Trigger_creation_ctx::create(THD *thd,
const char *db_name,
const char *table_name,
- const LEX_STRING *client_cs_name,
- const LEX_STRING *connection_cl_name,
- const LEX_STRING *db_cl_name)
+ const LEX_CSTRING *client_cs_name,
+ const LEX_CSTRING *connection_cl_name,
+ const LEX_CSTRING *db_cl_name)
{
CHARSET_INFO *client_cs;
CHARSET_INFO *connection_cl;
@@ -166,8 +167,8 @@ Trigger_creation_ctx::create(THD *thd,
/*************************************************************************/
-static const LEX_STRING triggers_file_type=
- { C_STRING_WITH_LEN("TRIGGERS") };
+static const LEX_CSTRING triggers_file_type=
+ { STRING_WITH_LEN("TRIGGERS") };
const char * const TRG_EXT= ".TRG";
@@ -180,37 +181,37 @@ const char * const TRG_EXT= ".TRG";
static File_option triggers_file_parameters[]=
{
{
- { C_STRING_WITH_LEN("triggers") },
+ { STRING_WITH_LEN("triggers") },
my_offsetof(class Table_triggers_list, definitions_list),
FILE_OPTIONS_STRLIST
},
{
- { C_STRING_WITH_LEN("sql_modes") },
+ { STRING_WITH_LEN("sql_modes") },
my_offsetof(class Table_triggers_list, definition_modes_list),
FILE_OPTIONS_ULLLIST
},
{
- { C_STRING_WITH_LEN("definers") },
+ { STRING_WITH_LEN("definers") },
my_offsetof(class Table_triggers_list, definers_list),
FILE_OPTIONS_STRLIST
},
{
- { C_STRING_WITH_LEN("client_cs_names") },
+ { STRING_WITH_LEN("client_cs_names") },
my_offsetof(class Table_triggers_list, client_cs_names),
FILE_OPTIONS_STRLIST
},
{
- { C_STRING_WITH_LEN("connection_cl_names") },
+ { STRING_WITH_LEN("connection_cl_names") },
my_offsetof(class Table_triggers_list, connection_cl_names),
FILE_OPTIONS_STRLIST
},
{
- { C_STRING_WITH_LEN("db_cl_names") },
+ { STRING_WITH_LEN("db_cl_names") },
my_offsetof(class Table_triggers_list, db_cl_names),
FILE_OPTIONS_STRLIST
},
{
- { C_STRING_WITH_LEN("created") },
+ { STRING_WITH_LEN("created") },
my_offsetof(class Table_triggers_list, create_times),
FILE_OPTIONS_ULLLIST
},
@@ -219,7 +220,7 @@ static File_option triggers_file_parameters[]=
File_option sql_modes_parameters=
{
- { C_STRING_WITH_LEN("sql_modes") },
+ { STRING_WITH_LEN("sql_modes") },
my_offsetof(class Table_triggers_list, definition_modes_list),
FILE_OPTIONS_ULLLIST
};
@@ -242,18 +243,18 @@ static const int TRG_NUM_REQUIRED_PARAMETERS= 7;
struct st_trigname
{
- LEX_STRING trigger_table;
+ LEX_CSTRING trigger_table;
};
-static const LEX_STRING trigname_file_type=
- { C_STRING_WITH_LEN("TRIGGERNAME") };
+static const LEX_CSTRING trigname_file_type=
+ { STRING_WITH_LEN("TRIGGERNAME") };
const char * const TRN_EXT= ".TRN";
static File_option trigname_file_parameters[]=
{
{
- { C_STRING_WITH_LEN("trigger_table")},
+ { STRING_WITH_LEN("trigger_table")},
offsetof(struct st_trigname, trigger_table),
FILE_OPTIONS_ESTRING
},
@@ -264,9 +265,9 @@ static File_option trigname_file_parameters[]=
class Handle_old_incorrect_sql_modes_hook: public Unknown_key_hook
{
private:
- char *path;
+ const char *path;
public:
- Handle_old_incorrect_sql_modes_hook(char *file_path)
+ Handle_old_incorrect_sql_modes_hook(const char *file_path)
:path(file_path)
{};
virtual bool process_unknown_string(const char *&unknown_key, uchar* base,
@@ -277,15 +278,15 @@ public:
class Handle_old_incorrect_trigger_table_hook: public Unknown_key_hook
{
public:
- Handle_old_incorrect_trigger_table_hook(char *file_path,
- LEX_STRING *trigger_table_arg)
+ Handle_old_incorrect_trigger_table_hook(const char *file_path,
+ LEX_CSTRING *trigger_table_arg)
:path(file_path), trigger_table_value(trigger_table_arg)
{};
virtual bool process_unknown_string(const char *&unknown_key, uchar* base,
MEM_ROOT *mem_root, const char *end);
private:
- char *path;
- LEX_STRING *trigger_table_value;
+ const char *path;
+ LEX_CSTRING *trigger_table_value;
};
@@ -303,7 +304,7 @@ class Deprecated_trigger_syntax_handler : public Internal_error_handler
private:
char m_message[MYSQL_ERRMSG_SIZE];
- LEX_STRING *m_trigger_name;
+ LEX_CSTRING *m_trigger_name;
public:
@@ -333,7 +334,7 @@ public:
return false;
}
- LEX_STRING *get_trigger_name() { return m_trigger_name; }
+ LEX_CSTRING *get_trigger_name() { return m_trigger_name; }
char *get_error_message() { return m_message; }
};
@@ -424,7 +425,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
need second part of condition below, since check_access() function also
checks that db is specified.
*/
- if (!thd->lex->spname->m_db.length || (create && !tables->db_length))
+ if (!thd->lex->spname->m_db.length || (create && !tables->db.length))
{
my_error(ER_NO_DB_ERROR, MYF(0));
DBUG_RETURN(TRUE);
@@ -433,7 +434,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
/*
We don't allow creating triggers on tables in the 'mysql' schema
*/
- if (create && !my_strcasecmp(system_charset_info, "mysql", tables->db))
+ if (create && lex_string_eq(&tables->db, STRING_WITH_LEN("mysql")))
{
my_error(ER_NO_TRIGGERS_ON_SYSTEM_SCHEMA, MYF(0));
DBUG_RETURN(TRUE);
@@ -529,12 +530,12 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
/* We do not allow creation of triggers on temporary tables. */
if (create && thd->find_tmp_table_share(tables))
{
- my_error(ER_TRG_ON_VIEW_OR_TEMP_TABLE, MYF(0), tables->alias);
+ my_error(ER_TRG_ON_VIEW_OR_TEMP_TABLE, MYF(0), tables->alias.str);
goto end;
}
/* We also don't allow creation of triggers on views. */
- tables->required_type= FRMTYPE_TABLE;
+ tables->required_type= TABLE_TYPE_NORMAL;
/*
Also prevent DROP TRIGGER from opening temporary table which might
shadow the subject table on which trigger to be dropped is defined.
@@ -547,8 +548,8 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
if (thd->locked_tables_mode)
{
/* Under LOCK TABLES we must only accept write locked tables. */
- if (!(tables->table= find_table_for_mdl_upgrade(thd, tables->db,
- tables->table_name,
+ if (!(tables->table= find_table_for_mdl_upgrade(thd, tables->db.str,
+ tables->table_name.str,
NULL)))
goto end;
}
@@ -666,12 +667,12 @@ WSREP_ERROR_LABEL:
static void build_trig_stmt_query(THD *thd, TABLE_LIST *tables,
String *stmt_query, String *trigger_def,
- LEX_STRING *trg_definer,
+ LEX_CSTRING *trg_definer,
char trg_definer_holder[])
{
- LEX_STRING stmt_definition;
+ LEX_CSTRING stmt_definition;
LEX *lex= thd->lex;
- uint prefix_trimmed, suffix_trimmed;
+ size_t prefix_trimmed, suffix_trimmed;
size_t original_length;
/*
@@ -685,7 +686,7 @@ static void build_trig_stmt_query(THD *thd, TABLE_LIST *tables,
if (lex->create_info.or_replace())
stmt_query->append(STRING_WITH_LEN("OR REPLACE "));
- if (lex->sphead->m_chistics->suid != SP_IS_NOT_SUID)
+ if (lex->sphead->suid() != SP_IS_NOT_SUID)
{
/* SUID trigger */
lex->definer->set_lex_string(trg_definer, trg_definer_holder);
@@ -694,12 +695,12 @@ static void build_trig_stmt_query(THD *thd, TABLE_LIST *tables,
}
else
{
- *trg_definer= empty_lex_str;
+ *trg_definer= empty_clex_str;
}
/* Create statement for binary logging */
- stmt_definition.str= (char*) lex->stmt_definition_begin;
+ stmt_definition.str= lex->stmt_definition_begin;
stmt_definition.length= (lex->stmt_definition_end -
lex->stmt_definition_begin);
original_length= stmt_definition.length;
@@ -710,7 +711,13 @@ static void build_trig_stmt_query(THD *thd, TABLE_LIST *tables,
/* Create statement for storing trigger (without trigger order) */
if (lex->trg_chistics.ordering_clause == TRG_ORDER_NONE)
+ {
+ /*
+ Not that here stmt_definition doesn't end with a \0, which is
+ normally expected from a LEX_CSTRING
+ */
trigger_def->append(stmt_definition.str, stmt_definition.length);
+ }
else
{
/* Copy data before FOLLOWS/PRECEDES trigger_name */
@@ -759,7 +766,7 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
LEX *lex= thd->lex;
TABLE *table= tables->table;
char file_buff[FN_REFLEN], trigname_buff[FN_REFLEN];
- LEX_STRING file, trigname_file;
+ LEX_CSTRING file, trigname_file;
char trg_definer_holder[USER_HOST_BUFF_SIZE];
Item_trigger_field *trg_field;
struct st_trigname trigname;
@@ -772,8 +779,7 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
DBUG_RETURN(true);
/* Trigger must be in the same schema as target table. */
- if (my_strcasecmp(table_alias_charset, table->s->db.str,
- lex->spname->m_db.str))
+ if (lex_string_cmp(table_alias_charset, &table->s->db, &lex->spname->m_db))
{
my_error(ER_TRG_IN_WRONG_SCHEMA, MYF(0));
DBUG_RETURN(true);
@@ -807,8 +813,7 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
*/
trg_field->setup_field(thd, table, NULL);
- if (!trg_field->fixed &&
- trg_field->fix_fields(thd, (Item **)0))
+ if (trg_field->fix_fields_if_needed(thd, (Item **)0))
DBUG_RETURN(true);
}
@@ -831,11 +836,11 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
versions
*/
file.length= build_table_filename(file_buff, FN_REFLEN - 1,
- tables->db, tables->table_name,
+ tables->db.str, tables->table_name.str,
TRG_EXT, 0);
file.str= file_buff;
trigname_file.length= build_table_filename(trigname_buff, FN_REFLEN-1,
- tables->db,
+ tables->db.str,
lex->spname->m_name.str,
TRN_EXT, 0);
trigname_file.str= trigname_buff;
@@ -856,11 +861,13 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
}
else if (lex->create_info.if_not_exists())
{
+ strxnmov(trigname_buff, sizeof(trigname_buff) - 1, tables->db.str, ".",
+ lex->spname->m_name.str, NullS);
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_TRG_ALREADY_EXISTS,
ER_THD(thd, ER_TRG_ALREADY_EXISTS),
trigname_buff);
- LEX_STRING trg_definer_tmp;
+ LEX_CSTRING trg_definer_tmp;
String trigger_def;
/*
@@ -873,13 +880,14 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
}
else
{
- my_error(ER_TRG_ALREADY_EXISTS, MYF(0));
+ strxnmov(trigname_buff, sizeof(trigname_buff) - 1, tables->db.str, ".",
+ lex->spname->m_name.str, NullS);
+ my_error(ER_TRG_ALREADY_EXISTS, MYF(0), trigname_buff);
DBUG_RETURN(true);
}
}
- trigname.trigger_table.str= tables->table_name;
- trigname.trigger_table.length= tables->table_name_length;
+ trigname.trigger_table= tables->table_name;
/*
We are not using lex->sphead here as an argument to Trigger() as we are
@@ -914,7 +922,7 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
lex_string_set(&trigger->connection_cl_name,
thd->variables.collation_connection->name);
lex_string_set(&trigger->db_cl_name,
- get_default_db_collation(thd, tables->db)->name);
+ get_default_db_collation(thd, tables->db.str)->name);
/* Add trigger in it's correct place */
add_trigger(lex->trg_chistics.event,
@@ -941,8 +949,8 @@ err_without_cleanup:
if (trigger_dropped)
{
String drop_trg_query;
- drop_trg_query.append("DROP TRIGGER /* generated by failed CREATE TRIGGER */ ");
- drop_trg_query.append(lex->spname->m_name.str);
+ drop_trg_query.append(STRING_WITH_LEN("DROP TRIGGER /* generated by failed CREATE TRIGGER */ "));
+ drop_trg_query.append(&lex->spname->m_name);
/*
We dropped an existing trigger and was not able to recreate it because
of an internal error. Ensure it's also dropped on the slave.
@@ -1022,10 +1030,10 @@ bool Trigger::add_to_file_list(void* param_arg)
True error
*/
-static bool rm_trigger_file(char *path, const char *db,
- const char *table_name)
+static bool rm_trigger_file(char *path, const LEX_CSTRING *db,
+ const LEX_CSTRING *table_name)
{
- build_table_filename(path, FN_REFLEN-1, db, table_name, TRG_EXT, 0);
+ 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));
}
@@ -1044,10 +1052,10 @@ static bool rm_trigger_file(char *path, const char *db,
True error
*/
-static bool rm_trigname_file(char *path, const char *db,
- const char *trigger_name)
+static bool rm_trigname_file(char *path, const LEX_CSTRING *db,
+ const LEX_CSTRING *trigger_name)
{
- build_table_filename(path, FN_REFLEN - 1, db, trigger_name, TRN_EXT, 0);
+ 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));
}
@@ -1065,16 +1073,16 @@ static bool rm_trigname_file(char *path, const char *db,
TRUE Error
*/
-bool Table_triggers_list::save_trigger_file(THD *thd, const char *db,
- const char *table_name)
+bool Table_triggers_list::save_trigger_file(THD *thd, const LEX_CSTRING *db,
+ const LEX_CSTRING *table_name)
{
char file_buff[FN_REFLEN];
- LEX_STRING file;
+ LEX_CSTRING file;
if (create_lists_needed_for_files(thd->mem_root))
return true;
- file.length= build_table_filename(file_buff, FN_REFLEN - 1, db, table_name,
+ file.length= build_table_filename(file_buff, FN_REFLEN - 1, db->str, table_name->str,
TRG_EXT, 0);
file.str= file_buff;
return sql_create_definition_file(NULL, &file, &triggers_file_type,
@@ -1089,7 +1097,7 @@ bool Table_triggers_list::save_trigger_file(THD *thd, const char *db,
@param remove_from_list If set, remove trigger if found
*/
-Trigger *Table_triggers_list::find_trigger(const LEX_STRING *name,
+Trigger *Table_triggers_list::find_trigger(const LEX_CSTRING *name,
bool remove_from_list)
{
for (uint i= 0; i < (uint)TRG_EVENT_MAX; i++)
@@ -1102,8 +1110,8 @@ Trigger *Table_triggers_list::find_trigger(const LEX_STRING *name,
(trigger= *parent);
parent= &trigger->next)
{
- if (my_strcasecmp(table_alias_charset,
- trigger->name.str, name->str) == 0)
+ if (lex_string_cmp(table_alias_charset,
+ &trigger->name, name) == 0)
{
if (remove_from_list)
{
@@ -1144,7 +1152,7 @@ Trigger *Table_triggers_list::find_trigger(const LEX_STRING *name,
bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables,
String *stmt_query)
{
- const LEX_STRING *sp_name= &thd->lex->spname->m_name; // alias
+ const LEX_CSTRING *sp_name= &thd->lex->spname->m_name; // alias
char path[FN_REFLEN];
Trigger *trigger;
@@ -1166,16 +1174,16 @@ 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))
return 1;
}
else
{
- if (save_trigger_file(thd, tables->db, tables->table_name))
+ if (save_trigger_file(thd, &tables->db, &tables->table_name))
return 1;
}
- if (rm_trigname_file(path, tables->db, sp_name->str))
+ if (rm_trigname_file(path, &tables->db, sp_name))
return 1;
delete trigger;
@@ -1247,7 +1255,7 @@ bool Table_triggers_list::prepare_record_accessors(TABLE *table)
uchar null_bit= 1;
for (fld= table->field, trg_fld= record0_field; *fld; fld++, trg_fld++)
{
- if (!(*fld)->null_ptr && !(*fld)->vcol_info)
+ if (!(*fld)->null_ptr && !(*fld)->vcol_info && !(*fld)->vers_sys_field())
{
Field *f;
if (!(f= *trg_fld= (*fld)->make_new_field(&table->mem_root, table,
@@ -1255,6 +1263,7 @@ bool Table_triggers_list::prepare_record_accessors(TABLE *table)
return 1;
f->flags= (*fld)->flags;
+ f->invisible= (*fld)->invisible;
f->null_ptr= null_ptr;
f->null_bit= null_bit;
if (null_bit == 128)
@@ -1270,7 +1279,9 @@ bool Table_triggers_list::prepare_record_accessors(TABLE *table)
bzero(extra_null_bitmap, null_bytes);
}
else
+ {
record0_field= table->field;
+ }
if (has_triggers(TRG_EVENT_UPDATE,TRG_ACTION_BEFORE) ||
has_triggers(TRG_EVENT_UPDATE,TRG_ACTION_AFTER) ||
@@ -1318,18 +1329,18 @@ bool Table_triggers_list::prepare_record_accessors(TABLE *table)
True error
*/
-bool Table_triggers_list::check_n_load(THD *thd, const char *db,
- const char *table_name, TABLE *table,
+bool Table_triggers_list::check_n_load(THD *thd, const LEX_CSTRING *db,
+ const LEX_CSTRING *table_name, TABLE *table,
bool names_only)
{
char path_buff[FN_REFLEN];
- LEX_STRING path;
+ LEX_CSTRING path;
File_parser *parser;
- LEX_STRING save_db;
+ LEX_CSTRING save_db;
DBUG_ENTER("Table_triggers_list::check_n_load");
path.length= build_table_filename(path_buff, FN_REFLEN - 1,
- db, table_name, TRG_EXT, 0);
+ db->str, table_name->str, TRG_EXT, 0);
path.str= path_buff;
// QQ: should we analyze errno somehow ?
@@ -1343,7 +1354,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
if (is_equal(&triggers_file_type, parser->type()))
{
Handle_old_incorrect_sql_modes_hook sql_modes_hook(path.str);
- LEX_STRING *trg_create_str;
+ LEX_CSTRING *trg_create_str;
ulonglong *trg_sql_mode, *trg_create_time;
Trigger *trigger;
Table_triggers_list *trigger_list=
@@ -1357,7 +1368,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
&sql_modes_hook))
goto error;
- List_iterator_fast<LEX_STRING> it(trigger_list->definitions_list);
+ List_iterator_fast<LEX_CSTRING> it(trigger_list->definitions_list);
if (!trigger_list->definitions_list.is_empty() &&
(trigger_list->client_cs_names.is_empty() ||
@@ -1368,18 +1379,18 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_TRG_NO_CREATION_CTX,
ER_THD(thd, ER_TRG_NO_CREATION_CTX),
- (const char*) db,
- (const char*) table_name);
+ db->str,
+ table_name->str);
}
table->triggers= trigger_list;
status_var_increment(thd->status_var.feature_trigger);
List_iterator_fast<ulonglong> itm(trigger_list->definition_modes_list);
- List_iterator_fast<LEX_STRING> it_definer(trigger_list->definers_list);
- List_iterator_fast<LEX_STRING> it_client_cs_name(trigger_list->client_cs_names);
- List_iterator_fast<LEX_STRING> it_connection_cl_name(trigger_list->connection_cl_names);
- List_iterator_fast<LEX_STRING> it_db_cl_name(trigger_list->db_cl_names);
+ List_iterator_fast<LEX_CSTRING> it_definer(trigger_list->definers_list);
+ List_iterator_fast<LEX_CSTRING> it_client_cs_name(trigger_list->client_cs_names);
+ List_iterator_fast<LEX_CSTRING> it_connection_cl_name(trigger_list->connection_cl_names);
+ List_iterator_fast<LEX_CSTRING> it_db_cl_name(trigger_list->db_cl_names);
List_iterator_fast<ulonglong> it_create_times(trigger_list->create_times);
LEX *old_lex= thd->lex;
LEX lex;
@@ -1388,14 +1399,13 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
thd->lex= &lex;
- save_db.str= thd->db;
- save_db.length= thd->db_length;
- thd->reset_db((char*) db, strlen(db));
+ save_db= thd->db;
+ thd->reset_db(db);
while ((trg_create_str= it++))
{
sp_head *sp;
sql_mode_t sql_mode;
- LEX_STRING *trg_definer;
+ LEX_CSTRING *trg_definer;
Trigger_creation_ctx *creation_ctx;
/*
@@ -1412,13 +1422,14 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
thd->variables.sql_mode= sql_mode;
Parser_state parser_state;
- if (parser_state.init(thd, trg_create_str->str, trg_create_str->length))
+ if (parser_state.init(thd, (char*) trg_create_str->str,
+ trg_create_str->length))
goto err_with_lex_cleanup;
if (!trigger_list->client_cs_names.is_empty())
creation_ctx= Trigger_creation_ctx::create(thd,
- db,
- table_name,
+ db->str,
+ table_name->str,
it_client_cs_name++,
it_connection_cl_name++,
it_db_cl_name++);
@@ -1455,7 +1466,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
lex.set_trg_event_type_for_tables();
if (lex.sphead)
- lex.sphead->set_info(0, 0, &lex.sp_chistics, sql_mode);
+ lex.sphead->m_sql_mode= sql_mode;
if (unlikely(!(trigger= (new (&table->mem_root)
Trigger(trigger_list, lex.sphead)))))
@@ -1467,7 +1478,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
trigger->sql_mode= sql_mode;
trigger->definition= *trg_create_str;
trigger->create_time= trg_create_time ? *trg_create_time : 0;
- trigger->name= sp ? sp->m_name : empty_lex_str;
+ trigger->name= sp ? sp->m_name : empty_clex_str;
trigger->on_table_name.str= (char*) lex.raw_trg_on_table_name_begin;
trigger->on_table_name.length= (lex.raw_trg_on_table_name_end -
lex.raw_trg_on_table_name_begin);
@@ -1488,9 +1499,9 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
&lex.trg_chistics.anchor_trigger_name,
trigger);
- if (parse_error)
+ if (unlikely(parse_error))
{
- LEX_STRING *name;
+ LEX_CSTRING *name;
/*
In case of errors, disable all triggers for the table, but keep
@@ -1502,18 +1513,18 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
DBUG_ASSERT(lex.sphead == 0);
lex_end(&lex);
- if ((name= error_handler.get_trigger_name()))
+ if (likely((name= error_handler.get_trigger_name())))
{
- if (!(make_lex_string(&trigger->name, name->str,
- name->length, &table->mem_root)))
+ if (unlikely(!(make_lex_string(&trigger->name, name->str,
+ name->length, &table->mem_root))))
goto err_with_lex_cleanup;
}
trigger->definer= ((!trg_definer || !trg_definer->length) ?
- empty_lex_str : *trg_definer);
+ empty_clex_str : *trg_definer);
continue;
}
- sp->set_info(0, 0, &lex.sp_chistics, sql_mode);
+ sp->m_sql_mode= sql_mode;
sp->set_creation_ctx(creation_ctx);
if (!trg_definer || !trg_definer->length)
@@ -1527,23 +1538,22 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_TRG_NO_DEFINER,
ER_THD(thd, ER_TRG_NO_DEFINER),
- (const char*) db,
- (const char*) sp->m_name.str);
+ db->str, sp->m_name.str);
/*
Set definer to the '' to correct displaying in the information
schema.
*/
- sp->set_definer((char*) "", 0);
- trigger->definer= empty_lex_str;
+ sp->set_definer("", 0);
+ trigger->definer= empty_clex_str;
/*
trigger_list without definer information are executed under the
authorization of the invoker.
*/
- sp->m_chistics->suid= SP_IS_NOT_SUID;
+ sp->set_suid(SP_IS_NOT_SUID);
}
else
{
@@ -1564,12 +1574,12 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
*/
char fname[SAFE_NAME_LEN + 1];
- DBUG_ASSERT((!my_strcasecmp(table_alias_charset, lex.query_tables->db, db) ||
- (check_n_cut_mysql50_prefix(db, fname, sizeof(fname)) &&
- !my_strcasecmp(table_alias_charset, lex.query_tables->db, fname))));
- DBUG_ASSERT((!my_strcasecmp(table_alias_charset, lex.query_tables->table_name, table_name) ||
- (check_n_cut_mysql50_prefix(table_name, fname, sizeof(fname)) &&
- !my_strcasecmp(table_alias_charset, lex.query_tables->table_name, fname))));
+ DBUG_ASSERT((!my_strcasecmp(table_alias_charset, lex.query_tables->db.str, db->str) ||
+ (check_n_cut_mysql50_prefix(db->str, fname, sizeof(fname)) &&
+ !my_strcasecmp(table_alias_charset, lex.query_tables->db.str, fname))));
+ DBUG_ASSERT((!my_strcasecmp(table_alias_charset, lex.query_tables->table_name.str, table_name->str) ||
+ (check_n_cut_mysql50_prefix(table_name->str, fname, sizeof(fname)) &&
+ !my_strcasecmp(table_alias_charset, lex.query_tables->table_name.str, fname))));
#endif
if (names_only)
{
@@ -1602,7 +1612,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
lex_end(&lex);
}
- thd->reset_db(save_db.str, save_db.length);
+ thd->reset_db(&save_db);
thd->lex= old_lex;
thd->spcont= save_spcont;
thd->variables.sql_mode= save_sql_mode;
@@ -1619,20 +1629,20 @@ err_with_lex_cleanup:
thd->lex= old_lex;
thd->spcont= save_spcont;
thd->variables.sql_mode= save_sql_mode;
- thd->reset_db(save_db.str, save_db.length);
- /* Fall through to error */
+ thd->reset_db(&save_db);
+ /* Fall trough to error */
}
}
error:
- if (!thd->is_error())
+ if (unlikely(!thd->is_error()))
{
/*
We don't care about this error message much because .TRG files will
be merged into .FRM anyway.
*/
my_error(ER_WRONG_OBJECT, MYF(0),
- table_name, TRG_EXT + 1, "TRIGGER");
+ table_name->str, TRG_EXT + 1, "TRIGGER");
}
DBUG_RETURN(1);
}
@@ -1648,7 +1658,7 @@ error:
void Table_triggers_list::add_trigger(trg_event_type event,
trg_action_time_type action_time,
trigger_order_type ordering_clause,
- LEX_STRING *anchor_trigger_name,
+ LEX_CSTRING *anchor_trigger_name,
Trigger *trigger)
{
Trigger **parent= &triggers[event][action_time];
@@ -1657,8 +1667,8 @@ void Table_triggers_list::add_trigger(trg_event_type event,
for ( ; *parent ; parent= &(*parent)->next, position++)
{
if (ordering_clause != TRG_ORDER_NONE &&
- !my_strcasecmp(table_alias_charset, anchor_trigger_name->str,
- (*parent)->name.str))
+ !lex_string_cmp(table_alias_charset, anchor_trigger_name,
+ &(*parent)->name))
{
if (ordering_clause == TRG_ORDER_FOLLOWS)
{
@@ -1687,8 +1697,8 @@ void Table_triggers_list::add_trigger(trg_event_type event,
/**
Obtains and returns trigger metadata.
- @param thd current thread context
@param trigger_stmt returns statement of trigger
+ @param body returns body of trigger
@param definer returns definer/creator of trigger. The caller is
responsible to allocate enough space for storing
definer information.
@@ -1699,8 +1709,9 @@ void Table_triggers_list::add_trigger(trg_event_type event,
True error
*/
-void Trigger::get_trigger_info(LEX_STRING *trigger_stmt,
- LEX_STRING *trigger_body, LEX_STRING *definer)
+void Trigger::get_trigger_info(LEX_CSTRING *trigger_stmt,
+ LEX_CSTRING *trigger_body,
+ LEX_STRING *definer)
{
DBUG_ENTER("get_trigger_info");
@@ -1714,14 +1725,14 @@ void Trigger::get_trigger_info(LEX_STRING *trigger_stmt,
}
*trigger_body= body->m_body_utf8;
- if (body->m_chistics->suid == SP_IS_NOT_SUID)
+ if (body->suid() == SP_IS_NOT_SUID)
{
*definer= empty_lex_str;
}
else
{
- definer->length= strxmov(definer->str, body->m_definer_user.str, "@",
- body->m_definer_host.str, NullS) - definer->str;
+ definer->length= strxmov(definer->str, body->m_definer.user.str, "@",
+ body->m_definer.host.str, NullS) - definer->str;
}
DBUG_VOID_RETURN;
}
@@ -1751,12 +1762,12 @@ bool add_table_for_trigger(THD *thd,
{
LEX *lex= thd->lex;
char trn_path_buff[FN_REFLEN];
- LEX_STRING trn_path= { trn_path_buff, 0 };
- LEX_STRING tbl_name= null_lex_str;
+ LEX_CSTRING trn_path= { trn_path_buff, 0 };
+ LEX_CSTRING tbl_name= null_clex_str;
DBUG_ENTER("add_table_for_trigger");
- build_trn_path(thd, trg_name, &trn_path);
+ build_trn_path(thd, trg_name, (LEX_STRING*) &trn_path);
if (check_trn_exists(&trn_path))
{
@@ -1779,8 +1790,8 @@ bool add_table_for_trigger(THD *thd,
if (load_table_name_for_trigger(thd, trg_name, &trn_path, &tbl_name))
DBUG_RETURN(TRUE);
- *table= sp_add_to_query_tables(thd, lex, trg_name->m_db.str,
- tbl_name.str, TL_IGNORE,
+ *table= sp_add_to_query_tables(thd, lex, &trg_name->m_db,
+ &tbl_name, TL_IGNORE,
MDL_SHARED_NO_WRITE);
DBUG_RETURN(*table ? FALSE : TRUE);
@@ -1800,15 +1811,17 @@ bool add_table_for_trigger(THD *thd,
True error
*/
-bool Table_triggers_list::drop_all_triggers(THD *thd, char *db, char *name)
+bool Table_triggers_list::drop_all_triggers(THD *thd, const LEX_CSTRING *db,
+ const LEX_CSTRING *name)
{
TABLE table;
char path[FN_REFLEN];
bool result= 0;
- DBUG_ENTER("drop_all_triggers");
+ DBUG_ENTER("Triggers::drop_all_triggers");
table.reset();
- init_sql_alloc(&table.mem_root, 8192, 0, MYF(0));
+ init_sql_alloc(&table.mem_root, "Triggers::drop_all_triggers", 8192, 0,
+ MYF(0));
if (Table_triggers_list::check_n_load(thd, db, name, &table, 1))
{
@@ -1832,7 +1845,7 @@ bool Table_triggers_list::drop_all_triggers(THD *thd, char *db, char *name)
Such triggers have zero-length name and are skipped here.
*/
if (trigger->name.length &&
- rm_trigname_file(path, db, trigger->name.str))
+ rm_trigname_file(path, db, &trigger->name))
{
/*
Instead of immediately bailing out with error if we were unable
@@ -1872,40 +1885,41 @@ end:
struct change_table_name_param
{
THD *thd;
- const char *old_db_name;
- const char *new_db_name;
- LEX_STRING *new_table_name;
+ LEX_CSTRING *old_db_name;
+ LEX_CSTRING *new_db_name;
+ LEX_CSTRING *new_table_name;
Trigger *stopper;
};
bool
-Table_triggers_list::change_table_name_in_triggers(THD *thd,
- const char *old_db_name,
- const char *new_db_name,
- LEX_STRING *old_table_name,
- LEX_STRING *new_table_name)
+Table_triggers_list::
+change_table_name_in_triggers(THD *thd,
+ const LEX_CSTRING *old_db_name,
+ const LEX_CSTRING *new_db_name,
+ const LEX_CSTRING *old_table_name,
+ const LEX_CSTRING *new_table_name)
{
struct change_table_name_param param;
sql_mode_t save_sql_mode= thd->variables.sql_mode;
char path_buff[FN_REFLEN];
param.thd= thd;
- param.new_table_name= new_table_name;
+ param.new_table_name= const_cast<LEX_CSTRING*>(new_table_name);
for_all_triggers(&Trigger::change_table_name, &param);
thd->variables.sql_mode= save_sql_mode;
- if (thd->is_fatal_error)
+ if (unlikely(thd->is_fatal_error))
return TRUE; /* OOM */
- if (save_trigger_file(thd, new_db_name, new_table_name->str))
+ 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->str))
+ if (rm_trigger_file(path_buff, old_db_name, old_table_name))
{
- (void) rm_trigger_file(path_buff, new_db_name, new_table_name->str);
+ (void) rm_trigger_file(path_buff, new_db_name, new_table_name);
return TRUE;
}
return FALSE;
@@ -1916,9 +1930,8 @@ bool Trigger::change_table_name(void* param_arg)
{
change_table_name_param *param= (change_table_name_param*) param_arg;
THD *thd= param->thd;
- LEX_STRING *new_table_name= param->new_table_name;
-
- LEX_STRING *def= &definition, new_def;
+ LEX_CSTRING *new_table_name= param->new_table_name;
+ LEX_CSTRING *def= &definition, new_def;
size_t on_q_table_name_len, before_on_len;
String buff;
@@ -1934,7 +1947,7 @@ bool Trigger::change_table_name(void* param_arg)
buff.append(def->str, before_on_len);
buff.append(STRING_WITH_LEN("ON "));
- append_identifier(thd, &buff, new_table_name->str, new_table_name->length);
+ append_identifier(thd, &buff, new_table_name);
buff.append(STRING_WITH_LEN(" "));
on_q_table_name_len= buff.length() - before_on_len;
buff.append(on_table_name.str + on_table_name.length,
@@ -1971,15 +1984,16 @@ bool Trigger::change_table_name(void* param_arg)
*/
Trigger *
-Table_triggers_list::change_table_name_in_trignames(const char *old_db_name,
- const char *new_db_name,
- LEX_STRING *new_table_name,
- Trigger *trigger)
+Table_triggers_list::
+change_table_name_in_trignames(const LEX_CSTRING *old_db_name,
+ const LEX_CSTRING *new_db_name,
+ const LEX_CSTRING *new_table_name,
+ Trigger *trigger)
{
struct change_table_name_param param;
- param.old_db_name= old_db_name;
- param.new_db_name= new_db_name;
- param.new_table_name= new_table_name;
+ param.old_db_name= const_cast<LEX_CSTRING*>(old_db_name);
+ param.new_db_name= const_cast<LEX_CSTRING*>(new_db_name);
+ param.new_table_name= const_cast<LEX_CSTRING*>(new_table_name);
param.stopper= trigger;
return for_all_triggers(&Trigger::change_on_table_name, &param);
@@ -1992,13 +2006,13 @@ bool Trigger::change_on_table_name(void* param_arg)
char trigname_buff[FN_REFLEN];
struct st_trigname trigname;
- LEX_STRING trigname_file;
+ LEX_CSTRING trigname_file;
if (param->stopper == this)
return 0; // Stop processing
trigname_file.length= build_table_filename(trigname_buff, FN_REFLEN-1,
- param->new_db_name, name.str,
+ param->new_db_name->str, name.str,
TRN_EXT, 0);
trigname_file.str= trigname_buff;
@@ -2014,9 +2028,9 @@ 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.str))
+ if (rm_trigname_file(trigname_buff, param->old_db_name, &name))
{
- (void) rm_trigname_file(trigname_buff, param->new_db_name, name.str);
+ (void) rm_trigname_file(trigname_buff, param->new_db_name, &name);
return 1;
}
}
@@ -2045,30 +2059,32 @@ bool Trigger::change_on_table_name(void* param_arg)
@retval TRUE Error
*/
-bool Table_triggers_list::change_table_name(THD *thd, const char *db,
- const char *old_alias,
- const char *old_table,
- const char *new_db,
- const char *new_table)
+bool Table_triggers_list::change_table_name(THD *thd, const LEX_CSTRING *db,
+ const LEX_CSTRING *old_alias,
+ const LEX_CSTRING *old_table,
+ const LEX_CSTRING *new_db,
+ const LEX_CSTRING *new_table)
{
TABLE table;
bool result= 0;
bool upgrading50to51= FALSE;
Trigger *err_trigger;
- DBUG_ENTER("change_table_name");
+ DBUG_ENTER("Triggers::change_table_name");
table.reset();
- init_sql_alloc(&table.mem_root, 8192, 0, MYF(0));
+ init_sql_alloc(&table.mem_root, "Triggers::change_table_name", 8192, 0,
+ MYF(0));
/*
This method interfaces the mysql server code protected by
an exclusive metadata lock.
*/
- DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, db, old_table,
+ DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, db->str,
+ old_table->str,
MDL_EXCLUSIVE));
- DBUG_ASSERT(my_strcasecmp(table_alias_charset, db, new_db) ||
- my_strcasecmp(table_alias_charset, old_alias, new_table));
+ DBUG_ASSERT(my_strcasecmp(table_alias_charset, db->str, new_db->str) ||
+ my_strcasecmp(table_alias_charset, old_alias->str, new_table->str));
if (Table_triggers_list::check_n_load(thd, db, old_table, &table, TRUE))
{
@@ -2082,8 +2098,6 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db,
result= 1;
goto end;
}
- LEX_STRING old_table_name= { (char *) old_alias, strlen(old_alias) };
- LEX_STRING new_table_name= { (char *) new_table, strlen(new_table) };
/*
Since triggers should be in the same schema as their subject tables
moving table with them between two schemas raises too many questions.
@@ -2094,11 +2108,11 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db,
we will be given table name with "#mysql50#" prefix
To remove this prefix we use check_n_cut_mysql50_prefix().
*/
- if (my_strcasecmp(table_alias_charset, db, new_db))
+ if (my_strcasecmp(table_alias_charset, db->str, new_db->str))
{
char dbname[SAFE_NAME_LEN + 1];
- if (check_n_cut_mysql50_prefix(db, dbname, sizeof(dbname)) &&
- !my_strcasecmp(table_alias_charset, dbname, new_db))
+ if (check_n_cut_mysql50_prefix(db->str, dbname, sizeof(dbname)) &&
+ !my_strcasecmp(table_alias_charset, dbname, new_db->str))
{
upgrading50to51= TRUE;
}
@@ -2109,16 +2123,16 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db,
goto end;
}
}
- if (table.triggers->change_table_name_in_triggers(thd, db, new_db,
- &old_table_name,
- &new_table_name))
+ if (unlikely(table.triggers->change_table_name_in_triggers(thd, db, new_db,
+ old_alias,
+ new_table)))
{
result= 1;
goto end;
}
if ((err_trigger= table.triggers->
change_table_name_in_trignames( upgrading50to51 ? db : NULL,
- new_db, &new_table_name, 0)))
+ new_db, new_table, 0)))
{
/*
If we were unable to update one of .TRN files properly we will
@@ -2128,10 +2142,10 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db,
*/
(void) table.triggers->change_table_name_in_trignames(
upgrading50to51 ? new_db : NULL, db,
- &old_table_name, err_trigger);
+ old_alias, err_trigger);
(void) table.triggers->change_table_name_in_triggers(
thd, db, new_db,
- &new_table_name, &old_table_name);
+ new_table, old_alias);
result= 1;
goto end;
}
@@ -2191,8 +2205,7 @@ bool Table_triggers_list::process_triggers(THD *thd,
This trigger must have been processed by the pre-locking
algorithm.
*/
- DBUG_ASSERT(trigger_table->pos_in_table_list->trg_event_map &
- static_cast<uint>(1 << static_cast<int>(event)));
+ DBUG_ASSERT(trigger_table->pos_in_table_list->trg_event_map & trg2bit(event));
thd->reset_sub_statement_state(&statement_state, SUB_STMT_TRIGGER);
@@ -2244,8 +2257,7 @@ add_tables_and_routines_for_triggers(THD *thd,
for (int i= 0; i < (int)TRG_EVENT_MAX; i++)
{
- if (table_list->trg_event_map &
- static_cast<uint8>(1 << static_cast<int>(i)))
+ if (table_list->trg_event_map & trg2bit(static_cast<trg_event_type>(i)))
{
for (int j= 0; j < (int)TRG_ACTION_MAX; j++)
{
@@ -2255,13 +2267,14 @@ add_tables_and_routines_for_triggers(THD *thd,
{
sp_head *trigger= triggers->body;
- if (!triggers->body) // Parse error
+ if (unlikely(!triggers->body)) // Parse error
continue;
MDL_key key(MDL_key::TRIGGER, trigger->m_db.str, trigger->m_name.str);
if (sp_add_used_routine(prelocking_ctx, thd->stmt_arena,
- &key, table_list->belong_to_view))
+ &key, &sp_handler_trigger,
+ table_list->belong_to_view))
{
trigger->add_used_tables_to_table_list(thd,
&prelocking_ctx->query_tables_last,
@@ -2473,7 +2486,7 @@ void build_trn_path(THD *thd, const sp_name *trg_name, LEX_STRING *trn_path)
@retval FALSE if TRN-file exists.
*/
-bool check_trn_exists(const LEX_STRING *trn_path)
+bool check_trn_exists(const LEX_CSTRING *trn_path)
{
return access(trn_path->str, F_OK) != 0;
}
@@ -2494,16 +2507,14 @@ bool check_trn_exists(const LEX_STRING *trn_path)
bool load_table_name_for_trigger(THD *thd,
const sp_name *trg_name,
- const LEX_STRING *trn_path,
- LEX_STRING *tbl_name)
+ const LEX_CSTRING *trn_path,
+ LEX_CSTRING *tbl_name)
{
File_parser *parser;
struct st_trigname trn_data;
-
Handle_old_incorrect_trigger_table_hook trigger_table_hook(
trn_path->str,
&trn_data.trigger_table);
-
DBUG_ENTER("load_table_name_for_trigger");
/* Parse the TRN-file. */
diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h
index e2e6e1b7acc..ae3d1738b16 100644
--- a/sql/sql_trigger.h
+++ b/sql/sql_trigger.h
@@ -38,6 +38,9 @@ enum trg_event_type
TRG_EVENT_MAX
};
+static inline uint8 trg2bit(enum trg_event_type trg)
+{ return static_cast<uint8>(1 << static_cast<int>(trg)); }
+
#include "table.h" /* GRANT_INFO */
/*
@@ -71,7 +74,7 @@ struct st_trg_execution_order
Trigger name referenced in the FOLLOWS/PRECEDES clause of the
CREATE TRIGGER statement.
*/
- LEX_STRING anchor_trigger_name;
+ LEX_CSTRING anchor_trigger_name;
};
@@ -99,15 +102,15 @@ public:
grouped by event and action_time.
*/
Item_trigger_field *trigger_fields;
- LEX_STRING name;
- LEX_STRING on_table_name; /* Raw table name */
- LEX_STRING definition;
- LEX_STRING definer;
+ LEX_CSTRING name;
+ LEX_CSTRING on_table_name; /* Raw table name */
+ LEX_CSTRING definition;
+ LEX_CSTRING definer;
/* Character sets used */
- LEX_STRING client_cs_name;
- LEX_STRING connection_cl_name;
- LEX_STRING db_cl_name;
+ LEX_CSTRING client_cs_name;
+ LEX_CSTRING connection_cl_name;
+ LEX_CSTRING db_cl_name;
GRANT_INFO subject_table_grants;
sql_mode_t sql_mode;
@@ -118,7 +121,7 @@ public:
uint action_order;
bool is_fields_updated_in_trigger(MY_BITMAP *used_fields);
- void get_trigger_info(LEX_STRING *stmt, LEX_STRING *body,
+ void get_trigger_info(LEX_CSTRING *stmt, LEX_CSTRING *body,
LEX_STRING *definer);
/* Functions executed over each active trigger */
bool change_on_table_name(void* param_arg);
@@ -189,7 +192,7 @@ public:
Field responsible for storing triggers definitions in file.
It have to be public because we are using it directly from parser.
*/
- List<LEX_STRING> definitions_list;
+ List<LEX_CSTRING> definitions_list;
/**
List of sql modes for triggers
*/
@@ -197,13 +200,13 @@ public:
/** Create times for triggers */
List<ulonglong> create_times;
- List<LEX_STRING> definers_list;
+ List<LEX_CSTRING> definers_list;
/* Character set context, used for parsing and executing triggers. */
- List<LEX_STRING> client_cs_names;
- List<LEX_STRING> connection_cl_names;
- List<LEX_STRING> db_cl_names;
+ List<LEX_CSTRING> client_cs_names;
+ List<LEX_CSTRING> connection_cl_names;
+ List<LEX_CSTRING> db_cl_names;
/* End of character ser context. */
@@ -223,20 +226,21 @@ public:
bool old_row_is_record1);
void empty_lists();
bool create_lists_needed_for_files(MEM_ROOT *root);
- bool save_trigger_file(THD *thd, const char *db, const char *table_name);
+ bool save_trigger_file(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *table_name);
- static bool check_n_load(THD *thd, const char *db, const char *table_name,
+ 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, char *db, char *table_name);
- static bool change_table_name(THD *thd, const char *db,
- const char *old_alias,
- const char *old_table,
- const char *new_db,
- const char *new_table);
+ static bool drop_all_triggers(THD *thd, const LEX_CSTRING *db,
+ const LEX_CSTRING *table_name);
+ static bool change_table_name(THD *thd, const LEX_CSTRING *db,
+ const LEX_CSTRING *old_alias,
+ const LEX_CSTRING *old_table,
+ const LEX_CSTRING *new_db,
+ const LEX_CSTRING *new_table);
void add_trigger(trg_event_type event_type,
trg_action_time_type action_time,
trigger_order_type ordering_clause,
- LEX_STRING *anchor_trigger_name,
+ LEX_CSTRING *anchor_trigger_name,
Trigger *trigger);
Trigger *get_trigger(trg_event_type event_type,
trg_action_time_type action_time)
@@ -279,21 +283,21 @@ public:
bzero(extra_null_bitmap, null_bytes);
}
- Trigger *find_trigger(const LEX_STRING *name, bool remove_from_list);
+ Trigger *find_trigger(const LEX_CSTRING *name, bool remove_from_list);
Trigger* for_all_triggers(Triggers_processor func, void *arg);
private:
bool prepare_record_accessors(TABLE *table);
- Trigger *change_table_name_in_trignames(const char *old_db_name,
- const char *new_db_name,
- LEX_STRING *new_table_name,
+ Trigger *change_table_name_in_trignames(const LEX_CSTRING *old_db_name,
+ const LEX_CSTRING *new_db_name,
+ const LEX_CSTRING *new_table_name,
Trigger *trigger);
bool change_table_name_in_triggers(THD *thd,
- const char *old_db_name,
- const char *new_db_name,
- LEX_STRING *old_table_name,
- LEX_STRING *new_table_name);
+ const LEX_CSTRING *old_db_name,
+ const LEX_CSTRING *new_db_name,
+ const LEX_CSTRING *old_table_name,
+ const LEX_CSTRING *new_table_name);
bool check_for_broken_triggers()
{
@@ -306,13 +310,6 @@ private:
}
};
-inline Field **TABLE::field_to_fill()
-{
- return triggers && triggers->nullable_fields() ? triggers->nullable_fields()
- : field;
-}
-
-
bool add_table_for_trigger(THD *thd,
const sp_name *trg_name,
bool continue_if_not_exist,
@@ -320,12 +317,12 @@ bool add_table_for_trigger(THD *thd,
void build_trn_path(THD *thd, const sp_name *trg_name, LEX_STRING *trn_path);
-bool check_trn_exists(const LEX_STRING *trn_path);
+bool check_trn_exists(const LEX_CSTRING *trn_path);
bool load_table_name_for_trigger(THD *thd,
const sp_name *trg_name,
- const LEX_STRING *trn_path,
- LEX_STRING *tbl_name);
+ const LEX_CSTRING *trn_path,
+ LEX_CSTRING *tbl_name);
bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create);
extern const char * const TRG_EXT;
diff --git a/sql/sql_truncate.cc b/sql/sql_truncate.cc
index ea4d7399ea3..577f5a2b00f 100644
--- a/sql/sql_truncate.cc
+++ b/sql/sql_truncate.cc
@@ -14,6 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
+#include "mariadb.h"
#include "debug_sync.h" // DEBUG_SYNC
#include "table.h" // TABLE, FOREIGN_KEY_INFO
#include "sql_class.h" // THD
@@ -26,7 +27,8 @@
#include "sql_truncate.h"
#include "wsrep_mysqld.h"
#include "sql_show.h" //append_identifier()
-
+#include "sql_select.h"
+#include "sql_delete.h"
/**
Append a list of field names to a string.
@@ -38,15 +40,15 @@
*/
static bool fk_info_append_fields(THD *thd, String *str,
- List<LEX_STRING> *fields)
+ List<LEX_CSTRING> *fields)
{
bool res= FALSE;
- LEX_STRING *field;
- List_iterator_fast<LEX_STRING> it(*fields);
+ LEX_CSTRING *field;
+ List_iterator_fast<LEX_CSTRING> it(*fields);
while ((field= it++))
{
- res|= append_identifier(thd, str, field->str, field->length);
+ res|= append_identifier(thd, str, field);
res|= str->append(", ");
}
@@ -78,22 +80,17 @@ static const char *fk_info_str(THD *thd, FOREIGN_KEY_INFO *fk_info)
`db`.`tbl`, CONSTRAINT `id` FOREIGN KEY (`fk`) REFERENCES `db`.`tbl` (`fk`)
*/
- res|= append_identifier(thd, &str, fk_info->foreign_db->str,
- fk_info->foreign_db->length);
+ res|= append_identifier(thd, &str, fk_info->foreign_db);
res|= str.append(".");
- res|= append_identifier(thd, &str, fk_info->foreign_table->str,
- fk_info->foreign_table->length);
+ res|= append_identifier(thd, &str, fk_info->foreign_table);
res|= str.append(", CONSTRAINT ");
- res|= append_identifier(thd, &str, fk_info->foreign_id->str,
- fk_info->foreign_id->length);
+ res|= append_identifier(thd, &str, fk_info->foreign_id);
res|= str.append(" FOREIGN KEY (");
res|= fk_info_append_fields(thd, &str, &fk_info->foreign_fields);
res|= str.append(") REFERENCES ");
- res|= append_identifier(thd, &str, fk_info->referenced_db->str,
- fk_info->referenced_db->length);
+ res|= append_identifier(thd, &str, fk_info->referenced_db);
res|= str.append(".");
- res|= append_identifier(thd, &str, fk_info->referenced_table->str,
- fk_info->referenced_table->length);
+ res|= append_identifier(thd, &str, fk_info->referenced_table);
res|= str.append(" (");
res|= fk_info_append_fields(thd, &str, &fk_info->referenced_fields);
res|= str.append(')');
@@ -142,7 +139,7 @@ fk_truncate_illegal_if_parent(THD *thd, TABLE *table)
table->file->get_parent_foreign_key_list(thd, &fk_list);
/* Out of memory when building list. */
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
return TRUE;
it.init(fk_list);
@@ -150,14 +147,14 @@ fk_truncate_illegal_if_parent(THD *thd, TABLE *table)
/* Loop over the set of foreign keys for which this table is a parent. */
while ((fk_info= it++))
{
- if (my_strcasecmp(system_charset_info, fk_info->referenced_db->str,
- table->s->db.str) ||
- my_strcasecmp(system_charset_info, fk_info->referenced_table->str,
- table->s->table_name.str) ||
- my_strcasecmp(system_charset_info, fk_info->foreign_db->str,
- table->s->db.str) ||
- my_strcasecmp(system_charset_info, fk_info->foreign_table->str,
- table->s->table_name.str))
+ if (lex_string_cmp(system_charset_info, fk_info->referenced_db,
+ &table->s->db) ||
+ lex_string_cmp(system_charset_info, fk_info->referenced_table,
+ &table->s->table_name) ||
+ lex_string_cmp(system_charset_info, fk_info->foreign_db,
+ &table->s->db) ||
+ lex_string_cmp(system_charset_info, fk_info->foreign_table,
+ &table->s->table_name))
break;
}
@@ -212,7 +209,7 @@ Sql_cmd_truncate_table::handler_truncate(THD *thd, TABLE_LIST *table_ref,
or writing into the table. Yet, to open a write cursor we need
a thr_lock lock. Allow to open base tables only.
*/
- table_ref->required_type= FRMTYPE_TABLE;
+ table_ref->required_type= TABLE_TYPE_NORMAL;
/*
Ignore pending FLUSH TABLES since we don't want to release
the MDL lock taken above and otherwise there is no way to
@@ -239,13 +236,13 @@ Sql_cmd_truncate_table::handler_truncate(THD *thd, TABLE_LIST *table_ref,
DBUG_RETURN(TRUNCATE_FAILED_SKIP_BINLOG);
error= table_ref->table->file->ha_truncate();
- if (error)
+ if (unlikely(error))
{
table_ref->table->file->print_error(error, MYF(0));
/*
If truncate method is not implemented then we don't binlog the
- statement. If truncation has failed in a transactional engine then also we
- donot binlog the statment. Only in non transactional engine we binlog
+ statement. If truncation has failed in a transactional engine then also
+ we don't binlog the statment. Only in non transactional engine we binlog
inspite of errors.
*/
if (error == HA_ERR_WRONG_COMMAND ||
@@ -297,27 +294,31 @@ bool Sql_cmd_truncate_table::lock_table(THD *thd, TABLE_LIST *table_ref,
*/
if (thd->locked_tables_mode)
{
- if (!(table= find_table_for_mdl_upgrade(thd, table_ref->db,
- table_ref->table_name, NULL)))
+ if (!(table= find_table_for_mdl_upgrade(thd, table_ref->db.str,
+ table_ref->table_name.str, NULL)))
DBUG_RETURN(TRUE);
- *hton_can_recreate= ha_check_storage_engine_flag(table->s->db_type(),
+ *hton_can_recreate= ha_check_storage_engine_flag(table->file->ht,
HTON_CAN_RECREATE);
table_ref->mdl_request.ticket= table->mdl_ticket;
}
else
{
+ handlerton *hton;
+ bool is_sequence;
+
/* Acquire an exclusive lock. */
DBUG_ASSERT(table_ref->next_global == NULL);
if (lock_table_names(thd, table_ref, NULL,
thd->variables.lock_wait_timeout, 0))
DBUG_RETURN(TRUE);
- handlerton *hton;
- if (!ha_table_exists(thd, table_ref->db, table_ref->table_name, &hton) ||
+ if (!ha_table_exists(thd, &table_ref->db, &table_ref->table_name,
+ &hton, &is_sequence) ||
hton == view_pseudo_hton)
{
- my_error(ER_NO_SUCH_TABLE, MYF(0), table_ref->db, table_ref->table_name);
+ my_error(ER_NO_SUCH_TABLE, MYF(0), table_ref->db.str,
+ table_ref->table_name.str);
DBUG_RETURN(TRUE);
}
@@ -333,7 +334,7 @@ bool Sql_cmd_truncate_table::lock_table(THD *thd, TABLE_LIST *table_ref,
*hton_can_recreate= false;
}
else
- *hton_can_recreate= hton->flags & HTON_CAN_RECREATE;
+ *hton_can_recreate= !is_sequence && hton->flags & HTON_CAN_RECREATE;
}
/*
@@ -356,8 +357,8 @@ bool Sql_cmd_truncate_table::lock_table(THD *thd, TABLE_LIST *table_ref,
else
{
/* Table is already locked exclusively. Remove cached instances. */
- tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_ref->db,
- table_ref->table_name, FALSE);
+ tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_ref->db.str,
+ table_ref->table_name.str, FALSE);
}
DBUG_RETURN(FALSE);
@@ -412,7 +413,7 @@ bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref)
bool hton_can_recreate;
if (WSREP(thd) &&
- wsrep_to_isolation_begin(thd, table_ref->db, table_ref->table_name, 0))
+ wsrep_to_isolation_begin(thd, table_ref->db.str, table_ref->table_name.str, 0))
DBUG_RETURN(TRUE);
if (lock_table(thd, table_ref, &hton_can_recreate))
DBUG_RETURN(TRUE);
@@ -423,10 +424,13 @@ bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref)
The storage engine can truncate the table by creating an
empty table with the same structure.
*/
- error= dd_recreate_table(thd, table_ref->db, table_ref->table_name);
+ error= dd_recreate_table(thd, table_ref->db.str, table_ref->table_name.str);
if (thd->locked_tables_mode && thd->locked_tables_list.reopen_tables(thd, false))
- thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
+ {
+ thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
+ error=1;
+ }
/* No need to binlog a failed truncate-by-recreate. */
binlog_stmt= !error;
@@ -445,7 +449,7 @@ bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref)
query must be written to the binary log. The only exception is a
unimplemented truncate method.
*/
- if (error == TRUNCATE_OK || error == TRUNCATE_FAILED_BUT_BINLOG)
+ if (unlikely(error == TRUNCATE_OK || error == TRUNCATE_FAILED_BUT_BINLOG))
binlog_stmt= true;
else
binlog_stmt= false;
@@ -476,7 +480,6 @@ bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref)
DBUG_RETURN(error);
}
-
/**
Execute a TRUNCATE statement at runtime.
@@ -488,13 +491,13 @@ bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref)
bool Sql_cmd_truncate_table::execute(THD *thd)
{
bool res= TRUE;
- TABLE_LIST *first_table= thd->lex->select_lex.table_list.first;
+ TABLE_LIST *table= thd->lex->select_lex.table_list.first;
DBUG_ENTER("Sql_cmd_truncate_table::execute");
- if (check_one_table_access(thd, DROP_ACL, first_table))
+ if (check_one_table_access(thd, DROP_ACL, table))
DBUG_RETURN(res);
- if (! (res= truncate_table(thd, first_table)))
+ if (! (res= truncate_table(thd, table)))
my_ok(thd);
DBUG_RETURN(res);
diff --git a/sql/sql_tvc.cc b/sql/sql_tvc.cc
new file mode 100644
index 00000000000..def519035d9
--- /dev/null
+++ b/sql/sql_tvc.cc
@@ -0,0 +1,1109 @@
+/* Copyright (c) 2017, 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 */
+
+#include "mariadb.h"
+#include "sql_list.h"
+#include "sql_tvc.h"
+#include "sql_class.h"
+#include "opt_range.h"
+#include "sql_select.h"
+#include "sql_explain.h"
+#include "sql_parse.h"
+#include "sql_cte.h"
+
+/**
+ @brief
+ Fix fields for TVC values
+
+ @param
+ @param thd The context of the statement
+ @param li The iterator on the list of lists
+
+ @details
+ Call fix_fields procedure for TVC values.
+
+ @retval
+ true if an error was reported
+ false otherwise
+*/
+
+bool fix_fields_for_tvc(THD *thd, List_iterator_fast<List_item> &li)
+{
+ DBUG_ENTER("fix_fields_for_tvc");
+ List_item *lst;
+ li.rewind();
+
+ while ((lst= li++))
+ {
+ List_iterator_fast<Item> it(*lst);
+ Item *item;
+
+ while ((item= it++))
+ {
+ /*
+ Some items have already been fixed.
+ For example Item_splocal items get fixed in
+ Item_splocal::append_for_log(), which is called from subst_spvars()
+ while replacing their values to NAME_CONST()s.
+ So fix only those that have not been.
+ */
+ if (item->fix_fields_if_needed(thd, 0) ||
+ item->check_is_evaluable_expression_or_error())
+ DBUG_RETURN(true);
+ }
+ }
+ DBUG_RETURN(false);
+}
+
+
+/**
+ @brief
+ Defines types of matrix columns elements where matrix rows are defined by
+ some lists of values.
+
+ @param
+ @param thd The context of the statement
+ @param li The iterator on the list of lists
+ @param holders The structure where types of matrix columns are stored
+ @param first_list_el_count Count of the list values. It should be the same
+ for each list of lists elements. It contains
+ number of elements of the first list from list of
+ lists.
+
+ @details
+ For each list list_a from list of lists the procedure gets its elements
+ types and aggregates them with the previous ones stored in holders. If
+ list_a is the first one in the list of lists its elements types are put in
+ holders. The errors can be reported when count of list_a elements is
+ different from the first_list_el_count. Also error can be reported whe
+ n aggregation can't be made.
+
+ @retval
+ true if an error was reported
+ false otherwise
+*/
+
+bool join_type_handlers_for_tvc(THD *thd, List_iterator_fast<List_item> &li,
+ Type_holder *holders, uint first_list_el_count)
+{
+ DBUG_ENTER("join_type_handlers_for_tvc");
+ List_item *lst;
+ li.rewind();
+ bool first= true;
+
+ while ((lst= li++))
+ {
+ List_iterator_fast<Item> it(*lst);
+ Item *item;
+
+ if (first_list_el_count != lst->elements)
+ {
+ my_message(ER_WRONG_NUMBER_OF_VALUES_IN_TVC,
+ ER_THD(thd, ER_WRONG_NUMBER_OF_VALUES_IN_TVC),
+ MYF(0));
+ DBUG_RETURN(true);
+ }
+ for (uint pos= 0; (item=it++); pos++)
+ {
+ const Type_handler *item_type_handler= item->real_type_handler();
+ if (first)
+ holders[pos].set_handler(item_type_handler);
+ else if (holders[pos].aggregate_for_result(item_type_handler))
+ {
+ my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0),
+ holders[pos].type_handler()->name().ptr(),
+ item_type_handler->name().ptr(),
+ "TABLE VALUE CONSTRUCTOR");
+ DBUG_RETURN(true);
+ }
+ }
+ first= false;
+ }
+ DBUG_RETURN(false);
+}
+
+
+/**
+ @brief
+ Define attributes of matrix columns elements where matrix rows are defined
+ by some lists of values.
+
+ @param
+ @param thd The context of the statement
+ @param li The iterator on the list of lists
+ @param holders The structure where names of matrix columns are stored
+ @param count_of_lists Count of list of lists elements
+ @param first_list_el_count Count of the list values. It should be the same
+ for each list of lists elements. It contains
+ number of elements of the first list from list
+ of lists.
+
+ @details
+ For each list list_a from list of lists the procedure gets its elements
+ attributes and aggregates them with the previous ones stored in holders.
+ The errors can be reported when aggregation can't be made.
+
+ @retval
+ true if an error was reported
+ false otherwise
+*/
+
+bool get_type_attributes_for_tvc(THD *thd,
+ List_iterator_fast<List_item> &li,
+ Type_holder *holders, uint count_of_lists,
+ uint first_list_el_count)
+{
+ DBUG_ENTER("get_type_attributes_for_tvc");
+ List_item *lst;
+ li.rewind();
+
+ for (uint pos= 0; pos < first_list_el_count; pos++)
+ {
+ if (holders[pos].alloc_arguments(thd, count_of_lists))
+ DBUG_RETURN(true);
+ }
+
+ while ((lst= li++))
+ {
+ List_iterator_fast<Item> it(*lst);
+ Item *item;
+ for (uint holder_pos= 0 ; (item= it++); holder_pos++)
+ {
+ DBUG_ASSERT(item->fixed);
+ holders[holder_pos].add_argument(item);
+ }
+ }
+
+ for (uint pos= 0; pos < first_list_el_count; pos++)
+ {
+ if (holders[pos].aggregate_attributes(thd))
+ DBUG_RETURN(true);
+ }
+ DBUG_RETURN(false);
+}
+
+
+/**
+ @brief
+ Prepare of TVC
+
+ @param
+ @param thd The context of the statement
+ @param sl The select where this TVC is defined
+ @param tmp_result Structure that contains the information
+ about where to send the result of the query
+ @param unit_arg The union where sl is defined
+
+ @details
+ Gets types and attributes of values of this TVC that will be used
+ for temporary table creation for this TVC. It creates Item_type_holders
+ for each element of the first list from list of lists (VALUES from tvc),
+ using its elements name, defined type and attribute.
+
+ @retval
+ true if an error was reported
+ false otherwise
+*/
+
+bool table_value_constr::prepare(THD *thd, SELECT_LEX *sl,
+ select_result *tmp_result,
+ st_select_lex_unit *unit_arg)
+{
+ DBUG_ENTER("table_value_constr::prepare");
+ select_lex->in_tvc= true;
+ List_iterator_fast<List_item> li(lists_of_values);
+
+ List_item *first_elem= li++;
+ uint cnt= first_elem->elements;
+ Type_holder *holders;
+
+ if (cnt == 0)
+ {
+ my_error(ER_EMPTY_ROW_IN_TVC, MYF(0));
+ DBUG_RETURN(true);
+ }
+
+ if (fix_fields_for_tvc(thd, li))
+ DBUG_RETURN(true);
+
+ if (!(holders= new (thd->stmt_arena->mem_root) Type_holder[cnt]) ||
+ join_type_handlers_for_tvc(thd, li, holders, cnt) ||
+ get_type_attributes_for_tvc(thd, li, holders,
+ lists_of_values.elements, cnt))
+ DBUG_RETURN(true);
+
+ List_iterator_fast<Item> it(*first_elem);
+ Item *item;
+ Query_arena *arena, backup;
+ arena=thd->activate_stmt_arena_if_needed(&backup);
+
+ sl->item_list.empty();
+ for (uint pos= 0; (item= it++); pos++)
+ {
+ /* Error's in 'new' will be detected after loop */
+ Item_type_holder *new_holder= new (thd->mem_root)
+ Item_type_holder(thd, item, holders[pos].type_handler(),
+ &holders[pos]/*Type_all_attributes*/,
+ holders[pos].get_maybe_null());
+ new_holder->fix_fields(thd, 0);
+ sl->item_list.push_back(new_holder);
+ }
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+
+ if (unlikely(thd->is_fatal_error))
+ DBUG_RETURN(true); // out of memory
+
+ result= tmp_result;
+
+ if (result && result->prepare(sl->item_list, unit_arg))
+ DBUG_RETURN(true);
+
+ /*
+ setup_order() for a TVC is not called when the following is true
+ (thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_VIEW)
+ */
+
+ thd->where="order clause";
+ ORDER *order= sl->order_list.first;
+ for (; order; order=order->next)
+ {
+ Item *order_item= *order->item;
+ if (order_item->type() == Item::INT_ITEM && order_item->basic_const_item())
+ {
+ uint count= 0;
+ if (order->counter_used)
+ count= order->counter; // counter was once resolved
+ else
+ count= (uint) order_item->val_int();
+ if (!count || count > first_elem->elements)
+ {
+ my_error(ER_BAD_FIELD_ERROR, MYF(0),
+ order_item->full_name(), thd->where);
+ DBUG_RETURN(true);
+ }
+ order->in_field_list= 1;
+ order->counter= count;
+ order->counter_used= 1;
+ }
+ }
+
+ select_lex->in_tvc= false;
+ DBUG_RETURN(false);
+}
+
+
+/**
+ Save Query Plan Footprint
+*/
+
+int table_value_constr::save_explain_data_intern(THD *thd,
+ Explain_query *output)
+{
+ const char *message= "No tables used";
+ DBUG_ENTER("table_value_constr::save_explain_data_intern");
+ DBUG_PRINT("info", ("Select %p, type %s, message %s",
+ select_lex, select_lex->type,
+ message));
+ DBUG_ASSERT(have_query_plan == QEP_AVAILABLE);
+
+ /* There should be no attempts to save query plans for merged selects */
+ DBUG_ASSERT(!select_lex->master_unit()->derived ||
+ select_lex->master_unit()->derived->is_materialized_derived() ||
+ select_lex->master_unit()->derived->is_with_table());
+
+ explain= new (output->mem_root) Explain_select(output->mem_root,
+ thd->lex->analyze_stmt);
+ if (!explain)
+ DBUG_RETURN(1);
+
+ select_lex->set_explain_type(true);
+
+ explain->select_id= select_lex->select_number;
+ explain->select_type= select_lex->type;
+ explain->linkage= select_lex->linkage;
+ explain->using_temporary= false;
+ explain->using_filesort= false;
+ /* Setting explain->message means that all other members are invalid */
+ explain->message= message;
+
+ if (select_lex->master_unit()->derived)
+ explain->connection_type= Explain_node::EXPLAIN_NODE_DERIVED;
+
+ output->add_node(explain);
+
+ if (select_lex->is_top_level_node())
+ output->query_plan_ready();
+
+ DBUG_RETURN(0);
+}
+
+
+/**
+ Optimization of TVC
+*/
+
+bool table_value_constr::optimize(THD *thd)
+{
+ create_explain_query_if_not_exists(thd->lex, thd->mem_root);
+ have_query_plan= QEP_AVAILABLE;
+
+ if (select_lex->select_number != UINT_MAX &&
+ select_lex->select_number != INT_MAX /* this is not a UNION's "fake select */ &&
+ have_query_plan != QEP_NOT_PRESENT_YET &&
+ thd->lex->explain && // for "SET" command in SPs.
+ (!thd->lex->explain->get_select(select_lex->select_number)))
+ {
+ return save_explain_data_intern(thd, thd->lex->explain);
+ }
+ return 0;
+}
+
+
+/**
+ Execute of TVC
+*/
+
+bool table_value_constr::exec(SELECT_LEX *sl)
+{
+ DBUG_ENTER("table_value_constr::exec");
+ List_iterator_fast<List_item> li(lists_of_values);
+ List_item *elem;
+ ha_rows send_records= 0;
+
+ if (select_options & SELECT_DESCRIBE)
+ DBUG_RETURN(false);
+
+ if (result->send_result_set_metadata(sl->item_list,
+ Protocol::SEND_NUM_ROWS |
+ Protocol::SEND_EOF))
+ {
+ DBUG_RETURN(true);
+ }
+
+ while ((elem= li++))
+ {
+ if (send_records >= sl->master_unit()->select_limit_cnt)
+ break;
+ int rc= result->send_data(*elem);
+ if (!rc)
+ send_records++;
+ else if (rc > 0)
+ DBUG_RETURN(true);
+ }
+
+ if (result->send_eof())
+ DBUG_RETURN(true);
+
+ DBUG_RETURN(false);
+}
+
+
+/**
+ @brief
+ Print list
+
+ @param str The reference on the string representation of the list
+ @param list The list that needed to be print
+ @param query_type The mode of printing
+
+ @details
+ The method saves a string representation of list in the
+ string str.
+*/
+
+void print_list_item(String *str, List_item *list,
+ enum_query_type query_type)
+{
+ bool is_first_elem= true;
+ List_iterator_fast<Item> it(*list);
+ Item *item;
+
+ str->append('(');
+
+ while ((item= it++))
+ {
+ if (is_first_elem)
+ is_first_elem= false;
+ else
+ str->append(',');
+
+ item->print(str, query_type);
+ }
+
+ str->append(')');
+}
+
+
+/**
+ @brief
+ Print this TVC
+
+ @param thd The context of the statement
+ @param str The reference on the string representation of this TVC
+ @param query_type The mode of printing
+
+ @details
+ The method saves a string representation of this TVC in the
+ string str.
+*/
+
+void table_value_constr::print(THD *thd, String *str,
+ enum_query_type query_type)
+{
+ DBUG_ASSERT(thd);
+
+ str->append(STRING_WITH_LEN("values "));
+
+ bool is_first_elem= true;
+ List_iterator_fast<List_item> li(lists_of_values);
+ List_item *list;
+
+ while ((list= li++))
+ {
+ if (is_first_elem)
+ is_first_elem= false;
+ else
+ str->append(',');
+
+ print_list_item(str, list, query_type);
+ }
+ if (select_lex->order_list.elements)
+ {
+ str->append(STRING_WITH_LEN(" order by "));
+ select_lex->print_order(str, select_lex->order_list.first, query_type);
+ }
+ select_lex->print_limit(thd, str, query_type);
+}
+
+
+/**
+ @brief
+ Create list of lists for TVC from the list of this IN predicate
+
+ @param thd The context of the statement
+ @param values TVC list of values
+
+ @details
+ The method uses the list of values of this IN predicate to build
+ an equivalent list of values that can be used in TVC.
+
+ E.g.:
+
+ <value_list> = 5,2,7
+ <transformed_value_list> = (5),(2),(7)
+
+ <value_list> = (5,2),(7,1)
+ <transformed_value_list> = (5,2),(7,1)
+
+ @retval
+ false if the method succeeds
+ true otherwise
+*/
+
+bool Item_func_in::create_value_list_for_tvc(THD *thd,
+ List< List<Item> > *values)
+{
+ bool is_list_of_rows= args[1]->type() == Item::ROW_ITEM;
+
+ for (uint i=1; i < arg_count; i++)
+ {
+ char col_name[8];
+ List<Item> *tvc_value;
+ if (!(tvc_value= new (thd->mem_root) List<Item>()))
+ return true;
+
+ if (is_list_of_rows)
+ {
+ Item_row *row_list= (Item_row *)(args[i]);
+
+ for (uint j=0; j < row_list->cols(); j++)
+ {
+ if (i == 1)
+ {
+ sprintf(col_name, "_col_%i", j+1);
+ row_list->element_index(j)->set_name(thd, col_name, strlen(col_name),
+ thd->charset());
+ }
+ if (tvc_value->push_back(row_list->element_index(j),
+ thd->mem_root))
+ return true;
+ }
+ }
+ else
+ {
+ if (i == 1)
+ {
+ sprintf(col_name, "_col_%i", 1);
+ args[i]->set_name(thd, col_name, strlen(col_name), thd->charset());
+ }
+ if (tvc_value->push_back(args[i]->real_item()))
+ return true;
+ }
+
+ if (values->push_back(tvc_value, thd->mem_root))
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ @brief
+ Create name for the derived table defined by TVC
+
+ @param thd The context of the statement
+ @param parent_select The SELECT where derived table is used
+ @param alias The returned created name
+
+ @details
+ Create name for the derived table using current TVC number
+ for this parent_select stored in parent_select
+
+ @retval
+ true if creation fails
+ false otherwise
+*/
+
+static bool create_tvc_name(THD *thd, st_select_lex *parent_select,
+ LEX_CSTRING *alias)
+{
+ char buff[6];
+
+ alias->length= my_snprintf(buff, sizeof(buff),
+ "tvc_%u",
+ parent_select ? parent_select->curr_tvc_name : 0);
+ alias->str= thd->strmake(buff, alias->length);
+ if (!alias->str)
+ return true;
+
+ return false;
+}
+
+
+/**
+ @brief
+ Check whether TVC used in unit is to be wrapped into select
+
+ @details
+ TVC used in unit that contains more than one members is to be wrapped
+ into select if it is tailed with ORDER BY ... LIMIT n [OFFSET m]
+
+ @retval
+ true if TVC is to be wrapped
+ false otherwise
+*/
+
+bool table_value_constr::to_be_wrapped_as_with_tail()
+{
+ return select_lex->master_unit()->first_select()->next_select() &&
+ select_lex->order_list.elements && select_lex->explicit_limit;
+}
+
+
+/**
+ @brief
+ Wrap table value constructor into a select
+
+ @param thd The context handler
+ @param tvc_sl The TVC to wrap
+ @parent_select The parent select if tvc_sl used in a subquery
+
+ @details
+ The function wraps the TVC tvc_sl into a select:
+ the function transforms the TVC of the form VALUES (v1), ... (vn) into
+ the select of the form
+ SELECT * FROM (VALUES (v1), ... (vn)) tvc_x
+
+ @retval pointer to the result of of the transformation if successful
+ NULL - otherwise
+*/
+
+static
+st_select_lex *wrap_tvc(THD *thd, st_select_lex *tvc_sl,
+ st_select_lex *parent_select)
+{
+ LEX *lex= thd->lex;
+ select_result *save_result= thd->lex->result;
+ uint8 save_derived_tables= lex->derived_tables;
+ thd->lex->result= NULL;
+
+ Query_arena backup;
+ Query_arena *arena= thd->activate_stmt_arena_if_needed(&backup);
+ /*
+ Create SELECT_LEX of the select used in the result of transformation
+ */
+ lex->current_select= tvc_sl;
+ if (mysql_new_select(lex, 0, NULL))
+ goto err;
+ mysql_init_select(lex);
+ /* Create item list as '*' for the subquery SQ */
+ Item *item;
+ SELECT_LEX *wrapper_sl;
+ wrapper_sl= lex->current_select;
+ wrapper_sl->linkage= tvc_sl->linkage;
+ wrapper_sl->parsing_place= SELECT_LIST;
+ item= new (thd->mem_root) Item_field(thd, &wrapper_sl->context,
+ NULL, NULL, &star_clex_str);
+ if (item == NULL || add_item_to_list(thd, item))
+ goto err;
+ (wrapper_sl->with_wild)++;
+
+ /* Exclude SELECT with TVC */
+ tvc_sl->exclude();
+ /*
+ Create derived table DT that will wrap TVC in the result of transformation
+ */
+ SELECT_LEX *tvc_select; // select for tvc
+ SELECT_LEX_UNIT *derived_unit; // unit for tvc_select
+ if (mysql_new_select(lex, 1, tvc_sl))
+ goto err;
+ tvc_select= lex->current_select;
+ derived_unit= tvc_select->master_unit();
+ tvc_select->linkage= DERIVED_TABLE_TYPE;
+
+ lex->current_select= wrapper_sl;
+
+ /*
+ Create the name of the wrapping derived table and
+ add it to the FROM list of the wrapper
+ */
+ Table_ident *ti;
+ LEX_CSTRING alias;
+ TABLE_LIST *derived_tab;
+ if (!(ti= new (thd->mem_root) Table_ident(derived_unit)) ||
+ create_tvc_name(thd, parent_select, &alias))
+ goto err;
+ if (!(derived_tab=
+ wrapper_sl->add_table_to_list(thd,
+ ti, &alias, 0,
+ TL_READ, MDL_SHARED_READ)))
+ goto err;
+ wrapper_sl->add_joined_table(derived_tab);
+ wrapper_sl->add_where_field(derived_unit->first_select());
+ wrapper_sl->context.table_list= wrapper_sl->table_list.first;
+ wrapper_sl->context.first_name_resolution_table= wrapper_sl->table_list.first;
+ wrapper_sl->table_list.first->derived_type= DTYPE_TABLE | DTYPE_MATERIALIZE;
+ lex->derived_tables|= DERIVED_SUBQUERY;
+
+ wrapper_sl->where= 0;
+ wrapper_sl->set_braces(false);
+ derived_unit->set_with_clause(0);
+
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+ thd->lex->result= save_result;
+ return wrapper_sl;
+
+err:
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+ thd->lex->result= save_result;
+ lex->derived_tables= save_derived_tables;
+ return 0;
+}
+
+
+/**
+ @brief
+ Wrap TVC with ORDER BY ... LIMIT tail into a select
+
+ @param thd The context handler
+ @param tvc_sl The TVC to wrap
+
+ @details
+ The function wraps the TVC tvc_sl into a select:
+ the function transforms the TVC with tail of the form
+ VALUES (v1), ... (vn) ORDER BY ... LIMIT n [OFFSET m]
+ into the select with the same tail of the form
+ SELECT * FROM (VALUES (v1), ... (vn)) tvc_x
+ ORDER BY ... LIMIT n [OFFSET m]
+
+ @retval pointer to the result of of the transformation if successful
+ NULL - otherwise
+*/
+
+st_select_lex *wrap_tvc_with_tail(THD *thd, st_select_lex *tvc_sl)
+{
+ st_select_lex *wrapper_sl= wrap_tvc(thd, tvc_sl, NULL);
+ if (!wrapper_sl)
+ return NULL;
+
+ wrapper_sl->order_list= tvc_sl->order_list;
+ wrapper_sl->select_limit= tvc_sl->select_limit;
+ wrapper_sl->offset_limit= tvc_sl->offset_limit;
+ wrapper_sl->braces= tvc_sl->braces;
+ wrapper_sl->explicit_limit= tvc_sl->explicit_limit;
+ tvc_sl->order_list.empty();
+ tvc_sl->select_limit= NULL;
+ tvc_sl->offset_limit= NULL;
+ tvc_sl->braces= 0;
+ tvc_sl->explicit_limit= false;
+ if (tvc_sl->select_number == 1)
+ {
+ tvc_sl->select_number= wrapper_sl->select_number;
+ wrapper_sl->select_number= 1;
+ }
+ if (tvc_sl->master_unit()->union_distinct == tvc_sl)
+ {
+ wrapper_sl->master_unit()->union_distinct= wrapper_sl;
+ }
+ thd->lex->current_select= wrapper_sl;
+ return wrapper_sl;
+}
+
+
+/**
+ @brief
+ Wrap TVC in a subselect into a select
+
+ @param thd The context handler
+ @param tvc_sl The TVC to wrap
+
+ @details
+ The function wraps the TVC tvc_sl used in a subselect into a select
+ the function transforms the TVC of the form VALUES (v1), ... (vn)
+ into the select the form
+ SELECT * FROM (VALUES (v1), ... (vn)) tvc_x
+ and replaces the subselect with the result of the transformation.
+
+ @retval false if successfull
+ true otherwise
+*/
+
+bool Item_subselect::wrap_tvc_into_select(THD *thd, st_select_lex *tvc_sl)
+{
+ LEX *lex= thd->lex;
+ /* SELECT_LEX object where the transformation is performed */
+ SELECT_LEX *parent_select= lex->current_select;
+ SELECT_LEX *wrapper_sl= wrap_tvc(thd, tvc_sl, parent_select);
+ if (wrapper_sl)
+ {
+ if (engine->engine_type() == subselect_engine::SINGLE_SELECT_ENGINE)
+ ((subselect_single_select_engine *) engine)->change_select(wrapper_sl);
+ lex->current_select= wrapper_sl;
+ return false;
+ }
+ else
+ {
+ lex->current_select= parent_select;
+ return true;
+ }
+}
+
+
+/*
+ @brief
+ Check whether the items are of comparable type or not
+
+ @details
+ This check are done because materialization is not performed
+ if the left expr and right expr are of the same types.
+ @see subquery_types_allow_materialization()
+
+ @retval
+ 0 comparable
+ 1 not comparable
+*/
+
+static bool cmp_row_types(Item* item1, Item* item2)
+{
+ uint n= item1->cols();
+ if (item2->check_cols(n))
+ return true;
+
+ for (uint i=0; i < n; i++)
+ {
+ Item *inner= item1->element_index(i);
+ Item *outer= item2->element_index(i);
+ if (!inner->type_handler()->subquery_type_allows_materialization(inner,
+ outer))
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ @brief
+ Transform IN predicate into IN subquery
+
+ @param thd The context of the statement
+ @param arg Not used
+
+ @details
+ The method transforms this IN predicate into in equivalent IN subquery:
+
+ <left_expr> IN (<value_list>)
+ =>
+ <left_expr> IN (SELECT * FROM (VALUES <transformed_value_list>) AS tvc_#)
+
+ E.g.:
+
+ <value_list> = 5,2,7
+ <transformed_value_list> = (5),(2),(7)
+
+ <value_list> = (5,2),(7,1)
+ <transformed_value_list> = (5,2),(7,1)
+
+ If the transformation succeeds the method returns the result IN subquery,
+ otherwise this IN predicate is returned.
+
+ @retval
+ pointer to the result of transformation if succeeded
+ pointer to this IN predicate otherwise
+*/
+
+Item *Item_func_in::in_predicate_to_in_subs_transformer(THD *thd,
+ uchar *arg)
+{
+ if (!transform_into_subq)
+ return this;
+
+ transform_into_subq= false;
+
+ List<List_item> values;
+
+ LEX *lex= thd->lex;
+ /* SELECT_LEX object where the transformation is performed */
+ SELECT_LEX *parent_select= lex->current_select;
+ uint8 save_derived_tables= lex->derived_tables;
+
+ /*
+ Make sure that create_tmp_table will not fail due to too long keys.
+ Here the strategy would mainly use materialization, so we need to make
+ sure that the materialized table can be created.
+
+ The checks here are the same as in subquery_type_allows_materialization()
+ */
+ uint32 length= max_length_of_left_expr();
+ if (!length || length > tmp_table_max_key_length() ||
+ args[0]->cols() > tmp_table_max_key_parts())
+ return this;
+
+ for (uint i=1; i < arg_count; i++)
+ {
+ if (!args[i]->const_item() || cmp_row_types(args[0], args[i]))
+ return this;
+ }
+
+ Query_arena backup;
+ Query_arena *arena= thd->activate_stmt_arena_if_needed(&backup);
+
+ /*
+ Create SELECT_LEX of the subquery SQ used in the result of transformation
+ */
+ if (mysql_new_select(lex, 1, NULL))
+ goto err;
+ mysql_init_select(lex);
+ /* Create item list as '*' for the subquery SQ */
+ Item *item;
+ SELECT_LEX *sq_select; // select for IN subquery;
+ 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);
+ if (item == NULL || add_item_to_list(thd, item))
+ goto err;
+ (sq_select->with_wild)++;
+ /*
+ Create derived table DT that will wrap TVC in the result of transformation
+ */
+ SELECT_LEX *tvc_select; // select for tvc
+ SELECT_LEX_UNIT *derived_unit; // unit for tvc_select
+ if (mysql_new_select(lex, 1, NULL))
+ goto err;
+ mysql_init_select(lex);
+ tvc_select= lex->current_select;
+ derived_unit= tvc_select->master_unit();
+ tvc_select->linkage= DERIVED_TABLE_TYPE;
+
+ /* Create TVC used in the transformation */
+ if (create_value_list_for_tvc(thd, &values))
+ goto err;
+ if (!(tvc_select->tvc=
+ new (thd->mem_root)
+ table_value_constr(values,
+ tvc_select,
+ tvc_select->options)))
+ goto err;
+
+ lex->current_select= sq_select;
+
+ /*
+ Create the name of the wrapping derived table and
+ add it to the FROM list of the subquery SQ
+ */
+ Table_ident *ti;
+ LEX_CSTRING alias;
+ TABLE_LIST *derived_tab;
+ if (!(ti= new (thd->mem_root) Table_ident(derived_unit)) ||
+ create_tvc_name(thd, parent_select, &alias))
+ goto err;
+ if (!(derived_tab=
+ sq_select->add_table_to_list(thd,
+ ti, &alias, 0,
+ TL_READ, MDL_SHARED_READ)))
+ goto err;
+ sq_select->add_joined_table(derived_tab);
+ sq_select->add_where_field(derived_unit->first_select());
+ sq_select->context.table_list= sq_select->table_list.first;
+ sq_select->context.first_name_resolution_table= sq_select->table_list.first;
+ sq_select->table_list.first->derived_type= DTYPE_TABLE | DTYPE_MATERIALIZE;
+ lex->derived_tables|= DERIVED_SUBQUERY;
+
+ sq_select->where= 0;
+ sq_select->set_braces(false);
+ derived_unit->set_with_clause(0);
+
+ /* Create IN subquery predicate */
+ sq_select->parsing_place= parent_select->parsing_place;
+ Item_in_subselect *in_subs;
+ Item *sq;
+ if (!(in_subs=
+ new (thd->mem_root) Item_in_subselect(thd, args[0], sq_select)))
+ goto err;
+ sq= in_subs;
+ if (negated)
+ sq= negate_expression(thd, in_subs);
+ else
+ in_subs->emb_on_expr_nest= emb_on_expr_nest;
+
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+ thd->lex->current_select= parent_select;
+
+ if (sq->fix_fields(thd, (Item **)&sq))
+ goto err;
+
+ parent_select->curr_tvc_name++;
+ return sq;
+
+err:
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+ lex->derived_tables= save_derived_tables;
+ thd->lex->current_select= parent_select;
+ return NULL;
+}
+
+
+uint32 Item_func_in::max_length_of_left_expr()
+{
+ uint n= args[0]->cols();
+ uint32 length= 0;
+ for (uint i=0; i < n; i++)
+ length+= args[0]->element_index(i)->max_length;
+ return length;
+}
+
+
+/**
+ @brief
+ Check if this IN-predicate can be transformed in IN-subquery
+ with TVC
+
+ @param thd The context of the statement
+
+ @details
+ Compare the number of elements in the list of
+ values in this IN-predicate with the
+ in_subquery_conversion_threshold special variable
+
+ @retval
+ true if transformation can be made
+ false otherwise
+*/
+
+bool Item_func_in::to_be_transformed_into_in_subq(THD *thd)
+{
+ uint values_count= arg_count-1;
+
+ if (args[1]->type() == Item::ROW_ITEM)
+ values_count*= ((Item_row *)(args[1]))->cols();
+
+ if (thd->variables.in_subquery_conversion_threshold == 0 ||
+ thd->variables.in_subquery_conversion_threshold > values_count)
+ return false;
+
+ return true;
+}
+
+
+/**
+ @brief
+ Transform IN predicates into IN subqueries in WHERE and ON expressions
+
+ @param thd The context of the statement
+
+ @details
+ For each IN predicate from AND parts of the WHERE condition and/or
+ ON expressions of the SELECT for this join the method performs
+ the intransformation into an equivalent IN sunquery if it's needed.
+
+ @retval
+ false always
+*/
+
+bool JOIN::transform_in_predicates_into_in_subq(THD *thd)
+{
+ DBUG_ENTER("JOIN::transform_in_predicates_into_in_subq");
+ if (!select_lex->in_funcs.elements)
+ DBUG_RETURN(false);
+
+ SELECT_LEX *save_current_select= thd->lex->current_select;
+ enum_parsing_place save_parsing_place= select_lex->parsing_place;
+ thd->lex->current_select= select_lex;
+ if (conds)
+ {
+ select_lex->parsing_place= IN_WHERE;
+ conds=
+ conds->transform(thd,
+ &Item::in_predicate_to_in_subs_transformer,
+ (uchar*) 0);
+ if (!conds)
+ DBUG_RETURN(true);
+ select_lex->prep_where= conds ? conds->copy_andor_structure(thd) : 0;
+ select_lex->where= conds;
+ }
+
+ if (join_list)
+ {
+ TABLE_LIST *table;
+ List_iterator<TABLE_LIST> li(*join_list);
+ select_lex->parsing_place= IN_ON;
+
+ while ((table= li++))
+ {
+ if (table->on_expr)
+ {
+ table->on_expr=
+ table->on_expr->transform(thd,
+ &Item::in_predicate_to_in_subs_transformer,
+ (uchar*) 0);
+ if (!table->on_expr)
+ DBUG_RETURN(true);
+ table->prep_on_expr= table->on_expr ?
+ table->on_expr->copy_andor_structure(thd) : 0;
+ }
+ }
+ }
+
+ select_lex->in_funcs.empty();
+ select_lex->parsing_place= save_parsing_place;
+ thd->lex->current_select= save_current_select;
+ DBUG_RETURN(false);
+}
+
diff --git a/sql/sql_tvc.h b/sql/sql_tvc.h
new file mode 100644
index 00000000000..594a77af65c
--- /dev/null
+++ b/sql/sql_tvc.h
@@ -0,0 +1,72 @@
+/* Copyright (c) 2017, 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_TVC_INCLUDED
+#define SQL_TVC_INCLUDED
+#include "sql_type.h"
+
+typedef List<Item> List_item;
+class select_result;
+class Explain_select;
+class Explain_query;
+class Item_func_in;
+class st_select_lex_unit;
+typedef class st_select_lex SELECT_LEX;
+
+/**
+ @class table_value_constr
+ @brief Definition of a Table Value Construction(TVC)
+
+ It contains a list of lists of values which this TVC is defined by and
+ reference on SELECT where this TVC is defined.
+*/
+class table_value_constr : public Sql_alloc
+{
+public:
+ List<List_item> lists_of_values;
+ select_result *result;
+ SELECT_LEX *select_lex;
+
+ enum { QEP_NOT_PRESENT_YET, QEP_AVAILABLE} have_query_plan;
+
+ Explain_select *explain;
+ ulonglong select_options;
+
+ table_value_constr(List<List_item> tvc_values, SELECT_LEX *sl,
+ ulonglong select_options_arg) :
+ lists_of_values(tvc_values), result(0), select_lex(sl),
+ have_query_plan(QEP_NOT_PRESENT_YET), explain(0),
+ select_options(select_options_arg)
+ { };
+
+ ha_rows get_records() { return lists_of_values.elements; }
+
+ bool prepare(THD *thd_arg, SELECT_LEX *sl,
+ select_result *tmp_result,
+ st_select_lex_unit *unit_arg);
+
+ bool to_be_wrapped_as_with_tail();
+
+ int save_explain_data_intern(THD *thd_arg,
+ Explain_query *output);
+ bool optimize(THD *thd_arg);
+ bool exec(SELECT_LEX *sl);
+
+ void print(THD *thd_arg, String *str, enum_query_type query_type);
+};
+
+st_select_lex *wrap_tvc_with_tail(THD *thd, st_select_lex *tvc_sl);
+
+#endif /* SQL_TVC_INCLUDED */
diff --git a/sql/sql_type.cc b/sql/sql_type.cc
index c1f192b0622..9d3a47adfa5 100644
--- a/sql/sql_type.cc
+++ b/sql/sql_type.cc
@@ -14,50 +14,309 @@
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_type.h"
#include "sql_const.h"
#include "sql_class.h"
+#include "sql_time.h"
#include "item.h"
#include "log.h"
-static Type_handler_tiny type_handler_tiny;
-static Type_handler_short type_handler_short;
-static Type_handler_long type_handler_long;
-static Type_handler_longlong type_handler_longlong;
-static Type_handler_int24 type_handler_int24;
-static Type_handler_year type_handler_year;
-static Type_handler_bit type_handler_bit;
-static Type_handler_float type_handler_float;
-static Type_handler_double type_handler_double;
-static Type_handler_time type_handler_time;
-static Type_handler_time2 type_handler_time2;
-static Type_handler_date type_handler_date;
-static Type_handler_newdate type_handler_newdate;
-static Type_handler_datetime type_handler_datetime;
-static Type_handler_datetime2 type_handler_datetime2;
-static Type_handler_timestamp type_handler_timestamp;
-static Type_handler_timestamp2 type_handler_timestamp2;
-static Type_handler_olddecimal type_handler_olddecimal;
-static Type_handler_newdecimal type_handler_newdecimal;
-static Type_handler_null type_handler_null;
-static Type_handler_string type_handler_string;
-static Type_handler_varchar type_handler_varchar;
-static Type_handler_tiny_blob type_handler_tiny_blob;
-static Type_handler_medium_blob type_handler_medium_blob;
-static Type_handler_long_blob type_handler_long_blob;
-static Type_handler_blob type_handler_blob;
+Type_handler_row type_handler_row;
+
+Type_handler_null type_handler_null;
+
+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_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;
+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;
+
+#ifdef HAVE_SPATIAL
+Type_handler_geometry type_handler_geometry;
+#endif
+
+
+Schema *Type_handler::schema() const
+{
+ return &mariadb_schema;
+}
+
+
+bool Type_handler_data::init()
+{
#ifdef HAVE_SPATIAL
-static Type_handler_geometry type_handler_geometry;
+
+#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;
#endif
-static Type_handler_enum type_handler_enum;
-static Type_handler_set type_handler_set;
+
+ 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_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;
+}
+
+
+Type_handler_data *type_handler_data= NULL;
+
+
+bool Float::to_string(String *val_buffer, uint dec) const
+{
+ uint to_length= 70;
+ if (val_buffer->alloc(to_length))
+ return true;
+
+ char *to=(char*) val_buffer->ptr();
+ size_t len;
+
+ if (dec >= FLOATING_POINT_DECIMALS)
+ len= my_gcvt(m_value, MY_GCVT_ARG_FLOAT, to_length - 1, to, NULL);
+ else
+ {
+ /*
+ We are safe here because the buffer length is 70, and
+ fabs(float) < 10^39, dec < FLOATING_POINT_DECIMALS. So the resulting string
+ will be not longer than 69 chars + terminating '\0'.
+ */
+ len= my_fcvt(m_value, (int) dec, to, NULL);
+ }
+ val_buffer->length((uint) len);
+ val_buffer->set_charset(&my_charset_numeric);
+ return false;
+}
+
+
+void Time::make_from_item(Item *item, const Options opt)
+{
+ if (item->get_date(this, opt.get_date_flags()))
+ time_type= MYSQL_TIMESTAMP_NONE;
+ else
+ valid_MYSQL_TIME_to_valid_value(opt);
+}
+
+
+void Temporal_with_date::make_from_item(THD *thd, Item *item, sql_mode_t flags)
+{
+ flags&= ~TIME_TIME_ONLY;
+ /*
+ Some TIME type items return error when trying to do get_date()
+ without TIME_TIME_ONLY set (e.g. Item_field for Field_time).
+ In the SQL standard time->datetime conversion mode we add TIME_TIME_ONLY.
+ In the legacy time->datetime conversion mode we do not add TIME_TIME_ONLY
+ and leave it to get_date() to check date.
+ */
+ ulonglong time_flag= (item->field_type() == MYSQL_TYPE_TIME &&
+ !(thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST)) ?
+ TIME_TIME_ONLY : 0;
+ if (item->get_date(this, flags | time_flag))
+ time_type= MYSQL_TIMESTAMP_NONE;
+ else if (time_type == MYSQL_TIMESTAMP_TIME)
+ {
+ MYSQL_TIME tmp;
+ if (time_to_datetime_with_warn(thd, this, &tmp, flags))
+ time_type= MYSQL_TIMESTAMP_NONE;
+ else
+ *(static_cast<MYSQL_TIME*>(this))= tmp;
+ }
+}
+
+
+uint Type_std_attributes::count_max_decimals(Item **item, uint nitems)
+{
+ uint res= 0;
+ for (uint i= 0; i < nitems; i++)
+ set_if_bigger(res, item[i]->decimals);
+ return res;
+}
+
+
+/**
+ 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)
+{
+ int max_int_part= 0;
+ decimals= 0;
+ unsigned_flag= 1;
+ 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);
+ }
+ 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));
+}
+
+
+/**
+ Set max_length of if it is maximum length of its arguments.
+*/
+
+void Type_std_attributes::count_only_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);
+}
+
+
+void Type_std_attributes::count_octet_length(Item **item, uint nitems)
+{
+ max_length= 0;
+ unsigned_flag= 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 max_length/decimals of function if function is floating point and
+ result length/precision depends on argument ones.
+*/
+
+void Type_std_attributes::count_real_length(Item **items, uint nitems)
+{
+ uint32 length= 0;
+ decimals= 0;
+ max_length= 0;
+ unsigned_flag= false;
+ for (uint i=0 ; i < nitems ; i++)
+ {
+ if (decimals < FLOATING_POINT_DECIMALS)
+ {
+ set_if_bigger(decimals, items[i]->decimals);
+ /* Will be ignored if items[i]->decimals >= FLOATING_POINT_DECIMALS */
+ set_if_bigger(length, (items[i]->max_length - items[i]->decimals));
+ }
+ set_if_bigger(max_length, items[i]->max_length);
+ }
+ if (decimals < FLOATING_POINT_DECIMALS)
+ {
+ max_length= length;
+ length+= decimals;
+ if (length < max_length) // If previous operation gave overflow
+ max_length= UINT_MAX32;
+ else
+ max_length= length;
+ }
+ // Corner case: COALESCE(DOUBLE(255,4), DOUBLE(255,3)) -> FLOAT(255, 4)
+ set_if_smaller(max_length, MAX_FIELD_CHARLENGTH);
+}
+
+
+/**
+ Calculate max_length and decimals for string functions.
+
+ @param field_type Field type.
+ @param items Argument array.
+ @param nitems Number of arguments.
+
+ @retval False on success, true on error.
+*/
+bool Type_std_attributes::count_string_length(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);
+ else
+ count_only_length(items, nitems);
+ decimals= max_length ? NOT_FIXED_DEC : 0;
+ return false;
+}
/**
This method is used by:
- Item_user_var_as_out_param::field_type()
- Item_func_udf_str::field_type()
- - Item_empty_string::make_field()
+ - Item_empty_string::make_send_field()
TODO: type_handler_adjusted_to_max_octet_length() and string_type_handler()
provide very similar functionality, to properly choose between
@@ -68,16 +327,48 @@ static Type_handler_set type_handler_set;
all around the code.
*/
const Type_handler *
-Type_handler::string_type_handler(uint max_octet_length) const
+Type_handler::string_type_handler(uint max_octet_length)
{
if (max_octet_length >= 16777216)
return &type_handler_long_blob;
else if (max_octet_length >= 65536)
return &type_handler_medium_blob;
+ else if (max_octet_length >= MAX_FIELD_VARCHARLENGTH)
+ return &type_handler_blob;
+ return &type_handler_varchar;
+}
+
+
+const Type_handler *
+Type_handler::varstring_type_handler(const Item *item)
+{
+ if (!item->max_length)
+ return &type_handler_string;
+ if (item->too_big_for_varchar())
+ return blob_type_handler(item->max_length);
return &type_handler_varchar;
}
+const Type_handler *
+Type_handler::blob_type_handler(uint max_octet_length)
+{
+ if (max_octet_length <= 255)
+ return &type_handler_tiny_blob;
+ if (max_octet_length <= 65535)
+ return &type_handler_blob;
+ if (max_octet_length <= 16777215)
+ return &type_handler_medium_blob;
+ return &type_handler_long_blob;
+}
+
+
+const Type_handler *
+Type_handler::blob_type_handler(const Item *item)
+{
+ return blob_type_handler(item->max_length);
+}
+
/**
This method is used by:
- Item_sum_hybrid, e.g. MAX(item), MIN(item).
@@ -98,19 +389,51 @@ Type_handler_string_result::type_handler_adjusted_to_max_octet_length(
}
+CHARSET_INFO *Type_handler::charset_for_protocol(const Item *item) const
+{
+ /*
+ For backward compatibility, to make numeric
+ data types return "binary" charset in client-side metadata.
+ */
+ return &my_charset_bin;
+}
+
+
+bool
+Type_handler::Item_func_or_sum_illegal_param(const char *funcname) const
+{
+ my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0),
+ name().ptr(), funcname);
+ return true;
+}
+
+
+bool
+Type_handler::Item_func_or_sum_illegal_param(const Item_func_or_sum *it) const
+{
+ return Item_func_or_sum_illegal_param(it->func_name());
+}
+
+
+CHARSET_INFO *
+Type_handler_string_result::charset_for_protocol(const Item *item) const
+{
+ return item->collation.collation;
+}
+
+
const Type_handler *
-Type_handler_hybrid_field_type::get_handler_by_result_type(Item_result type)
- const
+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 DECIMAL_RESULT: return &type_handler_newdecimal;
case STRING_RESULT: return &type_handler_long_blob;
- case TIME_RESULT:
- case ROW_RESULT:
- DBUG_ASSERT(0);
+ case TIME_RESULT: return &type_handler_datetime;
+ case ROW_RESULT: return &type_handler_row;
}
+ DBUG_ASSERT(0);
return &type_handler_string;
}
@@ -121,6 +444,516 @@ Type_handler_hybrid_field_type::Type_handler_hybrid_field_type()
}
+/***************************************************************************/
+
+/* number of bytes to store second_part part of the TIMESTAMP(N) */
+uint Type_handler_timestamp::m_sec_part_bytes[MAX_DATETIME_PRECISION + 1]=
+ { 0, 1, 1, 2, 2, 3, 3 };
+
+/* number of bytes to store DATETIME(N) */
+uint Type_handler_datetime::m_hires_bytes[MAX_DATETIME_PRECISION + 1]=
+ { 5, 6, 6, 7, 7, 7, 8 };
+
+/* number of bytes to store TIME(N) */
+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_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_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 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 Type_handler *Type_handler_null::type_handler_for_comparison() const
+{
+ return &type_handler_null;
+}
+
+
+const Type_handler *Type_handler_int_result::type_handler_for_comparison() const
+{
+ return &type_handler_longlong;
+}
+
+
+const Type_handler *Type_handler_string_result::type_handler_for_comparison() const
+{
+ return &type_handler_long_blob;
+}
+
+
+const Type_handler *Type_handler_decimal_result::type_handler_for_comparison() const
+{
+ return &type_handler_newdecimal;
+}
+
+
+const Type_handler *Type_handler_real_result::type_handler_for_comparison() const
+{
+ return &type_handler_double;
+}
+
+
+const Type_handler *Type_handler_time_common::type_handler_for_comparison() const
+{
+ return &type_handler_time;
+}
+
+const Type_handler *Type_handler_date_common::type_handler_for_comparison() const
+{
+ return &type_handler_newdate;
+}
+
+
+const Type_handler *Type_handler_datetime_common::type_handler_for_comparison() const
+{
+ return &type_handler_datetime;
+}
+
+
+const Type_handler *Type_handler_timestamp_common::type_handler_for_comparison() const
+{
+ return &type_handler_datetime;
+}
+
+
+const Type_handler *Type_handler_row::type_handler_for_comparison() const
+{
+ return &type_handler_row;
+}
+
+/***************************************************************************/
+
+const Type_handler *Type_handler_typelib::type_handler_for_item_field() const
+{
+ return &type_handler_string;
+}
+
+
+const Type_handler *Type_handler_typelib::cast_to_int_type_handler() const
+{
+ return &type_handler_longlong;
+}
+
+
+/***************************************************************************/
+
+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)
+ return true;
+ m_type_handler= other;
+ return false;
+}
+
+
+const Type_handler *
+Type_handler::type_handler_long_or_longlong(uint max_char_length)
+{
+ if (max_char_length <= MY_INT32_NUM_DECIMAL_DIGITS - 2)
+ return &type_handler_long;
+ return &type_handler_longlong;
+}
+
+/*
+ This method is called for CASE (and its abbreviations) and LEAST/GREATEST
+ when data type aggregation returned LONGLONG and there were some BIT
+ expressions. This helps to adjust the data type from LONGLONG to LONG
+ if all expressions fit.
+*/
+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;
+}
+
+
+/**
+ @brief Aggregates field types from the array of items.
+
+ @param[in] items array of items to aggregate the type from
+ @param[in] nitems number of items in the array
+ @param[in] treat_bit_as_number - if BIT should be aggregated to a non-BIT
+ counterpart as a LONGLONG number or as a VARBINARY string.
+
+ Currently behaviour depends on the function:
+ - LEAST/GREATEST treat BIT as VARBINARY when
+ aggregating with a non-BIT counterpart.
+ Note, UNION also works this way.
+
+ - CASE, COALESCE, IF, IFNULL treat BIT as LONGLONG when
+ aggregating with a non-BIT counterpart;
+
+ This inconsistency may be changed in the future. See MDEV-8867.
+
+ Note, independently from "treat_bit_as_number":
+ - a single BIT argument gives BIT as a result
+ - two BIT couterparts give BIT as a result
+
+ @details This function aggregates field types from the array of items.
+ Found type is supposed to be used later as the result field type
+ of a multi-argument function.
+ Aggregation itself is performed by Type_handler::aggregate_for_result().
+
+ @note The term "aggregation" is used here in the sense of inferring the
+ result type of a function from its argument types.
+
+ @retval false - on success
+ @retval true - on error
+*/
+
+bool
+Type_handler_hybrid_field_type::aggregate_for_result(const char *funcname,
+ Item **items, uint nitems,
+ bool treat_bit_as_number)
+{
+ bool bit_and_non_bit_mixture_found= false;
+ uint32 max_display_length;
+ if (!nitems || items[0]->result_type() == ROW_RESULT)
+ {
+ DBUG_ASSERT(0);
+ set_handler(&type_handler_null);
+ return true;
+ }
+ 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());
+ if (treat_bit_as_number &&
+ ((type_handler() == &type_handler_bit) ^ (cur == &type_handler_bit)))
+ {
+ bit_and_non_bit_mixture_found= true;
+ if (type_handler() == &type_handler_bit)
+ set_handler(&type_handler_longlong); // BIT + non-BIT
+ else
+ cur= &type_handler_longlong; // non-BIT + BIT
+ }
+ if (aggregate_for_result(cur))
+ {
+ my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0),
+ type_handler()->name().ptr(), cur->name().ptr(), funcname);
+ return true;
+ }
+ }
+ if (bit_and_non_bit_mixture_found && type_handler() == &type_handler_longlong)
+ set_handler(Type_handler::bit_and_int_mixture_handler(max_display_length));
+ return false;
+}
+
+/**
+ Collect built-in data type handlers for comparison.
+ This method is very similar to item_cmp_type() defined in item.cc.
+ Now they coexist. Later item_cmp_type() will be removed.
+ In addition to item_cmp_type(), this method correctly aggregates
+ TIME with DATETIME/TIMESTAMP/DATE, so no additional find_date_time_item()
+ is needed after this call.
+*/
+
+bool
+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());
+
+ 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();
+ 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)
+ {
+ 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.
+ */
+ 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.
+ - If both data types are TIME, we preserve TIME.
+ - If both data types are DATE, we preserve DATE.
+ Preserving DATE is needed for EXPLAIN FORMAT=JSON,
+ 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;
+ }
+ }
+ 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;
+}
+
+
+/**
+ Aggregate data type handler for LEAST/GRATEST.
+ aggregate_for_min_max() is close to aggregate_for_comparison(),
+ but tries to preserve the exact type handler for string, int and temporal
+ data types (instead of converting to super-types).
+ FLOAT is not preserved and is converted to its super-type (DOUBLE).
+ This should probably fixed eventually, for symmetry.
+*/
+
+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())
+ {
+ /*
+ 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)
+ return the same data type (or both expressions return error)
+ 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;
+ }
+
+ Item_result a= cmp_type();
+ Item_result b= h->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)
+ {
+ // BIT aggregates with non-BIT as BIGINT
+ if (m_type_handler != h)
+ {
+ if (m_type_handler == &type_handler_bit)
+ m_type_handler= &type_handler_longlong;
+ else if (h == &type_handler_bit)
+ h= &type_handler_longlong;
+ }
+ m_type_handler=
+ Type_handler::aggregate_for_result_traditional(m_type_handler, h);
+ }
+ else 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.
+ */
+ 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);
+ }
+ }
+ else 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 false;
+}
+
+
+bool
+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);
+ if (aggregate_for_min_max(cur))
+ {
+ my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0),
+ type_handler()->name().ptr(), cur->name().ptr(), funcname);
+ return true;
+ }
+ }
+ if (bit_and_non_bit_mixture_found && type_handler() == &type_handler_longlong)
+ 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)
+{
+ Item_result r0= h0->cmp_type();
+ Item_result r1= h1->cmp_type();
+
+ if (r0 == REAL_RESULT || r1 == REAL_RESULT ||
+ r0 == STRING_RESULT || r1 ==STRING_RESULT)
+ return &type_handler_double;
+
+ if (r0 == TIME_RESULT || r1 == TIME_RESULT)
+ return &type_handler_datetime;
+
+ if (r0 == DECIMAL_RESULT || r1 == DECIMAL_RESULT)
+ return &type_handler_newdecimal;
+
+ DBUG_ASSERT(r0 == INT_RESULT && r1 == INT_RESULT);
+ return &type_handler_longlong;
+}
+
+
+const Type_aggregator::Pair*
+Type_aggregator::find_pair(const Type_handler *handler1,
+ const Type_handler *handler2) const
+{
+ for (uint i= 0; i < m_array.elements(); i++)
+ {
+ const Pair& el= m_array.at(i);
+ if (el.eq(handler1, handler2) ||
+ (m_is_commutative && el.eq(handler2, handler1)))
+ return &el;
+ }
+ return NULL;
+}
+
+
+bool
+Type_handler_hybrid_field_type::aggregate_for_num_op(const Type_aggregator *agg,
+ const Type_handler *h0,
+ 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_handler *
Type_handler::get_handler_by_field_type(enum_field_types type)
{
@@ -167,6 +1000,9 @@ Type_handler::get_handler_by_field_type(enum_field_types type)
in field_type() context and add DBUG_ASSERT(0) here.
*/
return &type_handler_newdate;
+ case MYSQL_TYPE_VARCHAR_COMPRESSED:
+ case MYSQL_TYPE_BLOB_COMPRESSED:
+ break;
};
DBUG_ASSERT(0);
return &type_handler_string;
@@ -190,10 +1026,12 @@ Type_handler::get_handler_by_real_type(enum_field_types type)
case MYSQL_TYPE_DOUBLE: return &type_handler_double;
case MYSQL_TYPE_NULL: return &type_handler_null;
case MYSQL_TYPE_VARCHAR: return &type_handler_varchar;
+ case MYSQL_TYPE_VARCHAR_COMPRESSED: return &type_handler_varchar_compressed;
case MYSQL_TYPE_TINY_BLOB: return &type_handler_tiny_blob;
case MYSQL_TYPE_MEDIUM_BLOB: return &type_handler_medium_blob;
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(),
@@ -235,7 +1073,8 @@ Type_handler::make_num_distinct_aggregator_field(MEM_ROOT *mem_root,
Field_double(NULL, item->max_length,
(uchar *) (item->maybe_null ? "" : 0),
item->maybe_null ? 1 : 0, Field::NONE,
- item->name, item->decimals, 0, item->unsigned_flag);
+ &item->name, (uint8) item->decimals,
+ 0, item->unsigned_flag);
}
@@ -248,7 +1087,8 @@ Type_handler_float::make_num_distinct_aggregator_field(MEM_ROOT *mem_root,
Field_float(NULL, item->max_length,
(uchar *) (item->maybe_null ? "" : 0),
item->maybe_null ? 1 : 0, Field::NONE,
- item->name, item->decimals, 0, item->unsigned_flag);
+ &item->name, (uint8) item->decimals,
+ 0, item->unsigned_flag);
}
@@ -263,7 +1103,8 @@ Type_handler_decimal_result::make_num_distinct_aggregator_field(
Field_new_decimal(NULL, item->max_length,
(uchar *) (item->maybe_null ? "" : 0),
item->maybe_null ? 1 : 0, Field::NONE,
- item->name, item->decimals, 0, item->unsigned_flag);
+ &item->name, (uint8) item->decimals,
+ 0, item->unsigned_flag);
}
@@ -280,14 +1121,12 @@ Type_handler_int_result::make_num_distinct_aggregator_field(MEM_ROOT *mem_root,
Field_longlong(NULL, item->max_length,
(uchar *) (item->maybe_null ? "" : 0),
item->maybe_null ? 1 : 0, Field::NONE,
- item->name, 0, item->unsigned_flag);
+ &item->name, 0, item->unsigned_flag);
}
/***********************************************************************/
-#define TMPNAME ""
-
Field *Type_handler_tiny::make_conversion_table_field(TABLE *table,
uint metadata,
const Field *target)
@@ -301,7 +1140,7 @@ Field *Type_handler_tiny::make_conversion_table_field(TABLE *table,
bool unsigned_flag= ((Field_num*) target)->unsigned_flag;
return new (table->in_use->mem_root)
Field_tiny(NULL, 4 /*max_length*/, (uchar *) "", 1, Field::NONE,
- TMPNAME, 0/*zerofill*/, unsigned_flag);
+ &empty_clex_str, 0/*zerofill*/, unsigned_flag);
}
@@ -313,7 +1152,7 @@ Field *Type_handler_short::make_conversion_table_field(TABLE *table,
bool unsigned_flag= ((Field_num*) target)->unsigned_flag;
return new (table->in_use->mem_root)
Field_short(NULL, 6 /*max_length*/, (uchar *) "", 1, Field::NONE,
- TMPNAME, 0/*zerofill*/, unsigned_flag);
+ &empty_clex_str, 0/*zerofill*/, unsigned_flag);
}
@@ -325,7 +1164,7 @@ Field *Type_handler_int24::make_conversion_table_field(TABLE *table,
bool unsigned_flag= ((Field_num*) target)->unsigned_flag;
return new (table->in_use->mem_root)
Field_medium(NULL, 9 /*max_length*/, (uchar *) "", 1, Field::NONE,
- TMPNAME, 0/*zerofill*/, unsigned_flag);
+ &empty_clex_str, 0/*zerofill*/, unsigned_flag);
}
@@ -337,7 +1176,7 @@ Field *Type_handler_long::make_conversion_table_field(TABLE *table,
bool unsigned_flag= ((Field_num*) target)->unsigned_flag;
return new (table->in_use->mem_root)
Field_long(NULL, 11 /*max_length*/, (uchar *) "", 1, Field::NONE,
- TMPNAME, 0/*zerofill*/, unsigned_flag);
+ &empty_clex_str, 0/*zerofill*/, unsigned_flag);
}
@@ -349,7 +1188,7 @@ Field *Type_handler_longlong::make_conversion_table_field(TABLE *table,
bool unsigned_flag= ((Field_num*) target)->unsigned_flag;
return new (table->in_use->mem_root)
Field_longlong(NULL, 20 /*max_length*/,(uchar *) "", 1, Field::NONE,
- TMPNAME, 0/*zerofill*/, unsigned_flag);
+ &empty_clex_str, 0/*zerofill*/, unsigned_flag);
}
@@ -361,7 +1200,7 @@ Field *Type_handler_float::make_conversion_table_field(TABLE *table,
{
return new (table->in_use->mem_root)
Field_float(NULL, 12 /*max_length*/, (uchar *) "", 1, Field::NONE,
- TMPNAME, 0/*dec*/, 0/*zerofill*/, 0/*unsigned_flag*/);
+ &empty_clex_str, 0/*dec*/, 0/*zerofill*/, 0/*unsigned_flag*/);
}
@@ -372,7 +1211,7 @@ Field *Type_handler_double::make_conversion_table_field(TABLE *table,
{
return new (table->in_use->mem_root)
Field_double(NULL, 22 /*max_length*/, (uchar *) "", 1, Field::NONE,
- TMPNAME, 0/*dec*/, 0/*zerofill*/, 0/*unsigned_flag*/);
+ &empty_clex_str, 0/*dec*/, 0/*zerofill*/, 0/*unsigned_flag*/);
}
@@ -382,12 +1221,12 @@ Field *Type_handler_newdecimal::make_conversion_table_field(TABLE *table,
const
{
int precision= metadata >> 8;
- uint decimals= metadata & 0x00ff;
+ 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)
Field_new_decimal(NULL, max_length, (uchar *) "", 1, Field::NONE,
- TMPNAME, decimals, 0/*zerofill*/, 0/*unsigned*/);
+ &empty_clex_str, decimals, 0/*zerofill*/, 0/*unsigned*/);
}
@@ -403,7 +1242,7 @@ Field *Type_handler_olddecimal::make_conversion_table_field(TABLE *table,
" column Name: %s.%s.%s.",
target->table->s->db.str,
target->table->s->table_name.str,
- target->field_name);
+ target->field_name.str);
return NULL;
}
@@ -414,7 +1253,7 @@ Field *Type_handler_year::make_conversion_table_field(TABLE *table,
const
{
return new(table->in_use->mem_root)
- Field_year(NULL, 4, (uchar *) "", 1, Field::NONE, TMPNAME);
+ Field_year(NULL, 4, (uchar *) "", 1, Field::NONE, &empty_clex_str);
}
@@ -424,7 +1263,7 @@ Field *Type_handler_null::make_conversion_table_field(TABLE *table,
const
{
return new(table->in_use->mem_root)
- Field_null(NULL, 0, Field::NONE, TMPNAME, target->charset());
+ Field_null(NULL, 0, Field::NONE, &empty_clex_str, target->charset());
}
@@ -434,7 +1273,7 @@ Field *Type_handler_timestamp::make_conversion_table_field(TABLE *table,
const
{
return new_Field_timestamp(table->in_use->mem_root, NULL, (uchar *) "", 1,
- Field::NONE, TMPNAME, table->s, target->decimals());
+ Field::NONE, &empty_clex_str, table->s, target->decimals());
}
@@ -445,7 +1284,7 @@ Field *Type_handler_timestamp2::make_conversion_table_field(TABLE *table,
{
return new(table->in_use->mem_root)
Field_timestampf(NULL, (uchar *) "", 1, Field::NONE,
- TMPNAME, table->s, metadata);
+ &empty_clex_str, table->s, metadata);
}
@@ -455,7 +1294,7 @@ Field *Type_handler_newdate::make_conversion_table_field(TABLE *table,
const
{
return new(table->in_use->mem_root)
- Field_newdate(NULL, (uchar *) "", 1, Field::NONE, TMPNAME);
+ Field_newdate(NULL, (uchar *) "", 1, Field::NONE, &empty_clex_str);
}
@@ -465,7 +1304,7 @@ Field *Type_handler_date::make_conversion_table_field(TABLE *table,
const
{
return new(table->in_use->mem_root)
- Field_date(NULL, (uchar *) "", 1, Field::NONE, TMPNAME);
+ Field_date(NULL, (uchar *) "", 1, Field::NONE, &empty_clex_str);
}
@@ -475,7 +1314,7 @@ Field *Type_handler_time::make_conversion_table_field(TABLE *table,
const
{
return new_Field_time(table->in_use->mem_root, NULL, (uchar *) "", 1,
- Field::NONE, TMPNAME, target->decimals());
+ Field::NONE, &empty_clex_str, target->decimals());
}
@@ -485,7 +1324,7 @@ Field *Type_handler_time2::make_conversion_table_field(TABLE *table,
const
{
return new(table->in_use->mem_root)
- Field_timef(NULL, (uchar *) "", 1, Field::NONE, TMPNAME, metadata);
+ Field_timef(NULL, (uchar *) "", 1, Field::NONE, &empty_clex_str, metadata);
}
@@ -495,7 +1334,7 @@ Field *Type_handler_datetime::make_conversion_table_field(TABLE *table,
const
{
return new_Field_datetime(table->in_use->mem_root, NULL, (uchar *) "", 1,
- Field::NONE, TMPNAME, target->decimals());
+ Field::NONE, &empty_clex_str, target->decimals());
}
@@ -506,7 +1345,7 @@ Field *Type_handler_datetime2::make_conversion_table_field(TABLE *table,
{
return new(table->in_use->mem_root)
Field_datetimef(NULL, (uchar *) "", 1,
- Field::NONE, TMPNAME, metadata);
+ Field::NONE, &empty_clex_str, metadata);
}
@@ -519,7 +1358,7 @@ Field *Type_handler_bit::make_conversion_table_field(TABLE *table,
uint32 max_length= 8 * (metadata >> 8U) + (metadata & 0x00ff);
return new(table->in_use->mem_root)
Field_bit_as_char(NULL, max_length, (uchar *) "", 1,
- Field::NONE, TMPNAME);
+ Field::NONE, &empty_clex_str);
}
@@ -532,7 +1371,7 @@ Field *Type_handler_string::make_conversion_table_field(TABLE *table,
uint32 max_length= (((metadata >> 4) & 0x300) ^ 0x300) + (metadata & 0x00ff);
return new(table->in_use->mem_root)
Field_string(NULL, max_length, (uchar *) "", 1,
- Field::NONE, TMPNAME, target->charset());
+ Field::NONE, &empty_clex_str, target->charset());
}
@@ -541,28 +1380,56 @@ Field *Type_handler_varchar::make_conversion_table_field(TABLE *table,
const Field *target)
const
{
+ DBUG_ASSERT(HA_VARCHAR_PACKLENGTH(metadata) <= MAX_FIELD_VARCHARLENGTH);
return new(table->in_use->mem_root)
Field_varstring(NULL, metadata, HA_VARCHAR_PACKLENGTH(metadata),
- (uchar *) "", 1, Field::NONE, TMPNAME,
+ (uchar *) "", 1, Field::NONE, &empty_clex_str,
table->s, target->charset());
}
-Field *Type_handler_blob_common::make_conversion_table_field(TABLE *table,
- uint metadata,
- const Field *target)
- const
+Field *Type_handler_varchar_compressed::make_conversion_table_field(TABLE *table,
+ uint metadata,
+ const Field *target)
+ const
+{
+ return new(table->in_use->mem_root)
+ Field_varstring_compressed(NULL, metadata,
+ HA_VARCHAR_PACKLENGTH(metadata),
+ (uchar *) "", 1, Field::NONE,
+ &empty_clex_str,
+ table->s, target->charset(),
+ zlib_compression_method);
+}
+
+
+
+Field *Type_handler_blob_compressed::make_conversion_table_field(TABLE *table,
+ uint metadata,
+ const Field *target)
+ const
{
uint pack_length= metadata & 0x00ff;
if (pack_length < 1 || pack_length > 4)
return NULL; // Broken binary log?
return new(table->in_use->mem_root)
- Field_blob(NULL, (uchar *) "", 1, Field::NONE, TMPNAME,
- table->s, pack_length, target->charset());
+ Field_blob_compressed(NULL, (uchar *) "", 1, Field::NONE,
+ &empty_clex_str,
+ table->s, pack_length, target->charset(),
+ zlib_compression_method);
}
#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)
@@ -576,7 +1443,7 @@ Field *Type_handler_geometry::make_conversion_table_field(TABLE *table,
The statistics was already incremented when "target" was created.
*/
return new(table->in_use->mem_root)
- Field_geom(NULL, (uchar *) "", 1, Field::NONE, TMPNAME, table->s, 4,
+ Field_geom(NULL, (uchar *) "", 1, Field::NONE, &empty_clex_str, table->s, 4,
((const Field_geom*) target)->geom_type,
((const Field_geom*) target)->srid);
}
@@ -591,7 +1458,7 @@ Field *Type_handler_enum::make_conversion_table_field(TABLE *table,
DBUG_ASSERT(target->real_type() == MYSQL_TYPE_ENUM);
return new(table->in_use->mem_root)
Field_enum(NULL, target->field_length,
- (uchar *) "", 1, Field::NONE, TMPNAME,
+ (uchar *) "", 1, Field::NONE, &empty_clex_str,
metadata & 0x00ff/*pack_length()*/,
((const Field_enum*) target)->typelib, target->charset());
}
@@ -606,7 +1473,4500 @@ Field *Type_handler_set::make_conversion_table_field(TABLE *table,
DBUG_ASSERT(target->real_type() == MYSQL_TYPE_SET);
return new(table->in_use->mem_root)
Field_set(NULL, target->field_length,
- (uchar *) "", 1, Field::NONE, TMPNAME,
+ (uchar *) "", 1, Field::NONE, &empty_clex_str,
metadata & 0x00ff/*pack_length()*/,
((const Field_enum*) target)->typelib, target->charset());
}
+
+/*************************************************************************/
+bool Type_handler_null::
+ Column_definition_fix_attributes(Column_definition *def) const
+{
+ return false;
+}
+
+bool Type_handler_tiny::
+ Column_definition_fix_attributes(Column_definition *def) const
+{
+ return def->fix_attributes_int(MAX_TINYINT_WIDTH + def->sign_length());
+}
+
+bool Type_handler_short::
+ Column_definition_fix_attributes(Column_definition *def) const
+{
+ return def->fix_attributes_int(MAX_SMALLINT_WIDTH + def->sign_length());
+}
+
+bool Type_handler_int24::
+ Column_definition_fix_attributes(Column_definition *def) const
+{
+ return def->fix_attributes_int(MAX_MEDIUMINT_WIDTH + def->sign_length());
+}
+
+bool Type_handler_long::
+ Column_definition_fix_attributes(Column_definition *def) const
+{
+ return def->fix_attributes_int(MAX_INT_WIDTH + def->sign_length());
+}
+
+bool Type_handler_longlong::
+ Column_definition_fix_attributes(Column_definition *def) const
+{
+ return def->fix_attributes_int(MAX_BIGINT_WIDTH/*no sign_length() added*/);
+}
+
+bool Type_handler_newdecimal::
+ Column_definition_fix_attributes(Column_definition *def) const
+{
+ return def->fix_attributes_decimal();
+}
+
+bool Type_handler_olddecimal::
+ Column_definition_fix_attributes(Column_definition *def) const
+{
+ DBUG_ASSERT(0); // Obsolete
+ return true;
+}
+
+bool Type_handler_var_string::
+ Column_definition_fix_attributes(Column_definition *def) const
+{
+ DBUG_ASSERT(0); // Obsolete
+ return true;
+}
+
+bool Type_handler_varchar::
+ Column_definition_fix_attributes(Column_definition *def) const
+{
+ /*
+ Long VARCHAR's are automaticly converted to blobs in mysql_prepare_table
+ if they don't have a default value
+ */
+ return def->check_length(ER_TOO_BIG_DISPLAYWIDTH, MAX_FIELD_BLOBLENGTH);
+}
+
+bool Type_handler_string::
+ Column_definition_fix_attributes(Column_definition *def) const
+{
+ return def->check_length(ER_TOO_BIG_FIELDLENGTH, MAX_FIELD_CHARLENGTH);
+}
+
+bool Type_handler_blob_common::
+ Column_definition_fix_attributes(Column_definition *def) const
+{
+ def->flags|= BLOB_FLAG;
+ 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
+{
+ if (!def->length || def->length != 2)
+ def->length= 4; // Default length
+ def->flags|= ZEROFILL_FLAG | UNSIGNED_FLAG;
+ return false;
+}
+
+bool Type_handler_float::
+ Column_definition_fix_attributes(Column_definition *def) const
+{
+ return def->fix_attributes_real(MAX_FLOAT_STR_LENGTH);
+}
+
+
+bool Type_handler_double::
+ Column_definition_fix_attributes(Column_definition *def) const
+{
+ return def->fix_attributes_real(DBL_DIG + 7);
+}
+
+bool Type_handler_timestamp_common::
+ Column_definition_fix_attributes(Column_definition *def) const
+{
+ def->flags|= UNSIGNED_FLAG;
+ return def->fix_attributes_temporal_with_time(MAX_DATETIME_WIDTH);
+}
+
+bool Type_handler_date_common::
+ Column_definition_fix_attributes(Column_definition *def) const
+{
+ // We don't support creation of MYSQL_TYPE_DATE anymore
+ def->set_handler(&type_handler_newdate);
+ def->length= MAX_DATE_WIDTH;
+ return false;
+}
+
+bool Type_handler_time_common::
+ Column_definition_fix_attributes(Column_definition *def) const
+{
+ return def->fix_attributes_temporal_with_time(MIN_TIME_WIDTH);
+}
+
+bool Type_handler_datetime_common::
+ Column_definition_fix_attributes(Column_definition *def) const
+{
+ return def->fix_attributes_temporal_with_time(MAX_DATETIME_WIDTH);
+}
+
+bool Type_handler_set::
+ Column_definition_fix_attributes(Column_definition *def) const
+{
+ def->pack_length= get_set_pack_length(def->interval_list.elements);
+ return false;
+}
+
+bool Type_handler_enum::
+ Column_definition_fix_attributes(Column_definition *def) const
+{
+ def->pack_length= get_enum_pack_length(def->interval_list.elements);
+ return false;
+}
+
+bool Type_handler_bit::
+ Column_definition_fix_attributes(Column_definition *def) const
+{
+ return def->fix_attributes_bit();
+}
+
+/*************************************************************************/
+
+bool Type_handler::
+ 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_simple();
+ return false;
+}
+
+bool Type_handler_null::
+ 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_null();
+ return false;
+}
+
+bool Type_handler_row::
+ 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_null();
+ return false;
+}
+
+bool Type_handler_newdecimal::
+ 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_newdecimal();
+ return false;
+}
+
+bool Type_handler_bit::
+ Column_definition_prepare_stage1(THD *thd,
+ MEM_ROOT *mem_root,
+ Column_definition *def,
+ handler *file,
+ ulonglong table_flags) const
+{
+ return def->prepare_stage1_bit(thd, mem_root, file, table_flags);
+}
+
+bool Type_handler_typelib::
+ Column_definition_prepare_stage1(THD *thd,
+ MEM_ROOT *mem_root,
+ Column_definition *def,
+ handler *file,
+ ulonglong table_flags) const
+{
+ return def->prepare_stage1_typelib(thd, mem_root, file, table_flags);
+}
+
+
+bool Type_handler_string_result::
+ Column_definition_prepare_stage1(THD *thd,
+ MEM_ROOT *mem_root,
+ Column_definition *def,
+ handler *file,
+ ulonglong table_flags) const
+{
+ return def->prepare_stage1_string(thd, mem_root, file, table_flags);
+}
+
+
+#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::
+ Column_definition_redefine_stage1(Column_definition *def,
+ const Column_definition *dup,
+ const handler *file,
+ const Schema_specification_st *schema)
+ const
+{
+ def->redefine_stage1_common(dup, file, schema);
+ def->create_length_to_internal_length_simple();
+ return false;
+}
+
+
+bool Type_handler_null::
+ Column_definition_redefine_stage1(Column_definition *def,
+ const Column_definition *dup,
+ const handler *file,
+ const Schema_specification_st *schema)
+ const
+{
+ def->redefine_stage1_common(dup, file, schema);
+ def->create_length_to_internal_length_null();
+ return false;
+}
+
+
+bool Type_handler_newdecimal::
+ Column_definition_redefine_stage1(Column_definition *def,
+ const Column_definition *dup,
+ const handler *file,
+ const Schema_specification_st *schema)
+ const
+{
+ def->redefine_stage1_common(dup, file, schema);
+ def->create_length_to_internal_length_newdecimal();
+ return false;
+}
+
+
+bool Type_handler_string_result::
+ Column_definition_redefine_stage1(Column_definition *def,
+ const Column_definition *dup,
+ const handler *file,
+ const Schema_specification_st *schema)
+ const
+{
+ def->redefine_stage1_common(dup, file, schema);
+ def->set_compression_method(dup->compression_method());
+ def->create_length_to_internal_length_string();
+ return false;
+}
+
+
+bool Type_handler_typelib::
+ Column_definition_redefine_stage1(Column_definition *def,
+ const Column_definition *dup,
+ const handler *file,
+ const Schema_specification_st *schema)
+ const
+{
+ def->redefine_stage1_common(dup, file, schema);
+ def->create_length_to_internal_length_typelib();
+ return false;
+}
+
+
+bool Type_handler_bit::
+ Column_definition_redefine_stage1(Column_definition *def,
+ const Column_definition *dup,
+ const handler *file,
+ const Schema_specification_st *schema)
+ const
+{
+ def->redefine_stage1_common(dup, file, schema);
+ /*
+ If we are replacing a field with a BIT field, we need
+ to initialize pack_flag.
+ */
+ def->pack_flag= FIELDFLAG_NUMBER;
+ if (!(file->ha_table_flags() & HA_CAN_BIT_FIELD))
+ def->pack_flag|= FIELDFLAG_TREAT_BIT_AS_CHAR;
+ def->create_length_to_internal_length_bit();
+ return false;
+}
+
+
+/*************************************************************************/
+
+bool Type_handler::
+ Column_definition_prepare_stage2_legacy(Column_definition *def,
+ enum_field_types type) const
+{
+ def->pack_flag= f_settype((uint) type);
+ return false;
+}
+
+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);
+ return false;
+}
+
+bool Type_handler::
+ Column_definition_prepare_stage2_legacy_real(Column_definition *def,
+ enum_field_types type) const
+{
+ uint dec= def->decimals;
+ /*
+ User specified FLOAT() or DOUBLE() without precision. Change to
+ FLOATING_POINT_DECIMALS to keep things compatible with earlier MariaDB
+ versions.
+ */
+ if (dec >= FLOATING_POINT_DECIMALS)
+ dec= FLOATING_POINT_DECIMALS;
+ def->pack_flag= def->pack_flag_numeric(dec) | f_settype((uint) type);
+ return false;
+}
+
+bool Type_handler_newdecimal::
+ Column_definition_prepare_stage2(Column_definition *def,
+ handler *file,
+ ulonglong table_flags) const
+{
+ def->pack_flag= def->pack_flag_numeric(def->decimals);
+ return false;
+}
+
+bool Type_handler_blob_common::
+ Column_definition_prepare_stage2(Column_definition *def,
+ handler *file,
+ ulonglong table_flags) const
+{
+ 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,
+ ulonglong table_flags) const
+{
+ return def->prepare_stage2_varchar(table_flags);
+}
+
+bool Type_handler_string::
+ Column_definition_prepare_stage2(Column_definition *def,
+ handler *file,
+ ulonglong table_flags) const
+{
+ def->pack_flag= (def->charset->state & MY_CS_BINSORT) ? FIELDFLAG_BINARY : 0;
+ return false;
+}
+
+bool Type_handler_enum::
+ Column_definition_prepare_stage2(Column_definition *def,
+ handler *file,
+ ulonglong table_flags) const
+{
+ uint dummy;
+ return def->prepare_stage2_typelib("ENUM", FIELDFLAG_INTERVAL, &dummy);
+}
+
+bool Type_handler_set::
+ Column_definition_prepare_stage2(Column_definition *def,
+ handler *file,
+ ulonglong table_flags) const
+{
+ uint dup_count;
+ if (def->prepare_stage2_typelib("SET", FIELDFLAG_BITFIELD, &dup_count))
+ return true;
+ /* Check that count of unique members is not more then 64 */
+ if (def->interval->count - dup_count > sizeof(longlong)*8)
+ {
+ my_error(ER_TOO_BIG_SET, MYF(0), def->field_name.str);
+ return true;
+ }
+ return false;
+}
+
+bool Type_handler_bit::
+ Column_definition_prepare_stage2(Column_definition *def,
+ handler *file,
+ ulonglong table_flags) const
+{
+ /*
+ We have sql_field->pack_flag already set here, see
+ mysql_prepare_create_table().
+ */
+ return false;
+}
+
+/*************************************************************************/
+
+uint32 Type_handler_time::calc_pack_length(uint32 length) const
+{
+ return length > MIN_TIME_WIDTH ?
+ hires_bytes(length - 1 - MIN_TIME_WIDTH) : 3;
+}
+
+uint32 Type_handler_time2::calc_pack_length(uint32 length) const
+{
+ return length > MIN_TIME_WIDTH ?
+ my_time_binary_length(length - MIN_TIME_WIDTH - 1) : 3;
+}
+
+uint32 Type_handler_timestamp::calc_pack_length(uint32 length) const
+{
+ return length > MAX_DATETIME_WIDTH ?
+ 4 + sec_part_bytes(length - 1 - MAX_DATETIME_WIDTH) : 4;
+}
+
+uint32 Type_handler_timestamp2::calc_pack_length(uint32 length) const
+{
+ return length > MAX_DATETIME_WIDTH ?
+ my_timestamp_binary_length(length - MAX_DATETIME_WIDTH - 1) : 4;
+}
+
+uint32 Type_handler_datetime::calc_pack_length(uint32 length) const
+{
+ return length > MAX_DATETIME_WIDTH ?
+ hires_bytes(length - 1 - MAX_DATETIME_WIDTH) : 8;
+}
+
+uint32 Type_handler_datetime2::calc_pack_length(uint32 length) const
+{
+ return length > MAX_DATETIME_WIDTH ?
+ my_datetime_binary_length(length - MAX_DATETIME_WIDTH - 1) : 5;
+}
+
+uint32 Type_handler_tiny_blob::calc_pack_length(uint32 length) const
+{
+ return 1 + portable_sizeof_char_ptr;
+}
+
+uint32 Type_handler_blob::calc_pack_length(uint32 length) const
+{
+ return 2 + portable_sizeof_char_ptr;
+}
+
+uint32 Type_handler_medium_blob::calc_pack_length(uint32 length) const
+{
+ return 3 + portable_sizeof_char_ptr;
+}
+
+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
+ return 0;
+}
+
+uint32 Type_handler_set::calc_pack_length(uint32 length) const
+{
+ abort(); // This shouldn't happen
+ return 0;
+}
+
+uint32 Type_handler_enum::calc_pack_length(uint32 length) const
+{
+ abort(); // This shouldn't happen
+ return 0;
+}
+
+
+/*************************************************************************/
+Field *Type_handler::make_and_init_table_field(const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE *table) const
+{
+ Field *field= make_table_field(name, addr, attr, table);
+ if (field)
+ field->init(table);
+ return field;
+}
+
+
+Field *Type_handler_tiny::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_tiny(addr.ptr, attr.max_char_length(),
+ addr.null_ptr, addr.null_bit,
+ Field::NONE, name, 0/*zerofill*/, attr.unsigned_flag);
+}
+
+
+Field *Type_handler_short::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_short(addr.ptr, attr.max_char_length(),
+ addr.null_ptr, addr.null_bit,
+ Field::NONE, name, 0/*zerofill*/, attr.unsigned_flag);
+}
+
+
+Field *Type_handler_int24::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_medium(addr.ptr, attr.max_char_length(),
+ addr.null_ptr, addr.null_bit,
+ Field::NONE, name,
+ 0/*zerofill*/, attr.unsigned_flag);
+}
+
+
+Field *Type_handler_long::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_long(addr.ptr, attr.max_char_length(),
+ addr.null_ptr, addr.null_bit,
+ Field::NONE, name, 0/*zerofill*/, attr.unsigned_flag);
+}
+
+
+Field *Type_handler_longlong::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_longlong(addr.ptr, attr.max_char_length(),
+ addr.null_ptr, addr.null_bit,
+ Field::NONE, name,
+ 0/*zerofill*/, attr.unsigned_flag);
+}
+
+
+Field *Type_handler_vers_trx_id::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_vers_trx_id(addr.ptr, attr.max_char_length(),
+ addr.null_ptr, addr.null_bit,
+ Field::NONE, name,
+ 0/*zerofill*/, attr.unsigned_flag);
+}
+
+
+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
+{
+ 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);
+}
+
+
+Field *
+Type_handler_olddecimal::make_table_field(const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE *table) const
+{
+ /*
+ Currently make_table_field() is used for Item purpose only.
+ On Item level we have type_handler_newdecimal only.
+ For now we have DBUG_ASSERT(0).
+ It will be removed when we reuse Type_handler::make_table_field()
+ 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);
+}
+
+
+Field *
+Type_handler_newdecimal::make_table_field(const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE *table) const
+{
+ uint8 dec= (uint8) attr.decimals;
+ uint8 intg= (uint8) (attr.decimal_precision() - dec);
+ uint32 len= attr.max_char_length();
+
+ /*
+ Trying to put too many digits overall in a DECIMAL(prec,dec)
+ will always throw a warning. We must limit dec to
+ DECIMAL_MAX_SCALE however to prevent an assert() later.
+ */
+
+ if (dec > 0)
+ {
+ signed int overflow;
+
+ dec= MY_MIN(dec, DECIMAL_MAX_SCALE);
+
+ /*
+ If the value still overflows the field with the corrected dec,
+ we'll throw out decimals rather than integers. This is still
+ bad and of course throws a truncation warning.
+ +1: for decimal point
+ */
+
+ const int required_length=
+ my_decimal_precision_to_length(intg + dec, dec, attr.unsigned_flag);
+
+ overflow= required_length - len;
+
+ if (overflow > 0)
+ dec= MY_MAX(0, dec - overflow); // too long, discard fract
+ else
+ /* Corrected value fits. */
+ len= required_length;
+ }
+ return new (table->in_use->mem_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,
+ 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
+
+{
+ return new (table->in_use->mem_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,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE *table) const
+
+{
+ return new_Field_timestamp(table->in_use->mem_root,
+ addr.ptr, addr.null_ptr, addr.null_bit,
+ Field::NONE, name, table->s, attr.decimals);
+}
+
+
+Field *Type_handler_timestamp2::make_table_field(const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE *table) 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,
+ addr.ptr, addr.null_ptr, addr.null_bit,
+ Field::NONE, name, table->s, attr.decimals);
+}
+
+
+Field *Type_handler_newdate::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_newdate(addr.ptr, addr.null_ptr, addr.null_bit,
+ Field::NONE, name);
+}
+
+
+Field *Type_handler_date::make_table_field(const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE *table) const
+
+{
+ /*
+ DBUG_ASSERT will be removed when we reuse make_table_field()
+ for make_field() in field.cc
+ */
+ DBUG_ASSERT(0);
+ return new (table->in_use->mem_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,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE *table) const
+
+{
+ return new_Field_time(table->in_use->mem_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,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE *table) const
+
+
+{
+ /*
+ 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,
+ addr.ptr, addr.null_ptr, addr.null_bit,
+ Field::NONE, name, attr.decimals);
+}
+
+
+Field *Type_handler_datetime::make_table_field(const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE *table) const
+
+{
+ return new_Field_datetime(table->in_use->mem_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,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE *table) 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,
+ addr.ptr, addr.null_ptr, addr.null_bit,
+ Field::NONE, name, attr.decimals);
+}
+
+
+Field *Type_handler_bit::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_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,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE *table) const
+
+{
+ return new (table->in_use->mem_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,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE *table) const
+
+{
+ DBUG_ASSERT(HA_VARCHAR_PACKLENGTH(attr.max_length) <=
+ MAX_FIELD_VARCHARLENGTH);
+ return new (table->in_use->mem_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);
+}
+
+
+Field *Type_handler_tiny_blob::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_blob(addr.ptr, addr.null_ptr, addr.null_bit,
+ Field::NONE, name, table->s,
+ 1, attr.collation);
+}
+
+
+Field *Type_handler_blob::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_blob(addr.ptr, addr.null_ptr, addr.null_bit,
+ Field::NONE, name, table->s,
+ 2, attr.collation);
+}
+
+
+Field *
+Type_handler_medium_blob::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_blob(addr.ptr, addr.null_ptr, addr.null_bit,
+ Field::NONE, name, table->s,
+ 3, attr.collation);
+}
+
+
+Field *Type_handler_long_blob::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_blob(addr.ptr, addr.null_ptr, addr.null_bit,
+ Field::NONE, name, table->s,
+ 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,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE *table) const
+{
+ TYPELIB *typelib= attr.get_typelib();
+ DBUG_ASSERT(typelib);
+ return new (table->in_use->mem_root)
+ Field_enum(addr.ptr, attr.max_length, addr.null_ptr, addr.null_bit,
+ Field::NONE, name,
+ get_enum_pack_length(typelib->count), typelib,
+ attr.collation);
+}
+
+
+Field *Type_handler_set::make_table_field(const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE *table) const
+
+{
+ TYPELIB *typelib= attr.get_typelib();
+ DBUG_ASSERT(typelib);
+ return new (table->in_use->mem_root)
+ Field_set(addr.ptr, attr.max_length, addr.null_ptr, addr.null_bit,
+ Field::NONE, name,
+ get_enum_pack_length(typelib->count), typelib,
+ attr.collation);
+}
+
+/*************************************************************************/
+
+/*
+ If length is not specified for a varchar parameter, set length to the
+ maximum length of the actual argument. Goals are:
+ - avoid to allocate too much unused memory for m_var_table
+ - allow length check inside the callee rather than during copy of
+ returned values in output variables.
+ - allow varchar parameter size greater than 4000
+ Default length has been stored in "decimal" member during parse.
+*/
+bool Type_handler_varchar::adjust_spparam_type(Spvar_definition *def,
+ Item *from) const
+{
+ if (def->decimals)
+ {
+ uint def_max_char_length= MAX_FIELD_VARCHARLENGTH / def->charset->mbmaxlen;
+ uint arg_max_length= from->max_char_length();
+ set_if_smaller(arg_max_length, def_max_char_length);
+ def->length= arg_max_length > 0 ? arg_max_length : def->decimals;
+ def->create_length_to_internal_length_string();
+ }
+ return false;
+}
+
+/*************************************************************************/
+
+uint32 Type_handler_decimal_result::max_display_length(const Item *item) const
+{
+ return item->max_length;
+}
+
+
+uint32 Type_handler_temporal_result::max_display_length(const Item *item) const
+{
+ return item->max_length;
+}
+
+
+uint32 Type_handler_string_result::max_display_length(const Item *item) const
+{
+ return item->max_length;
+}
+
+
+uint32 Type_handler_year::max_display_length(const Item *item) const
+{
+ return item->max_length;
+}
+
+
+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();
+}
+
+
+/*************************************************************************/
+
+int Type_handler_time_common::Item_save_in_field(Item *item, Field *field,
+ bool no_conversions) const
+{
+ return item->save_time_in_field(field, no_conversions);
+}
+
+int Type_handler_temporal_with_date::Item_save_in_field(Item *item,
+ Field *field,
+ bool no_conversions)
+ const
+{
+ return item->save_date_in_field(field, no_conversions);
+}
+
+
+int Type_handler_string_result::Item_save_in_field(Item *item, Field *field,
+ bool no_conversions) const
+{
+ return item->save_str_in_field(field, no_conversions);
+}
+
+
+int Type_handler_real_result::Item_save_in_field(Item *item, Field *field,
+ bool no_conversions) const
+{
+ return item->save_real_in_field(field, no_conversions);
+}
+
+
+int Type_handler_decimal_result::Item_save_in_field(Item *item, Field *field,
+ bool no_conversions) const
+{
+ return item->save_decimal_in_field(field, no_conversions);
+}
+
+
+int Type_handler_int_result::Item_save_in_field(Item *item, Field *field,
+ bool no_conversions) const
+{
+ return item->save_int_in_field(field, no_conversions);
+}
+
+
+/***********************************************************************/
+
+bool Type_handler_row::set_comparator_func(Arg_comparator *cmp) const
+{
+ return cmp->set_cmp_func_row();
+}
+
+bool Type_handler_int_result::set_comparator_func(Arg_comparator *cmp) const
+{
+ return cmp->set_cmp_func_int();
+}
+
+bool Type_handler_real_result::set_comparator_func(Arg_comparator *cmp) const
+{
+ return cmp->set_cmp_func_real();
+}
+
+bool Type_handler_decimal_result::set_comparator_func(Arg_comparator *cmp) const
+{
+ return cmp->set_cmp_func_decimal();
+}
+
+bool Type_handler_string_result::set_comparator_func(Arg_comparator *cmp) const
+{
+ return cmp->set_cmp_func_string();
+}
+
+bool Type_handler_time_common::set_comparator_func(Arg_comparator *cmp) const
+{
+ return cmp->set_cmp_func_time();
+}
+
+bool
+Type_handler_temporal_with_date::set_comparator_func(Arg_comparator *cmp) const
+{
+ return cmp->set_cmp_func_datetime();
+}
+
+
+/*************************************************************************/
+
+bool Type_handler_temporal_result::
+ 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
+{
+ if (source->compare_type_handler()->cmp_type() != TIME_RESULT)
+ return false;
+
+ /*
+ Can't rewrite:
+ WHERE COALESCE(time_column)='00:00:00'
+ AND COALESCE(time_column)=DATE'2015-09-11'
+ to
+ WHERE DATE'2015-09-11'='00:00:00'
+ AND COALESCE(time_column)=DATE'2015-09-11'
+ because the left part will erroneously try to parse '00:00:00'
+ as DATE, not as TIME.
+
+ TODO: It could still be rewritten to:
+ WHERE DATE'2015-09-11'=TIME'00:00:00'
+ AND COALESCE(time_column)=DATE'2015-09-11'
+ i.e. we need to replace both target_expr and target_value
+ at the same time. This is not supported yet.
+ */
+ return target_value->cmp_type() == TIME_RESULT;
+}
+
+
+bool Type_handler_string_result::
+ 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
+{
+ if (source->compare_type_handler()->cmp_type() != STRING_RESULT)
+ return false;
+ /*
+ In this example:
+ SET NAMES utf8 COLLATE utf8_german2_ci;
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1 (a CHAR(10) CHARACTER SET utf8);
+ INSERT INTO t1 VALUES ('o-umlaut'),('oe');
+ SELECT * FROM t1 WHERE a='oe' COLLATE utf8_german2_ci AND a='oe';
+
+ the query should return only the row with 'oe'.
+ It should not return 'o-umlaut', because 'o-umlaut' does not match
+ the right part of the condition: a='oe'
+ ('o-umlaut' is not equal to 'oe' in utf8_general_ci,
+ which is the collation of the field "a").
+
+ If we change the right part from:
+ ... AND a='oe'
+ to
+ ... AND 'oe' COLLATE utf8_german2_ci='oe'
+ it will be evalulated to TRUE and removed from the condition,
+ so the overall query will be simplified to:
+
+ SELECT * FROM t1 WHERE a='oe' COLLATE utf8_german2_ci;
+
+ which will erroneously start to return both 'oe' and 'o-umlaut'.
+ So changing "expr" to "const" is not possible if the effective
+ collations of "target" and "source" are not exactly the same.
+
+ Note, the code before the fix for MDEV-7152 only checked that
+ collations of "source_const" and "target_value" are the same.
+ This was not enough, as the bug report demonstrated.
+ */
+ return
+ target->compare_collation() == source->compare_collation() &&
+ target_value->collation.collation == source_const->collation.collation;
+}
+
+
+bool Type_handler_numeric::
+ 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
+{
+ /*
+ The collations of "target" and "source" do not make sense for numeric
+ data types.
+ */
+ return target->compare_type_handler() == source->compare_type_handler();
+}
+
+
+/*************************************************************************/
+
+Item_cache *
+Type_handler_row::Item_get_cache(THD *thd, const Item *item) const
+{
+ return new (thd->mem_root) Item_cache_row(thd);
+}
+
+Item_cache *
+Type_handler_int_result::Item_get_cache(THD *thd, const Item *item) const
+{
+ return new (thd->mem_root) Item_cache_int(thd, item->type_handler());
+}
+
+Item_cache *
+Type_handler_year::Item_get_cache(THD *thd, const Item *item) const
+{
+ return new (thd->mem_root) Item_cache_year(thd);
+}
+
+Item_cache *
+Type_handler_double::Item_get_cache(THD *thd, const Item *item) const
+{
+ return new (thd->mem_root) Item_cache_double(thd);
+}
+
+Item_cache *
+Type_handler_float::Item_get_cache(THD *thd, const Item *item) const
+{
+ return new (thd->mem_root) Item_cache_float(thd);
+}
+
+Item_cache *
+Type_handler_decimal_result::Item_get_cache(THD *thd, const Item *item) const
+{
+ return new (thd->mem_root) Item_cache_decimal(thd);
+}
+
+Item_cache *
+Type_handler_string_result::Item_get_cache(THD *thd, const Item *item) const
+{
+ return new (thd->mem_root) Item_cache_str(thd, item);
+}
+
+Item_cache *
+Type_handler_timestamp_common::Item_get_cache(THD *thd, const Item *item) const
+{
+ return new (thd->mem_root) Item_cache_datetime(thd);
+}
+
+Item_cache *
+Type_handler_datetime_common::Item_get_cache(THD *thd, const Item *item) const
+{
+ return new (thd->mem_root) Item_cache_datetime(thd);
+}
+
+Item_cache *
+Type_handler_time_common::Item_get_cache(THD *thd, const Item *item) const
+{
+ return new (thd->mem_root) Item_cache_time(thd);
+}
+
+Item_cache *
+Type_handler_date_common::Item_get_cache(THD *thd, const Item *item) const
+{
+ return new (thd->mem_root) Item_cache_date(thd);
+}
+
+/*************************************************************************/
+
+bool Type_handler_int_result::
+ 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
+{
+ bool unsigned_flag= items[0]->unsigned_flag;
+ for (uint i= 1; i < nitems; i++)
+ {
+ if (unsigned_flag != items[i]->unsigned_flag)
+ {
+ // Convert a mixture of signed and unsigned int to decimal
+ handler->set_handler(&type_handler_newdecimal);
+ func->aggregate_attributes_decimal(items, nitems);
+ return false;
+ }
+ }
+ func->aggregate_attributes_int(items, nitems);
+ return false;
+}
+
+
+bool Type_handler_real_result::
+ 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
+{
+ func->aggregate_attributes_real(items, nitems);
+ return false;
+}
+
+
+bool Type_handler_decimal_result::
+ 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
+{
+ func->aggregate_attributes_decimal(items, nitems);
+ return false;
+}
+
+
+bool Type_handler_string_result::
+ 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
+{
+ return func->aggregate_attributes_string(func_name, items, nitems);
+}
+
+
+
+/*
+ We can have enum/set type after merging only if we have one enum|set
+ field (or MIN|MAX(enum|set field)) and number of NULL fields
+*/
+bool Type_handler_typelib::
+ 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
+{
+ TYPELIB *typelib= NULL;
+ for (uint i= 0; i < nitems; i++)
+ {
+ if ((typelib= items[i]->get_typelib()))
+ break;
+ }
+ DBUG_ASSERT(typelib); // There must be at least one typelib
+ func->set_typelib(typelib);
+ return func->aggregate_attributes_string(func_name, items, nitems);
+}
+
+
+bool Type_handler_blob_common::
+ 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
+{
+ if (func->aggregate_attributes_string(func_name, items, nitems))
+ return true;
+ handler->set_handler(blob_type_handler(func->max_length));
+ return false;
+}
+
+
+bool Type_handler_date_common::
+ 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
+{
+ func->fix_attributes_date();
+ return false;
+}
+
+
+bool Type_handler_time_common::
+ 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
+{
+ func->aggregate_attributes_temporal(MIN_TIME_WIDTH, items, nitems);
+ return false;
+}
+
+
+bool Type_handler_datetime_common::
+ 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
+{
+ func->aggregate_attributes_temporal(MAX_DATETIME_WIDTH, items, nitems);
+ return false;
+}
+
+
+bool Type_handler_timestamp_common::
+ 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
+{
+ func->aggregate_attributes_temporal(MAX_DATETIME_WIDTH, items, nitems);
+ 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::
+ Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func,
+ Item **items, uint nitems) const
+{
+ /*
+ Aggregating attributes for LEAST/GREATES is exactly the same
+ with aggregating for CASE-alike functions (e.g. COALESCE)
+ for the majority of data type handlers.
+ */
+ return Item_hybrid_func_fix_attributes(thd, func->func_name(),
+ func, func, items, nitems);
+}
+
+
+bool Type_handler_temporal_result::
+ Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func,
+ Item **items, uint nitems) const
+{
+ bool rc= Type_handler::Item_func_min_max_fix_attributes(thd, func,
+ items, nitems);
+ if (rc || func->maybe_null)
+ return rc;
+ /*
+ LEAST/GREATES(non-temporal, temporal) can return NULL.
+ CAST functions Item_{time|datetime|date}_typecast always set maybe_full
+ to true. Here we try to detect nullability more thoroughly.
+ Perhaps CAST functions should also reuse this idea eventually.
+ */
+ const Type_handler *hf= func->type_handler();
+ for (uint i= 0; i < nitems; i++)
+ {
+ /*
+ If items[i] does not need conversion to the current temporal data
+ type, then we trust items[i]->maybe_null, which was already ORred
+ to func->maybe_null in the argument loop in fix_fields().
+ If items[i] requires conversion to the current temporal data type,
+ then conversion can fail and return NULL even for NOT NULL items.
+ */
+ const Type_handler *ha= items[i]->type_handler();
+ if (hf == ha)
+ continue; // No conversion.
+ if (ha->cmp_type() != TIME_RESULT)
+ {
+ func->maybe_null= true; // Conversion from non-temporal is not safe
+ break;
+ }
+ timestamp_type tf= hf->mysql_timestamp_type();
+ timestamp_type ta= ha->mysql_timestamp_type();
+ if (tf == ta ||
+ (tf == MYSQL_TIMESTAMP_DATETIME && ta == MYSQL_TIMESTAMP_DATE))
+ {
+ /*
+ If handlers have the same mysql_timestamp_type(),
+ then conversion is NULL safe. Conversion from DATE to DATETIME
+ is also safe. This branch includes data type pairs:
+ Function return type Argument type Comment
+ -------------------- ------------- -------------
+ TIMESTAMP TIMESTAMP no conversion
+ TIMESTAMP DATETIME not possible
+ TIMESTAMP DATE not possible
+ DATETIME DATETIME no conversion
+ DATETIME TIMESTAMP safe conversion
+ DATETIME DATE safe conversion
+ DATE DATE no conversion
+ TIME TIME no conversion
+
+ Note, a function cannot return TIMESTAMP if it has non-TIMESTAMP
+ arguments (it would return DATETIME in such case).
+ */
+ DBUG_ASSERT(hf->field_type() != MYSQL_TYPE_TIMESTAMP || tf == ta);
+ continue;
+ }
+ /*
+ Here we have the following data type pairs that did not match
+ the condition above:
+
+ Function return type Argument type Comment
+ -------------------- ------------- -------
+ TIMESTAMP TIME Not possible
+ DATETIME TIME depends on OLD_MODE_ZERO_DATE_TIME_CAST
+ DATE TIMESTAMP Not possible
+ DATE DATETIME Not possible
+ DATE TIME Not possible
+ TIME TIMESTAMP Not possible
+ TIME DATETIME Not possible
+ TIME DATE Not possible
+
+ Most pairs are not possible, because the function data type
+ would be DATETIME (according to LEAST/GREATEST aggregation rules).
+ Conversion to DATETIME from TIME is not safe when
+ OLD_MODE_ZERO_DATE_TIME_CAST is set:
+ - negative TIME values cannot be converted to not-NULL DATETIME values
+ - TIME values can produce DATETIME values that do not pass
+ NO_ZERO_DATE and NO_ZERO_IN_DATE tests.
+ */
+ DBUG_ASSERT(hf->field_type() == MYSQL_TYPE_DATETIME);
+ if (!(thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST))
+ continue;
+ func->maybe_null= true;
+ break;
+ }
+ return rc;
+}
+
+
+bool Type_handler_real_result::
+ Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func,
+ Item **items, uint nitems) const
+{
+ /*
+ DOUBLE is an exception and aggregates attributes differently
+ for LEAST/GREATEST vs CASE-alike functions. See the comment in
+ Item_func_min_max::aggregate_attributes_real().
+ */
+ func->aggregate_attributes_real(items, nitems);
+ return false;
+}
+
+/*************************************************************************/
+
+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);
+}
+
+
+bool Type_handler_real_result::
+ Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const
+{
+ (void) func->fix_length_and_dec_numeric(&type_handler_double);
+ func->max_length= func->float_length(func->decimals);
+ return false;
+}
+
+
+bool Type_handler_decimal_result::
+ Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const
+{
+ return func->fix_length_and_dec_numeric(&type_handler_newdecimal);
+}
+
+
+bool Type_handler_string_result::
+ Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const
+{
+ return func->fix_length_and_dec_string();
+}
+
+
+/**
+ Traditional temporal types always preserve the type of the argument.
+*/
+bool Type_handler_temporal_result::
+ Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const
+{
+ return func->fix_length_and_dec_generic();
+}
+
+
+/*************************************************************************/
+
+bool Type_handler_int_result::
+ Item_sum_sum_fix_length_and_dec(Item_sum_sum *item) const
+{
+ item->fix_length_and_dec_decimal();
+ return false;
+}
+
+
+bool Type_handler_decimal_result::
+ Item_sum_sum_fix_length_and_dec(Item_sum_sum *item) const
+{
+ item->fix_length_and_dec_decimal();
+ return false;
+}
+
+
+bool Type_handler_temporal_result::
+ Item_sum_sum_fix_length_and_dec(Item_sum_sum *item) const
+{
+ item->fix_length_and_dec_decimal();
+ return false;
+}
+
+
+bool Type_handler_real_result::
+ Item_sum_sum_fix_length_and_dec(Item_sum_sum *item) const
+{
+ item->fix_length_and_dec_double();
+ return false;
+}
+
+
+bool Type_handler_string_result::
+ Item_sum_sum_fix_length_and_dec(Item_sum_sum *item) const
+{
+ item->fix_length_and_dec_double();
+ return false;
+}
+
+
+#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
+
+
+/*************************************************************************/
+
+bool Type_handler_int_result::
+ Item_sum_avg_fix_length_and_dec(Item_sum_avg *item) const
+{
+ item->fix_length_and_dec_decimal();
+ return false;
+}
+
+
+bool Type_handler_decimal_result::
+ Item_sum_avg_fix_length_and_dec(Item_sum_avg *item) const
+{
+ item->fix_length_and_dec_decimal();
+ return false;
+}
+
+
+bool Type_handler_temporal_result::
+ Item_sum_avg_fix_length_and_dec(Item_sum_avg *item) const
+{
+ item->fix_length_and_dec_decimal();
+ return false;
+}
+
+
+bool Type_handler_real_result::
+ Item_sum_avg_fix_length_and_dec(Item_sum_avg *item) const
+{
+ item->fix_length_and_dec_double();
+ return false;
+}
+
+
+bool Type_handler_string_result::
+ Item_sum_avg_fix_length_and_dec(Item_sum_avg *item) const
+{
+ item->fix_length_and_dec_double();
+ return false;
+}
+
+
+#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
+
+
+/*************************************************************************/
+
+bool Type_handler_int_result::
+ Item_sum_variance_fix_length_and_dec(Item_sum_variance *item) const
+{
+ item->fix_length_and_dec_decimal();
+ return false;
+}
+
+
+bool Type_handler_decimal_result::
+ Item_sum_variance_fix_length_and_dec(Item_sum_variance *item) const
+{
+ item->fix_length_and_dec_decimal();
+ return false;
+}
+
+
+bool Type_handler_temporal_result::
+ Item_sum_variance_fix_length_and_dec(Item_sum_variance *item) const
+{
+ item->fix_length_and_dec_decimal();
+ return false;
+}
+
+
+bool Type_handler_real_result::
+ Item_sum_variance_fix_length_and_dec(Item_sum_variance *item) const
+{
+ item->fix_length_and_dec_double();
+ return false;
+}
+
+
+bool Type_handler_string_result::
+ Item_sum_variance_fix_length_and_dec(Item_sum_variance *item) const
+{
+ item->fix_length_and_dec_double();
+ return false;
+}
+
+
+#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
+{
+ return item->val_real() != 0.0;
+}
+
+bool Type_handler_int_result::Item_val_bool(Item *item) const
+{
+ return item->val_int() != 0;
+}
+
+bool Type_handler_decimal_result::Item_val_bool(Item *item) const
+{
+ my_decimal decimal_value;
+ my_decimal *val= item->val_decimal(&decimal_value);
+ if (val)
+ return !my_decimal_is_zero(val);
+ return false;
+}
+
+bool Type_handler_temporal_result::Item_val_bool(Item *item) const
+{
+ return item->val_real() != 0.0;
+}
+
+bool Type_handler_string_result::Item_val_bool(Item *item) const
+{
+ return item->val_real() != 0.0;
+}
+
+
+/*************************************************************************/
+
+bool Type_handler_int_result::Item_get_date(Item *item, MYSQL_TIME *ltime,
+ ulonglong fuzzydate) const
+{
+ return item->get_date_from_int(ltime, fuzzydate);
+}
+
+
+bool Type_handler_year::Item_get_date(Item *item, MYSQL_TIME *ltime,
+ ulonglong fuzzydate) const
+{
+ return item->get_date_from_year(ltime, fuzzydate);
+}
+
+
+bool Type_handler_real_result::Item_get_date(Item *item, MYSQL_TIME *ltime,
+ ulonglong fuzzydate) const
+{
+ return item->get_date_from_real(ltime, fuzzydate);
+}
+
+
+bool Type_handler_decimal_result::Item_get_date(Item *item, MYSQL_TIME *ltime,
+ ulonglong fuzzydate) const
+{
+ return item->get_date_from_decimal(ltime, fuzzydate);
+}
+
+
+bool Type_handler_string_result::Item_get_date(Item *item, MYSQL_TIME *ltime,
+ ulonglong fuzzydate) const
+{
+ return item->get_date_from_string(ltime, fuzzydate);
+}
+
+
+bool Type_handler_temporal_result::Item_get_date(Item *item, MYSQL_TIME *ltime,
+ ulonglong fuzzydate) const
+{
+ DBUG_ASSERT(0); // Temporal type items must implement native get_date()
+ item->null_value= true;
+ set_zero_time(ltime, mysql_timestamp_type());
+ return true;
+}
+
+
+/*************************************************************************/
+
+longlong Type_handler_real_result::
+ Item_val_int_signed_typecast(Item *item) const
+{
+ return item->val_int();
+}
+
+longlong Type_handler_int_result::
+ Item_val_int_signed_typecast(Item *item) const
+{
+ return item->val_int_signed_typecast_from_int();
+}
+
+longlong Type_handler_decimal_result::
+ Item_val_int_signed_typecast(Item *item) const
+{
+ return item->val_int();
+}
+
+longlong Type_handler_temporal_result::
+ Item_val_int_signed_typecast(Item *item) const
+{
+ return item->val_int();
+}
+
+longlong Type_handler_string_result::
+ Item_val_int_signed_typecast(Item *item) const
+{
+ return item->val_int_signed_typecast_from_str();
+}
+
+/*************************************************************************/
+
+longlong Type_handler_real_result::
+ Item_val_int_unsigned_typecast(Item *item) const
+{
+ return item->val_int_unsigned_typecast_from_int();
+}
+
+longlong Type_handler_int_result::
+ Item_val_int_unsigned_typecast(Item *item) const
+{
+ return item->val_int_unsigned_typecast_from_int();
+}
+
+longlong Type_handler_decimal_result::
+ Item_val_int_unsigned_typecast(Item *item) const
+{
+ return item->val_int_unsigned_typecast_from_decimal();
+}
+
+longlong Type_handler_temporal_result::
+ Item_val_int_unsigned_typecast(Item *item) const
+{
+ return item->val_int_unsigned_typecast_from_int();
+}
+
+longlong Type_handler_string_result::
+ Item_val_int_unsigned_typecast(Item *item) const
+{
+ return item->val_int_unsigned_typecast_from_str();
+}
+
+/*************************************************************************/
+
+String *
+Type_handler_real_result::Item_func_hex_val_str_ascii(Item_func_hex *item,
+ String *str) const
+{
+ return item->val_str_ascii_from_val_real(str);
+}
+
+
+String *
+Type_handler_decimal_result::Item_func_hex_val_str_ascii(Item_func_hex *item,
+ String *str) const
+{
+ return item->val_str_ascii_from_val_real(str);
+}
+
+
+String *
+Type_handler_int_result::Item_func_hex_val_str_ascii(Item_func_hex *item,
+ String *str) const
+{
+ return item->val_str_ascii_from_val_int(str);
+}
+
+
+String *
+Type_handler_temporal_result::Item_func_hex_val_str_ascii(Item_func_hex *item,
+ String *str) const
+{
+ return item->val_str_ascii_from_val_str(str);
+}
+
+
+String *
+Type_handler_string_result::Item_func_hex_val_str_ascii(Item_func_hex *item,
+ String *str) const
+{
+ return item->val_str_ascii_from_val_str(str);
+}
+
+/***************************************************************************/
+
+String *
+Type_handler_decimal_result::Item_func_hybrid_field_type_val_str(
+ Item_func_hybrid_field_type *item,
+ String *str) const
+{
+ return item->val_str_from_decimal_op(str);
+}
+
+
+double
+Type_handler_decimal_result::Item_func_hybrid_field_type_val_real(
+ Item_func_hybrid_field_type *item)
+ const
+{
+ return item->val_real_from_decimal_op();
+}
+
+
+longlong
+Type_handler_decimal_result::Item_func_hybrid_field_type_val_int(
+ Item_func_hybrid_field_type *item)
+ const
+{
+ return item->val_int_from_decimal_op();
+}
+
+
+my_decimal *
+Type_handler_decimal_result::Item_func_hybrid_field_type_val_decimal(
+ Item_func_hybrid_field_type *item,
+ my_decimal *dec) const
+{
+ return item->val_decimal_from_decimal_op(dec);
+}
+
+
+bool
+Type_handler_decimal_result::Item_func_hybrid_field_type_get_date(
+ Item_func_hybrid_field_type *item,
+ MYSQL_TIME *ltime,
+ ulonglong fuzzydate) const
+{
+ return item->get_date_from_decimal_op(ltime, fuzzydate);
+}
+
+
+/***************************************************************************/
+
+
+String *
+Type_handler_int_result::Item_func_hybrid_field_type_val_str(
+ Item_func_hybrid_field_type *item,
+ String *str) const
+{
+ return item->val_str_from_int_op(str);
+}
+
+
+double
+Type_handler_int_result::Item_func_hybrid_field_type_val_real(
+ Item_func_hybrid_field_type *item)
+ const
+{
+ return item->val_real_from_int_op();
+}
+
+
+longlong
+Type_handler_int_result::Item_func_hybrid_field_type_val_int(
+ Item_func_hybrid_field_type *item)
+ const
+{
+ return item->val_int_from_int_op();
+}
+
+
+my_decimal *
+Type_handler_int_result::Item_func_hybrid_field_type_val_decimal(
+ Item_func_hybrid_field_type *item,
+ my_decimal *dec) const
+{
+ return item->val_decimal_from_int_op(dec);
+}
+
+
+bool
+Type_handler_int_result::Item_func_hybrid_field_type_get_date(
+ Item_func_hybrid_field_type *item,
+ MYSQL_TIME *ltime,
+ ulonglong fuzzydate) const
+{
+ return item->get_date_from_int_op(ltime, fuzzydate);
+}
+
+
+
+/***************************************************************************/
+
+String *
+Type_handler_double::Item_func_hybrid_field_type_val_str(
+ Item_func_hybrid_field_type *item,
+ String *str) const
+{
+ return item->val_str_from_real_op(str);
+}
+
+
+String *
+Type_handler_float::Item_func_hybrid_field_type_val_str(
+ Item_func_hybrid_field_type *item,
+ String *str) const
+{
+ Float nr(item->real_op());
+ if (item->null_value)
+ return 0;
+ nr.to_string(str, item->decimals);
+ return str;
+}
+
+
+double
+Type_handler_real_result::Item_func_hybrid_field_type_val_real(
+ Item_func_hybrid_field_type *item)
+ const
+{
+ return item->val_real_from_real_op();
+}
+
+
+longlong
+Type_handler_real_result::Item_func_hybrid_field_type_val_int(
+ Item_func_hybrid_field_type *item)
+ const
+{
+ return item->val_int_from_real_op();
+}
+
+
+my_decimal *
+Type_handler_real_result::Item_func_hybrid_field_type_val_decimal(
+ Item_func_hybrid_field_type *item,
+ my_decimal *dec) const
+{
+ return item->val_decimal_from_real_op(dec);
+}
+
+
+bool
+Type_handler_real_result::Item_func_hybrid_field_type_get_date(
+ Item_func_hybrid_field_type *item,
+ MYSQL_TIME *ltime,
+ ulonglong fuzzydate) const
+{
+ return item->get_date_from_real_op(ltime, fuzzydate);
+}
+
+
+/***************************************************************************/
+
+String *
+Type_handler_temporal_result::Item_func_hybrid_field_type_val_str(
+ Item_func_hybrid_field_type *item,
+ String *str) const
+{
+ return item->val_str_from_date_op(str);
+}
+
+
+double
+Type_handler_temporal_result::Item_func_hybrid_field_type_val_real(
+ Item_func_hybrid_field_type *item)
+ const
+{
+ return item->val_real_from_date_op();
+}
+
+
+longlong
+Type_handler_temporal_result::Item_func_hybrid_field_type_val_int(
+ Item_func_hybrid_field_type *item)
+ const
+{
+ return item->val_int_from_date_op();
+}
+
+
+my_decimal *
+Type_handler_temporal_result::Item_func_hybrid_field_type_val_decimal(
+ Item_func_hybrid_field_type *item,
+ my_decimal *dec) const
+{
+ return item->val_decimal_from_date_op(dec);
+}
+
+
+bool
+Type_handler_temporal_result::Item_func_hybrid_field_type_get_date(
+ Item_func_hybrid_field_type *item,
+ MYSQL_TIME *ltime,
+ ulonglong fuzzydate) const
+{
+ return item->date_op(ltime, fuzzydate);
+}
+
+
+/***************************************************************************/
+
+String *
+Type_handler_time_common::Item_func_hybrid_field_type_val_str(
+ Item_func_hybrid_field_type *item,
+ String *str) const
+{
+ return item->val_str_from_time_op(str);
+}
+
+
+double
+Type_handler_time_common::Item_func_hybrid_field_type_val_real(
+ Item_func_hybrid_field_type *item)
+ const
+{
+ return item->val_real_from_time_op();
+}
+
+
+longlong
+Type_handler_time_common::Item_func_hybrid_field_type_val_int(
+ Item_func_hybrid_field_type *item)
+ const
+{
+ return item->val_int_from_time_op();
+}
+
+
+my_decimal *
+Type_handler_time_common::Item_func_hybrid_field_type_val_decimal(
+ Item_func_hybrid_field_type *item,
+ my_decimal *dec) const
+{
+ return item->val_decimal_from_time_op(dec);
+}
+
+
+bool
+Type_handler_time_common::Item_func_hybrid_field_type_get_date(
+ Item_func_hybrid_field_type *item,
+ MYSQL_TIME *ltime,
+ ulonglong fuzzydate) const
+{
+ return item->time_op(ltime);
+}
+
+
+/***************************************************************************/
+
+String *
+Type_handler_string_result::Item_func_hybrid_field_type_val_str(
+ Item_func_hybrid_field_type *item,
+ String *str) const
+{
+ return item->val_str_from_str_op(str);
+}
+
+
+double
+Type_handler_string_result::Item_func_hybrid_field_type_val_real(
+ Item_func_hybrid_field_type *item)
+ const
+{
+ return item->val_real_from_str_op();
+}
+
+
+longlong
+Type_handler_string_result::Item_func_hybrid_field_type_val_int(
+ Item_func_hybrid_field_type *item)
+ const
+{
+ return item->val_int_from_str_op();
+}
+
+
+my_decimal *
+Type_handler_string_result::Item_func_hybrid_field_type_val_decimal(
+ Item_func_hybrid_field_type *item,
+ my_decimal *dec) const
+{
+ return item->val_decimal_from_str_op(dec);
+}
+
+
+bool
+Type_handler_string_result::Item_func_hybrid_field_type_get_date(
+ Item_func_hybrid_field_type *item,
+ MYSQL_TIME *ltime,
+ ulonglong fuzzydate) const
+{
+ return item->get_date_from_str_op(ltime, fuzzydate);
+}
+
+/***************************************************************************/
+
+bool Type_handler_numeric::
+ Item_func_between_fix_length_and_dec(Item_func_between *func) const
+{
+ return func->fix_length_and_dec_numeric(current_thd);
+}
+
+bool Type_handler_temporal_result::
+ Item_func_between_fix_length_and_dec(Item_func_between *func) const
+{
+ return func->fix_length_and_dec_temporal(current_thd);
+}
+
+bool Type_handler_string_result::
+ Item_func_between_fix_length_and_dec(Item_func_between *func) const
+{
+ return func->fix_length_and_dec_string(current_thd);
+}
+
+
+longlong Type_handler_row::
+ Item_func_between_val_int(Item_func_between *func) const
+{
+ DBUG_ASSERT(0);
+ func->null_value= true;
+ return 0;
+}
+
+longlong Type_handler_string_result::
+ Item_func_between_val_int(Item_func_between *func) const
+{
+ return func->val_int_cmp_string();
+}
+
+longlong Type_handler_temporal_result::
+ Item_func_between_val_int(Item_func_between *func) const
+{
+ return func->val_int_cmp_temporal();
+}
+
+longlong Type_handler_int_result::
+ Item_func_between_val_int(Item_func_between *func) const
+{
+ return func->val_int_cmp_int();
+}
+
+longlong Type_handler_real_result::
+ Item_func_between_val_int(Item_func_between *func) const
+{
+ return func->val_int_cmp_real();
+}
+
+longlong Type_handler_decimal_result::
+ Item_func_between_val_int(Item_func_between *func) const
+{
+ return func->val_int_cmp_decimal();
+}
+
+/***************************************************************************/
+
+cmp_item *Type_handler_int_result::make_cmp_item(THD *thd,
+ CHARSET_INFO *cs) const
+{
+ return new (thd->mem_root) cmp_item_int;
+}
+
+cmp_item *Type_handler_real_result::make_cmp_item(THD *thd,
+ CHARSET_INFO *cs) const
+{
+ return new (thd->mem_root) cmp_item_real;
+}
+
+cmp_item *Type_handler_decimal_result::make_cmp_item(THD *thd,
+ CHARSET_INFO *cs) const
+{
+ return new (thd->mem_root) cmp_item_decimal;
+}
+
+
+cmp_item *Type_handler_string_result::make_cmp_item(THD *thd,
+ CHARSET_INFO *cs) const
+{
+ return new (thd->mem_root) cmp_item_sort_string(cs);
+}
+
+cmp_item *Type_handler_row::make_cmp_item(THD *thd,
+ CHARSET_INFO *cs) const
+{
+ return new (thd->mem_root) cmp_item_row;
+}
+
+cmp_item *Type_handler_time_common::make_cmp_item(THD *thd,
+ CHARSET_INFO *cs) const
+{
+ return new (thd->mem_root) cmp_item_time;
+}
+
+cmp_item *Type_handler_temporal_with_date::make_cmp_item(THD *thd,
+ CHARSET_INFO *cs) const
+{
+ return new (thd->mem_root) cmp_item_datetime;
+}
+
+/***************************************************************************/
+
+static int srtcmp_in(CHARSET_INFO *cs, const String *x,const String *y)
+{
+ return cs->coll->strnncollsp(cs,
+ (uchar *) x->ptr(),x->length(),
+ (uchar *) y->ptr(),y->length());
+}
+
+in_vector *Type_handler_string_result::make_in_vector(THD *thd,
+ const Item_func_in *func,
+ uint nargs) const
+{
+ return new (thd->mem_root) in_string(thd, nargs, (qsort2_cmp) srtcmp_in,
+ func->compare_collation());
+
+}
+
+
+in_vector *Type_handler_int_result::make_in_vector(THD *thd,
+ const Item_func_in *func,
+ uint nargs) const
+{
+ return new (thd->mem_root) in_longlong(thd, nargs);
+}
+
+
+in_vector *Type_handler_real_result::make_in_vector(THD *thd,
+ const Item_func_in *func,
+ uint nargs) const
+{
+ return new (thd->mem_root) in_double(thd, nargs);
+}
+
+
+in_vector *Type_handler_decimal_result::make_in_vector(THD *thd,
+ const Item_func_in *func,
+ uint nargs) const
+{
+ return new (thd->mem_root) in_decimal(thd, nargs);
+}
+
+
+in_vector *Type_handler_time_common::make_in_vector(THD *thd,
+ const Item_func_in *func,
+ uint nargs) const
+{
+ return new (thd->mem_root) in_time(thd, nargs);
+}
+
+
+in_vector *
+Type_handler_temporal_with_date::make_in_vector(THD *thd,
+ const Item_func_in *func,
+ uint nargs) const
+{
+ return new (thd->mem_root) in_datetime(thd, nargs);
+}
+
+
+in_vector *Type_handler_row::make_in_vector(THD *thd,
+ const Item_func_in *func,
+ uint nargs) const
+{
+ return new (thd->mem_root) in_row(thd, nargs, 0);
+}
+
+/***************************************************************************/
+
+bool Type_handler_string_result::
+ Item_func_in_fix_comparator_compatible_types(THD *thd,
+ Item_func_in *func) const
+{
+ if (func->agg_all_arg_charsets_for_comparison())
+ return true;
+ if (func->compatible_types_scalar_bisection_possible())
+ {
+ return func->value_list_convert_const_to_int(thd) ||
+ func->fix_for_scalar_comparison_using_bisection(thd);
+ }
+ return
+ func->fix_for_scalar_comparison_using_cmp_items(thd,
+ 1U << (uint) STRING_RESULT);
+}
+
+
+bool Type_handler_int_result::
+ Item_func_in_fix_comparator_compatible_types(THD *thd,
+ Item_func_in *func) const
+{
+ /*
+ Does not need to call value_list_convert_const_to_int()
+ as already handled by int handler.
+ */
+ return func->compatible_types_scalar_bisection_possible() ?
+ func->fix_for_scalar_comparison_using_bisection(thd) :
+ func->fix_for_scalar_comparison_using_cmp_items(thd,
+ 1U << (uint) INT_RESULT);
+}
+
+
+bool Type_handler_real_result::
+ Item_func_in_fix_comparator_compatible_types(THD *thd,
+ Item_func_in *func) const
+{
+ return func->compatible_types_scalar_bisection_possible() ?
+ (func->value_list_convert_const_to_int(thd) ||
+ func->fix_for_scalar_comparison_using_bisection(thd)) :
+ func->fix_for_scalar_comparison_using_cmp_items(thd,
+ 1U << (uint) REAL_RESULT);
+}
+
+
+bool Type_handler_decimal_result::
+ Item_func_in_fix_comparator_compatible_types(THD *thd,
+ Item_func_in *func) const
+{
+ return func->compatible_types_scalar_bisection_possible() ?
+ (func->value_list_convert_const_to_int(thd) ||
+ func->fix_for_scalar_comparison_using_bisection(thd)) :
+ func->fix_for_scalar_comparison_using_cmp_items(thd,
+ 1U << (uint) DECIMAL_RESULT);
+}
+
+
+bool Type_handler_temporal_result::
+ Item_func_in_fix_comparator_compatible_types(THD *thd,
+ Item_func_in *func) const
+{
+ return func->compatible_types_scalar_bisection_possible() ?
+ (func->value_list_convert_const_to_int(thd) ||
+ func->fix_for_scalar_comparison_using_bisection(thd)) :
+ func->fix_for_scalar_comparison_using_cmp_items(thd,
+ 1U << (uint) TIME_RESULT);
+}
+
+
+bool Type_handler_row::Item_func_in_fix_comparator_compatible_types(THD *thd,
+ Item_func_in *func) const
+{
+ return func->compatible_types_row_bisection_possible() ?
+ func->fix_for_row_comparison_using_bisection(thd) :
+ func->fix_for_row_comparison_using_cmp_items(thd);
+}
+
+/***************************************************************************/
+
+String *Type_handler_string_result::
+ Item_func_min_max_val_str(Item_func_min_max *func, String *str) const
+{
+ return func->val_str_native(str);
+}
+
+
+String *Type_handler_temporal_result::
+ Item_func_min_max_val_str(Item_func_min_max *func, String *str) const
+{
+ return func->val_string_from_date(str);
+}
+
+
+String *Type_handler_int_result::
+ Item_func_min_max_val_str(Item_func_min_max *func, String *str) const
+{
+ return func->val_string_from_int(str);
+}
+
+
+String *Type_handler_decimal_result::
+ Item_func_min_max_val_str(Item_func_min_max *func, String *str) const
+{
+ return func->val_string_from_decimal(str);
+}
+
+
+String *Type_handler_double::
+ Item_func_min_max_val_str(Item_func_min_max *func, String *str) const
+{
+ return func->val_string_from_real(str);
+}
+
+
+String *Type_handler_float::
+ Item_func_min_max_val_str(Item_func_min_max *func, String *str) const
+{
+ Float nr(func->val_real());
+ if (func->null_value)
+ return 0;
+ nr.to_string(str, func->decimals);
+ return str;
+}
+
+
+double Type_handler_string_result::
+ Item_func_min_max_val_real(Item_func_min_max *func) const
+{
+ return func->val_real_native();
+}
+
+
+double Type_handler_temporal_result::
+ Item_func_min_max_val_real(Item_func_min_max *func) const
+{
+ MYSQL_TIME ltime;
+ if (func->get_date(&ltime, 0))
+ return 0;
+ return TIME_to_double(&ltime);
+}
+
+
+double Type_handler_numeric::
+ Item_func_min_max_val_real(Item_func_min_max *func) const
+{
+ return func->val_real_native();
+}
+
+
+longlong Type_handler_string_result::
+ Item_func_min_max_val_int(Item_func_min_max *func) const
+{
+ return func->val_int_native();
+}
+
+
+longlong Type_handler_temporal_result::
+ Item_func_min_max_val_int(Item_func_min_max *func) const
+{
+ MYSQL_TIME ltime;
+ if (func->get_date(&ltime, 0))
+ return 0;
+ return TIME_to_ulonglong(&ltime);
+}
+
+
+longlong Type_handler_numeric::
+ Item_func_min_max_val_int(Item_func_min_max *func) const
+{
+ return func->val_int_native();
+}
+
+
+my_decimal *Type_handler_string_result::
+ Item_func_min_max_val_decimal(Item_func_min_max *func,
+ my_decimal *dec) const
+{
+ return func->val_decimal_native(dec);
+}
+
+
+my_decimal *Type_handler_numeric::
+ Item_func_min_max_val_decimal(Item_func_min_max *func,
+ my_decimal *dec) const
+{
+ return func->val_decimal_native(dec);
+}
+
+
+my_decimal *Type_handler_temporal_result::
+ Item_func_min_max_val_decimal(Item_func_min_max *func,
+ my_decimal *dec) const
+{
+ MYSQL_TIME ltime;
+ if (func->get_date(&ltime, 0))
+ return 0;
+ return date2my_decimal(&ltime, dec);
+}
+
+
+bool Type_handler_string_result::
+ Item_func_min_max_get_date(Item_func_min_max *func,
+ MYSQL_TIME *ltime, ulonglong fuzzydate) const
+{
+ /*
+ just like ::val_int() method of a string item can be called,
+ for example, SELECT CONCAT("10", "12") + 1,
+ ::get_date() can be called for non-temporal values,
+ for example, SELECT MONTH(GREATEST("2011-11-21", "2010-10-09"))
+ */
+ return func->get_date_from_string(ltime, fuzzydate);
+}
+
+
+bool Type_handler_numeric::
+ Item_func_min_max_get_date(Item_func_min_max *func,
+ MYSQL_TIME *ltime, ulonglong fuzzydate) const
+{
+ return Item_get_date(func, ltime, fuzzydate);
+}
+
+
+bool Type_handler_temporal_result::
+ Item_func_min_max_get_date(Item_func_min_max *func,
+ MYSQL_TIME *ltime, ulonglong fuzzydate) const
+{
+ return func->get_date_native(ltime, fuzzydate);
+}
+
+bool Type_handler_time_common::
+ Item_func_min_max_get_date(Item_func_min_max *func,
+ MYSQL_TIME *ltime, ulonglong fuzzydate) const
+{
+ return func->get_time_native(ltime);
+}
+
+/***************************************************************************/
+
+/**
+ Get a string representation of the Item value.
+ See sql_type.h for details.
+*/
+String *Type_handler_row::
+ print_item_value(THD *thd, Item *item, String *str) const
+{
+ CHARSET_INFO *cs= thd->variables.character_set_client;
+ StringBuffer<STRING_BUFFER_USUAL_SIZE> val(cs);
+ str->append(STRING_WITH_LEN("ROW("));
+ for (uint i= 0 ; i < item->cols(); i++)
+ {
+ if (i > 0)
+ str->append(',');
+ Item *elem= item->element_index(i);
+ String *tmp= elem->type_handler()->print_item_value(thd, elem, &val);
+ if (tmp)
+ str->append(*tmp);
+ else
+ str->append(STRING_WITH_LEN("NULL"));
+ }
+ str->append(STRING_WITH_LEN(")"));
+ return str;
+}
+
+
+/**
+ Get a string representation of the Item value,
+ using the character string format with its charset and collation, e.g.
+ latin1 'string' COLLATE latin1_german2_ci
+*/
+String *Type_handler::
+ print_item_value_csstr(THD *thd, Item *item, String *str) const
+{
+ String *result= item->val_str(str);
+
+ if (!result)
+ return NULL;
+
+ StringBuffer<STRING_BUFFER_USUAL_SIZE> buf(result->charset());
+ CHARSET_INFO *cs= thd->variables.character_set_client;
+
+ buf.append('_');
+ buf.append(result->charset()->csname);
+ if (cs->escape_with_backslash_is_dangerous)
+ buf.append(' ');
+ append_query_string(cs, &buf, result->ptr(), result->length(),
+ thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES);
+ buf.append(" COLLATE '");
+ buf.append(item->collation.collation->name);
+ buf.append('\'');
+ str->copy(buf);
+
+ return str;
+}
+
+
+String *Type_handler_numeric::
+ print_item_value(THD *thd, Item *item, String *str) const
+{
+ return item->val_str(str);
+}
+
+
+String *Type_handler::
+ print_item_value_temporal(THD *thd, Item *item, String *str,
+ const Name &type_name, String *buf) const
+{
+ String *result= item->val_str(buf);
+ return !result ||
+ str->realloc(type_name.length() + result->length() + 2) ||
+ str->copy(type_name.ptr(), type_name.length(), &my_charset_latin1) ||
+ str->append('\'') ||
+ str->append(result->ptr(), result->length()) ||
+ str->append('\'') ?
+ NULL :
+ str;
+}
+
+
+String *Type_handler_time_common::
+ print_item_value(THD *thd, Item *item, String *str) const
+{
+ StringBuffer<MAX_TIME_FULL_WIDTH+1> buf;
+ return print_item_value_temporal(thd, item, str,
+ Name(STRING_WITH_LEN("TIME")), &buf);
+}
+
+
+String *Type_handler_date_common::
+ print_item_value(THD *thd, Item *item, String *str) const
+{
+ StringBuffer<MAX_DATE_WIDTH+1> buf;
+ return print_item_value_temporal(thd, item, str,
+ Name(STRING_WITH_LEN("DATE")), &buf);
+}
+
+
+String *Type_handler_datetime_common::
+ print_item_value(THD *thd, Item *item, String *str) const
+{
+ StringBuffer<MAX_DATETIME_FULL_WIDTH+1> buf;
+ return print_item_value_temporal(thd, item, str,
+ Name(STRING_WITH_LEN("TIMESTAMP")), &buf);
+}
+
+
+String *Type_handler_timestamp_common::
+ print_item_value(THD *thd, Item *item, String *str) const
+{
+ StringBuffer<MAX_DATETIME_FULL_WIDTH+1> buf;
+ return print_item_value_temporal(thd, item, str,
+ Name(STRING_WITH_LEN("TIMESTAMP")), &buf);
+}
+
+
+/***************************************************************************/
+
+bool Type_handler_row::
+ Item_func_round_fix_length_and_dec(Item_func_round *item) const
+{
+ DBUG_ASSERT(0);
+ return false;
+}
+
+
+bool Type_handler_int_result::
+ Item_func_round_fix_length_and_dec(Item_func_round *item) const
+{
+ item->fix_arg_int();
+ return false;
+}
+
+
+bool Type_handler_real_result::
+ Item_func_round_fix_length_and_dec(Item_func_round *item) const
+{
+ item->fix_arg_double();
+ return false;
+}
+
+
+bool Type_handler_decimal_result::
+ Item_func_round_fix_length_and_dec(Item_func_round *item) const
+{
+ item->fix_arg_decimal();
+ return false;
+}
+
+
+bool Type_handler_temporal_result::
+ Item_func_round_fix_length_and_dec(Item_func_round *item) const
+{
+ item->fix_arg_double();
+ return false;
+}
+
+
+bool Type_handler_string_result::
+ Item_func_round_fix_length_and_dec(Item_func_round *item) const
+{
+ item->fix_arg_double();
+ return false;
+}
+
+
+#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::
+ Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const
+{
+ DBUG_ASSERT(0);
+ return false;
+}
+
+
+bool Type_handler_int_result::
+ Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const
+{
+ item->fix_length_and_dec_int_or_decimal();
+ return false;
+}
+
+
+bool Type_handler_real_result::
+ Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const
+{
+ item->fix_length_and_dec_double();
+ return false;
+}
+
+
+bool Type_handler_decimal_result::
+ Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const
+{
+ item->fix_length_and_dec_int_or_decimal();
+ return false;
+}
+
+
+bool Type_handler_temporal_result::
+ Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const
+{
+ item->fix_length_and_dec_int_or_decimal();
+ return false;
+}
+
+
+bool Type_handler_string_result::
+ Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const
+{
+ item->fix_length_and_dec_double();
+ return false;
+}
+
+
+#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::
+ Item_func_abs_fix_length_and_dec(Item_func_abs *item) const
+{
+ DBUG_ASSERT(0);
+ return false;
+}
+
+
+bool Type_handler_int_result::
+ Item_func_abs_fix_length_and_dec(Item_func_abs *item) const
+{
+ item->fix_length_and_dec_int();
+ return false;
+}
+
+
+bool Type_handler_real_result::
+ Item_func_abs_fix_length_and_dec(Item_func_abs *item) const
+{
+ item->fix_length_and_dec_double();
+ return false;
+}
+
+
+bool Type_handler_decimal_result::
+ Item_func_abs_fix_length_and_dec(Item_func_abs *item) const
+{
+ item->fix_length_and_dec_decimal();
+ return false;
+}
+
+
+bool Type_handler_temporal_result::
+ Item_func_abs_fix_length_and_dec(Item_func_abs *item) const
+{
+ item->fix_length_and_dec_decimal();
+ return false;
+}
+
+
+bool Type_handler_string_result::
+ Item_func_abs_fix_length_and_dec(Item_func_abs *item) const
+{
+ item->fix_length_and_dec_double();
+ return false;
+}
+
+
+#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::
+ Item_func_neg_fix_length_and_dec(Item_func_neg *item) const
+{
+ DBUG_ASSERT(0);
+ return false;
+}
+
+
+bool Type_handler_int_result::
+ Item_func_neg_fix_length_and_dec(Item_func_neg *item) const
+{
+ item->fix_length_and_dec_int();
+ return false;
+}
+
+
+bool Type_handler_real_result::
+ Item_func_neg_fix_length_and_dec(Item_func_neg *item) const
+{
+ item->fix_length_and_dec_double();
+ return false;
+}
+
+
+bool Type_handler_decimal_result::
+ Item_func_neg_fix_length_and_dec(Item_func_neg *item) const
+{
+ item->fix_length_and_dec_decimal();
+ return false;
+}
+
+
+bool Type_handler_temporal_result::
+ Item_func_neg_fix_length_and_dec(Item_func_neg *item) const
+{
+ item->fix_length_and_dec_decimal();
+ return false;
+}
+
+
+bool Type_handler_string_result::
+ Item_func_neg_fix_length_and_dec(Item_func_neg *item) const
+{
+ item->fix_length_and_dec_double();
+ return false;
+}
+
+
+#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::
+ Item_func_signed_fix_length_and_dec(Item_func_signed *item) const
+{
+ item->fix_length_and_dec_generic();
+ return false;
+}
+
+
+bool Type_handler::
+ Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *item) const
+{
+ const Item *arg= item->arguments()[0];
+ if (!arg->unsigned_flag && arg->val_int_min() < 0)
+ {
+ /*
+ Negative arguments produce long results:
+ CAST(1-2 AS UNSIGNED) -> 18446744073709551615
+ */
+ item->max_length= MAX_BIGINT_WIDTH;
+ return false;
+ }
+ item->fix_length_and_dec_generic();
+ return false;
+}
+
+
+bool Type_handler_string_result::
+ Item_func_signed_fix_length_and_dec(Item_func_signed *item) const
+{
+ item->fix_length_and_dec_string();
+ return false;
+}
+
+
+bool Type_handler_string_result::
+ Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *item) const
+{
+ const Item *arg= item->arguments()[0];
+ if (!arg->unsigned_flag && // Not HEX hybrid
+ arg->max_char_length() > 1) // Can be negative
+ {
+ // String arguments can give long results: '-1' -> 18446744073709551614
+ item->max_length= MAX_BIGINT_WIDTH;
+ return false;
+ }
+ item->fix_length_and_dec_string();
+ return false;
+}
+
+bool Type_handler_real_result::
+ Item_func_signed_fix_length_and_dec(Item_func_signed *item) const
+{
+ item->fix_length_and_dec_double();
+ return false;
+}
+
+
+bool Type_handler_real_result::
+ Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *item) const
+{
+ item->fix_length_and_dec_double();
+ return false;
+}
+
+
+bool Type_handler::
+ Item_double_typecast_fix_length_and_dec(Item_double_typecast *item) const
+{
+ item->fix_length_and_dec_generic();
+ return false;
+}
+
+
+bool Type_handler::
+ Item_float_typecast_fix_length_and_dec(Item_float_typecast *item) const
+{
+ item->fix_length_and_dec_generic();
+ return false;
+}
+
+
+bool Type_handler::
+ Item_decimal_typecast_fix_length_and_dec(Item_decimal_typecast *item) const
+{
+ item->fix_length_and_dec_generic();
+ return false;
+}
+
+
+bool Type_handler::
+ Item_char_typecast_fix_length_and_dec(Item_char_typecast *item) const
+{
+ item->fix_length_and_dec_generic();
+ return false;
+}
+
+
+bool Type_handler_numeric::
+ Item_char_typecast_fix_length_and_dec(Item_char_typecast *item) const
+{
+ item->fix_length_and_dec_numeric();
+ return false;
+}
+
+
+bool Type_handler_string_result::
+ Item_char_typecast_fix_length_and_dec(Item_char_typecast *item) const
+{
+ item->fix_length_and_dec_str();
+ return false;
+}
+
+
+bool Type_handler::
+ Item_time_typecast_fix_length_and_dec(Item_time_typecast *item) const
+{
+ uint dec= item->decimals == NOT_FIXED_DEC ?
+ item->arguments()[0]->time_precision() :
+ item->decimals;
+ item->fix_attributes_temporal(MIN_TIME_WIDTH, dec);
+ item->maybe_null= true;
+ return false;
+}
+
+
+bool Type_handler::
+ Item_date_typecast_fix_length_and_dec(Item_date_typecast *item) const
+{
+ item->fix_attributes_temporal(MAX_DATE_WIDTH, 0);
+ item->maybe_null= true;
+ return false;
+}
+
+
+bool Type_handler::
+ Item_datetime_typecast_fix_length_and_dec(Item_datetime_typecast *item)
+ const
+{
+ uint dec= item->decimals == NOT_FIXED_DEC ?
+ item->arguments()[0]->datetime_precision() :
+ item->decimals;
+ item->fix_attributes_temporal(MAX_DATETIME_WIDTH, dec);
+ item->maybe_null= true;
+ return false;
+}
+
+
+#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::
+ Item_func_plus_fix_length_and_dec(Item_func_plus *item) const
+{
+ DBUG_ASSERT(0);
+ return true;
+}
+
+
+bool Type_handler_int_result::
+ Item_func_plus_fix_length_and_dec(Item_func_plus *item) const
+{
+ item->fix_length_and_dec_int();
+ return false;
+}
+
+
+bool Type_handler_real_result::
+ Item_func_plus_fix_length_and_dec(Item_func_plus *item) const
+{
+ item->fix_length_and_dec_double();
+ return false;
+}
+
+
+bool Type_handler_decimal_result::
+ Item_func_plus_fix_length_and_dec(Item_func_plus *item) const
+{
+ item->fix_length_and_dec_decimal();
+ return false;
+}
+
+
+bool Type_handler_temporal_result::
+ Item_func_plus_fix_length_and_dec(Item_func_plus *item) const
+{
+ item->fix_length_and_dec_temporal(true);
+ return false;
+}
+
+
+bool Type_handler_string_result::
+ Item_func_plus_fix_length_and_dec(Item_func_plus *item) const
+{
+ item->fix_length_and_dec_double();
+ return false;
+}
+
+/***************************************************************************/
+
+bool Type_handler_row::
+ Item_func_minus_fix_length_and_dec(Item_func_minus *item) const
+{
+ DBUG_ASSERT(0);
+ return true;
+}
+
+
+bool Type_handler_int_result::
+ Item_func_minus_fix_length_and_dec(Item_func_minus *item) const
+{
+ item->fix_length_and_dec_int();
+ return false;
+}
+
+
+bool Type_handler_real_result::
+ Item_func_minus_fix_length_and_dec(Item_func_minus *item) const
+{
+ item->fix_length_and_dec_double();
+ return false;
+}
+
+
+bool Type_handler_decimal_result::
+ Item_func_minus_fix_length_and_dec(Item_func_minus *item) const
+{
+ item->fix_length_and_dec_decimal();
+ return false;
+}
+
+
+bool Type_handler_temporal_result::
+ Item_func_minus_fix_length_and_dec(Item_func_minus *item) const
+{
+ item->fix_length_and_dec_temporal(true);
+ return false;
+}
+
+
+bool Type_handler_string_result::
+ Item_func_minus_fix_length_and_dec(Item_func_minus *item) const
+{
+ item->fix_length_and_dec_double();
+ return false;
+}
+
+/***************************************************************************/
+
+bool Type_handler_row::
+ Item_func_mul_fix_length_and_dec(Item_func_mul *item) const
+{
+ DBUG_ASSERT(0);
+ return true;
+}
+
+
+bool Type_handler_int_result::
+ Item_func_mul_fix_length_and_dec(Item_func_mul *item) const
+{
+ item->fix_length_and_dec_int();
+ return false;
+}
+
+
+bool Type_handler_real_result::
+ Item_func_mul_fix_length_and_dec(Item_func_mul *item) const
+{
+ item->fix_length_and_dec_double();
+ return false;
+}
+
+
+bool Type_handler_decimal_result::
+ Item_func_mul_fix_length_and_dec(Item_func_mul *item) const
+{
+ item->fix_length_and_dec_decimal();
+ return false;
+}
+
+
+bool Type_handler_temporal_result::
+ Item_func_mul_fix_length_and_dec(Item_func_mul *item) const
+{
+ item->fix_length_and_dec_temporal(true);
+ return false;
+}
+
+
+bool Type_handler_string_result::
+ Item_func_mul_fix_length_and_dec(Item_func_mul *item) const
+{
+ item->fix_length_and_dec_double();
+ return false;
+}
+
+/***************************************************************************/
+
+bool Type_handler_row::
+ Item_func_div_fix_length_and_dec(Item_func_div *item) const
+{
+ DBUG_ASSERT(0);
+ return true;
+}
+
+
+bool Type_handler_int_result::
+ Item_func_div_fix_length_and_dec(Item_func_div *item) const
+{
+ item->fix_length_and_dec_int();
+ return false;
+}
+
+
+bool Type_handler_real_result::
+ Item_func_div_fix_length_and_dec(Item_func_div *item) const
+{
+ item->fix_length_and_dec_double();
+ return false;
+}
+
+
+bool Type_handler_decimal_result::
+ Item_func_div_fix_length_and_dec(Item_func_div *item) const
+{
+ item->fix_length_and_dec_decimal();
+ return false;
+}
+
+
+bool Type_handler_temporal_result::
+ Item_func_div_fix_length_and_dec(Item_func_div *item) const
+{
+ // Item_func_div::int_op() is not implemented. Disallow DECIMAL->INT downcast.
+ item->fix_length_and_dec_temporal(false);
+ return false;
+}
+
+
+bool Type_handler_string_result::
+ Item_func_div_fix_length_and_dec(Item_func_div *item) const
+{
+ item->fix_length_and_dec_double();
+ return false;
+}
+
+/***************************************************************************/
+
+bool Type_handler_row::
+ Item_func_mod_fix_length_and_dec(Item_func_mod *item) const
+{
+ DBUG_ASSERT(0);
+ return true;
+}
+
+
+bool Type_handler_int_result::
+ Item_func_mod_fix_length_and_dec(Item_func_mod *item) const
+{
+ item->fix_length_and_dec_int();
+ return false;
+}
+
+
+bool Type_handler_real_result::
+ Item_func_mod_fix_length_and_dec(Item_func_mod *item) const
+{
+ item->fix_length_and_dec_double();
+ return false;
+}
+
+
+bool Type_handler_decimal_result::
+ Item_func_mod_fix_length_and_dec(Item_func_mod *item) const
+{
+ item->fix_length_and_dec_decimal();
+ return false;
+}
+
+
+bool Type_handler_temporal_result::
+ Item_func_mod_fix_length_and_dec(Item_func_mod *item) const
+{
+ item->fix_length_and_dec_temporal(true);
+ return false;
+}
+
+
+bool Type_handler_string_result::
+ Item_func_mod_fix_length_and_dec(Item_func_mod *item) const
+{
+ item->fix_length_and_dec_double();
+ return false;
+}
+
+/***************************************************************************/
+
+uint Type_handler::Item_time_precision(Item *item) const
+{
+ return MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS);
+}
+
+
+uint Type_handler::Item_datetime_precision(Item *item) const
+{
+ return MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS);
+}
+
+
+uint Type_handler_string_result::Item_temporal_precision(Item *item,
+ bool is_time) const
+{
+ MYSQL_TIME ltime;
+ StringBuffer<64> buf;
+ String *tmp;
+ MYSQL_TIME_STATUS status;
+ DBUG_ASSERT(item->fixed);
+ if ((tmp= item->val_str(&buf)) &&
+ !(is_time ?
+ str_to_time(tmp->charset(), tmp->ptr(), tmp->length(),
+ &ltime, TIME_TIME_ONLY, &status) :
+ str_to_datetime(tmp->charset(), tmp->ptr(), tmp->length(),
+ &ltime, TIME_FUZZY_DATES, &status)))
+ return MY_MIN(status.precision, TIME_SECOND_PART_DIGITS);
+ return MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS);
+}
+
+/***************************************************************************/
+
+uint Type_handler::Item_decimal_scale(const Item *item) const
+{
+ return item->decimals < NOT_FIXED_DEC ?
+ item->decimals :
+ MY_MIN(item->max_length, DECIMAL_MAX_SCALE);
+}
+
+uint Type_handler_temporal_result::
+ Item_decimal_scale_with_seconds(const Item *item) const
+{
+ return item->decimals < NOT_FIXED_DEC ?
+ item->decimals :
+ TIME_SECOND_PART_DIGITS;
+}
+
+uint Type_handler::Item_divisor_precision_increment(const Item *item) const
+{
+ return item->decimals;
+}
+
+uint Type_handler_temporal_result::
+ Item_divisor_precision_increment_with_seconds(const Item *item) const
+{
+ return item->decimals < NOT_FIXED_DEC ?
+ item->decimals :
+ TIME_SECOND_PART_DIGITS;
+}
+
+/***************************************************************************/
+
+uint Type_handler_string_result::Item_decimal_precision(const Item *item) const
+{
+ uint res= item->max_char_length();
+ /*
+ Return at least one decimal digit, even if Item::max_char_length()
+ returned 0. This is important to avoid attempts to create fields of types
+ INT(0) or DECIMAL(0,0) when converting NULL or empty strings to INT/DECIMAL:
+ CREATE TABLE t1 AS SELECT CONVERT(NULL,SIGNED) AS a;
+ */
+ return res ? MY_MIN(res, DECIMAL_MAX_PRECISION) : 1;
+}
+
+uint Type_handler_real_result::Item_decimal_precision(const Item *item) const
+{
+ uint res= item->max_char_length();
+ return res ? MY_MIN(res, DECIMAL_MAX_PRECISION) : 1;
+}
+
+uint Type_handler_decimal_result::Item_decimal_precision(const Item *item) const
+{
+ uint prec= my_decimal_length_to_precision(item->max_char_length(),
+ item->decimals,
+ item->unsigned_flag);
+ return MY_MIN(prec, DECIMAL_MAX_PRECISION);
+}
+
+uint Type_handler_int_result::Item_decimal_precision(const Item *item) const
+{
+ uint prec= my_decimal_length_to_precision(item->max_char_length(),
+ item->decimals,
+ item->unsigned_flag);
+ return MY_MIN(prec, DECIMAL_MAX_PRECISION);
+}
+
+uint Type_handler_time_common::Item_decimal_precision(const Item *item) const
+{
+ return 7 + MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS);
+}
+
+uint Type_handler_date_common::Item_decimal_precision(const Item *item) const
+{
+ return 8;
+}
+
+uint Type_handler_datetime_common::Item_decimal_precision(const Item *item) const
+{
+ return 14 + MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS);
+}
+
+uint Type_handler_timestamp_common::Item_decimal_precision(const Item *item) const
+{
+ return 14 + MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS);
+}
+
+/***************************************************************************/
+
+bool Type_handler_real_result::
+ subquery_type_allows_materialization(const Item *inner,
+ const Item *outer) const
+{
+ DBUG_ASSERT(inner->cmp_type() == REAL_RESULT);
+ return outer->cmp_type() == REAL_RESULT;
+}
+
+
+bool Type_handler_int_result::
+ subquery_type_allows_materialization(const Item *inner,
+ const Item *outer) const
+{
+ DBUG_ASSERT(inner->cmp_type() == INT_RESULT);
+ return outer->cmp_type() == INT_RESULT;
+}
+
+
+bool Type_handler_decimal_result::
+ subquery_type_allows_materialization(const Item *inner,
+ const Item *outer) const
+{
+ DBUG_ASSERT(inner->cmp_type() == DECIMAL_RESULT);
+ return outer->cmp_type() == DECIMAL_RESULT;
+}
+
+
+bool Type_handler_string_result::
+ subquery_type_allows_materialization(const Item *inner,
+ const Item *outer) const
+{
+ DBUG_ASSERT(inner->cmp_type() == STRING_RESULT);
+ return outer->cmp_type() == STRING_RESULT &&
+ outer->collation.collation == inner->collation.collation &&
+ /*
+ Materialization also is unable to work when create_tmp_table() will
+ create a blob column because item->max_length is too big.
+ The following test is copied from varstring_type_handler().
+ */
+ !inner->too_big_for_varchar();
+}
+
+
+bool Type_handler_temporal_result::
+ subquery_type_allows_materialization(const Item *inner,
+ const Item *outer) const
+{
+ DBUG_ASSERT(inner->cmp_type() == TIME_RESULT);
+ return mysql_timestamp_type() ==
+ outer->type_handler()->mysql_timestamp_type();
+}
+
+/***************************************************************************/
+
+
+const Type_handler *
+Type_handler_null::type_handler_for_tmp_table(const Item *item) const
+{
+ return &type_handler_string;
+}
+
+
+const Type_handler *
+Type_handler_null::type_handler_for_union(const Item *item) const
+{
+ return &type_handler_string;
+}
+
+
+const Type_handler *
+Type_handler_olddecimal::type_handler_for_tmp_table(const Item *item) const
+{
+ return &type_handler_newdecimal;
+}
+
+const Type_handler *
+Type_handler_olddecimal::type_handler_for_union(const Item *item) const
+{
+ return &type_handler_newdecimal;
+}
+
+
+/***************************************************************************/
+
+bool Type_handler::check_null(const Item *item, st_value *value) const
+{
+ if (item->null_value)
+ {
+ value->m_type= DYN_COL_NULL;
+ return true;
+ }
+ return false;
+}
+
+
+bool Type_handler_null::
+ Item_save_in_value(Item *item, st_value *value) const
+{
+ value->m_type= DYN_COL_NULL;
+ return true;
+}
+
+
+bool Type_handler_row::
+ Item_save_in_value(Item *item, st_value *value) const
+{
+ DBUG_ASSERT(0);
+ value->m_type= DYN_COL_NULL;
+ return true;
+}
+
+
+bool Type_handler_int_result::
+ Item_save_in_value(Item *item, st_value *value) const
+{
+ value->m_type= item->unsigned_flag ? DYN_COL_UINT : DYN_COL_INT;
+ value->value.m_longlong= item->val_int();
+ return check_null(item, value);
+}
+
+
+bool Type_handler_real_result::
+ Item_save_in_value(Item *item, st_value *value) const
+{
+ value->m_type= DYN_COL_DOUBLE;
+ value->value.m_double= item->val_real();
+ return check_null(item, value);
+}
+
+
+bool Type_handler_decimal_result::
+ Item_save_in_value(Item *item, st_value *value) const
+{
+ value->m_type= DYN_COL_DECIMAL;
+ my_decimal *dec= item->val_decimal(&value->m_decimal);
+ if (dec != &value->m_decimal && !item->null_value)
+ my_decimal2decimal(dec, &value->m_decimal);
+ return check_null(item, value);
+}
+
+
+bool Type_handler_string_result::
+ Item_save_in_value(Item *item, st_value *value) const
+{
+ value->m_type= DYN_COL_STRING;
+ String *str= item->val_str(&value->m_string);
+ if (str != &value->m_string && !item->null_value)
+ value->m_string.set(str->ptr(), str->length(), str->charset());
+ return check_null(item, value);
+}
+
+
+bool Type_handler_temporal_with_date::
+ Item_save_in_value(Item *item, st_value *value) const
+{
+ value->m_type= DYN_COL_DATETIME;
+ item->get_date(&value->value.m_time, sql_mode_for_dates(current_thd));
+ return check_null(item, value);
+}
+
+
+bool Type_handler_time_common::
+ Item_save_in_value(Item *item, st_value *value) const
+{
+ value->m_type= DYN_COL_DATETIME;
+ item->get_time(&value->value.m_time);
+ return check_null(item, value);
+}
+
+/***************************************************************************/
+
+bool Type_handler_row::
+ Item_param_set_from_value(THD *thd,
+ Item_param *param,
+ const Type_all_attributes *attr,
+ const st_value *val) const
+{
+ DBUG_ASSERT(0);
+ param->set_null();
+ return true;
+}
+
+
+bool Type_handler_real_result::
+ Item_param_set_from_value(THD *thd,
+ Item_param *param,
+ const Type_all_attributes *attr,
+ const st_value *val) const
+{
+ param->unsigned_flag= attr->unsigned_flag;
+ param->set_double(val->value.m_double);
+ return false;
+}
+
+
+bool Type_handler_int_result::
+ Item_param_set_from_value(THD *thd,
+ Item_param *param,
+ const Type_all_attributes *attr,
+ const st_value *val) const
+{
+ param->unsigned_flag= attr->unsigned_flag;
+ param->set_int(val->value.m_longlong, attr->max_length);
+ return false;
+}
+
+
+bool Type_handler_decimal_result::
+ Item_param_set_from_value(THD *thd,
+ Item_param *param,
+ const Type_all_attributes *attr,
+ const st_value *val) const
+{
+ param->unsigned_flag= attr->unsigned_flag;
+ param->set_decimal(&val->m_decimal, attr->unsigned_flag);
+ return false;
+}
+
+
+bool Type_handler_string_result::
+ 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_string(thd, attr->collation.collation);
+ /*
+ Exact value of max_length is not known unless data is converted to
+ charset of connection, so we have to set it later.
+ */
+ return param->set_str(val->m_string.ptr(), val->m_string.length(),
+ attr->collation.collation,
+ attr->collation.collation);
+}
+
+
+bool Type_handler_temporal_result::
+ Item_param_set_from_value(THD *thd,
+ Item_param *param,
+ const Type_all_attributes *attr,
+ const st_value *val) const
+{
+ param->unsigned_flag= attr->unsigned_flag;
+ param->set_time(&val->value.m_time, attr->max_length, attr->decimals);
+ return false;
+}
+
+
+#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::
+ Item_send(Item *item, Protocol *protocol, st_value *buf) const
+{
+ return protocol->store_null();
+}
+
+
+bool Type_handler::
+ Item_send_str(Item *item, Protocol *protocol, st_value *buf) const
+{
+ String *res;
+ if ((res= item->val_str(&buf->m_string)))
+ {
+ DBUG_ASSERT(!item->null_value);
+ return protocol->store(res->ptr(), res->length(), res->charset());
+ }
+ DBUG_ASSERT(item->null_value);
+ return protocol->store_null();
+}
+
+
+bool Type_handler::
+ Item_send_tiny(Item *item, Protocol *protocol, st_value *buf) const
+{
+ longlong nr= item->val_int();
+ if (!item->null_value)
+ return protocol->store_tiny(nr);
+ return protocol->store_null();
+}
+
+
+bool Type_handler::
+ Item_send_short(Item *item, Protocol *protocol, st_value *buf) const
+{
+ longlong nr= item->val_int();
+ if (!item->null_value)
+ return protocol->store_short(nr);
+ return protocol->store_null();
+}
+
+
+bool Type_handler::
+ Item_send_long(Item *item, Protocol *protocol, st_value *buf) const
+{
+ longlong nr= item->val_int();
+ if (!item->null_value)
+ return protocol->store_long(nr);
+ return protocol->store_null();
+}
+
+bool Type_handler::
+ Item_send_longlong(Item *item, Protocol *protocol, st_value *buf) const
+{
+ longlong nr= item->val_int();
+ if (!item->null_value)
+ return protocol->store_longlong(nr, item->unsigned_flag);
+ return protocol->store_null();
+}
+
+
+bool Type_handler::
+ Item_send_float(Item *item, Protocol *protocol, st_value *buf) const
+{
+ float nr= (float) item->val_real();
+ if (!item->null_value)
+ return protocol->store(nr, item->decimals, &buf->m_string);
+ return protocol->store_null();
+}
+
+
+bool Type_handler::
+ Item_send_double(Item *item, Protocol *protocol, st_value *buf) const
+{
+ double nr= item->val_real();
+ if (!item->null_value)
+ return protocol->store(nr, item->decimals, &buf->m_string);
+ return protocol->store_null();
+}
+
+
+bool Type_handler::
+ Item_send_datetime(Item *item, Protocol *protocol, st_value *buf) const
+{
+ item->get_date(&buf->value.m_time, sql_mode_for_dates(current_thd));
+ if (!item->null_value)
+ return protocol->store(&buf->value.m_time, item->decimals);
+ return protocol->store_null();
+}
+
+
+bool Type_handler::
+ Item_send_date(Item *item, Protocol *protocol, st_value *buf) const
+{
+ item->get_date(&buf->value.m_time, sql_mode_for_dates(current_thd));
+ if (!item->null_value)
+ return protocol->store_date(&buf->value.m_time);
+ return protocol->store_null();
+}
+
+
+bool Type_handler::
+ Item_send_time(Item *item, Protocol *protocol, st_value *buf) const
+{
+ item->get_time(&buf->value.m_time);
+ if (!item->null_value)
+ return protocol->store_time(&buf->value.m_time, item->decimals);
+ return protocol->store_null();
+}
+
+/***************************************************************************/
+
+Item *Type_handler_int_result::
+ make_const_item_for_comparison(THD *thd, Item *item, const Item *cmp) const
+{
+ longlong result= item->val_int();
+ if (item->null_value)
+ return new (thd->mem_root) Item_null(thd, item->name.str);
+ return new (thd->mem_root) Item_int(thd, item->name.str, result,
+ item->max_length);
+}
+
+
+Item *Type_handler_real_result::
+ make_const_item_for_comparison(THD *thd, Item *item, const Item *cmp) const
+{
+ double result= item->val_real();
+ if (item->null_value)
+ return new (thd->mem_root) Item_null(thd, item->name.str);
+ return new (thd->mem_root) Item_float(thd, item->name.str, result,
+ item->decimals, item->max_length);
+}
+
+
+Item *Type_handler_decimal_result::
+ make_const_item_for_comparison(THD *thd, Item *item, const Item *cmp) const
+{
+ my_decimal decimal_value;
+ my_decimal *result= item->val_decimal(&decimal_value);
+ if (item->null_value)
+ return new (thd->mem_root) Item_null(thd, item->name.str);
+ return new (thd->mem_root) Item_decimal(thd, item->name.str, result,
+ item->max_length, item->decimals);
+}
+
+
+Item *Type_handler_string_result::
+ make_const_item_for_comparison(THD *thd, Item *item, const Item *cmp) const
+{
+ StringBuffer<MAX_FIELD_WIDTH> tmp;
+ 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());
+}
+
+
+Item *Type_handler_time_common::
+ make_const_item_for_comparison(THD *thd, Item *item, const Item *cmp) const
+{
+ Item_cache_temporal *cache;
+ longlong value= item->val_time_packed();
+ if (item->null_value)
+ return new (thd->mem_root) Item_null(thd, item->name.str);
+ cache= new (thd->mem_root) Item_cache_time(thd);
+ if (cache)
+ cache->store_packed(value, item);
+ return cache;
+}
+
+
+Item *Type_handler_temporal_with_date::
+ make_const_item_for_comparison(THD *thd, Item *item, const Item *cmp) const
+{
+ Item_cache_temporal *cache;
+ longlong value= item->val_datetime_packed();
+ if (item->null_value)
+ return new (thd->mem_root) Item_null(thd, item->name.str);
+ cache= new (thd->mem_root) Item_cache_datetime(thd);
+ if (cache)
+ cache->store_packed(value, item);
+ return cache;
+}
+
+
+Item *Type_handler_row::
+ make_const_item_for_comparison(THD *thd, Item *item, const Item *cmp) const
+{
+ if (item->type() == Item::ROW_ITEM && cmp->type() == Item::ROW_ITEM)
+ {
+ /*
+ Substitute constants only in Item_row's. Don't affect other Items
+ with ROW_RESULT (eg Item_singlerow_subselect).
+
+ For such Items more optimal is to detect if it is constant and replace
+ it with Item_row. This would optimize queries like this:
+ SELECT * FROM t1 WHERE (a,b) = (SELECT a,b FROM t2 LIMIT 1);
+ */
+ Item_row *item_row= (Item_row*) item;
+ Item_row *comp_item_row= (Item_row*) cmp;
+ uint col;
+ /*
+ If item and comp_item are both Item_row's and have same number of cols
+ then process items in Item_row one by one.
+ We can't ignore NULL values here as this item may be used with <=>, in
+ which case NULL's are significant.
+ */
+ DBUG_ASSERT(item->result_type() == cmp->result_type());
+ DBUG_ASSERT(item_row->cols() == comp_item_row->cols());
+ col= item_row->cols();
+ while (col-- > 0)
+ resolve_const_item(thd, item_row->addr(col),
+ comp_item_row->element_index(col));
+ }
+ return NULL;
+}
+
+/***************************************************************************/
+
+static const char* item_name(Item *a, String *str)
+{
+ if (a->name.str)
+ return a->name.str;
+ str->length(0);
+ a->print(str, QT_ORDINARY);
+ return str->c_ptr_safe();
+}
+
+
+static void wrong_precision_error(uint errcode, Item *a,
+ ulonglong number, uint maximum)
+{
+ StringBuffer<1024> buf(system_charset_info);
+ my_error(errcode, MYF(0), number, item_name(a, &buf), maximum);
+}
+
+
+/**
+ Get precision and scale for a declaration
+
+ return
+ 0 ok
+ 1 error
+*/
+
+bool get_length_and_scale(ulonglong length, ulonglong decimals,
+ uint *out_length, uint *out_decimals,
+ uint max_precision, uint max_scale,
+ Item *a)
+{
+ if (length > (ulonglong) max_precision)
+ {
+ wrong_precision_error(ER_TOO_BIG_PRECISION, a, length, max_precision);
+ return 1;
+ }
+ if (decimals > (ulonglong) max_scale)
+ {
+ wrong_precision_error(ER_TOO_BIG_SCALE, a, decimals, max_scale);
+ return 1;
+ }
+
+ *out_decimals= (uint) decimals;
+ my_decimal_trim(&length, out_decimals);
+ *out_length= (uint) length;
+
+ if (*out_length < *out_decimals)
+ {
+ my_error(ER_M_BIGGER_THAN_D, MYF(0), "");
+ return 1;
+ }
+ return 0;
+}
+
+
+Item *Type_handler_longlong::
+ create_typecast_item(THD *thd, Item *item,
+ const Type_cast_attributes &attr) const
+{
+ if (this != &type_handler_ulonglong)
+ return new (thd->mem_root) Item_func_signed(thd, item);
+ return new (thd->mem_root) Item_func_unsigned(thd, item);
+
+}
+
+
+Item *Type_handler_date_common::
+ create_typecast_item(THD *thd, Item *item,
+ const Type_cast_attributes &attr) const
+{
+ return new (thd->mem_root) Item_date_typecast(thd, item);
+}
+
+
+
+Item *Type_handler_time_common::
+ create_typecast_item(THD *thd, Item *item,
+ const Type_cast_attributes &attr) const
+{
+ if (attr.decimals() > MAX_DATETIME_PRECISION)
+ {
+ wrong_precision_error(ER_TOO_BIG_PRECISION, item, attr.decimals(),
+ MAX_DATETIME_PRECISION);
+ return 0;
+ }
+ return new (thd->mem_root)
+ Item_time_typecast(thd, item, (uint) attr.decimals());
+}
+
+
+Item *Type_handler_datetime_common::
+ create_typecast_item(THD *thd, Item *item,
+ const Type_cast_attributes &attr) const
+{
+ if (attr.decimals() > MAX_DATETIME_PRECISION)
+ {
+ wrong_precision_error(ER_TOO_BIG_PRECISION, item, attr.decimals(),
+ MAX_DATETIME_PRECISION);
+ return 0;
+ }
+ return new (thd->mem_root)
+ Item_datetime_typecast(thd, item, (uint) attr.decimals());
+
+}
+
+
+Item *Type_handler_decimal_result::
+ create_typecast_item(THD *thd, Item *item,
+ const Type_cast_attributes &attr) const
+{
+ uint len, dec;
+ if (get_length_and_scale(attr.length(), attr.decimals(), &len, &dec,
+ DECIMAL_MAX_PRECISION, DECIMAL_MAX_SCALE, item))
+ return NULL;
+ return new (thd->mem_root) Item_decimal_typecast(thd, item, len, dec);
+}
+
+
+Item *Type_handler_double::
+ create_typecast_item(THD *thd, Item *item,
+ const Type_cast_attributes &attr) const
+{
+ uint len, dec;
+ if (!attr.length_specified())
+ return new (thd->mem_root) Item_double_typecast(thd, item,
+ DBL_DIG + 7,
+ NOT_FIXED_DEC);
+
+ if (get_length_and_scale(attr.length(), attr.decimals(), &len, &dec,
+ DECIMAL_MAX_PRECISION, NOT_FIXED_DEC - 1, item))
+ return NULL;
+ return new (thd->mem_root) Item_double_typecast(thd, item, len, dec);
+}
+
+
+Item *Type_handler_float::
+ create_typecast_item(THD *thd, Item *item,
+ const Type_cast_attributes &attr) const
+{
+ DBUG_ASSERT(!attr.length_specified());
+ return new (thd->mem_root) Item_float_typecast(thd, item);
+}
+
+
+Item *Type_handler_long_blob::
+ create_typecast_item(THD *thd, Item *item,
+ const Type_cast_attributes &attr) const
+{
+ int len= -1;
+ CHARSET_INFO *real_cs= attr.charset() ?
+ attr.charset() :
+ thd->variables.collation_connection;
+ if (attr.length_specified())
+ {
+ if (attr.length() > MAX_FIELD_BLOBLENGTH)
+ {
+ char buff[1024];
+ String buf(buff, sizeof(buff), system_charset_info);
+ my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), item_name(item, &buf),
+ MAX_FIELD_BLOBLENGTH);
+ return NULL;
+ }
+ len= (int) attr.length();
+ }
+ return new (thd->mem_root) Item_char_typecast(thd, item, len, real_cs);
+}
+
+/***************************************************************************/
+
+void Type_handler_string_result::Item_param_setup_conversion(THD *thd,
+ Item_param *param)
+ const
+{
+ param->setup_conversion_string(thd, thd->variables.character_set_client);
+}
+
+
+void Type_handler_blob_common::Item_param_setup_conversion(THD *thd,
+ Item_param *param)
+ const
+{
+ param->setup_conversion_blob(thd);
+}
+
+
+void Type_handler_tiny::Item_param_set_param_func(Item_param *param,
+ uchar **pos, ulong len) const
+{
+ param->set_param_tiny(pos, len);
+}
+
+
+void Type_handler_short::Item_param_set_param_func(Item_param *param,
+ uchar **pos, ulong len) const
+{
+ param->set_param_short(pos, len);
+}
+
+
+void Type_handler_long::Item_param_set_param_func(Item_param *param,
+ uchar **pos, ulong len) const
+{
+ param->set_param_int32(pos, len);
+}
+
+
+void Type_handler_longlong::Item_param_set_param_func(Item_param *param,
+ uchar **pos,
+ ulong len) const
+{
+ param->set_param_int64(pos, len);
+}
+
+
+void Type_handler_float::Item_param_set_param_func(Item_param *param,
+ uchar **pos,
+ ulong len) const
+{
+ param->set_param_float(pos, len);
+}
+
+
+void Type_handler_double::Item_param_set_param_func(Item_param *param,
+ uchar **pos,
+ ulong len) const
+{
+ param->set_param_double(pos, len);
+}
+
+
+void Type_handler_decimal_result::Item_param_set_param_func(Item_param *param,
+ uchar **pos,
+ ulong len) const
+{
+ param->set_param_decimal(pos, len);
+}
+
+
+void Type_handler_string_result::Item_param_set_param_func(Item_param *param,
+ uchar **pos,
+ ulong len) const
+{
+ param->set_param_str(pos, len);
+}
+
+
+void Type_handler_time_common::Item_param_set_param_func(Item_param *param,
+ uchar **pos,
+ ulong len) const
+{
+ param->set_param_time(pos, len);
+}
+
+
+void Type_handler_date_common::Item_param_set_param_func(Item_param *param,
+ uchar **pos,
+ ulong len) const
+{
+ param->set_param_date(pos, len);
+}
+
+
+void Type_handler_datetime_common::Item_param_set_param_func(Item_param *param,
+ uchar **pos,
+ ulong len) const
+{
+ param->set_param_datetime(pos, len);
+}
+
+Field *Type_handler_blob_common::make_conversion_table_field(TABLE *table,
+ uint metadata,
+ const Field *target)
+ const
+{
+ uint pack_length= metadata & 0x00ff;
+ if (pack_length < 1 || pack_length > 4)
+ return NULL; // Broken binary log?
+ return new(table->in_use->mem_root)
+ Field_blob(NULL, (uchar *) "", 1, Field::NONE, &empty_clex_str,
+ table->s, pack_length, target->charset());
+}
+
+
+void Type_handler_timestamp_common::Item_param_set_param_func(Item_param *param,
+ uchar **pos,
+ ulong len) const
+{
+ param->set_param_datetime(pos, len);
+}
+
+
+void Type_handler::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
+}
+
+
+void Type_handler_typelib::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
+}
+
+
+#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
+
+/***************************************************************************/
+
+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;
+}
+
+
+bool Type_handler_general_purpose_int::
+ Vers_history_point_resolve_unit(THD *thd,
+ Vers_history_point *point) const
+{
+ return point->resolve_unit_trx_id(thd);
+}
+
+
+bool Type_handler_bit::
+ Vers_history_point_resolve_unit(THD *thd,
+ Vers_history_point *point) const
+{
+ return point->resolve_unit_trx_id(thd);
+}
+
+
+bool Type_handler_temporal_result::
+ Vers_history_point_resolve_unit(THD *thd,
+ Vers_history_point *point) const
+{
+ return point->resolve_unit_timestamp(thd);
+}
+
+
+bool Type_handler_general_purpose_string::
+ Vers_history_point_resolve_unit(THD *thd,
+ Vers_history_point *point) const
+{
+ return point->resolve_unit_timestamp(thd);
+}
+
+/***************************************************************************/
diff --git a/sql/sql_type.h b/sql/sql_type.h
index 42090037ead..f20ccff0752 100644
--- a/sql/sql_type.h
+++ b/sql/sql_type.h
@@ -22,35 +22,1136 @@
#include "mysqld.h"
+#include "sql_array.h"
+#include "sql_const.h"
+#include "sql_time.h"
+#include "sql_type_real.h"
class Field;
+class Column_definition;
class Item;
-class Type_std_attributes;
+class Item_param;
+class Item_cache;
+class Item_func_or_sum;
+class Item_sum_hybrid;
+class Item_sum_sum;
+class Item_sum_avg;
+class Item_sum_variance;
+class Item_func_hex;
+class Item_hybrid_func;
+class Item_func_min_max;
+class Item_func_hybrid_field_type;
+class Item_bool_func2;
+class Item_func_between;
+class Item_func_in;
+class Item_func_round;
+class Item_func_int_val;
+class Item_func_abs;
+class Item_func_neg;
+class Item_func_signed;
+class Item_func_unsigned;
+class Item_double_typecast;
+class Item_float_typecast;
+class Item_decimal_typecast;
+class Item_char_typecast;
+class Item_time_typecast;
+class Item_date_typecast;
+class Item_datetime_typecast;
+class Item_func_plus;
+class Item_func_minus;
+class Item_func_mul;
+class Item_func_div;
+class Item_func_mod;
+class cmp_item;
+class in_vector;
+class Type_handler_hybrid_field_type;
class Sort_param;
+class Arg_comparator;
+class Spvar_definition;
+struct st_value;
+class Protocol;
+class handler;
+struct Schema_specification_st;
struct TABLE;
struct SORT_FIELD_ATTR;
+class Vers_history_point;
+class Schema;
+
+
+/**
+ Class Time is designed to store valid TIME values.
+
+ 1. Valid value:
+ a. MYSQL_TIMESTAMP_TIME - a valid TIME within the supported TIME range
+ b. MYSQL_TIMESTAMP_NONE - an undefined value
+
+ 2. Invalid value (internally only):
+ a. MYSQL_TIMESTAMP_TIME outside of the supported TIME range
+ a. MYSQL_TIMESTAMP_{DATE|DATETIME|ERROR}
+
+ Temporarily Time is allowed to have an invalid value, but only internally,
+ during initialization time. All constructors and modification methods must
+ leave the Time value as described above (see "Valid values").
+
+ Time derives from MYSQL_TIME privately to make sure it is accessed
+ externally only in the valid state.
+*/
+class Time: private MYSQL_TIME
+{
+public:
+ enum datetime_to_time_mode_t
+ {
+ DATETIME_TO_TIME_YYYYMMDD_000000DD_MIX_TO_HOURS,
+ DATETIME_TO_TIME_YYYYMMDD_TRUNCATE
+ };
+ class Options
+ {
+ sql_mode_t m_get_date_flags;
+ datetime_to_time_mode_t m_datetime_to_time_mode;
+ public:
+ Options()
+ :m_get_date_flags(flags_for_get_date()),
+ m_datetime_to_time_mode(DATETIME_TO_TIME_YYYYMMDD_000000DD_MIX_TO_HOURS)
+ { }
+ Options(sql_mode_t flags)
+ :m_get_date_flags(flags),
+ m_datetime_to_time_mode(DATETIME_TO_TIME_YYYYMMDD_000000DD_MIX_TO_HOURS)
+ { }
+ Options(sql_mode_t flags, datetime_to_time_mode_t dtmode)
+ :m_get_date_flags(flags),
+ m_datetime_to_time_mode(dtmode)
+ { }
+ sql_mode_t get_date_flags() const
+ { return m_get_date_flags; }
+ datetime_to_time_mode_t datetime_to_time_mode() const
+ { return m_datetime_to_time_mode; }
+ };
+ /*
+ CAST(AS TIME) historically does not mix days to hours.
+ This is different comparing to how implicit conversion
+ in Field::store_time_dec() works (e.g. on INSERT).
+ */
+ class Options_for_cast: public Options
+ {
+ public:
+ Options_for_cast()
+ :Options(flags_for_get_date(), DATETIME_TO_TIME_YYYYMMDD_TRUNCATE)
+ { }
+ };
+private:
+ bool is_valid_value_slow() const
+ {
+ return time_type == MYSQL_TIMESTAMP_NONE || is_valid_time_slow();
+ }
+ bool is_valid_time_slow() const
+ {
+ return time_type == MYSQL_TIMESTAMP_TIME &&
+ year == 0 && month == 0 && day == 0 &&
+ minute <= TIME_MAX_MINUTE &&
+ second <= TIME_MAX_SECOND &&
+ second_part <= TIME_MAX_SECOND_PART;
+ }
+
+ /*
+ Convert a valid DATE or DATETIME to TIME.
+ Before this call, "this" must be a valid DATE or DATETIME value,
+ e.g. returned from Item::get_date().
+ After this call, "this" is a valid TIME value.
+ */
+ void valid_datetime_to_valid_time(const Options opt)
+ {
+ DBUG_ASSERT(time_type == MYSQL_TIMESTAMP_DATE ||
+ time_type == MYSQL_TIMESTAMP_DATETIME);
+ /*
+ Make sure that day and hour are valid, so the result hour value
+ after mixing days to hours does not go out of the valid TIME range.
+ */
+ DBUG_ASSERT(day < 32);
+ DBUG_ASSERT(hour < 24);
+ if (year == 0 && month == 0 &&
+ opt.datetime_to_time_mode() ==
+ DATETIME_TO_TIME_YYYYMMDD_000000DD_MIX_TO_HOURS)
+ {
+ /*
+ The maximum hour value after mixing days will be 31*24+23=767,
+ which is within the supported TIME range.
+ Thus no adjust_time_range_or_invalidate() is needed here.
+ */
+ hour+= day * 24;
+ }
+ year= month= day= 0;
+ time_type= MYSQL_TIMESTAMP_TIME;
+ DBUG_ASSERT(is_valid_time_slow());
+ }
+ /**
+ Convert valid DATE/DATETIME to valid TIME if needed.
+ This method is called after Item::get_date(),
+ which can return only valid TIME/DATE/DATETIME values.
+ Before this call, "this" is:
+ - either a valid TIME/DATE/DATETIME value
+ (within the supported range for the corresponding type),
+ - or MYSQL_TIMESTAMP_NONE
+ After this call, "this" is:
+ - either a valid TIME (within the supported TIME range),
+ - or MYSQL_TIMESTAMP_NONE
+ */
+ void valid_MYSQL_TIME_to_valid_value(const Options opt)
+ {
+ switch (time_type) {
+ case MYSQL_TIMESTAMP_DATE:
+ case MYSQL_TIMESTAMP_DATETIME:
+ valid_datetime_to_valid_time(opt);
+ break;
+ case MYSQL_TIMESTAMP_NONE:
+ break;
+ case MYSQL_TIMESTAMP_ERROR:
+ set_zero_time(this, MYSQL_TIMESTAMP_TIME);
+ break;
+ case MYSQL_TIMESTAMP_TIME:
+ DBUG_ASSERT(is_valid_time_slow());
+ break;
+ }
+ }
+ void make_from_item(class Item *item, const Options opt);
+public:
+ Time() { time_type= MYSQL_TIMESTAMP_NONE; }
+ Time(Item *item) { make_from_item(item, Options()); }
+ Time(Item *item, const Options opt) { make_from_item(item, opt); }
+ static sql_mode_t flags_for_get_date()
+ { return TIME_TIME_ONLY | TIME_INVALID_DATES; }
+ static sql_mode_t comparison_flags_for_get_date()
+ { return TIME_TIME_ONLY | TIME_INVALID_DATES | TIME_FUZZY_DATES; }
+ bool is_valid_time() const
+ {
+ DBUG_ASSERT(is_valid_value_slow());
+ return time_type == MYSQL_TIMESTAMP_TIME;
+ }
+ const MYSQL_TIME *get_mysql_time() const
+ {
+ DBUG_ASSERT(is_valid_time_slow());
+ return this;
+ }
+ bool copy_to_mysql_time(MYSQL_TIME *ltime) const
+ {
+ if (time_type == MYSQL_TIMESTAMP_NONE)
+ {
+ ltime->time_type= MYSQL_TIMESTAMP_NONE;
+ return true;
+ }
+ DBUG_ASSERT(is_valid_time_slow());
+ *ltime= *this;
+ return false;
+ }
+ int cmp(const Time *other) const
+ {
+ DBUG_ASSERT(is_valid_time_slow());
+ DBUG_ASSERT(other->is_valid_time_slow());
+ longlong p0= pack_time(this);
+ longlong p1= pack_time(other);
+ if (p0 < p1)
+ return -1;
+ if (p0 > p1)
+ return 1;
+ return 0;
+ }
+ longlong to_seconds_abs() const
+ {
+ DBUG_ASSERT(is_valid_time_slow());
+ return hour * 3600L + minute * 60 + second;
+ }
+ longlong to_seconds() const
+ {
+ return neg ? -to_seconds_abs() : to_seconds_abs();
+ }
+};
+
+
+/**
+ Class Temporal_with_date is designed to store valid DATE or DATETIME values.
+ See also class Time.
+
+ 1. Valid value:
+ a. MYSQL_TIMESTAMP_{DATE|DATETIME} - a valid DATE or DATETIME value
+ b. MYSQL_TIMESTAMP_NONE - an undefined value
+
+ 2. Invalid value (internally only):
+ a. MYSQL_TIMESTAMP_{DATE|DATETIME} - a DATE or DATETIME value, but with
+ MYSQL_TIME members outside of the
+ valid/supported range
+ b. MYSQL_TIMESTAMP_TIME - a TIME value
+ c. MYSQL_TIMESTAMP_ERROR - error
+
+ Temporarily is allowed to have an invalid value, but only internally,
+ during initialization time. All constructors and modification methods must
+ leave the value as described above (see "Valid value").
+
+ Derives from MYSQL_TIME using "protected" inheritance to make sure
+ it is accessed externally only in the valid state.
+*/
+
+class Temporal_with_date: protected MYSQL_TIME
+{
+protected:
+ void make_from_item(THD *thd, Item *item, sql_mode_t flags);
+
+ ulong daynr() const
+ {
+ return (ulong) ::calc_daynr((uint) year, (uint) month, (uint) day);
+ }
+ int weekday(bool sunday_first_day_of_week) const
+ {
+ return ::calc_weekday(daynr(), sunday_first_day_of_week);
+ }
+ Temporal_with_date(THD *thd, Item *item, sql_mode_t flags)
+ {
+ make_from_item(thd, item, flags);
+ }
+};
+
+
+/**
+ Class Date is designed to store valid DATE values.
+ All constructors and modification methods leave instances
+ of this class in one of the following valid states:
+ a. MYSQL_TIMESTAMP_DATE - a DATE with all MYSQL_TIME members properly set
+ b. MYSQL_TIMESTAMP_NONE - an undefined value.
+ Other MYSQL_TIMESTAMP_XXX are not possible.
+ MYSQL_TIMESTAMP_DATE with MYSQL_TIME members improperly set is not possible.
+*/
+class Date: public Temporal_with_date
+{
+ bool is_valid_value_slow() const
+ {
+ return time_type == MYSQL_TIMESTAMP_NONE || is_valid_date_slow();
+ }
+ bool is_valid_date_slow() const
+ {
+ DBUG_ASSERT(time_type == MYSQL_TIMESTAMP_DATE);
+ return !check_datetime_range(this);
+ }
+public:
+ Date(THD *thd, Item *item, sql_mode_t flags)
+ :Temporal_with_date(thd, item, flags)
+ {
+ if (time_type == MYSQL_TIMESTAMP_DATETIME)
+ datetime_to_date(this);
+ DBUG_ASSERT(is_valid_value_slow());
+ }
+ Date(const Temporal_with_date *d)
+ :Temporal_with_date(*d)
+ {
+ datetime_to_date(this);
+ DBUG_ASSERT(is_valid_date_slow());
+ }
+ bool is_valid_date() const
+ {
+ DBUG_ASSERT(is_valid_value_slow());
+ return time_type == MYSQL_TIMESTAMP_DATE;
+ }
+ const MYSQL_TIME *get_mysql_time() const
+ {
+ DBUG_ASSERT(is_valid_date_slow());
+ return this;
+ }
+};
+
+
+/**
+ Class Datetime is designed to store valid DATETIME values.
+ All constructors and modification methods leave instances
+ of this class in one of the following valid states:
+ a. MYSQL_TIMESTAMP_DATETIME - a DATETIME with all members properly set
+ b. MYSQL_TIMESTAMP_NONE - an undefined value.
+ Other MYSQL_TIMESTAMP_XXX are not possible.
+ MYSQL_TIMESTAMP_DATETIME with MYSQL_TIME members
+ improperly set is not possible.
+*/
+class Datetime: public Temporal_with_date
+{
+ bool is_valid_value_slow() const
+ {
+ return time_type == MYSQL_TIMESTAMP_NONE || is_valid_datetime_slow();
+ }
+ bool is_valid_datetime_slow() const
+ {
+ DBUG_ASSERT(time_type == MYSQL_TIMESTAMP_DATETIME);
+ return !check_datetime_range(this);
+ }
+public:
+ Datetime(THD *thd, Item *item, sql_mode_t flags)
+ :Temporal_with_date(thd, item, flags)
+ {
+ if (time_type == MYSQL_TIMESTAMP_DATE)
+ date_to_datetime(this);
+ DBUG_ASSERT(is_valid_value_slow());
+ }
+ bool is_valid_datetime() const
+ {
+ /*
+ Here we quickly check for the type only.
+ If the type is valid, the rest of value must also be valid.
+ */
+ DBUG_ASSERT(is_valid_value_slow());
+ return time_type == MYSQL_TIMESTAMP_DATETIME;
+ }
+ bool hhmmssff_is_zero() const
+ {
+ DBUG_ASSERT(is_valid_datetime_slow());
+ return hour == 0 && minute == 0 && second == 0 && second_part == 0;
+ }
+ int weekday(bool sunday_first_day_of_week) const
+ {
+ DBUG_ASSERT(is_valid_datetime_slow());
+ return Temporal_with_date::weekday(sunday_first_day_of_week);
+ }
+ const MYSQL_TIME *get_mysql_time() const
+ {
+ DBUG_ASSERT(is_valid_datetime_slow());
+ return this;
+ }
+ bool copy_to_mysql_time(MYSQL_TIME *ltime) const
+ {
+ if (time_type == MYSQL_TIMESTAMP_NONE)
+ {
+ ltime->time_type= MYSQL_TIMESTAMP_NONE;
+ return true;
+ }
+ DBUG_ASSERT(is_valid_datetime_slow());
+ *ltime= *this;
+ return false;
+ }
+ /**
+ Copy without data loss, with an optional DATETIME to DATE conversion.
+ If the value of the "type" argument is MYSQL_TIMESTAMP_DATE,
+ then "this" must be a datetime with a zero hhmmssff part.
+ */
+ bool copy_to_mysql_time(MYSQL_TIME *ltime, timestamp_type type)
+ {
+ DBUG_ASSERT(type == MYSQL_TIMESTAMP_DATE ||
+ type == MYSQL_TIMESTAMP_DATETIME);
+ if (copy_to_mysql_time(ltime))
+ return true;
+ DBUG_ASSERT(type != MYSQL_TIMESTAMP_DATE || hhmmssff_is_zero());
+ ltime->time_type= type;
+ return false;
+ }
+};
+
+/*
+ Flags for collation aggregation modes, used in TDCollation::agg():
+
+ MY_COLL_ALLOW_SUPERSET_CONV - allow conversion to a superset
+ MY_COLL_ALLOW_COERCIBLE_CONV - allow conversion of a coercible value
+ (i.e. constant).
+ MY_COLL_ALLOW_CONV - allow any kind of conversion
+ (combination of the above two)
+ MY_COLL_ALLOW_NUMERIC_CONV - if all items were numbers, convert to
+ @@character_set_connection
+ MY_COLL_DISALLOW_NONE - don't allow return DERIVATION_NONE
+ (e.g. when aggregating for comparison)
+ MY_COLL_CMP_CONV - combination of MY_COLL_ALLOW_CONV
+ and MY_COLL_DISALLOW_NONE
+*/
+
+#define MY_COLL_ALLOW_SUPERSET_CONV 1
+#define MY_COLL_ALLOW_COERCIBLE_CONV 2
+#define MY_COLL_DISALLOW_NONE 4
+#define MY_COLL_ALLOW_NUMERIC_CONV 8
+
+#define MY_COLL_ALLOW_CONV (MY_COLL_ALLOW_SUPERSET_CONV | MY_COLL_ALLOW_COERCIBLE_CONV)
+#define MY_COLL_CMP_CONV (MY_COLL_ALLOW_CONV | MY_COLL_DISALLOW_NONE)
+
+
+#define my_charset_numeric my_charset_latin1
+#define MY_REPERTOIRE_NUMERIC MY_REPERTOIRE_ASCII
+
+
+enum Derivation
+{
+ DERIVATION_IGNORABLE= 6,
+ DERIVATION_NUMERIC= 5,
+ DERIVATION_COERCIBLE= 4,
+ DERIVATION_SYSCONST= 3,
+ DERIVATION_IMPLICIT= 2,
+ DERIVATION_NONE= 1,
+ DERIVATION_EXPLICIT= 0
+};
+
+
+/**
+ "Declared Type Collation"
+ A combination of collation and its derivation.
+*/
+
+class DTCollation {
+public:
+ CHARSET_INFO *collation;
+ enum Derivation derivation;
+ uint repertoire;
+
+ void set_repertoire_from_charset(CHARSET_INFO *cs)
+ {
+ repertoire= cs->state & MY_CS_PUREASCII ?
+ MY_REPERTOIRE_ASCII : MY_REPERTOIRE_UNICODE30;
+ }
+ DTCollation()
+ {
+ collation= &my_charset_bin;
+ derivation= DERIVATION_NONE;
+ repertoire= MY_REPERTOIRE_UNICODE30;
+ }
+ DTCollation(CHARSET_INFO *collation_arg)
+ {
+ /*
+ This constructor version is used in combination with Field constructors,
+ to pass "CHARSET_INFO" instead of the full DTCollation.
+ Therefore, derivation is set to DERIVATION_IMPLICIT, which is the
+ proper derivation for table fields.
+ We should eventually remove all code pieces that pass "CHARSET_INFO"
+ (e.g. in storage engine sources) and fix to pass the full DTCollation
+ instead. Then, this constructor can be removed.
+ */
+ collation= collation_arg;
+ derivation= DERIVATION_IMPLICIT;
+ repertoire= my_charset_repertoire(collation_arg);
+ }
+ DTCollation(CHARSET_INFO *collation_arg, Derivation derivation_arg)
+ {
+ collation= collation_arg;
+ derivation= derivation_arg;
+ set_repertoire_from_charset(collation_arg);
+ }
+ DTCollation(CHARSET_INFO *collation_arg,
+ Derivation derivation_arg,
+ uint 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;
+ }
+ void set(CHARSET_INFO *collation_arg, Derivation derivation_arg)
+ {
+ collation= collation_arg;
+ derivation= derivation_arg;
+ set_repertoire_from_charset(collation_arg);
+ }
+ void set(CHARSET_INFO *collation_arg,
+ Derivation derivation_arg,
+ uint 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;
+ set_repertoire_from_charset(collation_arg);
+ }
+ void set(Derivation derivation_arg)
+ { derivation= derivation_arg; }
+ bool aggregate(const DTCollation &dt, uint flags= 0);
+ bool set(DTCollation &dt1, DTCollation &dt2, uint flags= 0)
+ { set(dt1); return aggregate(dt2, flags); }
+ const char *derivation_name() const
+ {
+ switch(derivation)
+ {
+ case DERIVATION_NUMERIC: return "NUMERIC";
+ case DERIVATION_IGNORABLE: return "IGNORABLE";
+ case DERIVATION_COERCIBLE: return "COERCIBLE";
+ case DERIVATION_IMPLICIT: return "IMPLICIT";
+ case DERIVATION_SYSCONST: return "SYSCONST";
+ case DERIVATION_EXPLICIT: return "EXPLICIT";
+ case DERIVATION_NONE: return "NONE";
+ default: return "UNKNOWN";
+ }
+ }
+ int sortcmp(const String *s, const String *t) const
+ {
+ return collation->coll->strnncollsp(collation,
+ (uchar *) s->ptr(), s->length(),
+ (uchar *) t->ptr(), t->length());
+ }
+};
+
+
+static inline uint32
+char_to_byte_length_safe(size_t char_length_arg, uint32 mbmaxlen_arg)
+{
+ ulonglong tmp= ((ulonglong) char_length_arg) * 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
+{
+public:
+ DTCollation collation;
+ uint decimals;
+ /*
+ The maximum value length in characters multiplied by collation->mbmaxlen.
+ Almost always it's the maximum value length in bytes.
+ */
+ uint32 max_length;
+ bool unsigned_flag;
+ Type_std_attributes()
+ :collation(&my_charset_bin, DERIVATION_COERCIBLE),
+ decimals(0), max_length(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_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)
+ { }
+ void set(const Type_std_attributes *other)
+ {
+ *this= *other;
+ }
+ void set(const Type_std_attributes &other)
+ {
+ *this= other;
+ }
+ uint32 max_char_length() const
+ { return max_length / collation.collation->mbmaxlen; }
+ void fix_length_and_charset(uint32 max_char_length_arg, CHARSET_INFO *cs)
+ {
+ max_length= char_to_byte_length_safe(max_char_length_arg, cs->mbmaxlen);
+ collation.collation= cs;
+ }
+ void fix_char_length(uint32 max_char_length_arg)
+ {
+ 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)
+ {
+ 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);
+ }
+ void fix_attributes_date()
+ {
+ fix_attributes_temporal(MAX_DATE_WIDTH, 0);
+ }
+ void fix_attributes_time(uint dec)
+ {
+ fix_attributes_temporal(MIN_TIME_WIDTH, dec);
+ }
+ void fix_attributes_datetime(uint dec)
+ {
+ 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);
+ decimals= 0;
+ }
+ void aggregate_attributes_real(Item **items, uint nitems)
+ {
+ collation.set_numeric();
+ count_real_length(items, nitems);
+ }
+ void aggregate_attributes_decimal(Item **items, uint nitems)
+ {
+ collation.set_numeric();
+ count_decimal_length(items, nitems);
+ }
+ bool aggregate_attributes_string(const char *func_name,
+ Item **item, uint nitems)
+ {
+ return count_string_length(func_name, item, nitems);
+ }
+ void aggregate_attributes_temporal(uint int_part_length,
+ Item **item, uint nitems)
+ {
+ fix_attributes_temporal(int_part_length, count_max_decimals(item, nitems));
+ }
+
+ bool agg_item_collations(DTCollation &c, const char *name,
+ Item **items, uint nitems,
+ uint flags, int item_sep);
+ bool agg_item_set_converter(const DTCollation &coll, const char *fname,
+ Item **args, uint nargs,
+ uint flags, int item_sep);
+
+ /*
+ Collect arguments' character sets together.
+ We allow to apply automatic character set conversion in some cases.
+ The conditions when conversion is possible are:
+ - arguments A and B have different charsets
+ - A wins according to coercibility rules
+ (i.e. a column is stronger than a string constant,
+ an explicit COLLATE clause is stronger than a column)
+ - character set of A is either superset for character set of B,
+ or B is a string constant which can be converted into the
+ character set of A without data loss.
+
+ If all of the above is true, then it's possible to convert
+ B into the character set of A, and then compare according
+ to the collation of A.
+
+ For functions with more than two arguments:
+
+ collect(A,B,C) ::= collect(collect(A,B),C)
+
+ Since this function calls THD::change_item_tree() on the passed Item **
+ pointers, it is necessary to pass the original Item **'s, not copies.
+ Otherwise their values will not be properly restored (see BUG#20769).
+ If the items are not consecutive (eg. args[2] and args[5]), use the
+ item_sep argument, ie.
+
+ agg_item_charsets(coll, fname, &args[2], 2, flags, 3)
+ */
+ bool agg_arg_charsets(DTCollation &c, const char *func_name,
+ Item **items, uint nitems,
+ uint flags, int item_sep)
+ {
+ if (agg_item_collations(c, func_name, items, nitems, flags, item_sep))
+ return true;
+ return agg_item_set_converter(c, func_name, items, nitems, flags, item_sep);
+ }
+ /*
+ Aggregate arguments for string result, e.g: CONCAT(a,b)
+ - convert to @@character_set_connection if all arguments are numbers
+ - allow DERIVATION_NONE
+ */
+ bool agg_arg_charsets_for_string_result(DTCollation &c, const char *func_name,
+ Item **items, uint nitems,
+ int item_sep)
+ {
+ uint flags= MY_COLL_ALLOW_SUPERSET_CONV |
+ MY_COLL_ALLOW_COERCIBLE_CONV |
+ MY_COLL_ALLOW_NUMERIC_CONV;
+ return agg_arg_charsets(c, func_name, items, nitems, flags, item_sep);
+ }
+ /*
+ Aggregate arguments for string result, when some comparison
+ is involved internally, e.g: REPLACE(a,b,c)
+ - convert to @@character_set_connection if all arguments are numbers
+ - disallow DERIVATION_NONE
+ */
+ bool agg_arg_charsets_for_string_result_with_comparison(DTCollation &c,
+ const char *func_name,
+ Item **items,
+ uint nitems,
+ int item_sep)
+ {
+ uint flags= MY_COLL_ALLOW_SUPERSET_CONV |
+ MY_COLL_ALLOW_COERCIBLE_CONV |
+ MY_COLL_ALLOW_NUMERIC_CONV |
+ MY_COLL_DISALLOW_NONE;
+ return agg_arg_charsets(c, func_name, items, nitems, flags, item_sep);
+ }
+
+ /*
+ Aggregate arguments for comparison, e.g: a=b, a LIKE b, a RLIKE b
+ - don't convert to @@character_set_connection if all arguments are numbers
+ - don't allow DERIVATION_NONE
+ */
+ bool agg_arg_charsets_for_comparison(DTCollation &c,
+ const char *func_name,
+ Item **items, uint nitems,
+ int item_sep)
+ {
+ uint flags= MY_COLL_ALLOW_SUPERSET_CONV |
+ MY_COLL_ALLOW_COERCIBLE_CONV |
+ MY_COLL_DISALLOW_NONE;
+ return agg_arg_charsets(c, func_name, items, nitems, flags, item_sep);
+ }
+
+};
+
+
+class Type_all_attributes: public Type_std_attributes
+{
+public:
+ Type_all_attributes()
+ :Type_std_attributes()
+ { }
+ 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;
+};
+
+
+class Type_cast_attributes
+{
+ CHARSET_INFO *m_charset;
+ ulonglong m_length;
+ ulonglong m_decimals;
+ bool m_length_specified;
+ bool m_decimals_specified;
+public:
+ Type_cast_attributes(const char *c_len, const char *c_dec, CHARSET_INFO *cs)
+ :m_charset(cs), m_length(0), m_decimals(0),
+ m_length_specified(false), m_decimals_specified(false)
+ {
+ set_length_and_dec(c_len, c_dec);
+ }
+ Type_cast_attributes(CHARSET_INFO *cs)
+ :m_charset(cs), m_length(0), m_decimals(0),
+ m_length_specified(false), m_decimals_specified(false)
+ { }
+ void set_length_and_dec(const char *c_len, const char *c_dec)
+ {
+ int error;
+ /*
+ We don't have to check for error here as sql_yacc.yy has guaranteed
+ that the values are in range of ulonglong
+ */
+ if ((m_length_specified= (c_len != NULL)))
+ m_length= (ulonglong) my_strtoll10(c_len, NULL, &error);
+ if ((m_decimals_specified= (c_dec != NULL)))
+ m_decimals= (ulonglong) my_strtoll10(c_dec, NULL, &error);
+ }
+ CHARSET_INFO *charset() const { return m_charset; }
+ bool length_specified() const { return m_length_specified; }
+ bool decimals_specified() const { return m_decimals_specified; }
+ ulonglong length() const { return m_length; }
+ ulonglong decimals() const { return m_decimals; }
+};
+
+
+class Name: private LEX_CSTRING
+{
+public:
+ Name(const char *str_arg, uint length_arg)
+ {
+ DBUG_ASSERT(length_arg < UINT_MAX32);
+ LEX_CSTRING::str= str_arg;
+ LEX_CSTRING::length= length_arg;
+ }
+ const char *ptr() const { return LEX_CSTRING::str; }
+ uint length() const { return (uint) LEX_CSTRING::length; }
+};
+
+
+class Record_addr
+{
+public:
+ uchar *ptr; // Position to field in record
+ /**
+ Byte where the @c NULL bit is stored inside a record. If this Field is a
+ @c NOT @c NULL field, this member is @c NULL.
+ */
+ uchar *null_ptr;
+ uchar null_bit; // Bit used to test null bit
+ Record_addr(uchar *ptr_arg,
+ uchar *null_ptr_arg,
+ uchar null_bit_arg)
+ :ptr(ptr_arg),
+ null_ptr(null_ptr_arg),
+ null_bit(null_bit_arg)
+ { }
+ Record_addr(bool maybe_null)
+ :ptr(NULL),
+ null_ptr(maybe_null ? (uchar*) "" : 0),
+ null_bit(0)
+ { }
+};
+
+
+class Information_schema_numeric_attributes
+{
+ enum enum_attr
+ {
+ ATTR_NONE= 0,
+ ATTR_PRECISION= 1,
+ ATTR_SCALE= 2,
+ ATTR_PRECISION_AND_SCALE= (ATTR_PRECISION|ATTR_SCALE)
+ };
+ uint m_precision;
+ uint m_scale;
+ enum_attr m_available_attributes;
+public:
+ Information_schema_numeric_attributes()
+ :m_precision(0), m_scale(0),
+ m_available_attributes(ATTR_NONE)
+ { }
+ Information_schema_numeric_attributes(uint precision)
+ :m_precision(precision), m_scale(0),
+ m_available_attributes(ATTR_PRECISION)
+ { }
+ Information_schema_numeric_attributes(uint precision, uint scale)
+ :m_precision(precision), m_scale(scale),
+ m_available_attributes(ATTR_PRECISION_AND_SCALE)
+ { }
+ bool has_precision() const { return m_available_attributes & ATTR_PRECISION; }
+ bool has_scale() const { return m_available_attributes & ATTR_SCALE; }
+ uint precision() const
+ {
+ DBUG_ASSERT(has_precision());
+ return (uint) m_precision;
+ }
+ uint scale() const
+ {
+ DBUG_ASSERT(has_scale());
+ return (uint) m_scale;
+ }
+};
+
+
+class Information_schema_character_attributes
+{
+ uint32 m_octet_length;
+ uint32 m_char_length;
+ bool m_is_set;
+public:
+ Information_schema_character_attributes()
+ :m_octet_length(0), m_char_length(0), m_is_set(false)
+ { }
+ Information_schema_character_attributes(uint32 octet_length,
+ uint32 char_length)
+ :m_octet_length(octet_length), m_char_length(char_length), m_is_set(true)
+ { }
+ bool has_octet_length() const { return m_is_set; }
+ bool has_char_length() const { return m_is_set; }
+ uint32 octet_length() const
+ {
+ DBUG_ASSERT(has_octet_length());
+ return m_octet_length;
+ }
+ uint char_length() const
+ {
+ DBUG_ASSERT(has_char_length());
+ return m_char_length;
+ }
+};
+
class Type_handler
{
protected:
- const Type_handler *string_type_handler(uint max_octet_length) const;
+ 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;
void make_sort_key_longlong(uchar *to,
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;
+ 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;
+ bool Item_send_short(Item *item, Protocol *protocol, st_value *buf) const;
+ bool Item_send_long(Item *item, Protocol *protocol, st_value *buf) const;
+ bool Item_send_longlong(Item *item, Protocol *protocol, st_value *buf) const;
+ bool Item_send_float(Item *item, Protocol *protocol, st_value *buf) const;
+ bool Item_send_double(Item *item, Protocol *protocol, st_value *buf) const;
+ bool Item_send_time(Item *item, Protocol *protocol, st_value *buf) const;
+ bool Item_send_date(Item *item, Protocol *protocol, st_value *buf) const;
+ bool Item_send_datetime(Item *item, Protocol *protocol, st_value *buf) const;
+ bool Column_definition_prepare_stage2_legacy(Column_definition *c,
+ enum_field_types type)
+ const;
+ bool Column_definition_prepare_stage2_legacy_num(Column_definition *c,
+ enum_field_types type)
+ const;
+ bool Column_definition_prepare_stage2_legacy_real(Column_definition *c,
+ enum_field_types type)
+ const;
public:
+ 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);
+ /**
+ Return a string type handler for Item
+ If too_big_for_varchar() returns a BLOB variant, according to length.
+ If max_length > 0 create a VARCHAR(n)
+ If max_length == 0 create a CHAR(0)
+ @param item - the Item to get the handler to.
+ */
+ static const Type_handler *varstring_type_handler(const Item *item);
+ static const Type_handler *blob_type_handler(const Item *item);
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_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 enum_field_types field_type() const= 0;
virtual enum_field_types real_field_type() const { return field_type(); }
virtual Item_result result_type() const= 0;
virtual Item_result cmp_type() const= 0;
+ virtual enum_mysql_timestamp_type mysql_timestamp_type() const
+ {
+ return MYSQL_TIMESTAMP_ERROR;
+ }
+ virtual bool is_timestamp_type() const
+ {
+ return false;
+ }
+ /**
+ Check whether a field type can be partially indexed by a key.
+ @param type field type
+ @retval true Type can have a prefixed key
+ @retval false Type can not have a prefixed key
+ */
+ virtual bool type_can_have_key_part() const
+ {
+ return false;
+ }
+ virtual bool type_can_have_auto_increment_attribute() const
+ {
+ return false;
+ }
+ /**
+ Prepared statement long data:
+ Check whether this parameter data type is compatible with long data.
+ Used to detect whether a long data stream has been supplied to a
+ incompatible data type.
+ */
+ virtual bool is_param_long_data_type() const { return false; }
+ virtual const Type_handler *type_handler_for_comparison() const= 0;
+ virtual const Type_handler *type_handler_for_item_field() const
+ {
+ return this;
+ }
+ virtual const Type_handler *type_handler_for_tmp_table(const Item *) const
+ {
+ return this;
+ }
+ virtual const Type_handler *type_handler_for_union(const Item *) const
+ {
+ return this;
+ }
+ virtual const Type_handler *cast_to_int_type_handler() const
+ {
+ return this;
+ }
+ virtual CHARSET_INFO *charset_for_protocol(const Item *item) const;
virtual const Type_handler*
type_handler_adjusted_to_max_octet_length(uint max_octet_length,
CHARSET_INFO *cs) const
{ return this; }
+ virtual bool adjust_spparam_type(Spvar_definition *def, Item *from) const
+ {
+ return false;
+ }
virtual ~Type_handler() {}
/**
+ Determines MariaDB traditional data types that always present
+ in the server.
+ */
+ virtual bool is_traditional_type() const
+ {
+ return true;
+ }
+ virtual bool is_scalar_type() const { return true; }
+ virtual bool can_return_int() const { return true; }
+ virtual bool can_return_decimal() const { return true; }
+ virtual bool can_return_real() const { return true; }
+ virtual bool can_return_str() const { return true; }
+ 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 is_general_purpose_string_type() const { return false; }
+ virtual uint Item_time_precision(Item *item) const;
+ virtual uint Item_datetime_precision(Item *item) const;
+ virtual uint Item_decimal_scale(const Item *item) const;
+ virtual uint Item_decimal_precision(const Item *item) const= 0;
+ /*
+ Returns how many digits a divisor adds into a division result.
+ See Item::divisor_precision_increment() in item.h for more comments.
+ */
+ virtual uint Item_divisor_precision_increment(const Item *) const;
+ /**
Makes a temporary table Field to handle numeric aggregate functions,
e.g. SUM(DISTINCT expr), AVG(DISTINCT expr), etc.
*/
@@ -86,63 +1187,954 @@ public:
virtual Field *make_conversion_table_field(TABLE *TABLE,
uint metadata,
const Field *target) const= 0;
+ virtual bool Column_definition_fix_attributes(Column_definition *c) const= 0;
+ virtual bool Column_definition_prepare_stage1(THD *thd,
+ MEM_ROOT *mem_root,
+ Column_definition *c,
+ handler *file,
+ ulonglong table_flags) const;
+ /*
+ This method is called on queries like:
+ CREATE TABLE t2 (a INT) AS SELECT a FROM t1;
+ I.e. column "a" is queried from another table,
+ but its data type is redefined.
+ @param OUT def - The column definition to be redefined
+ @param IN dup - The column definition to take the data type from
+ (i.e. "a INT" in the above example).
+ @param IN file - Table owner handler. If it does not support certain
+ data types, some conversion can be applied.
+ I.g. true BIT to BIT-AS-CHAR.
+ @param IN schema - the owner schema definition, e.g. for the default
+ character set and collation.
+ @retval true - on error
+ @retval false - on success
+ */
+ virtual bool Column_definition_redefine_stage1(Column_definition *def,
+ const Column_definition *dup,
+ const handler *file,
+ const Schema_specification_st *
+ schema)
+ const;
+ 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,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE *table) const= 0;
+ Field *make_and_init_table_field(const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE *table) 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,
const Type_std_attributes *item,
SORT_FIELD_ATTR *attr) const= 0;
+
+ virtual uint32 max_display_length(const Item *item) const= 0;
+ virtual uint32 calc_pack_length(uint32 length) const= 0;
+ virtual bool Item_save_in_value(Item *item, st_value *value) const= 0;
+ virtual void Item_param_setup_conversion(THD *thd, Item_param *) const {}
+ virtual void Item_param_set_param_func(Item_param *param,
+ uchar **pos, ulong len) const;
+ virtual bool Item_param_set_from_value(THD *thd,
+ Item_param *param,
+ const Type_all_attributes *attr,
+ const st_value *value) const= 0;
+ virtual bool Item_send(Item *item, Protocol *p, st_value *buf) const= 0;
+ virtual int Item_save_in_field(Item *item, Field *field,
+ bool no_conversions) const= 0;
+
+ /**
+ Return a string representation of the Item value.
+
+ @param thd thread handle
+ @param str string buffer for representation of the value
+
+ @note
+ If the item has a string result type, the string is escaped
+ according to its character set.
+
+ @retval
+ NULL on error
+ @retval
+ non-NULL a pointer to a a valid string on success
+ */
+ virtual String *print_item_value(THD *thd, Item *item, String *str) const= 0;
+
+ /**
+ Check if
+ WHERE expr=value AND expr=const
+ can be rewritten as:
+ WHERE const=value AND expr=const
+
+ "this" is the comparison handler that is used by "target".
+
+ @param target - the predicate expr=value,
+ whose "expr" argument will be replaced to "const".
+ @param target_expr - the target's "expr" which will be replaced to "const".
+ @param target_value - the target's second argument, it will remain unchanged.
+ @param source - the equality predicate expr=const (or expr<=>const)
+ that can be used to rewrite the "target" part
+ (under certain conditions, see the code).
+ @param source_expr - the source's "expr". It should be exactly equal to
+ the target's "expr" to make condition rewrite possible.
+ @param source_const - the source's "const" argument, it will be inserted
+ into "target" instead of "expr".
+ */
+ virtual 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= 0;
+ virtual bool
+ subquery_type_allows_materialization(const Item *inner,
+ const Item *outer) const= 0;
+ /**
+ Make a simple constant replacement item for a constant "src",
+ so the new item can futher be used for comparison with "cmp", e.g.:
+ src = cmp -> replacement = cmp
+
+ "this" is the type handler that is used to compare "src" and "cmp".
+
+ @param thd - current thread, for mem_root
+ @param src - The item that we want to replace. It's a const item,
+ but it can be complex enough to calculate on every row.
+ @param cmp - The src's comparand.
+ @retval - a pointer to the created replacement Item
+ @retval - NULL, if could not create a replacement (e.g. on EOM).
+ NULL is also returned for ROWs, because instead of replacing
+ a Item_row to a new Item_row, Type_handler_row just replaces
+ its elements.
+ */
+ virtual Item *make_const_item_for_comparison(THD *thd,
+ Item *src,
+ const Item *cmp) const= 0;
+ virtual Item_cache *Item_get_cache(THD *thd, const Item *item) const= 0;
+ virtual Item *create_typecast_item(THD *thd, Item *item,
+ const Type_cast_attributes &attr) const
+ {
+ DBUG_ASSERT(0);
+ return NULL;
+ }
+ virtual bool set_comparator_func(Arg_comparator *cmp) const= 0;
+ virtual 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= 0;
+ virtual bool Item_func_min_max_fix_attributes(THD *thd,
+ Item_func_min_max *func,
+ Item **items,
+ uint nitems) const;
+ virtual bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *) const= 0;
+ virtual bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const= 0;
+ virtual bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const= 0;
+ virtual
+ bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const= 0;
+
+ virtual bool Item_val_bool(Item *item) const= 0;
+ virtual bool Item_get_date(Item *item, MYSQL_TIME *ltime,
+ ulonglong fuzzydate) const= 0;
+ virtual longlong Item_val_int_signed_typecast(Item *item) const= 0;
+ virtual longlong Item_val_int_unsigned_typecast(Item *item) const= 0;
+
+ virtual String *Item_func_hex_val_str_ascii(Item_func_hex *item,
+ String *str) const= 0;
+
+ virtual
+ String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *,
+ String *) const= 0;
+ virtual
+ double Item_func_hybrid_field_type_val_real(Item_func_hybrid_field_type *)
+ const= 0;
+ virtual
+ longlong Item_func_hybrid_field_type_val_int(Item_func_hybrid_field_type *)
+ const= 0;
+ virtual
+ my_decimal *Item_func_hybrid_field_type_val_decimal(
+ Item_func_hybrid_field_type *,
+ my_decimal *) const= 0;
+ virtual
+ bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *,
+ MYSQL_TIME *,
+ ulonglong fuzzydate) const= 0;
+ virtual
+ String *Item_func_min_max_val_str(Item_func_min_max *, String *) const= 0;
+ virtual
+ double Item_func_min_max_val_real(Item_func_min_max *) const= 0;
+ virtual
+ longlong Item_func_min_max_val_int(Item_func_min_max *) const= 0;
+ virtual
+ my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *,
+ my_decimal *) const= 0;
+ virtual
+ bool Item_func_min_max_get_date(Item_func_min_max*,
+ MYSQL_TIME *, ulonglong fuzzydate) const= 0;
+ virtual bool
+ Item_func_between_fix_length_and_dec(Item_func_between *func) const= 0;
+ virtual longlong
+ Item_func_between_val_int(Item_func_between *func) const= 0;
+
+ virtual cmp_item *
+ make_cmp_item(THD *thd, CHARSET_INFO *cs) const= 0;
+
+ virtual in_vector *
+ make_in_vector(THD *thd, const Item_func_in *func, uint nargs) const= 0;
+
+ virtual bool
+ Item_func_in_fix_comparator_compatible_types(THD *thd, Item_func_in *)
+ const= 0;
+
+ virtual bool
+ Item_func_round_fix_length_and_dec(Item_func_round *round) const= 0;
+
+ virtual bool
+ Item_func_int_val_fix_length_and_dec(Item_func_int_val *func) const= 0;
+
+ virtual bool
+ Item_func_abs_fix_length_and_dec(Item_func_abs *func) const= 0;
+
+ virtual bool
+ Item_func_neg_fix_length_and_dec(Item_func_neg *func) const= 0;
+
+ virtual bool
+ Item_func_signed_fix_length_and_dec(Item_func_signed *item) const;
+ virtual bool
+ Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *item) const;
+ virtual bool
+ Item_double_typecast_fix_length_and_dec(Item_double_typecast *item) const;
+ virtual bool
+ Item_float_typecast_fix_length_and_dec(Item_float_typecast *item) const;
+ virtual bool
+ Item_decimal_typecast_fix_length_and_dec(Item_decimal_typecast *item) const;
+ virtual bool
+ Item_char_typecast_fix_length_and_dec(Item_char_typecast *item) const;
+ virtual bool
+ Item_time_typecast_fix_length_and_dec(Item_time_typecast *item) const;
+ virtual bool
+ Item_date_typecast_fix_length_and_dec(Item_date_typecast *item) const;
+ virtual bool
+ Item_datetime_typecast_fix_length_and_dec(Item_datetime_typecast *item) const;
+
+ virtual bool
+ Item_func_plus_fix_length_and_dec(Item_func_plus *func) const= 0;
+ virtual bool
+ Item_func_minus_fix_length_and_dec(Item_func_minus *func) const= 0;
+ virtual bool
+ Item_func_mul_fix_length_and_dec(Item_func_mul *func) const= 0;
+ virtual bool
+ Item_func_div_fix_length_and_dec(Item_func_div *func) const= 0;
+ 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;
+};
+
+
+/*
+ Special handler for ROW
+*/
+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
+ {
+ DBUG_ASSERT(0);
+ return MYSQL_TYPE_NULL;
+ };
+ Item_result result_type() const
+ {
+ return ROW_RESULT;
+ }
+ Item_result cmp_type() const
+ {
+ return ROW_RESULT;
+ }
+ const Type_handler *type_handler_for_comparison() const;
+ bool subquery_type_allows_materialization(const Item *inner,
+ const Item *outer) const
+ {
+ DBUG_ASSERT(0);
+ return false;
+ }
+ Field *make_num_distinct_aggregator_field(MEM_ROOT *, const Item *) const
+ {
+ DBUG_ASSERT(0);
+ return NULL;
+ }
+ Field *make_conversion_table_field(TABLE *TABLE,
+ uint metadata,
+ const Field *target) const
+ {
+ DBUG_ASSERT(0);
+ return NULL;
+ }
+ bool Column_definition_fix_attributes(Column_definition *c) const
+ {
+ return false;
+ }
+ bool Column_definition_prepare_stage1(THD *thd,
+ MEM_ROOT *mem_root,
+ Column_definition *c,
+ handler *file,
+ ulonglong table_flags) const;
+ bool Column_definition_redefine_stage1(Column_definition *def,
+ const Column_definition *dup,
+ const handler *file,
+ const Schema_specification_st *schema)
+ const
+ {
+ DBUG_ASSERT(0);
+ return true;
+ }
+ bool Column_definition_prepare_stage2(Column_definition *c,
+ handler *file,
+ ulonglong table_flags) const
+ {
+ return false;
+ }
+ Field *make_table_field(const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE *table) const
+ {
+ DBUG_ASSERT(0);
+ return NULL;
+ }
+ void make_sort_key(uchar *to, Item *item,
+ const SORT_FIELD_ATTR *sort_field,
+ Sort_param *param) const
+ {
+ DBUG_ASSERT(0);
+ }
+ void sortlength(THD *thd, const Type_std_attributes *item,
+ SORT_FIELD_ATTR *attr) const
+ {
+ DBUG_ASSERT(0);
+ }
+ uint32 max_display_length(const Item *item) const
+ {
+ DBUG_ASSERT(0);
+ return 0;
+ }
+ uint32 calc_pack_length(uint32 length) const
+ {
+ DBUG_ASSERT(0);
+ return 0;
+ }
+ uint Item_decimal_precision(const Item *item) const
+ {
+ DBUG_ASSERT(0);
+ return DECIMAL_MAX_PRECISION;
+ }
+ bool Item_save_in_value(Item *item, st_value *value) const;
+ 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
+ {
+ DBUG_ASSERT(0);
+ return true;
+ }
+ int Item_save_in_field(Item *item, Field *field, bool no_conversions) const
+ {
+ DBUG_ASSERT(0);
+ return 1;
+ }
+ String *print_item_value(THD *thd, Item *item, String *str) const;
+ 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
+ {
+ 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;
+ bool set_comparator_func(Arg_comparator *cmp) const;
+ 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
+ {
+ DBUG_ASSERT(0);
+ return true;
+ }
+ bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const
+ {
+ DBUG_ASSERT(0);
+ return true;
+ }
+ bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const
+ {
+ DBUG_ASSERT(0);
+ return true;
+ }
+ bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const
+ {
+ DBUG_ASSERT(0);
+ return true;
+ }
+ bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const
+ {
+ DBUG_ASSERT(0);
+ return true;
+ }
+ bool Item_val_bool(Item *item) const
+ {
+ DBUG_ASSERT(0);
+ return false;
+ }
+ bool Item_get_date(Item *item, MYSQL_TIME *ltime, ulonglong fuzzydate) const
+ {
+ DBUG_ASSERT(0);
+ return true;
+ }
+ longlong Item_val_int_signed_typecast(Item *item) const
+ {
+ DBUG_ASSERT(0);
+ return 0;
+ }
+ longlong Item_val_int_unsigned_typecast(Item *item) const
+ {
+ DBUG_ASSERT(0);
+ return 0;
+ }
+ String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const
+ {
+ DBUG_ASSERT(0);
+ return NULL;
+ }
+ String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *,
+ String *) const
+ {
+ DBUG_ASSERT(0);
+ return NULL;
+ }
+ double Item_func_hybrid_field_type_val_real(Item_func_hybrid_field_type *)
+ const
+ {
+ DBUG_ASSERT(0);
+ return 0.0;
+ }
+ longlong Item_func_hybrid_field_type_val_int(Item_func_hybrid_field_type *)
+ const
+ {
+ DBUG_ASSERT(0);
+ return 0;
+ }
+ my_decimal *Item_func_hybrid_field_type_val_decimal(
+ Item_func_hybrid_field_type *,
+ my_decimal *) const
+ {
+ DBUG_ASSERT(0);
+ return NULL;
+ }
+ bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *,
+ MYSQL_TIME *,
+ ulonglong fuzzydate) const
+ {
+ DBUG_ASSERT(0);
+ return true;
+ }
+
+ String *Item_func_min_max_val_str(Item_func_min_max *, String *) const
+ {
+ DBUG_ASSERT(0);
+ return NULL;
+ }
+ double Item_func_min_max_val_real(Item_func_min_max *) const
+ {
+ DBUG_ASSERT(0);
+ return 0;
+ }
+ longlong Item_func_min_max_val_int(Item_func_min_max *) const
+ {
+ DBUG_ASSERT(0);
+ return 0;
+ }
+ my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *,
+ my_decimal *) const
+ {
+ DBUG_ASSERT(0);
+ return NULL;
+ }
+ bool Item_func_min_max_get_date(Item_func_min_max*,
+ MYSQL_TIME *, ulonglong fuzzydate) const
+ {
+ DBUG_ASSERT(0);
+ return true;
+ }
+ bool Item_func_between_fix_length_and_dec(Item_func_between *func) const
+ {
+ 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;
+
+ bool Item_func_signed_fix_length_and_dec(Item_func_signed *) const
+ {
+ DBUG_ASSERT(0);
+ return true;
+ }
+ bool Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *) const
+ {
+ DBUG_ASSERT(0);
+ return true;
+ }
+ bool Item_double_typecast_fix_length_and_dec(Item_double_typecast *) const
+ {
+ DBUG_ASSERT(0);
+ return true;
+ }
+ bool Item_float_typecast_fix_length_and_dec(Item_float_typecast *) const
+ {
+ DBUG_ASSERT(0);
+ return true;
+ }
+ bool Item_decimal_typecast_fix_length_and_dec(Item_decimal_typecast *) const
+ {
+ DBUG_ASSERT(0);
+ return true;
+ }
+ bool Item_char_typecast_fix_length_and_dec(Item_char_typecast *) const
+ {
+ DBUG_ASSERT(0);
+ return true;
+ }
+ bool Item_time_typecast_fix_length_and_dec(Item_time_typecast *) const
+ {
+ DBUG_ASSERT(0);
+ return true;
+ }
+ bool Item_date_typecast_fix_length_and_dec(Item_date_typecast *) const
+ {
+ DBUG_ASSERT(0);
+ return true;
+ }
+ bool Item_datetime_typecast_fix_length_and_dec(Item_datetime_typecast *) const
+ {
+ 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;
+};
+
+
+/*
+ A common parent class for numeric data type handlers
+*/
+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;
+ my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *,
+ my_decimal *) const;
+ bool Item_func_min_max_get_date(Item_func_min_max*,
+ MYSQL_TIME *, ulonglong fuzzydate) const;
+ 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;
};
/*** Abstract classes for every XXX_RESULT */
-class Type_handler_real_result: public Type_handler
+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; }
virtual ~Type_handler_real_result() {}
+ const Type_handler *type_handler_for_comparison() const;
+ 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;
+ uint Item_decimal_precision(const Item *item) const;
+ bool Item_save_in_value(Item *item, st_value *value) const;
+ bool Item_param_set_from_value(THD *thd,
+ Item_param *param,
+ const Type_all_attributes *attr,
+ const st_value *value) 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;
+ 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_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;
+ bool Item_get_date(Item *item, MYSQL_TIME *ltime, ulonglong 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;
+ double Item_func_hybrid_field_type_val_real(Item_func_hybrid_field_type *)
+ const;
+ longlong Item_func_hybrid_field_type_val_int(Item_func_hybrid_field_type *)
+ const;
+ my_decimal *Item_func_hybrid_field_type_val_decimal(
+ Item_func_hybrid_field_type *,
+ my_decimal *) const;
+ bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *,
+ MYSQL_TIME *,
+ ulonglong 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;
};
-class Type_handler_decimal_result: public Type_handler
+class Type_handler_decimal_result: public Type_handler_numeric
{
public:
Item_result result_type() const { return DECIMAL_RESULT; }
Item_result cmp_type() const { return DECIMAL_RESULT; }
virtual ~Type_handler_decimal_result() {};
+ const Type_handler *type_handler_for_comparison() const;
+ 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;
+ Item *create_typecast_item(THD *thd, Item *item,
+ const Type_cast_attributes &attr) const;
+ uint Item_decimal_precision(const Item *item) const;
+ bool Item_save_in_value(Item *item, st_value *value) const;
+ 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;
+ bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
+ {
+ return Item_send_str(item, protocol, buf);
+ }
+ 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;
+ 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;
+ bool Item_get_date(Item *item, MYSQL_TIME *ltime, ulonglong 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;
+ String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *,
+ String *) const;
+ double Item_func_hybrid_field_type_val_real(Item_func_hybrid_field_type *)
+ const;
+ longlong Item_func_hybrid_field_type_val_int(Item_func_hybrid_field_type *)
+ const;
+ my_decimal *Item_func_hybrid_field_type_val_decimal(
+ Item_func_hybrid_field_type *,
+ my_decimal *) const;
+ bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *,
+ MYSQL_TIME *,
+ ulonglong 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;
+};
+
+
+class Type_limits_int
+{
+private:
+ uint32 m_precision;
+ uint32 m_char_length;
+public:
+ Type_limits_int(uint32 prec, uint32 nchars)
+ :m_precision(prec), m_char_length(nchars)
+ { }
+ uint32 precision() const { return m_precision; }
+ uint32 char_length() const { return m_char_length; }
+};
+
+
+/*
+ UNDIGNED TINYINT: 0..255 digits=3 nchars=3
+ SIGNED TINYINT : -128..127 digits=3 nchars=4
+*/
+class Type_limits_uint8: public Type_limits_int
+{
+public:
+ Type_limits_uint8()
+ :Type_limits_int(MAX_TINYINT_WIDTH, MAX_TINYINT_WIDTH)
+ { }
+};
+
+
+class Type_limits_sint8: public Type_limits_int
+{
+public:
+ Type_limits_sint8()
+ :Type_limits_int(MAX_TINYINT_WIDTH, MAX_TINYINT_WIDTH + 1)
+ { }
};
-class Type_handler_int_result: public Type_handler
+/*
+ UNDIGNED SMALLINT: 0..65535 digits=5 nchars=5
+ SIGNED SMALLINT: -32768..32767 digits=5 nchars=6
+*/
+class Type_limits_uint16: public Type_limits_int
+{
+public:
+ Type_limits_uint16()
+ :Type_limits_int(MAX_SMALLINT_WIDTH, MAX_SMALLINT_WIDTH)
+ { }
+};
+
+
+class Type_limits_sint16: public Type_limits_int
+{
+public:
+ Type_limits_sint16()
+ :Type_limits_int(MAX_SMALLINT_WIDTH, MAX_SMALLINT_WIDTH + 1)
+ { }
+};
+
+
+/*
+ MEDIUMINT UNSIGNED 0 .. 16777215 digits=8 char_length=8
+ MEDIUMINT SIGNED: -8388608 .. 8388607 digits=7 char_length=8
+*/
+class Type_limits_uint24: public Type_limits_int
+{
+public:
+ Type_limits_uint24()
+ :Type_limits_int(MAX_MEDIUMINT_WIDTH, MAX_MEDIUMINT_WIDTH)
+ { }
+};
+
+
+class Type_limits_sint24: public Type_limits_int
+{
+public:
+ Type_limits_sint24()
+ :Type_limits_int(MAX_MEDIUMINT_WIDTH - 1, MAX_MEDIUMINT_WIDTH)
+ { }
+};
+
+
+/*
+ UNSIGNED INT: 0..4294967295 digits=10 nchars=10
+ SIGNED INT: -2147483648..2147483647 digits=10 nchars=11
+*/
+class Type_limits_uint32: public Type_limits_int
+{
+public:
+ Type_limits_uint32()
+ :Type_limits_int(MAX_INT_WIDTH, MAX_INT_WIDTH)
+ { }
+};
+
+
+
+class Type_limits_sint32: public Type_limits_int
+{
+public:
+ Type_limits_sint32()
+ :Type_limits_int(MAX_INT_WIDTH, MAX_INT_WIDTH + 1)
+ { }
+};
+
+
+/*
+ UNSIGNED BIGINT: 0..18446744073709551615 digits=20 nchars=20
+ SIGNED BIGINT: -9223372036854775808..9223372036854775807 digits=19 nchars=20
+*/
+class Type_limits_uint64: public Type_limits_int
+{
+public:
+ Type_limits_uint64(): Type_limits_int(MAX_BIGINT_WIDTH, MAX_BIGINT_WIDTH)
+ { }
+};
+
+
+class Type_limits_sint64: public Type_limits_int
+{
+public:
+ Type_limits_sint64()
+ :Type_limits_int(MAX_BIGINT_WIDTH - 1, MAX_BIGINT_WIDTH)
+ { }
+};
+
+
+
+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; }
virtual ~Type_handler_int_result() {}
+ const Type_handler *type_handler_for_comparison() const;
+ 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;
+ uint Item_decimal_precision(const Item *item) const;
+ bool Item_save_in_value(Item *item, st_value *value) const;
+ bool Item_param_set_from_value(THD *thd,
+ Item_param *param,
+ const Type_all_attributes *attr,
+ const st_value *value) 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;
+ 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;
+ bool Item_get_date(Item *item, MYSQL_TIME *ltime, ulonglong 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;
+ String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *,
+ String *) const;
+ double Item_func_hybrid_field_type_val_real(Item_func_hybrid_field_type *)
+ const;
+ longlong Item_func_hybrid_field_type_val_int(Item_func_hybrid_field_type *)
+ const;
+ my_decimal *Item_func_hybrid_field_type_val_decimal(
+ Item_func_hybrid_field_type *,
+ my_decimal *) const;
+ bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *,
+ MYSQL_TIME *,
+ ulonglong 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;
+};
+
+
+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;
+ bool Vers_history_point_resolve_unit(THD *thd, Vers_history_point *p) const;
};
class Type_handler_temporal_result: public Type_handler
{
+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; }
@@ -152,15 +2144,73 @@ public:
void sortlength(THD *thd,
const Type_std_attributes *item,
SORT_FIELD_ATTR *attr) const;
+ 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;
+ 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 subquery_type_allows_materialization(const Item *inner,
+ const Item *outer) const;
+ 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;
+ bool Item_get_date(Item *item, MYSQL_TIME *ltime, ulonglong 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;
+ String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *,
+ String *) const;
+ double Item_func_hybrid_field_type_val_real(Item_func_hybrid_field_type *)
+ const;
+ longlong Item_func_hybrid_field_type_val_int(Item_func_hybrid_field_type *)
+ const;
+ my_decimal *Item_func_hybrid_field_type_val_decimal(
+ Item_func_hybrid_field_type *,
+ my_decimal *) const;
+ bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *,
+ MYSQL_TIME *,
+ ulonglong 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;
+ my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *,
+ my_decimal *) const;
+ bool Item_func_min_max_get_date(Item_func_min_max*,
+ MYSQL_TIME *, ulonglong 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_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;
+ bool Vers_history_point_resolve_unit(THD *thd, Vers_history_point *p) const;
};
class Type_handler_string_result: public Type_handler
{
+ uint Item_temporal_precision(Item *item, bool is_time) const;
public:
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;
virtual ~Type_handler_string_result() {}
+ const Type_handler *type_handler_for_comparison() const;
const Type_handler *
type_handler_adjusted_to_max_octet_length(uint max_octet_length,
CHARSET_INFO *cs) const;
@@ -169,6 +2219,111 @@ public:
void sortlength(THD *thd,
const Type_std_attributes *item,
SORT_FIELD_ATTR *attr) const;
+ bool Column_definition_prepare_stage1(THD *thd,
+ MEM_ROOT *mem_root,
+ Column_definition *c,
+ handler *file,
+ ulonglong table_flags) const;
+ 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;
+ uint Item_time_precision(Item *item) const
+ {
+ return Item_temporal_precision(item, true);
+ }
+ uint Item_datetime_precision(Item *item) const
+ {
+ return Item_temporal_precision(item, false);
+ }
+ uint Item_decimal_precision(const Item *item) const;
+ bool Item_save_in_value(Item *item, st_value *value) const;
+ void Item_param_setup_conversion(THD *thd, Item_param *) const;
+ 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;
+ bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
+ {
+ 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
+ {
+ 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;
+ 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;
+ 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;
+ bool Item_get_date(Item *item, MYSQL_TIME *ltime, ulonglong 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;
+ String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *,
+ String *) const;
+ double Item_func_hybrid_field_type_val_real(Item_func_hybrid_field_type *)
+ const;
+ longlong Item_func_hybrid_field_type_val_int(Item_func_hybrid_field_type *)
+ const;
+ my_decimal *Item_func_hybrid_field_type_val_decimal(
+ Item_func_hybrid_field_type *,
+ my_decimal *) const;
+ bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *,
+ MYSQL_TIME *,
+ ulonglong 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;
+ my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *,
+ my_decimal *) const;
+ bool Item_func_min_max_get_date(Item_func_min_max*,
+ MYSQL_TIME *, ulonglong 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;
+};
+
+
+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;
};
@@ -192,302 +2347,1126 @@ public:
*/
-class Type_handler_tiny: public Type_handler_int_result
+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; }
+ 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
+ {
+ 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;
+ bool Column_definition_prepare_stage2(Column_definition *c,
+ handler *file,
+ ulonglong table_flags) const
+ { 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;
+ void Item_param_set_param_func(Item_param *param,
+ uchar **pos, ulong len) const;
};
-class Type_handler_short: public Type_handler_int_result
+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; }
+ bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
+ {
+ 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;
+ bool Column_definition_prepare_stage2(Column_definition *c,
+ handler *file,
+ ulonglong table_flags) const
+ { 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;
+ void Item_param_set_param_func(Item_param *param,
+ uchar **pos, ulong len) const;
};
-class Type_handler_long: public Type_handler_int_result
+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; }
+ 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
+ {
+ 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;
+ bool Column_definition_prepare_stage2(Column_definition *c,
+ handler *file,
+ ulonglong table_flags) const
+ { 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;
+ void Item_param_set_param_func(Item_param *param,
+ uchar **pos, ulong len) const;
};
-class Type_handler_longlong: public Type_handler_int_result
+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; }
+ 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; }
+ 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
+ {
+ 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;
+ bool Column_definition_prepare_stage2(Column_definition *c,
+ handler *file,
+ ulonglong table_flags) const
+ {
+ 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;
+ void Item_param_set_param_func(Item_param *param,
+ uchar **pos, ulong len) const;
};
-class Type_handler_int24: public Type_handler_int_result
+class Type_handler_vers_trx_id: public Type_handler_longlong
{
public:
+ virtual ~Type_handler_vers_trx_id() {}
+ Field *make_table_field(const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE *table) const;
+};
+
+
+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; }
+ bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
+ {
+ 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;
+ bool Column_definition_prepare_stage2(Column_definition *c,
+ handler *file,
+ ulonglong table_flags) const
+ { 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;
};
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; }
+ uint32 max_display_length(const Item *item) const;
+ uint32 calc_pack_length(uint32 length) const { return 1; }
+ bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
+ {
+ 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;
+ bool Column_definition_prepare_stage2(Column_definition *c,
+ handler *file,
+ ulonglong table_flags) const
+ { 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;
+ Item_cache *Item_get_cache(THD *thd, const Item *item) const;
+ bool Item_get_date(Item *item, MYSQL_TIME *ltime, ulonglong fuzzydate) const;
};
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; }
+ uint32 max_display_length(const Item *item) const;
+ uint32 calc_pack_length(uint32 length) const { return length / 8; }
+ bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
+ {
+ return Item_send_str(item, protocol, buf);
+ }
+ String *print_item_value(THD *thd, Item *item, String *str) const
+ {
+ return print_item_value_csstr(thd, item, str);
+ }
Field *make_conversion_table_field(TABLE *, uint metadata,
const Field *target) const;
+ bool Column_definition_fix_attributes(Column_definition *c) const;
+ bool Column_definition_prepare_stage1(THD *thd,
+ MEM_ROOT *mem_root,
+ Column_definition *c,
+ handler *file,
+ ulonglong table_flags) const;
+ bool Column_definition_redefine_stage1(Column_definition *def,
+ const Column_definition *dup,
+ const handler *file,
+ const Schema_specification_st *schema)
+ 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;
+ bool Vers_history_point_resolve_unit(THD *thd, Vers_history_point *p) const;
};
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; }
+ bool type_can_have_auto_increment_attribute() const { return true; }
+ uint32 max_display_length(const Item *item) const { return 25; }
+ uint32 calc_pack_length(uint32 length) const { 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
+ {
+ 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;
+ bool Column_definition_prepare_stage2(Column_definition *c,
+ handler *file,
+ ulonglong table_flags) const
+ { 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;
+ void Item_param_set_param_func(Item_param *param,
+ uchar **pos, ulong len) const;
+
+ Item_cache *Item_get_cache(THD *thd, const Item *item) const;
+ 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;
};
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; }
+ bool type_can_have_auto_increment_attribute() const { return true; }
+ uint32 max_display_length(const Item *item) const { return 53; }
+ uint32 calc_pack_length(uint32 length) const { 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
+ {
+ 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;
+ bool Column_definition_prepare_stage2(Column_definition *c,
+ handler *file,
+ ulonglong table_flags) const
+ { 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;
+ void Item_param_set_param_func(Item_param *param,
+ uchar **pos, ulong len) const;
+
+ Item_cache *Item_get_cache(THD *thd, const Item *item) const;
+ 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;
};
-class Type_handler_time: public Type_handler_temporal_result
+class Type_handler_time_common: public Type_handler_temporal_result
{
+ static const Name m_name_time;
public:
- virtual ~Type_handler_time() {}
+ virtual ~Type_handler_time_common() { }
+ const Name name() const { return m_name_time; }
enum_field_types field_type() const { return MYSQL_TYPE_TIME; }
+ enum_mysql_timestamp_type mysql_timestamp_type() const
+ {
+ return MYSQL_TIMESTAMP_TIME;
+ }
+ Item *create_typecast_item(THD *thd, Item *item,
+ const Type_cast_attributes &attr) const;
+ uint Item_decimal_scale(const Item *item) const
+ {
+ return Item_decimal_scale_with_seconds(item);
+ }
+ uint Item_decimal_precision(const Item *item) const;
+ uint Item_divisor_precision_increment(const Item *item) const
+ {
+ return Item_divisor_precision_increment_with_seconds(item);
+ }
+ const Type_handler *type_handler_for_comparison() const;
+ bool Column_definition_fix_attributes(Column_definition *c) const;
+ bool Item_save_in_value(Item *item, st_value *value) const;
+ bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
+ {
+ return Item_send_time(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;
+ 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;
+ String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *,
+ String *) const;
+ double Item_func_hybrid_field_type_val_real(Item_func_hybrid_field_type *)
+ const;
+ longlong Item_func_hybrid_field_type_val_int(Item_func_hybrid_field_type *)
+ const;
+ my_decimal *Item_func_hybrid_field_type_val_decimal(
+ Item_func_hybrid_field_type *,
+ my_decimal *) const;
+ bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *,
+ MYSQL_TIME *,
+ ulonglong fuzzydate) const;
+ bool Item_func_min_max_get_date(Item_func_min_max*,
+ MYSQL_TIME *, ulonglong fuzzydate) 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;
+};
+
+
+class Type_handler_time: public Type_handler_time_common
+{
+ /* number of bytes to store TIME(N) */
+ static uint m_hires_bytes[MAX_DATETIME_PRECISION+1];
+public:
+ static uint hires_bytes(uint dec) { return m_hires_bytes[dec]; }
+ virtual ~Type_handler_time() {}
+ uint32 calc_pack_length(uint32 length) const;
Field *make_conversion_table_field(TABLE *, uint metadata,
const Field *target) const;
+ bool Column_definition_prepare_stage2(Column_definition *c,
+ handler *file,
+ ulonglong table_flags) const
+ { return Column_definition_prepare_stage2_legacy(c, MYSQL_TYPE_TIME); }
+ Field *make_table_field(const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE *table) const;
};
-class Type_handler_time2: public Type_handler_temporal_result
+class Type_handler_time2: public Type_handler_time_common
{
public:
virtual ~Type_handler_time2() {}
- enum_field_types field_type() const { return MYSQL_TYPE_TIME; }
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;
+ bool Column_definition_prepare_stage2(Column_definition *c,
+ handler *file,
+ ulonglong table_flags) const
+ { return Column_definition_prepare_stage2_legacy(c, MYSQL_TYPE_TIME2); }
+ Field *make_table_field(const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE *table) const;
};
-class Type_handler_date: public Type_handler_temporal_result
+class Type_handler_temporal_with_date: public Type_handler_temporal_result
{
public:
- virtual ~Type_handler_date() {}
+ virtual ~Type_handler_temporal_with_date() {}
+ bool Item_save_in_value(Item *item, st_value *value) const;
+ bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
+ {
+ return Item_send_date(item, protocol, buf);
+ }
+ 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;
+};
+
+
+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; }
+ enum_mysql_timestamp_type mysql_timestamp_type() const
+ {
+ return MYSQL_TIMESTAMP_DATE;
+ }
+ 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;
+ 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;
+};
+
+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;
+ bool Column_definition_prepare_stage2(Column_definition *c,
+ handler *file,
+ ulonglong table_flags) const
+ { return Column_definition_prepare_stage2_legacy(c, MYSQL_TYPE_DATE); }
+ Field *make_table_field(const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE *table) const;
};
-class Type_handler_newdate: public Type_handler_temporal_result
+class Type_handler_newdate: public Type_handler_date_common
{
public:
virtual ~Type_handler_newdate() {}
- enum_field_types field_type() const { return MYSQL_TYPE_DATE; }
+ 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;
+ bool Column_definition_prepare_stage2(Column_definition *c,
+ handler *file,
+ ulonglong table_flags) const
+ { return Column_definition_prepare_stage2_legacy(c, MYSQL_TYPE_NEWDATE); }
+ Field *make_table_field(const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE *table) const;
};
-class Type_handler_datetime: public Type_handler_temporal_result
+class Type_handler_datetime_common: public Type_handler_temporal_with_date
{
+ static const Name m_name_datetime;
public:
- virtual ~Type_handler_datetime() {}
+ 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; }
+ enum_mysql_timestamp_type mysql_timestamp_type() const
+ {
+ return MYSQL_TIMESTAMP_DATETIME;
+ }
+ 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_scale(const Item *item) const
+ {
+ return Item_decimal_scale_with_seconds(item);
+ }
+ uint Item_decimal_precision(const Item *item) const;
+ uint Item_divisor_precision_increment(const Item *item) const
+ {
+ return Item_divisor_precision_increment_with_seconds(item);
+ }
+ bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
+ {
+ 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;
+ 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;
+};
+
+
+class Type_handler_datetime: public Type_handler_datetime_common
+{
+ /* number of bytes to store DATETIME(N) */
+ static uint m_hires_bytes[MAX_DATETIME_PRECISION + 1];
+public:
+ static uint hires_bytes(uint dec) { return m_hires_bytes[dec]; }
+ virtual ~Type_handler_datetime() {}
+ uint32 calc_pack_length(uint32 length) const;
Field *make_conversion_table_field(TABLE *, uint metadata,
const Field *target) const;
+ bool Column_definition_prepare_stage2(Column_definition *c,
+ handler *file,
+ ulonglong table_flags) const
+ { return Column_definition_prepare_stage2_legacy(c, MYSQL_TYPE_DATETIME); }
+ Field *make_table_field(const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE *table) const;
};
-class Type_handler_datetime2: public Type_handler_temporal_result
+class Type_handler_datetime2: public Type_handler_datetime_common
{
public:
virtual ~Type_handler_datetime2() {}
- enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; }
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;
+ bool Column_definition_prepare_stage2(Column_definition *c,
+ handler *file,
+ ulonglong table_flags) const
+ { return Column_definition_prepare_stage2_legacy(c, MYSQL_TYPE_DATETIME2); }
+ Field *make_table_field(const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE *table) const;
};
-class Type_handler_timestamp: public Type_handler_temporal_result
+class Type_handler_timestamp_common: public Type_handler_temporal_with_date
{
+ static const Name m_name_timestamp;
public:
- virtual ~Type_handler_timestamp() {}
+ virtual ~Type_handler_timestamp_common() {}
+ const Name name() const { return m_name_timestamp; }
+ const Type_handler *type_handler_for_comparison() const;
enum_field_types field_type() const { return MYSQL_TYPE_TIMESTAMP; }
+ enum_mysql_timestamp_type mysql_timestamp_type() const
+ {
+ return MYSQL_TIMESTAMP_DATETIME;
+ }
+ bool is_timestamp_type() const
+ {
+ return true;
+ }
+ bool Column_definition_fix_attributes(Column_definition *c) const;
+ uint Item_decimal_scale(const Item *item) const
+ {
+ return Item_decimal_scale_with_seconds(item);
+ }
+ uint Item_decimal_precision(const Item *item) const;
+ uint Item_divisor_precision_increment(const Item *item) const
+ {
+ return Item_divisor_precision_increment_with_seconds(item);
+ }
+ bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
+ {
+ 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;
+ 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;
+};
+
+
+class Type_handler_timestamp: public Type_handler_timestamp_common
+{
+ /* number of bytes to store second_part part of the TIMESTAMP(N) */
+ static uint m_sec_part_bytes[MAX_DATETIME_PRECISION + 1];
+public:
+ static uint sec_part_bytes(uint dec) { return m_sec_part_bytes[dec]; }
+ virtual ~Type_handler_timestamp() {}
+ uint32 calc_pack_length(uint32 length) const;
Field *make_conversion_table_field(TABLE *, uint metadata,
const Field *target) const;
+ bool Column_definition_prepare_stage2(Column_definition *c,
+ handler *file,
+ ulonglong table_flags) const
+ { return Column_definition_prepare_stage2_legacy_num(c, MYSQL_TYPE_TIMESTAMP); }
+ Field *make_table_field(const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE *table) const;
};
-class Type_handler_timestamp2: public Type_handler_temporal_result
+class Type_handler_timestamp2: public Type_handler_timestamp_common
{
public:
virtual ~Type_handler_timestamp2() {}
- enum_field_types field_type() const { return MYSQL_TYPE_TIMESTAMP; }
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;
+ bool Column_definition_prepare_stage2(Column_definition *c,
+ handler *file,
+ ulonglong table_flags) const
+ {
+ return Column_definition_prepare_stage2_legacy_num(c, MYSQL_TYPE_TIMESTAMP2);
+ }
+ Field *make_table_field(const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE *table) const;
};
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;
+ bool Column_definition_prepare_stage2(Column_definition *c,
+ handler *file,
+ ulonglong table_flags) const
+ { return Column_definition_prepare_stage2_legacy_num(c, MYSQL_TYPE_DECIMAL); }
+ Field *make_table_field(const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE *table) const;
};
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;
+ bool Column_definition_prepare_stage1(THD *thd,
+ MEM_ROOT *mem_root,
+ Column_definition *c,
+ handler *file,
+ ulonglong table_flags) const;
+ bool Column_definition_redefine_stage1(Column_definition *def,
+ const Column_definition *dup,
+ const handler *file,
+ const Schema_specification_st *schema)
+ 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;
};
-class Type_handler_null: public Type_handler_string_result
+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; }
+ bool Item_save_in_value(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 Column_definition_prepare_stage1(THD *thd,
+ MEM_ROOT *mem_root,
+ Column_definition *c,
+ handler *file,
+ ulonglong table_flags) const;
+ bool Column_definition_redefine_stage1(Column_definition *def,
+ const Column_definition *dup,
+ const handler *file,
+ const Schema_specification_st *schema)
+ const;
+ bool Column_definition_prepare_stage2(Column_definition *c,
+ handler *file,
+ ulonglong table_flags) const
+ { return Column_definition_prepare_stage2_legacy(c, MYSQL_TYPE_NULL); }
+ Field *make_table_field(const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE *table) const;
};
-class Type_handler_string: public Type_handler_string_result
+class Type_handler_longstr: public Type_handler_general_purpose_string
{
public:
+ bool type_can_have_key_part() const
+ {
+ return true;
+ }
+};
+
+
+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
+ {
+ 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 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;
+};
+
+
+/* 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; }
+ const Type_handler *type_handler_for_tmp_table(const Item *item) const
+ {
+ return varstring_type_handler(item);
+ }
+ bool Column_definition_fix_attributes(Column_definition *c) const;
+ bool Column_definition_prepare_stage2(Column_definition *c,
+ handler *file,
+ ulonglong table_flags) const
+ { return Column_definition_prepare_stage2_legacy_num(c, MYSQL_TYPE_STRING); }
+ const Type_handler *type_handler_for_union(const Item *item) const
+ {
+ return varstring_type_handler(item);
+ }
};
-class Type_handler_varchar: public Type_handler_string_result
+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; }
+ uint32 calc_pack_length(uint32 length) const
+ {
+ return (length + (length < 256 ? 1: 2));
+ }
+ const Type_handler *type_handler_for_tmp_table(const Item *item) const
+ {
+ return varstring_type_handler(item);
+ }
+ const Type_handler *type_handler_for_union(const Item *item) const
+ {
+ 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 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;
+ bool adjust_spparam_type(Spvar_definition *def, Item *from) const;
};
-class Type_handler_blob_common: public Type_handler_string_result
+class Type_handler_varchar_compressed: public Type_handler_varchar
+{
+public:
+ Field *make_conversion_table_field(TABLE *, uint metadata,
+ const Field *target) const;
+};
+
+
+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;
+ const Type_handler *type_handler_for_tmp_table(const Item *item) const
+ {
+ return blob_type_handler(item);
+ }
+ const Type_handler *type_handler_for_union(const Item *item) const
+ {
+ return blob_type_handler(item);
+ }
+ bool subquery_type_allows_materialization(const Item *inner,
+ const Item *outer) const
+ {
+ 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;
+ bool Column_definition_prepare_stage2(Column_definition *c,
+ handler *file,
+ ulonglong table_flags) const;
+ 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;
+
};
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,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE *table) const;
};
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,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE *table) const;
};
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;
+ Item *create_typecast_item(THD *thd, Item *item,
+ const Type_cast_attributes &attr) const;
+ Field *make_table_field(const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE *table) const;
};
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,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE *table) const;
+};
+
+
+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
+ {
+ return true;
+ }
+ bool subquery_type_allows_materialization(const Item *inner,
+ const Item *outer) const
+ {
+ return false; // Materialization does not work with GEOMETRY columns
+ }
+ 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;
+ bool Column_definition_fix_attributes(Column_definition *c) 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;
+
+ 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
+ {
+ return false;
+ }
+ 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_enum: public Type_handler_string_result
+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_hybrid_func_fix_attributes(THD *thd,
+ const char *name,
+ Type_handler_hybrid_field_type *,
+ Type_all_attributes *atrr,
+ Item **items, uint nitems) const;
+ bool Column_definition_prepare_stage1(THD *thd,
+ MEM_ROOT *mem_root,
+ Column_definition *c,
+ handler *file,
+ ulonglong table_flags) const;
+ bool Column_definition_redefine_stage1(Column_definition *def,
+ const Column_definition *dup,
+ const handler *file,
+ const Schema_specification_st *schema)
+ const;
+ 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;
+};
+
+
+class Type_handler_enum: public Type_handler_typelib
+{
+ static const Name m_name_enum;
+public:
virtual ~Type_handler_enum() {}
- enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; }
+ const Name name() const { return m_name_enum; }
virtual enum_field_types real_field_type() const { 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;
+ 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;
};
-class Type_handler_set: public Type_handler_string_result
+class Type_handler_set: public Type_handler_typelib
{
+ static const Name m_name_set;
public:
virtual ~Type_handler_set() {}
- enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; }
+ const Name name() const { return m_name_set; }
virtual enum_field_types real_field_type() const { 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;
+ 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;
};
@@ -500,97 +3479,194 @@ public:
Makes sure that field_type(), cmp_type() and result_type()
are always in sync to each other for hybrid functions.
*/
-class Type_handler_hybrid_field_type: public Type_handler
+class Type_handler_hybrid_field_type
{
const Type_handler *m_type_handler;
- const Type_handler *get_handler_by_result_type(Item_result type) const;
+ bool aggregate_for_min_max(const Type_handler *other);
+
public:
Type_handler_hybrid_field_type();
Type_handler_hybrid_field_type(const Type_handler *handler)
:m_type_handler(handler)
{ }
- Type_handler_hybrid_field_type(enum_field_types type)
- :m_type_handler(get_handler_by_field_type(type))
- { }
Type_handler_hybrid_field_type(const Type_handler_hybrid_field_type *other)
:m_type_handler(other->m_type_handler)
{ }
- enum_field_types field_type() const { return m_type_handler->field_type(); }
+ void swap(Type_handler_hybrid_field_type &other)
+ {
+ swap_variables(const Type_handler *, m_type_handler, other.m_type_handler);
+ }
+ const Type_handler *type_handler() const { return m_type_handler; }
enum_field_types real_field_type() const
{
return m_type_handler->real_field_type();
}
- Item_result result_type() const { return m_type_handler->result_type(); }
Item_result cmp_type() const { return m_type_handler->cmp_type(); }
+ enum_mysql_timestamp_type mysql_timestamp_type() const
+ {
+ return m_type_handler->mysql_timestamp_type();
+ }
+ bool is_timestamp_type() const
+ {
+ return m_type_handler->is_timestamp_type();
+ }
void set_handler(const Type_handler *other)
{
m_type_handler= other;
}
const Type_handler *set_handler_by_result_type(Item_result type)
{
- return (m_type_handler= get_handler_by_result_type(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= get_handler_by_result_type(type);
+ 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= get_handler_by_field_type(type));
+ return (m_type_handler= Type_handler::get_handler_by_field_type(type));
}
const Type_handler *set_handler_by_real_type(enum_field_types type)
{
- return (m_type_handler= get_handler_by_real_type(type));
- }
- const Type_handler *
- type_handler_adjusted_to_max_octet_length(uint max_octet_length,
- CHARSET_INFO *cs) const
- {
- return
- m_type_handler->type_handler_adjusted_to_max_octet_length(max_octet_length,
- cs);
+ return (m_type_handler= Type_handler::get_handler_by_real_type(type));
}
- Field *make_num_distinct_aggregator_field(MEM_ROOT *mem_root,
- const Item *item) const
- {
- return m_type_handler->make_num_distinct_aggregator_field(mem_root, item);
- }
- Field *make_conversion_table_field(TABLE *table, uint metadata,
- const Field *target) const
+ bool aggregate_for_comparison(const Type_handler *other);
+ bool aggregate_for_comparison(const char *funcname,
+ Item **items, uint nitems,
+ bool treat_int_to_uint_as_decimal);
+ bool aggregate_for_result(const Type_handler *other);
+ bool aggregate_for_result(const char *funcname,
+ Item **item, uint nitems, bool treat_bit_as_number);
+ bool aggregate_for_min_max(const char *funcname, Item **item, uint nitems);
+
+ bool aggregate_for_num_op(const class Type_aggregator *aggregator,
+ const Type_handler *h0, const Type_handler *h1);
+};
+
+
+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_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_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_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_tiny_blob type_handler_tiny_blob;
+extern MYSQL_PLUGIN_IMPORT Type_handler_blob type_handler_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;
+
+class Type_aggregator
+{
+ bool m_is_commutative;
+ class Pair
{
- return m_type_handler->make_conversion_table_field(table, metadata, target);
- }
- void make_sort_key(uchar *to, Item *item, const SORT_FIELD_ATTR *sort_field,
- Sort_param *param) const
+ public:
+ const Type_handler *m_handler1;
+ const Type_handler *m_handler2;
+ const Type_handler *m_result;
+ Pair() { }
+ Pair(const Type_handler *handler1,
+ const Type_handler *handler2,
+ const Type_handler *result)
+ :m_handler1(handler1), m_handler2(handler2), m_result(result)
+ { }
+ bool eq(const Type_handler *handler1, const Type_handler *handler2) const
+ {
+ return m_handler1 == handler1 && m_handler2 == handler2;
+ }
+ };
+ 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)
+ { }
+ bool add(const Type_handler *handler1,
+ const Type_handler *handler2,
+ const Type_handler *result)
{
- m_type_handler->make_sort_key(to, item, sort_field, param);
+ return m_array.append(Pair(handler1, handler2, result));
}
- void sortlength(THD *thd,
- const Type_std_attributes *item,
- SORT_FIELD_ATTR *attr) const
+ const Type_handler *find_handler(const Type_handler *handler1,
+ const Type_handler *handler2) const
{
- m_type_handler->sortlength(thd, item, attr);
+ const Pair* el= find_pair(handler1, handler2);
+ return el ? el->m_result : NULL;
}
-
+ bool is_commutative() const { return m_is_commutative; }
};
-/**
- This class is used for Item_type_holder, which preserves real_type.
-*/
-class Type_handler_hybrid_real_field_type:
- public Type_handler_hybrid_field_type
+class Type_aggregator_commutative: public Type_aggregator
{
public:
- Type_handler_hybrid_real_field_type(enum_field_types type)
- :Type_handler_hybrid_field_type(get_handler_by_real_type(type))
+ Type_aggregator_commutative()
+ :Type_aggregator(true)
{ }
};
+class Type_handler_data
+{
+public:
+ Type_aggregator_commutative m_type_aggregator_for_result;
+ Type_aggregator_commutative m_type_aggregator_for_comparison;
+
+ Type_aggregator_commutative m_type_aggregator_for_plus;
+ Type_aggregator_commutative m_type_aggregator_for_mul;
+
+ Type_aggregator m_type_aggregator_for_minus;
+ Type_aggregator m_type_aggregator_for_div;
+ Type_aggregator m_type_aggregator_for_mod;
+#ifndef DBUG_OFF
+ // This is used for mtr purposes in debug builds
+ Type_aggregator m_type_aggregator_non_commutative_test;
+#endif
+ bool init();
+};
+
+
+extern Type_handler_data *type_handler_data;
+
#endif /* SQL_TYPE_H_INCLUDED */
diff --git a/sql/sql_type_int.h b/sql/sql_type_int.h
index 5988a985a2d..3e5e6a345fa 100644
--- a/sql/sql_type_int.h
+++ b/sql/sql_type_int.h
@@ -52,6 +52,22 @@ public:
return ((ulonglong) LONGLONG_MAX) + 1;
return m_value < 0 ? -m_value : m_value;
}
+ /*
+ Convert to an unsigned number:
+ - Negative numbers are converted to 0.
+ - Positive numbers bigger than upper_bound are converted to upper_bound.
+ - Other numbers are returned as is.
+ */
+ ulonglong to_ulonglong(ulonglong upper_bound) const
+ {
+ return neg() ? 0 :
+ (ulonglong) m_value > upper_bound ? upper_bound :
+ (ulonglong) m_value;
+ }
+ uint to_uint(uint upper_bound) const
+ {
+ return (uint) to_ulonglong(upper_bound);
+ }
int cmp(const Longlong_hybrid& other) const
{
if (m_unsigned == other.m_unsigned)
diff --git a/sql/sql_type_real.h b/sql/sql_type_real.h
new file mode 100644
index 00000000000..5a484fbeffb
--- /dev/null
+++ b/sql/sql_type_real.h
@@ -0,0 +1,47 @@
+/* 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_REAL_INCLUDED
+#define SQL_TYPE_REAL_INCLUDED
+
+#include <cmath>
+
+class Float
+{
+ float m_value;
+public:
+ Float(float nr)
+ :m_value(nr)
+ {
+ DBUG_ASSERT(!std::isnan(nr));
+ DBUG_ASSERT(!std::isinf(nr));
+ }
+ Float(double nr)
+ :m_value((float) nr)
+ {
+ DBUG_ASSERT(!std::isnan(nr));
+ DBUG_ASSERT(!std::isinf(nr));
+ DBUG_ASSERT(nr <= FLT_MAX);
+ DBUG_ASSERT(nr >= -FLT_MAX);
+ }
+ Float(const uchar *ptr)
+ {
+ float4get(m_value, ptr);
+ }
+ bool to_string(String *to, uint dec) const;
+};
+
+
+#endif // SQL_TYPE_REAL_INCLUDED
diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc
index 9099345a64c..3a01ea1118b 100644
--- a/sql/sql_udf.cc
+++ b/sql/sql_udf.cc
@@ -31,7 +31,7 @@
#pragma implementation // gcc: Class implementation
#endif
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#include "sql_base.h" // close_mysql_tables
@@ -52,14 +52,14 @@ static bool initialized = 0;
static MEM_ROOT mem;
static HASH udf_hash;
static mysql_rwlock_t THR_LOCK_udf;
+static LEX_CSTRING MYSQL_FUNC_NAME= {STRING_WITH_LEN("func") };
-
-static udf_func *add_udf(LEX_STRING *name, Item_result ret,
- char *dl, Item_udftype typ);
+static udf_func *add_udf(LEX_CSTRING *name, Item_result ret,
+ const char *dl, Item_udftype typ);
static void del_udf(udf_func *udf);
static void *find_udf_dl(const char *dl);
-static char *init_syms(udf_func *tmp, char *nm)
+static const char *init_syms(udf_func *tmp, char *nm)
{
char *end;
@@ -142,7 +142,6 @@ void udf_init()
TABLE *table;
int error;
DBUG_ENTER("ufd_init");
- char db[]= "mysql"; /* A subject to casednstr, can't be constant */
if (initialized || opt_noacl)
DBUG_VOID_RETURN;
@@ -153,7 +152,7 @@ void udf_init()
mysql_rwlock_init(key_rwlock_THR_LOCK_udf, &THR_LOCK_udf);
- init_sql_alloc(&mem, UDF_ALLOC_BLOCK_SIZE, 0, MYF(0));
+ init_sql_alloc(&mem, "udf", 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))
@@ -167,9 +166,9 @@ void udf_init()
initialized = 1;
new_thd->thread_stack= (char*) &new_thd;
new_thd->store_globals();
- new_thd->set_db(db, sizeof(db)-1);
+ new_thd->set_db(&MYSQL_SCHEMA_NAME);
- tables.init_one_table(db, sizeof(db)-1, "func", 4, "func", TL_READ);
+ tables.init_one_table(&new_thd->db, &MYSQL_FUNC_NAME, 0, TL_READ);
if (open_and_lock_tables(new_thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
{
@@ -189,10 +188,10 @@ void udf_init()
}
table->use_all_columns();
- while (!(error= read_record_info.read_record(&read_record_info)))
+ while (!(error= read_record_info.read_record()))
{
DBUG_PRINT("info",("init udf record"));
- LEX_STRING name;
+ LEX_CSTRING name;
name.str=get_field(&mem, table->field[0]);
name.length = (uint) strlen(name.str);
char *dl_name= get_field(&mem, table->field[2]);
@@ -242,7 +241,8 @@ void udf_init()
}
tmp->dlhandle = dl;
{
- char buf[SAFE_NAME_LEN+16], *missing;
+ char buf[SAFE_NAME_LEN+16];
+ const char *missing;
if ((missing= init_syms(tmp, buf)))
{
sql_print_error(ER_THD(new_thd, ER_CANT_FIND_DL_ENTRY), missing);
@@ -252,7 +252,7 @@ void udf_init()
}
}
}
- if (error > 0)
+ if (unlikely(error > 0))
sql_print_error("Got unknown error: %d", my_errno);
end_read_record(&read_record_info);
@@ -313,9 +313,9 @@ static void del_udf(udf_func *udf)
The functions will be automaticly removed when the least threads
doesn't use it anymore
*/
- char *name= udf->name.str;
- uint name_length=udf->name.length;
- udf->name.str=(char*) "*";
+ const char *name= udf->name.str;
+ size_t name_length=udf->name.length;
+ udf->name.str= "*";
udf->name.length=1;
my_hash_update(&udf_hash,(uchar*) udf,(uchar*) name,name_length);
}
@@ -349,10 +349,11 @@ void free_udf(udf_func *udf)
/* This is only called if using_udf_functions != 0 */
-udf_func *find_udf(const char *name,uint length,bool mark_used)
+udf_func *find_udf(const char *name,size_t length,bool mark_used)
{
udf_func *udf=0;
DBUG_ENTER("find_udf");
+ DBUG_ASSERT(strlen(name) == length);
if (!initialized)
DBUG_RETURN(NULL);
@@ -364,8 +365,7 @@ udf_func *find_udf(const char *name,uint length,bool mark_used)
else
mysql_rwlock_rdlock(&THR_LOCK_udf); /* Called during parsing */
- if ((udf=(udf_func*) my_hash_search(&udf_hash,(uchar*) name,
- length ? length : (uint) strlen(name))))
+ if ((udf=(udf_func*) my_hash_search(&udf_hash,(uchar*) name, length)))
{
if (!udf->dlhandle)
udf=0; // Could not be opened
@@ -397,7 +397,7 @@ static void *find_udf_dl(const char *dl)
/* Assume that name && dl is already allocated */
-static udf_func *add_udf(LEX_STRING *name, Item_result ret, char *dl,
+static udf_func *add_udf(LEX_CSTRING *name, Item_result ret, const char *dl,
Item_udftype type)
{
if (!name || !dl || !(uint) type || (uint) type > (uint) UDFTYPE_AGGREGATE)
@@ -433,8 +433,8 @@ static int mysql_drop_function_internal(THD *thd, udf_func *udf, TABLE *table)
{
DBUG_ENTER("mysql_drop_function_internal");
- char *exact_name_str= udf->name.str;
- uint exact_name_len= udf->name.length;
+ const char *exact_name_str= udf->name.str;
+ size_t exact_name_len= udf->name.length;
del_udf(udf);
/*
@@ -455,7 +455,7 @@ static int mysql_drop_function_internal(THD *thd, udf_func *udf, TABLE *table)
HA_READ_KEY_EXACT))
{
int error;
- if ((error= table->file->ha_delete_row(table->record[0])))
+ if (unlikely((error= table->file->ha_delete_row(table->record[0]))))
table->file->print_error(error, MYF(0));
}
DBUG_RETURN(0);
@@ -505,8 +505,7 @@ int mysql_create_function(THD *thd,udf_func *udf)
if (check_ident_length(&udf->name))
DBUG_RETURN(1);
- tables.init_one_table(STRING_WITH_LEN("mysql"), STRING_WITH_LEN("func"),
- "func", TL_WRITE);
+ tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_FUNC_NAME, 0, TL_WRITE);
table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT);
mysql_rwlock_wrlock(&THR_LOCK_udf);
@@ -516,7 +515,7 @@ int mysql_create_function(THD *thd,udf_func *udf)
{
if (thd->lex->create_info.or_replace())
{
- if ((error= mysql_drop_function_internal(thd, u_d, table)))
+ if (unlikely((error= mysql_drop_function_internal(thd, u_d, table))))
goto err;
}
else if (thd->lex->create_info.if_not_exists())
@@ -550,7 +549,8 @@ int mysql_create_function(THD *thd,udf_func *udf)
}
udf->dlhandle=dl;
{
- char buf[SAFE_NAME_LEN+16], *missing;
+ char buf[SAFE_NAME_LEN+16];
+ const char *missing;
if ((missing= init_syms(udf, buf)))
{
my_error(ER_CANT_FIND_DL_ENTRY, MYF(0), missing);
@@ -571,7 +571,7 @@ int mysql_create_function(THD *thd,udf_func *udf)
/* create entry in mysql.func table */
/* Allow creation of functions even if we can't open func table */
- if (!table)
+ if (unlikely(!table))
goto err;
table->use_all_columns();
restore_record(table, s->default_values); // Default values for fields
@@ -580,9 +580,9 @@ int mysql_create_function(THD *thd,udf_func *udf)
table->field[2]->store(u_d->dl,(uint) strlen(u_d->dl), system_charset_info);
if (table->s->fields >= 4) // If not old func format
table->field[3]->store((longlong) u_d->type, TRUE);
- error = table->file->ha_write_row(table->record[0]);
+ error= table->file->ha_write_row(table->record[0]);
- if (error)
+ if (unlikely(error))
{
my_error(ER_ERROR_ON_WRITE, MYF(0), "mysql.func", error);
del_udf(u_d);
@@ -593,7 +593,7 @@ done:
mysql_rwlock_unlock(&THR_LOCK_udf);
/* Binlog the create function. */
- if (write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
+ if (unlikely(write_bin_log(thd, TRUE, thd->query(), thd->query_length())))
DBUG_RETURN(1);
DBUG_RETURN(0);
@@ -606,7 +606,7 @@ err:
}
-int mysql_drop_function(THD *thd,const LEX_STRING *udf_name)
+int mysql_drop_function(THD *thd, const LEX_CSTRING *udf_name)
{
TABLE *table;
TABLE_LIST tables;
@@ -623,8 +623,7 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name)
DBUG_RETURN(1);
}
- tables.init_one_table(STRING_WITH_LEN("mysql"), STRING_WITH_LEN("func"),
- "func", TL_WRITE);
+ tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_FUNC_NAME, 0, TL_WRITE);
table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT);
mysql_rwlock_wrlock(&THR_LOCK_udf);
diff --git a/sql/sql_udf.h b/sql/sql_udf.h
index a683b8a7554..694e4fdd9c6 100644
--- a/sql/sql_udf.h
+++ b/sql/sql_udf.h
@@ -37,10 +37,10 @@ typedef longlong (*Udf_func_longlong)(UDF_INIT *, UDF_ARGS *, uchar *,
typedef struct st_udf_func
{
- LEX_STRING name;
+ LEX_CSTRING name;
Item_result returns;
Item_udftype type;
- char *dl;
+ const char *dl;
void *dlhandle;
Udf_func_any func;
Udf_func_init func_init;
@@ -137,10 +137,10 @@ class udf_handler :public Sql_alloc
#ifdef HAVE_DLOPEN
void udf_init(void),udf_free(void);
-udf_func *find_udf(const char *name, uint len=0,bool mark_used=0);
+udf_func *find_udf(const char *name, size_t size, bool mark_used=0);
void free_udf(udf_func *udf);
int mysql_create_function(THD *thd,udf_func *udf);
-int mysql_drop_function(THD *thd,const LEX_STRING *name);
+int mysql_drop_function(THD *thd, const LEX_CSTRING *name);
#else
static inline void udf_init(void) { }
static inline void udf_free(void) { }
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index 8b1719c60cb..d15cc3fa617 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2017, Oracle and/or its affiliates.
- Copyright (c) 2010, 2017, 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
@@ -20,7 +20,7 @@
UNION's were introduced by Monty and Sinisa <sinisa@mysql.com>
*/
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#include "sql_union.h"
@@ -36,7 +36,7 @@ bool mysql_union(THD *thd, LEX *lex, select_result *result,
{
DBUG_ENTER("mysql_union");
bool res;
- if (!(res= unit->prepare(thd, result, SELECT_NO_UNLOCK |
+ if (!(res= unit->prepare(unit->derived, result, SELECT_NO_UNLOCK |
setup_tables_done_option)))
res= unit->exec();
res|= unit->cleanup();
@@ -48,15 +48,68 @@ bool mysql_union(THD *thd, LEX *lex, select_result *result,
** store records in temporary table for UNION
***************************************************************************/
-int select_union::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
+int select_unit::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
{
unit= u;
return 0;
}
+/**
+ This called by SELECT_LEX_UNIT::exec when select changed
+*/
-int select_union::send_data(List<Item> &values)
+void select_unit::change_select()
+{
+ uint current_select_number= thd->lex->current_select->select_number;
+ DBUG_ENTER("select_unit::change_select");
+ DBUG_PRINT("enter", ("select in unit change: %u -> %u",
+ curr_sel, current_select_number));
+ DBUG_ASSERT(curr_sel != current_select_number);
+ curr_sel= current_select_number;
+ /* New SELECT processing starts */
+ DBUG_ASSERT(table->file->inited == 0);
+ step= thd->lex->current_select->linkage;
+ switch (step)
+ {
+ case INTERSECT_TYPE:
+ intersect_mark->value= prev_step= curr_step;
+ curr_step= current_select_number;
+ break;
+ case EXCEPT_TYPE:
+ break;
+ default:
+ step= UNION_TYPE;
+ break;
+ }
+ DBUG_VOID_RETURN;
+}
+/**
+ Fill temporary tables for UNION/EXCEPT/INTERSECT
+
+ @Note
+UNION:
+ just add records to the table (with 'counter' field first if INTERSECT
+ present in the sequence).
+EXCEPT:
+ looks for the record in the table (with 'counter' field first if
+ INTERSECT present in the sequence) and delete it if found
+INTESECT:
+ 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
+ 'counter' after processing all records in send_eof and only if it last
+ SELECT of sequence of INTERSECTS.
+
+ @param values List of record items to process.
+
+ @retval 0 - OK
+ @retval -1 - duplicate
+ @retval 1 - error
+*/
+int select_unit::send_data(List<Item> &values)
{
+ int rc;
+ int not_reported_error= 0;
if (unit->offset_limit_cnt)
{ // using limit offset,count
unit->offset_limit_cnt--;
@@ -66,46 +119,187 @@ int select_union::send_data(List<Item> &values)
return 0;
if (table->no_rows_with_nulls)
table->null_catch_flags= CHECK_ROW_FOR_NULLS_TO_REJECT;
- fill_record(thd, table, table->field, values, TRUE, FALSE);
- if (thd->is_error())
- return 1;
+ if (intersect_mark)
+ {
+ fill_record(thd, table, table->field + 1, values, TRUE, FALSE);
+ table->field[0]->store((ulonglong) curr_step, 1);
+ }
+ else
+ fill_record(thd, table, table->field, values, TRUE, FALSE);
+ if (unlikely(thd->is_error()))
+ {
+ rc= 1;
+ goto end;
+ }
if (table->no_rows_with_nulls)
{
table->null_catch_flags&= ~CHECK_ROW_FOR_NULLS_TO_REJECT;
if (table->null_catch_flags)
- return 0;
+ {
+ rc= 0;
+ goto end;
+ }
}
- if ((write_err= table->file->ha_write_tmp_row(table->record[0])))
+ // select_unit::change_select() change step & Co correctly for each SELECT
+ switch (step)
{
- if (write_err == HA_ERR_FOUND_DUPP_KEY)
+ 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;
+ }
+
+ if (is_duplicate)
+ {
+ rc= -1;
+ goto end;
+ }
+ }
+ break;
+ }
+ case EXCEPT_TYPE:
+ {
+ int find_res;
/*
- Inform upper level that we found a duplicate key, that should not
- be counted as part of limit
+ The temporary table uses very first index or constrain for
+ checking unique constrain.
*/
- return -1;
+ 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;
}
- 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))
- return 1;
- if (is_duplicate)
- return -1;
+ case INTERSECT_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);
+ 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]);
+ 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);
}
- return 0;
+ rc= 0;
+
+end:
+ if (unlikely(not_reported_error))
+ {
+ DBUG_ASSERT(rc);
+ table->file->print_error(not_reported_error, MYF(0));
+ }
+ return rc;
+}
+
+bool select_unit::send_eof()
+{
+ if (step != INTERSECT_TYPE ||
+ (thd->lex->current_select->next_select() &&
+ thd->lex->current_select->next_select()->linkage == INTERSECT_TYPE))
+ {
+ /*
+ it is not INTESECT or next SELECT in the sequence is INTERSECT so no
+ need filtering (the last INTERSECT in this sequence of intersects will
+ filter).
+ */
+ return 0;
+ }
+
+ /*
+ It is last select in the sequence of INTERSECTs so we should filter out
+ all records except marked with actual counter.
+
+ 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)))
+ return 1;
+
+ do
+ {
+ if (unlikely(error= file->ha_rnd_next(table->record[0])))
+ {
+ if (error == HA_ERR_END_OF_FILE)
+ {
+ error= 0;
+ break;
+ }
+ 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();
+
+ if (unlikely(error))
+ table->file->print_error(error, MYF(0));
+
+ return(MY_TEST(error));
}
int select_union_recursive::send_data(List<Item> &values)
{
- int rc= select_union::send_data(values);
+ int rc= select_unit::send_data(values);
- if (write_err != HA_ERR_FOUND_DUPP_KEY &&
+ if (rc == 0 &&
+ write_err != HA_ERR_FOUND_DUPP_KEY &&
write_err != HA_ERR_FOUND_DUPP_UNIQUE)
{
int err;
@@ -123,16 +317,10 @@ int select_union_recursive::send_data(List<Item> &values)
}
-bool select_union::send_eof()
-{
- return 0;
-}
-
-
-bool select_union::flush()
+bool select_unit::flush()
{
int error;
- if ((error=table->file->extra(HA_EXTRA_NO_CACHE)))
+ if (unlikely((error=table->file->extra(HA_EXTRA_NO_CACHE))))
{
table->file->print_error(error, MYF(0));
return 1;
@@ -140,11 +328,12 @@ bool select_union::flush()
return 0;
}
+
/*
Create a temporary table to store the result of select_union.
SYNOPSIS
- select_union::create_result_table()
+ select_unit::create_result_table()
thd thread handle
column_types a list of items used to define columns of the
temporary table
@@ -155,6 +344,7 @@ bool select_union::flush()
bit_fields_as_long convert bit fields to ulonglong
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)
DESCRIPTION
Create a temporary table that is used to store the result of a UNION,
@@ -166,16 +356,18 @@ bool select_union::flush()
*/
bool
-select_union::create_result_table(THD *thd_arg, List<Item> *column_types,
+select_unit::create_result_table(THD *thd_arg, List<Item> *column_types,
bool is_union_distinct, ulonglong options,
- const char *alias,
+ const LEX_CSTRING *alias,
bool bit_fields_as_long, bool create_table,
- bool keep_row_order)
+ bool keep_row_order,
+ uint hidden)
{
DBUG_ASSERT(table == 0);
tmp_table_param.init();
tmp_table_param.field_count= column_types->elements;
tmp_table_param.bit_fields_as_long= bit_fields_as_long;
+ tmp_table_param.hidden_field_count= hidden;
if (! (table= create_tmp_table(thd_arg, &tmp_table_param, *column_types,
(ORDER*) 0, is_union_distinct, 1,
@@ -200,20 +392,22 @@ select_union_recursive::create_result_table(THD *thd_arg,
List<Item> *column_types,
bool is_union_distinct,
ulonglong options,
- const char *alias,
+ const LEX_CSTRING *alias,
bool bit_fields_as_long,
bool create_table,
- bool keep_row_order)
+ bool keep_row_order,
+ uint hidden)
{
- if (select_union::create_result_table(thd_arg, column_types,
- is_union_distinct, options,
- "", bit_fields_as_long,
- create_table, keep_row_order))
+ if (select_unit::create_result_table(thd_arg, column_types,
+ is_union_distinct, options,
+ &empty_clex_str, bit_fields_as_long,
+ create_table, keep_row_order,
+ hidden))
return true;
if (! (incr_table= create_tmp_table(thd_arg, &tmp_table_param, *column_types,
(ORDER*) 0, false, 1,
- options, HA_POS_ERROR, "",
+ options, HA_POS_ERROR, &empty_clex_str,
true, keep_row_order)))
return true;
@@ -247,7 +441,7 @@ select_union_recursive::create_result_table(THD *thd_arg,
tables of JOIN - exec_tmp_table_[1 | 2].
*/
-void select_union::cleanup()
+void select_unit::cleanup()
{
table->file->extra(HA_EXTRA_RESET_STATE);
table->file->ha_delete_all_rows();
@@ -258,7 +452,7 @@ void select_union_recursive::cleanup()
{
if (table)
{
- select_union::cleanup();
+ select_unit::cleanup();
free_tmp_table(thd, table);
}
@@ -305,14 +499,14 @@ void select_union_recursive::cleanup()
bool select_union_direct::change_result(select_result *new_result)
{
result= new_result;
- return (result->prepare(unit->types, unit) || result->prepare2());
+ return (result->prepare(unit->types, unit) || result->prepare2(NULL));
}
bool select_union_direct::postponed_prepare(List<Item> &types)
{
if (result != NULL)
- return (result->prepare(types, unit) || result->prepare2());
+ return (result->prepare(types, unit) || result->prepare2(NULL));
else
return false;
}
@@ -353,7 +547,7 @@ int select_union_direct::send_data(List<Item> &items)
send_records++;
fill_record(thd, table, table->field, items, true, false);
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
return true; /* purecov: inspected */
return result->send_data(unit->item_list);
@@ -438,20 +632,208 @@ 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,
+ bool is_union_select)
+{
+ DBUG_ENTER("st_select_lex_unit::prepare_join");
+ TABLE_LIST *derived= sl->master_unit()->derived;
+ bool can_skip_order_by;
+ sl->options|= SELECT_NO_UNLOCK;
+ JOIN *join= new JOIN(thd_arg, sl->item_list,
+ (sl->options | thd_arg->variables.option_bits |
+ additional_options),
+ tmp_result);
+ if (!join)
+ DBUG_RETURN(true);
+
+ thd_arg->lex->current_select= 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) +
+ sl->group_list.elements,
+ can_skip_order_by ?
+ NULL : sl->order_list.first,
+ can_skip_order_by,
+ sl->group_list.first,
+ sl->having,
+ (is_union_select ? NULL :
+ 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)))
+ DBUG_RETURN(true);
+ /*
+ Remove all references from the select_lex_units to the subqueries that
+ are inside the ORDER BY clause.
+ */
+ if (can_skip_order_by)
+ {
+ for (ORDER *ord= (ORDER *)sl->order_list.first; ord; ord= ord->next)
+ {
+ (*ord->item)->walk(&Item::eliminate_subselect_processor, FALSE, NULL);
+ }
+ }
+ DBUG_RETURN(false);
+}
+
+
+/**
+ Aggregate data type handlers for the "count" leftmost UNION parts.
+*/
+bool st_select_lex_unit::join_union_type_handlers(THD *thd_arg,
+ Type_holder *holders,
+ uint count)
+{
+ DBUG_ENTER("st_select_lex_unit::join_union_type_handlers");
+ SELECT_LEX *first_sl= first_select(), *sl= first_sl;
+ for (uint i= 0; i < count ; sl= sl->next_select(), i++)
+ {
+ Item *item;
+ List_iterator_fast<Item> it(sl->item_list);
+ for (uint pos= 0; (item= it++); pos++)
+ {
+ const Type_handler *item_type_handler= item->real_type_handler();
+ if (sl == first_sl)
+ holders[pos].set_handler(item_type_handler);
+ else
+ {
+ DBUG_ASSERT(first_sl->item_list.elements == sl->item_list.elements);
+ if (holders[pos].aggregate_for_result(item_type_handler))
+ {
+ my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0),
+ holders[pos].type_handler()->name().ptr(),
+ item_type_handler->name().ptr(),
+ "UNION");
+ DBUG_RETURN(true);
+ }
+ }
+ }
+ }
+ DBUG_RETURN(false);
+}
+
+
+/**
+ Aggregate data type attributes for the "count" leftmost UNION parts.
+*/
+bool st_select_lex_unit::join_union_type_attributes(THD *thd_arg,
+ Type_holder *holders,
+ uint count)
+{
+ DBUG_ENTER("st_select_lex_unit::join_union_type_attributes");
+ SELECT_LEX *sl, *first_sl= first_select();
+ uint item_pos;
+ for (uint pos= 0; pos < first_sl->item_list.elements; pos++)
+ {
+ if (holders[pos].alloc_arguments(thd_arg, count))
+ DBUG_RETURN(true);
+ }
+ for (item_pos= 0, sl= first_sl ;
+ item_pos < count;
+ sl= sl->next_select(), item_pos++)
+ {
+ Item *item_tmp;
+ List_iterator_fast<Item> itx(sl->item_list);
+ for (uint holder_pos= 0 ; (item_tmp= itx++); holder_pos++)
+ {
+ /*
+ If the outer query has a GROUP BY clause, an outer reference to this
+ query block may have been wrapped in a Item_outer_ref, which has not
+ been fixed yet. An Item_type_holder must be created based on a fixed
+ Item, so use the inner Item instead.
+ */
+ DBUG_ASSERT(item_tmp->fixed ||
+ (item_tmp->type() == Item::REF_ITEM &&
+ ((Item_ref *)(item_tmp))->ref_type() ==
+ Item_ref::OUTER_REF));
+ if (!item_tmp->fixed)
+ item_tmp= item_tmp->real_item();
+ holders[holder_pos].add_argument(item_tmp);
+ }
+ }
+ for (uint pos= 0; pos < first_sl->item_list.elements; pos++)
+ {
+ if (holders[pos].aggregate_attributes(thd_arg))
+ DBUG_RETURN(true);
+ }
+ DBUG_RETURN(false);
+}
+
+
+/**
+ Join data types for the leftmost "count" UNION parts
+ and store corresponding Item_type_holder's into "types".
+*/
+bool st_select_lex_unit::join_union_item_types(THD *thd_arg,
+ List<Item> &types,
+ uint count)
+{
+ DBUG_ENTER("st_select_lex_unit::join_union_select_list_types");
+ SELECT_LEX *first_sl= first_select();
+ Type_holder *holders;
+
+ if (!(holders= new (thd_arg->mem_root)
+ Type_holder[first_sl->item_list.elements]) ||
+ join_union_type_handlers(thd_arg, holders, count) ||
+ join_union_type_attributes(thd_arg, holders, count))
+ DBUG_RETURN(true);
+
+ bool is_recursive= with_element && with_element->is_recursive;
+ types.empty();
+ List_iterator_fast<Item> it(first_sl->item_list);
+ Item *item_tmp;
+ for (uint pos= 0; (item_tmp= it++); pos++)
+ {
+ /*
+ SQL standard requires forced nullability only for
+ recursive columns. However type aggregation in our
+ implementation so far does not differentiate between
+ recursive and non-recursive columns of a recursive CTE.
+ TODO: this should be fixed.
+ */
+ bool pos_maybe_null= is_recursive ? true : holders[pos].get_maybe_null();
+
+ /* Error's in 'new' will be detected after loop */
+ types.push_back(new (thd_arg->mem_root)
+ Item_type_holder(thd_arg,
+ item_tmp,
+ holders[pos].type_handler(),
+ &holders[pos]/*Type_all_attributes*/,
+ pos_maybe_null));
+ }
+ if (unlikely(thd_arg->is_fatal_error))
+ DBUG_RETURN(true); // out of memory
+ DBUG_RETURN(false);
+}
-bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
+bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg,
+ select_result *sel_result,
ulong additional_options)
{
- SELECT_LEX *lex_select_save= thd_arg->lex->current_select;
+ SELECT_LEX *lex_select_save= thd->lex->current_select;
SELECT_LEX *sl, *first_sl= first_select();
bool is_recursive= with_element && with_element->is_recursive;
bool is_rec_result_table_created= false;
+ uint union_part_count= 0;
select_result *tmp_result;
bool is_union_select;
+ bool have_except= FALSE, have_intersect= FALSE;
bool instantiate_tmp_table= false;
+ bool single_tvc= !first_sl->next_select() && first_sl->tvc &&
+ !fake_select_lex;
DBUG_ENTER("st_select_lex_unit::prepare");
- DBUG_ASSERT(thd == thd_arg);
DBUG_ASSERT(thd == current_thd);
if (is_recursive && (sl= first_sl->next_select()))
@@ -493,16 +875,26 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
/* fast reinit for EXPLAIN */
for (sl= first_sl; sl; sl= sl->next_select())
{
- sl->join->result= result;
- select_limit_cnt= HA_POS_ERROR;
- offset_limit_cnt= 0;
- if (!sl->join->procedure &&
- result->prepare(sl->join->fields_list, this))
+ if (sl->tvc)
{
- DBUG_RETURN(TRUE);
+ sl->tvc->result= result;
+ if (result->prepare(sl->item_list, this))
+ DBUG_RETURN(TRUE);
+ sl->tvc->select_options|= SELECT_DESCRIBE;
+ }
+ else
+ {
+ sl->join->result= result;
+ select_limit_cnt= HA_POS_ERROR;
+ offset_limit_cnt= 0;
+ if (!sl->join->procedure &&
+ result->prepare(sl->join->fields_list, this))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ sl->join->select_options|= SELECT_DESCRIBE;
+ sl->join->reinit();
}
- sl->join->select_options|= SELECT_DESCRIBE;
- sl->join->reinit();
}
}
DBUG_RETURN(FALSE);
@@ -510,9 +902,9 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
prepared= 1;
saved_error= FALSE;
- thd_arg->lex->current_select= sl= first_sl;
+ thd->lex->current_select= sl= first_sl;
found_rows_for_union= first_sl->options & OPTION_FOUND_ROWS;
- is_union_select= is_union() || fake_select_lex;
+ is_union_select= is_unit_op() || fake_select_lex || single_tvc;
/*
If we are reading UNION output and the UNION is in the
@@ -526,24 +918,40 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
(For non-UNION this removal of ORDER BY clause is done in
check_and_do_in_subquery_rewrites())
*/
- if (is_union() && item &&
+ if (item && is_unit_op() &&
(item->is_in_predicate() || item->is_exists_predicate()))
{
global_parameters()->order_list.first= NULL;
global_parameters()->order_list.elements= 0;
}
+ for (SELECT_LEX *s= first_sl; s; s= s->next_select())
+ {
+ switch (s->linkage)
+ {
+ case INTERSECT_TYPE:
+ have_intersect= TRUE;
+ break;
+ case EXCEPT_TYPE:
+ have_except= TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+
/* Global option */
if (is_union_select || is_recursive)
{
- if (is_union() && !union_needs_tmp_table())
+ if ((is_unit_op() && !union_needs_tmp_table() &&
+ !have_except && !have_intersect) || single_tvc)
{
SELECT_LEX *last= first_select();
while (last->next_select())
last= last->next_select();
if (!(tmp_result= union_result=
- new (thd_arg->mem_root) select_union_direct(thd_arg, sel_result,
+ new (thd->mem_root) select_union_direct(thd, sel_result,
last)))
goto err; /* purecov: inspected */
fake_select_lex= NULL;
@@ -552,11 +960,11 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
else
{
if (!is_recursive)
- union_result= new (thd_arg->mem_root) select_union(thd_arg);
+ union_result= new (thd->mem_root) select_unit(thd);
else
{
with_element->rec_result=
- new (thd_arg->mem_root) select_union_recursive(thd_arg);
+ new (thd->mem_root) select_union_recursive(thd);
union_result= with_element->rec_result;
if (fake_select_lex)
{
@@ -580,14 +988,60 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
tmp_result= sel_result;
sl->context.resolve_in_select_list= TRUE;
+
+ if (!is_union_select && !is_recursive)
+ {
+ if (sl->tvc)
+ {
+ if (sl->tvc->prepare(thd, sl, tmp_result, this))
+ goto err;
+ }
+ else
+ {
+ if (prepare_join(thd, first_sl, tmp_result, additional_options,
+ is_union_select))
+ goto err;
+
+ if (derived_arg && derived_arg->table &&
+ derived_arg->derived_type == VIEW_ALGORITHM_MERGE &&
+ derived_arg->table->versioned())
+ {
+ /* Got versioning conditions (see vers_setup_conds()), need to update
+ derived_arg. */
+ derived_arg->where= first_sl->where;
+ }
+ }
+ types= first_sl->item_list;
+ goto cont;
+ }
- for (;sl; sl= sl->next_select())
- {
- bool can_skip_order_by;
- sl->options|= SELECT_NO_UNLOCK;
- JOIN *join= new JOIN(thd_arg, sl->item_list,
- sl->options | thd_arg->variables.option_bits | additional_options,
- tmp_result);
+ for (;sl; sl= sl->next_select(), union_part_count++)
+ {
+ if (sl->tvc)
+ {
+ if (sl->tvc->to_be_wrapped_as_with_tail() &&
+ !(thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_VIEW))
+
+ {
+ st_select_lex *wrapper_sl= wrap_tvc_with_tail(thd, sl);
+ if (!wrapper_sl)
+ goto err;
+
+ if (sl == first_sl)
+ first_sl= wrapper_sl;
+ sl= wrapper_sl;
+
+ if (prepare_join(thd, sl, tmp_result, additional_options,
+ is_union_select))
+ goto err;
+ }
+ else if (sl->tvc->prepare(thd, sl, tmp_result, this))
+ goto err;
+ }
+ else if (prepare_join(thd, sl, tmp_result, additional_options,
+ is_union_select))
+ goto err;
+
/*
setup_tables_done_option should be set only for very first SELECT,
because it protect from secont setup_tables call for select-like non
@@ -595,136 +1049,68 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
SELECT (for union it can be only INSERT ... SELECT).
*/
additional_options&= ~OPTION_SETUP_TABLES_DONE;
- if (!join)
- goto err;
-
- thd_arg->lex->current_select= sl;
-
- can_skip_order_by= is_union_select && !(sl->braces && sl->explicit_limit);
-
- saved_error= join->prepare(sl->table_list.first,
- sl->with_wild,
- sl->where,
- (can_skip_order_by ? 0 :
- sl->order_list.elements) +
- sl->group_list.elements,
- can_skip_order_by ?
- NULL : sl->order_list.first,
- can_skip_order_by,
- sl->group_list.first,
- sl->having,
- (is_union_select ? NULL :
- 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 (saved_error || (saved_error= thd_arg->is_fatal_error))
- goto err;
- /*
- Remove all references from the select_lex_units to the subqueries that
- are inside the ORDER BY clause.
- */
- if (can_skip_order_by)
- {
- for (ORDER *ord= (ORDER *)sl->order_list.first; ord; ord= ord->next)
- {
- (*ord->item)->walk(&Item::eliminate_subselect_processor, FALSE, NULL);
- }
- }
/*
Use items list of underlaid select for derived tables to preserve
information about fields lengths and exact types
*/
- if (!is_union_select && !is_recursive)
- types= first_sl->item_list;
- else if (sl == first_sl)
+ if (sl == first_sl)
{
if (with_element)
{
if (with_element->rename_columns_of_derived_unit(thd, this))
- goto err;
+ goto err;
if (check_duplicate_names(thd, sl->item_list, 0))
goto err;
}
- types.empty();
- List_iterator_fast<Item> it(sl->item_list);
- Item *item_tmp;
- while ((item_tmp= it++))
- {
- /*
- If the outer query has a GROUP BY clause, an outer reference to this
- query block may have been wrapped in a Item_outer_ref, which has not
- been fixed yet. An Item_type_holder must be created based on a fixed
- Item, so use the inner Item instead.
- */
- DBUG_ASSERT(item_tmp->fixed ||
- (item_tmp->type() == Item::REF_ITEM &&
- ((Item_ref *)(item_tmp))->ref_type() ==
- Item_ref::OUTER_REF));
- if (!item_tmp->fixed)
- item_tmp= item_tmp->real_item();
-
- /* Error's in 'new' will be detected after loop */
- types.push_back(new (thd_arg->mem_root)
- Item_type_holder(thd_arg, item_tmp));
- }
-
- if (thd_arg->is_fatal_error)
- goto err; // out of memory
}
else
{
- if (types.elements != sl->item_list.elements)
+ if (first_sl->item_list.elements != sl->item_list.elements)
{
- my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT,
- ER_THD(thd, ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT),MYF(0));
- goto err;
- }
- if (!is_rec_result_table_created)
- {
- List_iterator_fast<Item> it(sl->item_list);
- List_iterator_fast<Item> tp(types);
- Item *type, *item_tmp;
- while ((type= tp++, item_tmp= it++))
- {
- if (((Item_type_holder*)type)->join_types(thd_arg, item_tmp))
- DBUG_RETURN(TRUE);
- }
+ my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT,
+ ER_THD(thd, ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT),
+ MYF(0));
+ goto err;
}
}
if (is_recursive)
{
if (!with_element->is_anchor(sl))
sl->uncacheable|= UNCACHEABLE_UNITED;
- if(!is_rec_result_table_created &&
- (!sl->next_select() ||
- sl->next_select() == with_element->first_recursive))
+ if (!is_rec_result_table_created &&
+ (!sl->next_select() ||
+ sl->next_select() == with_element->first_recursive))
{
ulonglong create_options;
- create_options= (first_sl->options | thd_arg->variables.option_bits |
+ create_options= (first_sl->options | thd->variables.option_bits |
TMP_TABLE_ALL_COLUMNS);
+ // Join data types for all non-recursive parts of a recursive UNION
+ if (join_union_item_types(thd, types, union_part_count + 1))
+ goto err;
if (union_result->create_result_table(thd, &types,
MY_TEST(union_distinct),
- create_options, derived->alias,
- false,
- instantiate_tmp_table, false))
+ create_options,
+ &derived_arg->alias, false,
+ instantiate_tmp_table, false,
+ 0))
goto err;
- if (!derived->table)
+ if (!derived_arg->table)
{
- derived->table= with_element->rec_result->rec_tables.head();
- if (derived->derived_result)
- derived->derived_result->table= derived->table;
+ derived_arg->table= with_element->rec_result->rec_tables.head();
+ if (derived_arg->derived_result)
+ derived_arg->derived_result->table= derived_arg->table;
}
with_element->mark_as_with_prepared_anchor();
is_rec_result_table_created= true;
}
}
}
+ // 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;
+cont:
/*
If the query is using select_union_direct, we have postponed
preparation of the underlying select_result until column types
@@ -784,7 +1170,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
}
- create_options= (first_sl->options | thd_arg->variables.option_bits |
+ create_options= (first_sl->options | thd->variables.option_bits |
TMP_TABLE_ALL_COLUMNS);
/*
Force the temporary table to be a MyISAM table if we're going to use
@@ -795,12 +1181,48 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
if (global_parameters()->ftfunc_list->elements)
create_options= create_options | TMP_TABLE_FORCE_MYISAM;
-
- if (!is_recursive &&
- union_result->create_result_table(thd, &types, MY_TEST(union_distinct),
- create_options, "", false,
- instantiate_tmp_table, false))
- goto err;
+ if (!is_recursive)
+ {
+ uint hidden= 0;
+ if (have_intersect)
+ {
+ 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);
+
+ 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;
+ }
+ bool error=
+ union_result->create_result_table(thd, &types,
+ MY_TEST(union_distinct),
+ create_options, &empty_clex_str, false,
+ instantiate_tmp_table, false,
+ hidden);
+ if (intersect_mark)
+ types.pop();
+ if (unlikely(error))
+ goto err;
+ }
if (fake_select_lex && !fake_select_lex->first_cond_optimization)
{
save_tablenr= result_table_list.tablenr_exec;
@@ -808,8 +1230,10 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
save_maybe_null= result_table_list.maybe_null_exec;
}
bzero((char*) &result_table_list, sizeof(result_table_list));
- result_table_list.db= (char*) "";
- result_table_list.table_name= result_table_list.alias= (char*) "union";
+ result_table_list.db.str= (char*) "";
+ result_table_list.db.length= 0;
+ result_table_list.table_name.str= result_table_list.alias.str= "union";
+ result_table_list.table_name.length= result_table_list.alias.length= sizeof("union")-1;
result_table_list.table= table= union_result->table;
if (fake_select_lex && !fake_select_lex->first_cond_optimization)
{
@@ -818,7 +1242,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
result_table_list.maybe_null_exec= save_maybe_null;
}
- thd_arg->lex->current_select= lex_select_save;
+ thd->lex->current_select= lex_select_save;
if (!item_list.elements)
{
Query_arena *arena, backup_arena;
@@ -826,11 +1250,14 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
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
if (arena)
thd->restore_active_arena(arena, &backup_arena);
- if (saved_error)
+ if (unlikely(saved_error))
goto err;
if (fake_select_lex != NULL &&
@@ -855,7 +1282,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
*/
fake_select_lex->item_list= item_list;
- thd_arg->lex->current_select= fake_select_lex;
+ thd->lex->current_select= fake_select_lex;
/*
We need to add up n_sum_items in order to make the correct
@@ -870,7 +1297,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
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);
+ table->reset_item_list(&item_list, intersect_mark ? 1 : 0);
}
if (fake_select_lex != NULL &&
(thd->stmt_arena->is_stmt_prepare() ||
@@ -892,12 +1319,12 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
}
}
- thd_arg->lex->current_select= lex_select_save;
+ thd->lex->current_select= lex_select_save;
- DBUG_RETURN(saved_error || thd_arg->is_fatal_error);
+ DBUG_RETURN(saved_error || thd->is_fatal_error);
err:
- thd_arg->lex->current_select= lex_select_save;
+ thd->lex->current_select= lex_select_save;
(void) cleanup();
DBUG_RETURN(TRUE);
}
@@ -932,11 +1359,11 @@ bool st_select_lex_unit::optimize()
{
item->assigned(0); // We will reinit & rexecute unit
item->reset();
- if (table->is_created())
- {
- table->file->ha_delete_all_rows();
- table->file->info(HA_STATUS_VARIABLE);
- }
+ }
+ if (table->is_created())
+ {
+ table->file->ha_delete_all_rows();
+ 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))
@@ -946,6 +1373,20 @@ bool st_select_lex_unit::optimize()
}
for (SELECT_LEX *sl= select_cursor; sl; sl= sl->next_select())
{
+ if (sl->tvc)
+ {
+ sl->tvc->select_options=
+ (select_limit_cnt == HA_POS_ERROR || sl->braces) ?
+ sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union;
+ if (sl->tvc->optimize(thd))
+ {
+ thd->lex->current_select= lex_select_save;
+ DBUG_RETURN(TRUE);
+ }
+ if (derived)
+ sl->increase_derived_records(sl->tvc->get_records());
+ continue;
+ }
thd->lex->current_select= sl;
if (optimized)
@@ -969,14 +1410,14 @@ bool st_select_lex_unit::optimize()
we don't calculate found_rows() per union part.
Otherwise, SQL_CALC_FOUND_ROWS should be done on all sub parts.
*/
- sl->join->select_options=
+ sl->join->select_options=
(select_limit_cnt == HA_POS_ERROR || sl->braces) ?
sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union;
saved_error= sl->join->optimize();
}
- if (saved_error)
+ if (unlikely(saved_error))
{
thd->lex->current_select= lex_select_save;
DBUG_RETURN(saved_error);
@@ -1014,9 +1455,20 @@ bool st_select_lex_unit::exec()
if (!saved_error && !was_executed)
save_union_explain(thd->lex->explain);
- if (saved_error)
+ if (unlikely(saved_error))
DBUG_RETURN(saved_error);
+ if (union_result)
+ {
+ union_result->init();
+ if (uncacheable & UNCACHEABLE_DEPENDENT &&
+ union_result->table && union_result->table->is_created())
+ {
+ union_result->table->file->ha_delete_all_rows();
+ union_result->table->file->ha_enable_indexes(HA_KEY_SWITCH_ALL);
+ }
+ }
+
if (uncacheable || !item || !item->assigned() || describe)
{
if (!fake_select_lex && !(with_element && with_element->is_recursive))
@@ -1025,6 +1477,8 @@ bool st_select_lex_unit::exec()
{
ha_rows records_at_start= 0;
thd->lex->current_select= sl;
+ if (union_result)
+ union_result->change_select();
if (fake_select_lex)
{
if (sl != &thd->lex->select_lex)
@@ -1051,28 +1505,42 @@ bool st_select_lex_unit::exec()
we don't calculate found_rows() per union part.
Otherwise, SQL_CALC_FOUND_ROWS should be done on all sub parts.
*/
- sl->join->select_options=
- (select_limit_cnt == HA_POS_ERROR || sl->braces) ?
- sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union;
- saved_error= sl->join->optimize();
+ if (sl->tvc)
+ {
+ sl->tvc->select_options=
+ (select_limit_cnt == HA_POS_ERROR || 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->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union;
+ saved_error= sl->join->optimize();
+ }
}
- if (!saved_error)
+ if (likely(!saved_error))
{
records_at_start= table->file->stats.records;
- sl->join->exec();
+ if (sl->tvc)
+ sl->tvc->exec(sl);
+ else
+ sl->join->exec();
if (sl == union_distinct && !(with_element && with_element->is_recursive))
{
// This is UNION DISTINCT, so there should be a fake_select_lex
DBUG_ASSERT(fake_select_lex != NULL);
- if (table->file->ha_disable_indexes(HA_KEY_SWITCH_ALL))
+ if (unlikely(table->file->ha_disable_indexes(HA_KEY_SWITCH_ALL)))
DBUG_RETURN(TRUE);
table->no_keyread=1;
}
- saved_error= sl->join->error;
+ if (!sl->tvc)
+ saved_error= sl->join->error;
offset_limit_cnt= (ha_rows)(sl->offset_limit ?
sl->offset_limit->val_uint() :
0);
- if (!saved_error)
+ if (likely(!saved_error))
{
examined_rows+= thd->get_examined_row_count();
thd->set_examined_row_count(0);
@@ -1083,7 +1551,7 @@ bool st_select_lex_unit::exec()
}
}
}
- if (saved_error)
+ if (unlikely(saved_error))
{
thd->lex->current_select= lex_select_save;
DBUG_RETURN(saved_error);
@@ -1092,7 +1560,7 @@ bool st_select_lex_unit::exec()
{
/* Needed for the following test and for records_at_start in next loop */
int error= table->file->info(HA_STATUS_VARIABLE);
- if(error)
+ if (unlikely(error))
{
table->file->print_error(error, MYF(0));
DBUG_RETURN(1);
@@ -1138,7 +1606,8 @@ bool st_select_lex_unit::exec()
*/
thd->lex->limit_rows_examined_cnt= ULONGLONG_MAX;
- if (fake_select_lex != NULL && !thd->is_fatal_error) // Check if EOM
+ // Check if EOM
+ if (fake_select_lex != NULL && likely(!thd->is_fatal_error))
{
/* Send result to 'result' */
saved_error= true;
@@ -1157,8 +1626,9 @@ bool st_select_lex_unit::exec()
don't let it allocate the join. Perhaps this is because we need
some special parameter values passed to join constructor?
*/
- if (!(fake_select_lex->join= new JOIN(thd, item_list,
- fake_select_lex->options, result)))
+ if (unlikely(!(fake_select_lex->join=
+ new JOIN(thd, item_list, fake_select_lex->options,
+ result))))
{
fake_select_lex->table_list.empty();
goto err;
@@ -1224,7 +1694,7 @@ bool st_select_lex_unit::exec()
}
fake_select_lex->table_list.empty();
- if (!saved_error)
+ if (likely(!saved_error))
{
thd->limit_found_rows = (ulonglong)table->file->stats.records + add_rows;
thd->inc_examined_row_count(examined_rows);
@@ -1300,26 +1770,45 @@ bool st_select_lex_unit::exec_recursive()
if (with_element->with_anchor)
end= with_element->first_recursive;
}
- else if ((saved_error= incr_table->file->ha_delete_all_rows()))
+ else if (unlikely((saved_error= incr_table->file->ha_delete_all_rows())))
goto err;
for (st_select_lex *sl= start ; sl != end; sl= sl->next_select())
{
+ if (with_element->level)
+ {
+ for (TABLE_LIST *derived= with_element->derived_with_rec_ref.first;
+ derived;
+ derived= derived->next_with_rec_ref)
+ {
+ if (derived->is_materialized_derived())
+ {
+ if (derived->table->is_created())
+ derived->table->file->ha_delete_all_rows();
+ derived->table->reginfo.join_tab->preread_init_done= false;
+ }
+ }
+ }
thd->lex->current_select= sl;
set_limit(sl);
- sl->join->exec();
- saved_error= sl->join->error;
- if (!saved_error)
+ if (sl->tvc)
+ sl->tvc->exec(sl);
+ else
+ {
+ sl->join->exec();
+ saved_error= sl->join->error;
+ }
+ if (likely(!saved_error))
{
examined_rows+= thd->get_examined_row_count();
thd->set_examined_row_count(0);
- if (union_result->flush())
+ if (unlikely(union_result->flush()))
{
thd->lex->current_select= lex_select_save;
DBUG_RETURN(1);
}
}
- if (saved_error)
+ if (unlikely(saved_error))
{
thd->lex->current_select= lex_select_save;
goto err;
@@ -1344,7 +1833,7 @@ bool st_select_lex_unit::exec_recursive()
if (!with_element->rec_result->first_rec_table_to_update)
with_element->rec_result->first_rec_table_to_update= rec_table;
if (with_element->level == 1 && rec_table->reginfo.join_tab)
- rec_table->reginfo.join_tab->preread_init_done= true;
+ rec_table->reginfo.join_tab->preread_init_done= true;
}
for (Item_subselect *sq= with_element->sq_with_rec_ref.first;
sq;
@@ -1363,7 +1852,7 @@ err:
bool st_select_lex_unit::cleanup()
{
- int error= 0;
+ bool error= 0;
DBUG_ENTER("st_select_lex_unit::cleanup");
if (cleaned)
@@ -1401,6 +1890,7 @@ bool st_select_lex_unit::cleanup()
DBUG_RETURN(FALSE);
}
}
+ columns_are_renamed= false;
cleaned= 1;
for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select())
@@ -1457,7 +1947,7 @@ bool st_select_lex_unit::cleanup()
void st_select_lex_unit::reinit_exec_mechanism()
{
- prepared= optimized= executed= 0;
+ prepared= optimized= optimized_2= executed= 0;
optimize_started= 0;
if (with_element && with_element->is_recursive)
with_element->reset_recursive_for_exec();
@@ -1516,7 +2006,7 @@ bool st_select_lex_unit::change_result(select_result_interceptor *new_result,
List<Item> *st_select_lex_unit::get_column_types(bool for_cursor)
{
SELECT_LEX *sl= first_select();
- bool is_procedure= MY_TEST(sl->join->procedure);
+ bool is_procedure= !sl->tvc && sl->join->procedure ;
if (is_procedure)
{
@@ -1526,7 +2016,7 @@ List<Item> *st_select_lex_unit::get_column_types(bool for_cursor)
}
- if (is_union())
+ if (is_unit_op())
{
DBUG_ASSERT(prepared);
/* Types are generated during prepare */
@@ -1551,6 +2041,7 @@ bool st_select_lex::cleanup()
cleanup_order(order_list.first);
cleanup_order(group_list.first);
+ cleanup_ftfuncs(this);
if (join)
{
@@ -1621,3 +2112,43 @@ void st_select_lex_unit::set_unique_exclude()
}
}
}
+
+/**
+ @brief
+ Check if the derived table is guaranteed to have distinct rows because of
+ UNION operations used to populate it.
+
+ @detail
+ UNION operation removes duplicate rows from its output. That is, a query like
+
+ select * from t1 UNION select * from t2
+
+ will not produce duplicate rows in its output, even if table t1 (and/or t2)
+ contain duplicate rows. EXCEPT and INTERSECT operations also have this
+ property.
+
+ On the other hand, UNION ALL operation doesn't remove duplicates. (The SQL
+ standard also defines EXCEPT ALL and INTERSECT ALL, but we don't support
+ them).
+
+ st_select_lex_unit computes its value left to right. That is, if there is
+ a st_select_lex_unit object describing
+
+ (select #1) OP1 (select #2) OP2 (select #3)
+
+ then ((select #1) OP1 (select #2)) is computed first, and OP2 is computed
+ second.
+
+ How can one tell if st_select_lex_unit is guaranteed to have distinct
+ output rows? This depends on whether the last operation was duplicate-
+ removing or not:
+ - UNION ALL is not duplicate-removing
+ - all other operations are duplicate-removing
+*/
+
+bool st_select_lex_unit::check_distinct_in_union()
+{
+ if (union_distinct && !union_distinct->next_select())
+ return true;
+ return false;
+}
diff --git a/sql/sql_union.h b/sql/sql_union.h
index 7b65bccd40d..3776831bdd7 100644
--- a/sql/sql_union.h
+++ b/sql/sql_union.h
@@ -16,8 +16,6 @@
#ifndef SQL_UNION_INCLUDED
#define SQL_UNION_INCLUDED
-#include "my_global.h" /* ulong */
-
class THD;
class select_result;
struct LEX;
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 2e9752eeabd..2eb2374091e 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -20,7 +20,7 @@
Multi-table updates were introduced by Sinisa & Monty
*/
-#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
#include "sql_update.h"
#include "sql_cache.h" // query_cache_*
@@ -44,6 +44,9 @@
// mysql_derived_filling
+#include "sql_insert.h" // For vers_insert_history_row() that may be
+ // needed for System Versioning.
+
/**
True if the table's input and output record buffers are comparable using
compare_record(TABLE*).
@@ -133,29 +136,74 @@ bool compare_record(const TABLE *table)
FALSE Items are OK
*/
-static bool check_fields(THD *thd, List<Item> &items)
+static bool check_fields(THD *thd, List<Item> &items, bool update_view)
{
- List_iterator<Item> it(items);
Item *item;
- Item_field *field;
+ if (update_view)
+ {
+ List_iterator<Item> it(items);
+ Item_field *field;
+ while ((item= it++))
+ {
+ if (!(field= item->field_for_view_update()))
+ {
+ /* item has name, because it comes from VIEW SELECT list */
+ my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), item->name.str);
+ return TRUE;
+ }
+ /*
+ we make temporary copy of Item_field, to avoid influence of changing
+ result_field on Item_ref which refer on this field
+ */
+ thd->change_item_tree(it.ref(),
+ new (thd->mem_root) Item_field(thd, field));
+ }
+ }
- while ((item= it++))
+ if (thd->variables.sql_mode & MODE_SIMULTANEOUS_ASSIGNMENT)
{
- if (!(field= item->field_for_view_update()))
+ // Make sure that a column is updated only once
+ List_iterator_fast<Item> it(items);
+ while ((item= it++))
{
- /* item has name, because it comes from VIEW SELECT list */
- my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), item->name);
- return TRUE;
+ item->field_for_view_update()->field->clear_has_explicit_value();
+ }
+ it.rewind();
+ while ((item= it++))
+ {
+ Field *f= item->field_for_view_update()->field;
+ if (f->has_explicit_value())
+ {
+ my_error(ER_UPDATED_COLUMN_ONLY_ONCE, MYF(0),
+ *(f->table_name), f->field_name.str);
+ return TRUE;
+ }
+ f->set_has_explicit_value();
}
- /*
- we make temporary copy of Item_field, to avoid influence of changing
- result_field on Item_ref which refer on this field
- */
- thd->change_item_tree(it.ref(), new (thd->mem_root) Item_field(thd, field));
}
return FALSE;
}
+bool TABLE::vers_check_update(List<Item> &items)
+{
+ List_iterator<Item> it(items);
+ if (!versioned_write())
+ return false;
+
+ while (Item *item= it++)
+ {
+ if (Item_field *item_field= item->field_for_view_update())
+ {
+ Field *field= item_field->field;
+ if (field->table == this && !field->vers_update_unversioned())
+ {
+ no_cache= true;
+ return true;
+ }
+ }
+ }
+ return false;
+}
/**
Re-read record if more columns are needed for error message.
@@ -190,7 +238,7 @@ static void prepare_record_for_error_message(int error, TABLE *table)
Get the number of the offended index.
We will see MAX_KEY if the engine cannot determine the affected index.
*/
- if ((keynr= table->file->get_dup_key(error)) >= MAX_KEY)
+ if (unlikely((keynr= table->file->get_dup_key(error)) >= MAX_KEY))
DBUG_VOID_RETURN;
/* Create unique_map with all fields used by that index. */
@@ -246,7 +294,6 @@ static void prepare_record_for_error_message(int error, TABLE *table)
order_num number of elemen in ORDER BY clause
order ORDER BY clause list
limit limit clause
- handle_duplicates how to handle duplicates
RETURN
0 - OK
@@ -261,17 +308,18 @@ int mysql_update(THD *thd,
List<Item> &values,
COND *conds,
uint order_num, ORDER *order,
- ha_rows limit,
- enum enum_duplicates handle_duplicates, bool ignore,
+ ha_rows limit,
+ bool ignore,
ha_rows *found_return, ha_rows *updated_return)
{
bool using_limit= limit != HA_POS_ERROR;
bool safe_update= thd->variables.option_bits & OPTION_SAFE_UPDATES;
- bool used_key_is_modified= FALSE, transactional_table, will_batch;
+ bool used_key_is_modified= FALSE, transactional_table;
+ bool will_batch= FALSE;
bool can_compare_record;
int res;
int error, loc_error;
- uint dup_key_found;
+ ha_rows dup_key_found;
bool need_sort= TRUE;
bool reverse= FALSE;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -288,23 +336,31 @@ int mysql_update(THD *thd,
ulonglong id;
List<Item> all_fields;
killed_state killed_status= NOT_KILLED;
+ bool has_triggers, binlog_is_row, do_direct_update= FALSE;
Update_plan query_plan(thd->mem_root);
Explain_update *explain;
+ TABLE_LIST *update_source_table;
query_plan.index= MAX_KEY;
query_plan.using_filesort= FALSE;
+
+ // For System Versioning (may need to insert new fields to a table).
+ ha_rows updated_sys_ver= 0;
+
DBUG_ENTER("mysql_update");
create_explain_query(thd->lex, thd->mem_root);
if (open_tables(thd, &table_list, &table_count, 0))
DBUG_RETURN(1);
- //Prepare views so they are handled correctly.
+ /* Prepare views so they are handled correctly */
if (mysql_handle_derived(thd->lex, DT_INIT))
DBUG_RETURN(1);
- if (table_list->is_multitable())
+ if (((update_source_table=unique_table(thd, table_list,
+ table_list->next_global, 0)) ||
+ table_list->is_multitable()))
{
- DBUG_ASSERT(table_list->view != 0);
+ DBUG_ASSERT(update_source_table || table_list->view != 0);
DBUG_PRINT("info", ("Switch to multi-update"));
/* pass counter value */
thd->lex->table_count= table_count;
@@ -316,17 +372,17 @@ int mysql_update(THD *thd,
(void) read_statistics_for_tables_if_needed(thd, table_list);
+ THD_STAGE_INFO(thd, stage_init_update);
if (table_list->handle_derived(thd->lex, DT_MERGE_FOR_INSERT))
DBUG_RETURN(1);
if (table_list->handle_derived(thd->lex, DT_PREPARE))
DBUG_RETURN(1);
- THD_STAGE_INFO(thd, stage_init);
table= table_list->table;
if (!table_list->single_table_updatable())
{
- my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "UPDATE");
+ 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);
@@ -358,16 +414,20 @@ int mysql_update(THD *thd,
if (setup_fields_with_no_wrap(thd, Ref_ptr_array(),
fields, MARK_COLUMNS_WRITE, 0, 0))
DBUG_RETURN(1); /* purecov: inspected */
- if (table_list->view && check_fields(thd, fields))
+ if (check_fields(thd, fields, table_list->view))
{
DBUG_RETURN(1);
}
+ bool has_vers_fields= table->vers_check_update(fields);
if (check_key_in_view(thd, table_list))
{
- my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "UPDATE");
+ my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "UPDATE");
DBUG_RETURN(1);
}
+ if (table->default_field)
+ table->mark_default_fields_for_write(false);
+
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/* Check values */
table_list->grant.want_privilege= table->grant.want_privilege=
@@ -427,8 +487,8 @@ int mysql_update(THD *thd,
set_statistics_for_table(thd, table);
select= make_select(table, 0, 0, conds, (SORT_INFO*) 0, 0, &error);
- if (error || !limit || thd->is_error() ||
- (select && select->check_quick(thd, safe_update, limit)))
+ if (unlikely(error || !limit || thd->is_error() ||
+ (select && select->check_quick(thd, safe_update, limit))))
{
query_plan.set_impossible_where();
if (thd->lex->describe || thd->lex->analyze_stmt)
@@ -462,7 +522,8 @@ int mysql_update(THD *thd,
goto err;
}
}
- init_ftfuncs(thd, select_lex, 1);
+ if (unlikely(init_ftfuncs(thd, select_lex, 1)))
+ goto err;
table->mark_columns_needed_for_update();
@@ -525,7 +586,6 @@ int mysql_update(THD *thd,
query_plan.using_io_buffer= true;
}
-
/*
Ok, we have generated a query plan for the UPDATE.
- if we're running EXPLAIN UPDATE, goto produce explain output
@@ -533,16 +593,78 @@ int mysql_update(THD *thd,
*/
if (thd->lex->describe)
goto produce_explain_and_leave;
- explain= query_plan.save_explain_update_data(query_plan.mem_root, thd);
+ if (!(explain= query_plan.save_explain_update_data(query_plan.mem_root, thd)))
+ goto err;
ANALYZE_START_TRACKING(&explain->command_tracker);
DBUG_EXECUTE_IF("show_explain_probe_update_exec_start",
dbug_serve_apcs(thd, 1););
-
+
+ has_triggers= (table->triggers &&
+ (table->triggers->has_triggers(TRG_EVENT_UPDATE,
+ TRG_ACTION_BEFORE) ||
+ table->triggers->has_triggers(TRG_EVENT_UPDATE,
+ TRG_ACTION_AFTER)));
+ DBUG_PRINT("info", ("has_triggers: %s", has_triggers ? "TRUE" : "FALSE"));
+ binlog_is_row= thd->is_current_stmt_binlog_format_row();
+ DBUG_PRINT("info", ("binlog_is_row: %s", binlog_is_row ? "TRUE" : "FALSE"));
+
if (!(select && select->quick))
status_var_increment(thd->status_var.update_scan_count);
+ /*
+ We can use direct update (update that is done silently in the handler)
+ if none of the following conditions are true:
+ - There are triggers
+ - There is binary logging
+ - using_io_buffer
+ - This means that the partition changed or the key we want
+ to use for scanning the table is changed
+ - ignore is set
+ - Direct updates don't return the number of ignored rows
+ - There is a virtual not stored column in the WHERE clause
+ - Changing a field used by a stored virtual column, which
+ would require the column to be recalculated.
+ - ORDER BY or LIMIT
+ - As this requires the rows to be updated in a specific order
+ - Note that Spider can handle ORDER BY and LIMIT in a cluster with
+ one data node. These conditions are therefore checked in
+ direct_update_rows_init().
+ - Update fields include a unique timestamp field
+ - The storage engine may not be able to avoid false duplicate key
+ errors. This condition is checked in direct_update_rows_init().
+
+ Direct update does not require a WHERE clause
+
+ Later we also ensure that we are only using one table (no sub queries)
+ */
+ if ((table->file->ha_table_flags() & HA_CAN_DIRECT_UPDATE_AND_DELETE) &&
+ !has_triggers && !binlog_is_row &&
+ !query_plan.using_io_buffer && !ignore &&
+ !table->check_virtual_columns_marked_for_read() &&
+ !table->check_virtual_columns_marked_for_write())
+ {
+ DBUG_PRINT("info", ("Trying direct update"));
+ if (select && select->cond &&
+ (select->cond->used_tables() == table->map))
+ {
+ DBUG_ASSERT(!table->file->pushed_cond);
+ if (!table->file->cond_push(select->cond))
+ table->file->pushed_cond= select->cond;
+ }
+
+ if (!table->file->info_push(INFO_KIND_UPDATE_FIELDS, &fields) &&
+ !table->file->info_push(INFO_KIND_UPDATE_VALUES, &values) &&
+ !table->file->direct_update_rows_init(&fields))
+ {
+ do_direct_update= TRUE;
+
+ /* Direct update is not using_filesort and is not using_io_buffer */
+ goto update_begin;
+ }
+ }
+
if (query_plan.using_filesort || query_plan.using_io_buffer)
{
/*
@@ -621,7 +743,7 @@ int mysql_update(THD *thd,
error= init_read_record_idx(&info, thd, table, 1, query_plan.index,
reverse);
- if (error)
+ if (unlikely(error))
{
close_cached_file(&tempfile);
goto err;
@@ -630,7 +752,7 @@ int mysql_update(THD *thd,
THD_STAGE_INFO(thd, stage_searching_rows_for_update);
ha_rows tmp_limit= limit;
- while (!(error=info.read_record(&info)) && !thd->killed)
+ while (likely(!(error=info.read_record())) && likely(!thd->killed))
{
explain->buf_tracker.on_record_read();
thd->inc_examined_row_count(1);
@@ -641,8 +763,8 @@ int mysql_update(THD *thd,
explain->buf_tracker.on_record_after_where();
table->file->position(table->record[0]);
- if (my_b_write(&tempfile,table->file->ref,
- table->file->ref_length))
+ if (unlikely(my_b_write(&tempfile,table->file->ref,
+ table->file->ref_length)))
{
error=1; /* purecov: inspected */
break; /* purecov: inspected */
@@ -660,7 +782,7 @@ int mysql_update(THD *thd,
error since in this case the transaction might have been
rolled back already.
*/
- if (error < 0)
+ if (unlikely(error < 0))
{
/* Fatal error from select->skip_record() */
error= 1;
@@ -670,7 +792,7 @@ int mysql_update(THD *thd,
table->file->unlock_row();
}
}
- if (thd->killed && !error)
+ if (unlikely(thd->killed) && !error)
error= 1; // Aborted
limit= tmp_limit;
table->file->try_semi_consistent_read(0);
@@ -687,14 +809,15 @@ int mysql_update(THD *thd,
}
else
{
- select= new SQL_SELECT;
+ if (!(select= new SQL_SELECT))
+ goto err;
select->head=table;
}
- if (reinit_io_cache(&tempfile,READ_CACHE,0L,0,0))
- error=1; /* purecov: inspected */
- select->file=tempfile; // Read row ptrs from this file
- if (error >= 0)
+ if (unlikely(reinit_io_cache(&tempfile,READ_CACHE,0L,0,0)))
+ error= 1; /* purecov: inspected */
+ select->file= tempfile; // Read row ptrs from this file
+ if (unlikely(error >= 0))
goto err;
table->file->ha_end_keyread();
@@ -702,6 +825,7 @@ int mysql_update(THD *thd,
}
}
+update_begin:
if (ignore)
table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
@@ -718,15 +842,23 @@ int mysql_update(THD *thd,
*/
thd->count_cuted_fields= CHECK_FIELD_WARN;
thd->cuted_fields=0L;
- THD_STAGE_INFO(thd, stage_updating);
transactional_table= table->file->has_transactions();
thd->abort_on_warning= !ignore && thd->is_strict_mode();
- if (table->prepare_triggers_for_update_stmt_or_event())
+
+ if (do_direct_update)
{
- will_batch= FALSE;
+ /* Direct updating is supported */
+ DBUG_PRINT("info", ("Using direct update"));
+ table->reset_default_fields();
+ if (unlikely(!(error= table->file->ha_direct_update_rows(&updated))))
+ error= -1;
+ found= updated;
+ goto update_end;
}
- else
+
+ if ((table->file->ha_table_flags() & HA_CAN_FORCE_BULK_UPDATE) &&
+ !table->prepare_triggers_for_update_stmt_or_event())
will_batch= !table->file->start_bulk_update();
/*
@@ -746,7 +878,8 @@ int mysql_update(THD *thd,
can_compare_record= records_are_comparable(table);
explain->tracker.on_scan_init();
- while (!(error=info.read_record(&info)) && !thd->killed)
+ THD_STAGE_INFO(thd, stage_updating);
+ while (!(error=info.read_record()) && !thd->killed)
{
explain->tracker.on_record_read();
thd->inc_examined_row_count(1);
@@ -757,6 +890,7 @@ int mysql_update(THD *thd,
explain->tracker.on_record_after_where();
store_record(table,record[1]);
+
if (fill_record_n_invoke_before_triggers(thd, table, fields, values, 0,
TRG_EVENT_UPDATE))
break; /* purecov: inspected */
@@ -805,6 +939,7 @@ int mysql_update(THD *thd,
call then it should be included in the count of dup_key_found
and error should be set to 0 (only if these errors are ignored).
*/
+ DBUG_PRINT("info", ("Batched update"));
error= table->file->ha_bulk_update_row(table->record[1],
table->record[0],
&dup_key_found);
@@ -814,19 +949,34 @@ int mysql_update(THD *thd,
else
{
/* Non-batched update */
- error= table->file->ha_update_row(table->record[1],
+ error= table->file->ha_update_row(table->record[1],
table->record[0]);
}
- if (!error || error == HA_ERR_RECORD_IS_THE_SAME)
- {
- if (error != HA_ERR_RECORD_IS_THE_SAME)
+ if (unlikely(error == HA_ERR_RECORD_IS_THE_SAME))
+ {
+ error= 0;
+ }
+ else if (likely(!error))
+ {
+ if (has_vers_fields && table->versioned())
+ {
+ if (table->versioned(VERS_TIMESTAMP))
+ {
+ store_record(table, record[2]);
+ table->mark_columns_per_binlog_row_image();
+ error= vers_insert_history_row(table);
+ restore_record(table, record[2]);
+ }
+ if (likely(!error))
+ updated_sys_ver++;
+ }
+ if (likely(!error))
updated++;
- else
- error= 0;
- }
- else if (!ignore ||
- table->file->is_fatal_error(error, HA_CHECK_ALL))
- {
+ }
+
+ if (unlikely(error) &&
+ (!ignore || table->file->is_fatal_error(error, HA_CHECK_ALL)))
+ {
/*
If (ignore && error is ignorable) we don't have to
do anything; otherwise...
@@ -844,8 +994,8 @@ int mysql_update(THD *thd,
}
if (table->triggers &&
- table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
- TRG_ACTION_AFTER, TRUE))
+ unlikely(table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
+ TRG_ACTION_AFTER, TRUE)))
{
error= 1;
break;
@@ -898,7 +1048,7 @@ int mysql_update(THD *thd,
Don't try unlocking the row if skip_record reported an error since in
this case the transaction might have been rolled back already.
*/
- else if (!thd->is_error())
+ else if (likely(!thd->is_error()))
table->file->unlock_row();
else
{
@@ -906,7 +1056,7 @@ int mysql_update(THD *thd,
break;
}
thd->get_stmt_da()->inc_current_row_for_warning();
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
{
error= 1;
break;
@@ -931,7 +1081,7 @@ int mysql_update(THD *thd,
};);
error= (killed_status == NOT_KILLED)? error : 1;
- if (error &&
+ if (likely(error) &&
will_batch &&
(loc_error= table->file->exec_bulk_update(&dup_key_found)))
/*
@@ -953,6 +1103,8 @@ int mysql_update(THD *thd,
updated-= dup_key_found;
if (will_batch)
table->file->end_bulk_update();
+
+update_end:
table->file->try_semi_consistent_read(0);
if (!transactional_table && updated > 0)
@@ -987,19 +1139,22 @@ int mysql_update(THD *thd,
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 ((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())
{
int errcode= 0;
- if (error < 0)
+ if (likely(error < 0))
thd->clear_error();
else
errcode= query_error_code(thd, killed_status == NOT_KILLED);
+ ScopedStatementReplication scoped_stmt_rpl(
+ table->versioned(VERS_TRX_ID) ? thd : NULL);
+
if (thd->binlog_query(THD::ROW_QUERY_TYPE,
thd->query(), thd->query_length(),
- transactional_table, FALSE, FALSE, errcode))
+ transactional_table, FALSE, FALSE, errcode) > 0)
{
error=1; // Rollback update
}
@@ -1008,17 +1163,28 @@ int mysql_update(THD *thd,
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)
+ {
+ table->file->pushed_cond= 0;
+ table->file->cond_pop();
+ }
/* If LAST_INSERT_ID(X) was used, report X */
id= thd->arg_of_last_insert_id_function ?
thd->first_successful_insert_id_in_prev_stmt : 0;
- if (error < 0 && !thd->lex->analyze_stmt)
+ if (likely(error < 0) && likely(!thd->lex->analyze_stmt))
{
char buff[MYSQL_ERRMSG_SIZE];
- my_snprintf(buff, sizeof(buff), ER_THD(thd, ER_UPDATE_INFO), (ulong) found,
- (ulong) updated,
- (ulong) thd->get_stmt_da()->current_statement_warn_count());
+ if (!table->versioned(VERS_TIMESTAMP))
+ my_snprintf(buff, sizeof(buff), ER_THD(thd, ER_UPDATE_INFO), (ulong) found,
+ (ulong) updated,
+ (ulong) thd->get_stmt_da()->current_statement_warn_count());
+ else
+ my_snprintf(buff, sizeof(buff),
+ ER_THD(thd, ER_UPDATE_INFO_WITH_SYSTEM_VERSIONING),
+ (ulong) found, (ulong) updated, (ulong) updated_sys_ver,
+ (ulong) thd->get_stmt_da()->current_statement_warn_count());
my_ok(thd, (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated,
id, buff);
DBUG_PRINT("info",("%ld records updated", (long) updated));
@@ -1033,8 +1199,7 @@ int mysql_update(THD *thd,
*found_return= found;
*updated_return= updated;
-
- if (thd->lex->analyze_stmt)
+ if (unlikely(thd->lex->analyze_stmt))
goto emit_explain_and_leave;
DBUG_RETURN((error >= 0 || thd->is_error()) ? 1 : 0);
@@ -1044,6 +1209,8 @@ err:
delete file_sort;
free_underlaid_joins(thd, select_lex);
table->file->ha_end_keyread();
+ if (table->file->pushed_cond)
+ table->file->cond_pop();
thd->abort_on_warning= 0;
DBUG_RETURN(1);
@@ -1052,7 +1219,8 @@ produce_explain_and_leave:
We come here for various "degenerate" query plans: impossible WHERE,
no-partitions-used, impossible-range, etc.
*/
- query_plan.save_explain_update_data(query_plan.mem_root, thd);
+ if (unlikely(!query_plan.save_explain_update_data(query_plan.mem_root, thd)))
+ goto err;
emit_explain_and_leave:
int err2= thd->lex->explain->send_explain(thd);
@@ -1094,7 +1262,16 @@ bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
table_list->register_want_access(SELECT_ACL);
#endif
- thd->lex->allow_sum_func= 0;
+ thd->lex->allow_sum_func.clear_all();
+
+ DBUG_ASSERT(table_list->table);
+ // conds could be cached from previous SP call
+ DBUG_ASSERT(!table_list->vers_conditions.need_setup() ||
+ !*conds || thd->stmt_arena->is_stmt_execute());
+ if (select_lex->vers_setup_conds(thd, table_list))
+ DBUG_RETURN(TRUE);
+
+ *conds= select_lex->where;
/*
We do not call DT_MERGE_FOR_INSERT because it has no sense for simple
@@ -1230,8 +1407,8 @@ bool unsafe_key_update(List<TABLE_LIST> leaves, table_map tables_for_update)
{
// Partitioned key is updated
my_error(ER_MULTI_UPDATE_KEY_CONFLICT, MYF(0),
- tl->top_table()->alias,
- tl2->top_table()->alias);
+ tl->top_table()->alias.str,
+ tl2->top_table()->alias.str);
return true;
}
@@ -1249,8 +1426,8 @@ bool unsafe_key_update(List<TABLE_LIST> leaves, table_map tables_for_update)
{
// Clustered primary key is updated
my_error(ER_MULTI_UPDATE_KEY_CONFLICT, MYF(0),
- tl->top_table()->alias,
- tl2->top_table()->alias);
+ tl->top_table()->alias.str,
+ tl2->top_table()->alias.str);
return true;
}
}
@@ -1401,14 +1578,13 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd)
*fields, MARK_COLUMNS_WRITE, 0, 0))
DBUG_RETURN(1);
+ // Check if we have a view in the list ...
for (tl= table_list; tl ; tl= tl->next_local)
if (tl->view)
- {
- if (check_fields(thd, *fields))
- DBUG_RETURN(1);
- else
- break;
- }
+ break;
+ // ... and pass this knowlage in check_fields call
+ if (check_fields(thd, *fields, tl != NULL ))
+ DBUG_RETURN(1);
table_map tables_for_update= thd->table_map_for_update= get_table_map(fields);
@@ -1433,12 +1609,12 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd)
if (!tl->single_table_updatable() || check_key_in_view(thd, tl))
{
my_error(ER_NON_UPDATABLE_TABLE, MYF(0),
- tl->top_table()->alias, "UPDATE");
+ tl->top_table()->alias.str, "UPDATE");
DBUG_RETURN(1);
}
DBUG_PRINT("info",("setting table `%s` for update",
- tl->top_table()->alias));
+ tl->top_table()->alias.str));
/*
If table will be updated we should not downgrade lock for it and
leave it as is.
@@ -1451,7 +1627,7 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd)
}
else
{
- DBUG_PRINT("info",("setting table `%s` for read-only", tl->alias));
+ DBUG_PRINT("info",("setting table `%s` for read-only", tl->alias.str));
/*
If we are using the binary log, we need TL_READ_NO_INSERT to get
correct order of statements. Otherwise, we use a TL_READ lock to
@@ -1577,18 +1753,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,
+ DBUG_PRINT("info", ("table: %s want_privilege: %u", tl->alias.str,
(uint) table->grant.want_privilege));
- if (tl->lock_type != TL_READ &&
- tl->lock_type != TL_READ_NO_INSERT)
- {
- TABLE_LIST *duplicate;
- if ((duplicate= unique_table(thd, tl, table_list, 0)))
- {
- update_non_unique_table_error(table_list, "UPDATE", duplicate);
- DBUG_RETURN(TRUE);
- }
- }
}
/*
Set exclude_from_table_unique_test value back to FALSE. It is needed for
@@ -1626,9 +1792,13 @@ bool mysql_multi_update(THD *thd, TABLE_LIST *table_list, List<Item> *fields,
thd->abort_on_warning= !ignore && thd->is_strict_mode();
List<Item> total_list;
+ if (select_lex->vers_setup_conds(thd, table_list))
+ DBUG_RETURN(1);
+
res= mysql_select(thd,
- table_list, select_lex->with_wild, total_list,
- conds, 0, NULL, NULL, NULL, NULL,
+ table_list, select_lex->with_wild, total_list, conds,
+ select_lex->order_list.elements,
+ select_lex->order_list.first, NULL, NULL, NULL,
options | SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK |
OPTION_SETUP_TABLES_DONE,
*result, unit, select_lex);
@@ -1657,8 +1827,10 @@ multi_update::multi_update(THD *thd_arg, TABLE_LIST *table_list,
tmp_tables(0), updated(0), found(0), fields(field_list),
values(value_list), table_count(0), copy_field(0),
handle_duplicates(handle_duplicates_arg), do_update(1), trans_safe(1),
- transactional_tables(0), ignore(ignore_arg), error_handled(0), prepared(0)
-{}
+ transactional_tables(0), ignore(ignore_arg), error_handled(0), prepared(0),
+ updated_sys_ver(0)
+{
+}
/*
@@ -1736,7 +1908,7 @@ int multi_update::prepare(List<Item> &not_used_values,
bitmap_union(table->read_set, &table->tmp_set);
}
}
- if (error)
+ if (unlikely(error))
DBUG_RETURN(1);
/*
@@ -1780,14 +1952,14 @@ int multi_update::prepare(List<Item> &not_used_values,
table_count);
values_for_table= (List_item **) thd->alloc(sizeof(List_item *) *
table_count);
- if (thd->is_fatal_error)
+ if (unlikely(thd->is_fatal_error))
DBUG_RETURN(1);
for (i=0 ; i < table_count ; i++)
{
fields_for_table[i]= new List_item;
values_for_table[i]= new List_item;
}
- if (thd->is_fatal_error)
+ if (unlikely(thd->is_fatal_error))
DBUG_RETURN(1);
/* Split fields into fields_for_table[] and values_by_table[] */
@@ -1799,7 +1971,7 @@ int multi_update::prepare(List<Item> &not_used_values,
fields_for_table[offset]->push_back(item, thd->mem_root);
values_for_table[offset]->push_back(value, thd->mem_root);
}
- if (thd->is_fatal_error)
+ if (unlikely(thd->is_fatal_error))
DBUG_RETURN(1);
/* Allocate copy fields */
@@ -1814,7 +1986,7 @@ int multi_update::prepare(List<Item> &not_used_values,
switch_to_nullable_trigger_fields(*values_for_table[i], table);
}
}
- copy_field= new Copy_field[max_fields];
+ copy_field= new (thd->mem_root) Copy_field[max_fields];
DBUG_RETURN(thd->is_fatal_error != 0);
}
@@ -1887,6 +2059,8 @@ static bool safe_update_on_fly(THD *thd, JOIN_TAB *join_tab,
TABLE *table= join_tab->table;
if (unique_table(thd, table_ref, all_tables, 0))
return 0;
+ if (join_tab->join->order) // FIXME this is probably too strong
+ return 0;
switch (join_tab->type) {
case JT_SYSTEM:
case JT_CONST:
@@ -1907,7 +2081,7 @@ static bool safe_update_on_fly(THD *thd, JOIN_TAB *join_tab,
return !is_key_used(table, table->s->primary_key, table->write_set);
return TRUE;
default:
- break; // Avoid compler warning
+ break; // Avoid compiler warning
}
return FALSE;
@@ -1929,7 +2103,8 @@ multi_update::initialize_tables(JOIN *join)
TABLE_LIST *table_ref;
DBUG_ENTER("initialize_tables");
- if ((thd->variables.option_bits & OPTION_SAFE_UPDATES) && error_if_full_join(join))
+ if (unlikely((thd->variables.option_bits & OPTION_SAFE_UPDATES) &&
+ error_if_full_join(join)))
DBUG_RETURN(1);
main_table=join->join_tab->table;
table_to_update= 0;
@@ -1960,10 +2135,12 @@ multi_update::initialize_tables(JOIN *join)
if (safe_update_on_fly(thd, join->join_tab, table_ref, all_tables))
{
table_to_update= table; // Update table on the fly
+ has_vers_fields= table->vers_check_update(*fields);
continue;
}
}
table->prepare_for_position();
+ join->map2table[table->tablenr]->keep_current_rowid= true;
/*
enable uncacheable flag if we update a view with check option
@@ -2022,29 +2199,23 @@ loop_end:
TABLE *tbl= table;
do
{
+ LEX_CSTRING field_name;
+ field_name.str= tbl->alias.c_ptr();
+ field_name.length= strlen(field_name.str);
/*
Signal each table (including tables referenced by WITH CHECK OPTION
clause) for which we will store row position in the temporary table
that we need a position to be read first.
*/
tbl->prepare_for_position();
+ join->map2table[tbl->tablenr]->keep_current_rowid= true;
- Field_string *field= new Field_string(tbl->file->ref_length, 0,
- tbl->alias.c_ptr(),
- &my_charset_bin);
- if (!field)
- DBUG_RETURN(1);
- field->init(tbl);
- /*
- The field will be converted to varstring when creating tmp table if
- table to be updated was created by mysql 4.1. Deny this.
- */
- field->can_alter_field_type= 0;
- Item_field *ifield= new (thd->mem_root) Item_field(join->thd, (Field *) field);
- if (!ifield)
+ Item_temptable_rowid *item=
+ new (thd->mem_root) Item_temptable_rowid(tbl);
+ if (!item)
DBUG_RETURN(1);
- ifield->maybe_null= 0;
- if (temp_fields.push_back(ifield, thd->mem_root))
+ item->fix_fields(thd, 0);
+ if (temp_fields.push_back(item, thd->mem_root))
DBUG_RETURN(1);
} while ((tbl= tbl_it++));
@@ -2055,25 +2226,81 @@ loop_end:
group.direction= ORDER::ORDER_ASC;
group.item= (Item**) temp_fields.head_ref();
- tmp_param->quick_group=1;
- tmp_param->field_count=temp_fields.elements;
- tmp_param->group_parts=1;
- tmp_param->group_length= table->file->ref_length;
+ tmp_param->quick_group= 1;
+ 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 */
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,
(ORDER*) &group, 0, 0,
- TMP_TABLE_ALL_COLUMNS, HA_POS_ERROR, "");
+ TMP_TABLE_ALL_COLUMNS, HA_POS_ERROR, &empty_clex_str);
thd->variables.big_tables= save_big_tables;
if (!tmp_tables[cnt])
DBUG_RETURN(1);
tmp_tables[cnt]->file->extra(HA_EXTRA_WRITE_CACHE);
}
+ join->tmp_table_keep_current_rowid= TRUE;
DBUG_RETURN(0);
}
+static TABLE *item_rowid_table(Item *item)
+{
+ if (item->type() != Item::FUNC_ITEM)
+ return NULL;
+ Item_func *func= (Item_func *)item;
+ if (func->functype() != Item_func::TEMPTABLE_ROWID)
+ return NULL;
+ Item_temptable_rowid *itr= (Item_temptable_rowid *)func;
+ return itr->table;
+}
+
+
+/*
+ multi_update stores a rowid and new field values for every updated row in a
+ temporary table (one temporary table per updated table). These rowids are
+ obtained via Item_temptable_rowid's by calling handler::position(). But if
+ the join is resolved via a temp table, rowids cannot be obtained from
+ handler::position() in the multi_update::send_data(). So, they're stored in
+ the join's temp table (JOIN::add_fields_for_current_rowid()) and here we
+ replace Item_temptable_rowid's (that would've done handler::position()) with
+ Item_field's (that will simply take the corresponding field value from the
+ temp table).
+*/
+int multi_update::prepare2(JOIN *join)
+{
+ if (!join->need_tmp || !join->tmp_table_keep_current_rowid)
+ return 0;
+
+ // there cannot be many tmp tables in multi-update
+ JOIN_TAB *tmptab= join->join_tab + join->exec_join_tab_cnt();
+
+ for (Item **it= tmptab->tmp_table_param->items_to_copy; *it ; it++)
+ {
+ TABLE *tbl= item_rowid_table(*it);
+ if (!tbl)
+ continue;
+ for (uint i= 0; i < table_count; i++)
+ {
+ for (Item **it2= tmp_table_param[i].items_to_copy; *it2; it2++)
+ {
+ if (item_rowid_table(*it2) != tbl)
+ continue;
+ Item *fld= new (thd->mem_root)
+ Item_field(thd, (*it)->get_tmp_table_field());
+ if (!fld)
+ return 1;
+ fld->set_result_field((*it2)->get_tmp_table_field());
+ *it2= fld;
+ }
+ }
+ }
+ return 0;
+}
+
+
multi_update::~multi_update()
{
TABLE_LIST *table;
@@ -2139,6 +2366,7 @@ int multi_update::send_data(List<Item> &not_used_values)
table->status|= STATUS_UPDATED;
store_record(table,record[1]);
+
if (fill_record_n_invoke_before_triggers(thd, table,
*fields_for_table[offset],
*values_for_table[offset], 0,
@@ -2160,10 +2388,10 @@ int multi_update::send_data(List<Item> &not_used_values)
found--;
if (error == VIEW_CHECK_SKIP)
continue;
- else if (error == VIEW_CHECK_ERROR)
+ else if (unlikely(error == VIEW_CHECK_ERROR))
DBUG_RETURN(1);
}
- if (!updated++)
+ if (unlikely(!updated++))
{
/*
Inform the main table that we are going to update the table even
@@ -2172,8 +2400,8 @@ int multi_update::send_data(List<Item> &not_used_values)
*/
main_table->file->extra(HA_EXTRA_PREPARE_FOR_UPDATE);
}
- if ((error=table->file->ha_update_row(table->record[1],
- table->record[0])) &&
+ if (unlikely((error=table->file->ha_update_row(table->record[1],
+ table->record[0]))) &&
error != HA_ERR_RECORD_IS_THE_SAME)
{
updated--;
@@ -2196,11 +2424,26 @@ int multi_update::send_data(List<Item> &not_used_values)
}
else
{
- if (error == HA_ERR_RECORD_IS_THE_SAME)
+ if (unlikely(error == HA_ERR_RECORD_IS_THE_SAME))
{
error= 0;
updated--;
}
+ else if (has_vers_fields && table->versioned())
+ {
+ if (table->versioned(VERS_TIMESTAMP))
+ {
+ store_record(table, record[2]);
+ if (vers_insert_history_row(table))
+ {
+ restore_record(table, record[2]);
+ error= 1;
+ break;
+ }
+ restore_record(table, record[2]);
+ }
+ updated_sys_ver++;
+ }
/* non-transactional or transactional table got modified */
/* either multi_update class' flag is raised in its branch */
if (table->file->has_transactions())
@@ -2213,54 +2456,45 @@ int multi_update::send_data(List<Item> &not_used_values)
}
}
if (table->triggers &&
- table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
- TRG_ACTION_AFTER, TRUE))
+ unlikely(table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
+ TRG_ACTION_AFTER, TRUE)))
DBUG_RETURN(1);
}
else
{
int error;
TABLE *tmp_table= tmp_tables[offset];
- /*
- For updatable VIEW store rowid of the updated table and
- rowids of tables used in the CHECK OPTION condition.
- */
- uint field_num= 0;
- List_iterator_fast<TABLE> tbl_it(unupdated_check_opt_tables);
- TABLE *tbl= table;
- do
- {
- tbl->file->position(tbl->record[0]);
- memcpy((char*) tmp_table->field[field_num]->ptr,
- (char*) tbl->file->ref, tbl->file->ref_length);
- /*
- For outer joins a rowid field may have no NOT_NULL_FLAG,
- so we have to reset NULL bit for this field.
- (set_notnull() resets NULL bit only if available).
- */
- tmp_table->field[field_num]->set_notnull();
- field_num++;
- } while ((tbl= tbl_it++));
-
+ if (copy_funcs(tmp_table_param[offset].items_to_copy, thd))
+ DBUG_RETURN(1);
+ /* rowid field is NULL if join tmp table has null row from outer join */
+ if (tmp_table->field[0]->is_null())
+ continue;
/* Store regular updated fields in the row. */
+ DBUG_ASSERT(1 + unupdated_check_opt_tables.elements ==
+ tmp_table_param[offset].func_count);
fill_record(thd, tmp_table,
tmp_table->field + 1 + unupdated_check_opt_tables.elements,
*values_for_table[offset], TRUE, FALSE);
/* Write row, ignoring duplicated updates to a row */
error= tmp_table->file->ha_write_tmp_row(tmp_table->record[0]);
- if (error != HA_ERR_FOUND_DUPP_KEY && error != HA_ERR_FOUND_DUPP_UNIQUE)
+ found++;
+ if (unlikely(error))
{
- if (error &&
- create_internal_tmp_table_from_heap(thd, tmp_table,
- tmp_table_param[offset].start_recinfo,
- &tmp_table_param[offset].recinfo,
- error, 1, NULL))
+ found--;
+ if (error != HA_ERR_FOUND_DUPP_KEY &&
+ error != HA_ERR_FOUND_DUPP_UNIQUE)
{
- do_update= 0;
- DBUG_RETURN(1); // Not a table_is_full error
+ if (create_internal_tmp_table_from_heap(thd, tmp_table,
+ tmp_table_param[offset].start_recinfo,
+ &tmp_table_param[offset].recinfo,
+ error, 1, NULL))
+ {
+ do_update= 0;
+ DBUG_RETURN(1); // Not a table_is_full error
+ }
+ found++;
}
- found++;
}
}
}
@@ -2271,8 +2505,8 @@ int multi_update::send_data(List<Item> &not_used_values)
void multi_update::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 && !updated))
+ if (unlikely(error_handled ||
+ (!thd->transaction.stmt.modified_non_trans_table && !updated)))
return;
/* Something already updated so we have to invalidate cache */
@@ -2289,10 +2523,6 @@ void multi_update::abort_result_set()
if (do_update && table_count > 1)
{
/* Add warning here */
- /*
- todo/fixme: do_update() is never called with the arg 1.
- should it change the signature to become argless?
- */
(void) do_updates();
}
}
@@ -2366,7 +2596,7 @@ int multi_update::do_updates()
org_updated= updated;
tmp_table= tmp_tables[cur_table->shared];
tmp_table->file->extra(HA_EXTRA_CACHE); // Change to read cache
- if ((local_error= table->file->ha_rnd_init(0)))
+ if (unlikely((local_error= table->file->ha_rnd_init(0))))
{
err_table= table;
goto err;
@@ -2384,10 +2614,12 @@ int multi_update::do_updates()
if (table->vfield)
empty_record(table);
+ has_vers_fields= table->vers_check_update(*fields);
+
check_opt_it.rewind();
while(TABLE *tbl= check_opt_it++)
{
- if ((local_error= tbl->file->ha_rnd_init(0)))
+ if (unlikely((local_error= tbl->file->ha_rnd_init(0))))
{
err_table= tbl;
goto err;
@@ -2411,7 +2643,7 @@ int multi_update::do_updates()
}
copy_field_end=copy_field_ptr;
- if ((local_error= tmp_table->file->ha_rnd_init(1)))
+ if (unlikely((local_error= tmp_table->file->ha_rnd_init(1))))
{
err_table= tmp_table;
goto err;
@@ -2426,12 +2658,11 @@ int multi_update::do_updates()
thd->fatal_error();
goto err2;
}
- if ((local_error= tmp_table->file->ha_rnd_next(tmp_table->record[0])))
+ if (unlikely((local_error=
+ tmp_table->file->ha_rnd_next(tmp_table->record[0]))))
{
if (local_error == HA_ERR_END_OF_FILE)
break;
- if (local_error == HA_ERR_RECORD_DELETED)
- continue; // May happen on dup key
err_table= tmp_table;
goto err;
}
@@ -2442,9 +2673,11 @@ int multi_update::do_updates()
uint field_num= 0;
do
{
- if ((local_error=
- tbl->file->ha_rnd_pos(tbl->record[0],
- (uchar *) tmp_table->field[field_num]->ptr)))
+ DBUG_ASSERT(!tmp_table->field[field_num]->is_null());
+ if (unlikely((local_error=
+ tbl->file->ha_rnd_pos(tbl->record[0],
+ (uchar *) tmp_table->
+ field[field_num]->ptr))))
{
err_table= tbl;
goto err;
@@ -2453,8 +2686,8 @@ int multi_update::do_updates()
} while ((tbl= check_opt_it++));
if (table->vfield &&
- table->update_virtual_fields(table->file,
- VCOL_UPDATE_INDEXED_FOR_UPDATE))
+ unlikely(table->update_virtual_fields(table->file,
+ VCOL_UPDATE_INDEXED_FOR_UPDATE)))
goto err2;
table->status|= STATUS_UPDATED;
@@ -2486,32 +2719,54 @@ int multi_update::do_updates()
{
if (error == VIEW_CHECK_SKIP)
continue;
- else if (error == VIEW_CHECK_ERROR)
+ else if (unlikely(error == VIEW_CHECK_ERROR))
{
thd->fatal_error();
goto err2;
}
}
- if ((local_error=table->file->ha_update_row(table->record[1],
- table->record[0])) &&
+ if (has_vers_fields && table->versioned())
+ table->vers_update_fields();
+
+ if (unlikely((local_error=
+ table->file->ha_update_row(table->record[1],
+ table->record[0]))) &&
local_error != HA_ERR_RECORD_IS_THE_SAME)
{
if (!ignore ||
table->file->is_fatal_error(local_error, HA_CHECK_ALL))
{
err_table= table;
- goto err;
+ goto err;
}
- }
+ }
if (local_error != HA_ERR_RECORD_IS_THE_SAME)
+ {
updated++;
+
+ if (has_vers_fields && table->versioned())
+ {
+ if (table->versioned(VERS_TIMESTAMP))
+ {
+ store_record(table, record[2]);
+ if ((local_error= vers_insert_history_row(table)))
+ {
+ restore_record(table, record[2]);
+ err_table = table;
+ goto err;
+ }
+ restore_record(table, record[2]);
+ }
+ updated_sys_ver++;
+ }
+ }
else
local_error= 0;
}
if (table->triggers &&
- table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
- TRG_ACTION_AFTER, TRUE))
+ unlikely(table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
+ TRG_ACTION_AFTER, TRUE)))
goto err2;
}
@@ -2581,7 +2836,7 @@ bool multi_update::send_eof()
error takes into account killed status gained in do_updates()
*/
int local_error= thd->is_error();
- if (!local_error)
+ if (likely(!local_error))
local_error = (table_count) ? do_updates() : 0;
/*
if local_error is not set ON until after do_updates() then
@@ -2611,30 +2866,45 @@ bool multi_update::send_eof()
thd->transaction.all.m_unsafe_rollback_flags|=
(thd->transaction.stmt.m_unsafe_rollback_flags & THD_TRANS::DID_WAIT);
- if (local_error == 0 || thd->transaction.stmt.modified_non_trans_table)
+ if (likely(local_error == 0 ||
+ thd->transaction.stmt.modified_non_trans_table))
{
if (WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open())
{
int errcode= 0;
- if (local_error == 0)
+ if (likely(local_error == 0))
thd->clear_error();
else
errcode= query_error_code(thd, killed_status == NOT_KILLED);
- if (thd->binlog_query(THD::ROW_QUERY_TYPE,
- thd->query(), thd->query_length(),
- transactional_tables, FALSE, FALSE, errcode))
+
+ bool force_stmt= false;
+ for (TABLE *table= all_tables->table; table; table= table->next)
{
- local_error= 1; // Rollback update
+ if (table->versioned(VERS_TRX_ID))
+ {
+ force_stmt= true;
+ break;
+ }
}
+ enum_binlog_format save_binlog_format;
+ save_binlog_format= thd->get_current_stmt_binlog_format();
+ if (force_stmt)
+ thd->set_current_stmt_binlog_format_stmt();
+
+ if (thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query(),
+ thd->query_length(), transactional_tables, FALSE,
+ FALSE, errcode) > 0)
+ local_error= 1; // Rollback update
+ thd->set_current_stmt_binlog_format(save_binlog_format);
}
}
- DBUG_ASSERT(trans_safe || !updated ||
+ DBUG_ASSERT(trans_safe || !updated ||
thd->transaction.stmt.modified_non_trans_table);
- if (local_error != 0)
+ if (likely(local_error != 0))
error_handled= TRUE; // to force early leave from ::abort_result_set()
- if (local_error > 0) // if the above log write did not fail ...
+ if (unlikely(local_error > 0)) // if the above log write did not fail ...
{
/* Safety: If we haven't got an error before (can happen in do_updates) */
my_message(ER_UNKNOWN_ERROR, "An error occurred in multi-table update",
diff --git a/sql/sql_update.h b/sql/sql_update.h
index d8cd302880f..65e44d112a4 100644
--- a/sql/sql_update.h
+++ b/sql/sql_update.h
@@ -31,8 +31,7 @@ bool check_unique_table(THD *thd, TABLE_LIST *table_list);
int mysql_update(THD *thd,TABLE_LIST *tables,List<Item> &fields,
List<Item> &values,COND *conds,
uint order_num, ORDER *order, ha_rows limit,
- enum enum_duplicates handle_duplicates, bool ignore,
- ha_rows *found_return, ha_rows *updated_return);
+ bool ignore, ha_rows *found_return, ha_rows *updated_return);
bool mysql_multi_update(THD *thd, TABLE_LIST *table_list,
List<Item> *fields, List<Item> *values,
COND *conds, ulonglong options,
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index 8bfe6896ea2..5ffa9936098 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -16,7 +16,7 @@
*/
#define MYSQL_LEX 1
-#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
#include "unireg.h"
#include "sql_view.h"
@@ -39,7 +39,7 @@
#define MD5_BUFF_LENGTH 33
-const LEX_STRING view_type= { C_STRING_WITH_LEN("VIEW") };
+const LEX_CSTRING view_type= { STRING_WITH_LEN("VIEW") };
static int mysql_register_view(THD *, TABLE_LIST *, enum_view_create_mode);
@@ -63,9 +63,9 @@ static void make_unique_view_field_name(THD *thd, Item *target,
List<Item> &item_list,
Item *last_element)
{
- char *name= (target->orig_name ?
- target->orig_name :
- target->name);
+ const char *name= (target->orig_name ?
+ target->orig_name :
+ target->name.str);
size_t name_len;
uint attempt;
char buff[NAME_LEN+1];
@@ -85,7 +85,7 @@ static void make_unique_view_field_name(THD *thd, Item *target,
{
check= itc++;
if (check != target &&
- my_strcasecmp(system_charset_info, buff, check->name) == 0)
+ my_strcasecmp(system_charset_info, buff, check->name.str) == 0)
{
ok= FALSE;
break;
@@ -96,7 +96,7 @@ static void make_unique_view_field_name(THD *thd, Item *target,
itc.rewind();
}
- target->orig_name= target->name;
+ target->orig_name= target->name.str;
target->set_name(thd, buff, name_len, system_charset_info);
}
@@ -140,7 +140,7 @@ bool check_duplicate_names(THD *thd, List<Item> &item_list, bool gen_unique_view
itc.rewind();
while ((check= itc++) && check != item)
{
- if (my_strcasecmp(system_charset_info, item->name, check->name) == 0)
+ if (lex_string_cmp(system_charset_info, &item->name, &check->name) == 0)
{
if (!gen_unique_view_name)
goto err;
@@ -156,7 +156,7 @@ bool check_duplicate_names(THD *thd, List<Item> &item_list, bool gen_unique_view
DBUG_RETURN(FALSE);
err:
- my_error(ER_DUP_FIELDNAME, MYF(0), item->name);
+ my_error(ER_DUP_FIELDNAME, MYF(0), item->name.str);
DBUG_RETURN(TRUE);
}
@@ -171,17 +171,17 @@ err:
void make_valid_column_names(THD *thd, List<Item> &item_list)
{
Item *item;
- uint name_len;
+ size_t name_len;
List_iterator_fast<Item> it(item_list);
char buff[NAME_LEN];
DBUG_ENTER("make_valid_column_names");
for (uint column_no= 1; (item= it++); column_no++)
{
- if (!item->is_autogenerated_name || !check_column_name(item->name))
+ 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;
+ item->orig_name= item->name.str;
item->set_name(thd, buff, name_len, system_charset_info);
}
@@ -226,10 +226,10 @@ fill_defined_view_parts (THD *thd, TABLE_LIST *view)
view->definer.user= decoy.definer.user;
lex->definer= &view->definer;
}
- if (lex->create_view_algorithm == VIEW_ALGORITHM_INHERIT)
- lex->create_view_algorithm= (uint8) decoy.algorithm;
- if (lex->create_view_suid == VIEW_SUID_DEFAULT)
- lex->create_view_suid= decoy.view_suid ?
+ if (lex->create_view->algorithm == VIEW_ALGORITHM_INHERIT)
+ lex->create_view->algorithm= (uint8) decoy.algorithm;
+ if (lex->create_view->suid == VIEW_SUID_DEFAULT)
+ lex->create_view->suid= decoy.view_suid ?
VIEW_SUID_DEFINER : VIEW_SUID_INVOKER;
return FALSE;
@@ -274,13 +274,13 @@ bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view,
checked that we have not more privileges on correspondent column of view
table (i.e. user will not get some privileges by view creation)
*/
- if ((check_access(thd, CREATE_VIEW_ACL, view->db,
+ if ((check_access(thd, CREATE_VIEW_ACL, view->db.str,
&view->grant.privilege,
&view->grant.m_internal,
0, 0) ||
check_grant(thd, CREATE_VIEW_ACL, view, FALSE, 1, FALSE)) ||
(mode != VIEW_CREATE_NEW &&
- (check_access(thd, DROP_ACL, view->db,
+ (check_access(thd, DROP_ACL, view->db.str,
&view->grant.privilege,
&view->grant.m_internal,
0, 0) ||
@@ -299,7 +299,7 @@ bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view,
{
my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
"ANY", thd->security_ctx->priv_user,
- thd->security_ctx->priv_host, tbl->table_name);
+ thd->security_ctx->priv_host, tbl->table_name.str);
goto err;
}
/*
@@ -320,8 +320,7 @@ bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view,
tbl->table_name will be correct name of table because VIEWs are
not opened yet.
*/
- fill_effective_table_privileges(thd, &tbl->grant, tbl->db,
- tbl->table_name);
+ fill_effective_table_privileges(thd, &tbl->grant, tbl->db.str, tbl->table_name.str);
}
}
@@ -456,9 +455,9 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
view= lex->unlink_first_table(&link_to_local);
- if (check_db_dir_existence(view->db))
+ if (check_db_dir_existence(view->db.str))
{
- my_error(ER_BAD_DB_ERROR, MYF(0), view->db);
+ my_error(ER_BAD_DB_ERROR, MYF(0), view->db.str);
res= TRUE;
goto err;
}
@@ -494,8 +493,8 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
{
/* is this table view and the same view which we creates now? */
if (tbl->view &&
- strcmp(tbl->view_db.str, view->db) == 0 &&
- strcmp(tbl->view_name.str, view->table_name) == 0)
+ cmp(&tbl->view_db, &view->db) == 0 &&
+ cmp(&tbl->view_name, &view->table_name) == 0)
{
my_error(ER_NO_SUCH_TABLE, MYF(0), tbl->view_db.str, tbl->view_name.str);
res= TRUE;
@@ -515,7 +514,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
if (tbl->table->s->tmp_table != NO_TMP_TABLE && !tbl->view &&
!tbl->schema_table)
{
- my_error(ER_VIEW_SELECT_TMPTABLE, MYF(0), tbl->alias);
+ my_error(ER_VIEW_SELECT_TMPTABLE, MYF(0), tbl->alias.str);
res= TRUE;
goto err;
}
@@ -530,7 +529,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
/* prepare select to resolve all fields */
lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_VIEW;
- if (unit->prepare(thd, 0, 0))
+ if (unit->prepare(unit->derived, 0, 0))
{
/*
some errors from prepare are reported to user, if is not then
@@ -544,9 +543,9 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
if (lex->view_list.elements)
{
List_iterator_fast<Item> it(select_lex->item_list);
- List_iterator_fast<LEX_STRING> nm(lex->view_list);
+ List_iterator_fast<LEX_CSTRING> nm(lex->view_list);
Item *item;
- LEX_STRING *name;
+ LEX_CSTRING *name;
if (lex->view_list.elements != select_lex->item_list.elements)
{
@@ -576,8 +575,8 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
Compare/check grants on view with grants of underlying tables
*/
- fill_effective_table_privileges(thd, &view->grant, view->db,
- view->table_name);
+ fill_effective_table_privileges(thd, &view->grant, view->db.str,
+ view->table_name.str);
/*
Make sure that the current user does not have more column-level privileges
@@ -596,24 +595,32 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
for (sl= select_lex; sl; sl= sl->next_select())
{
- DBUG_ASSERT(view->db); /* Must be set in the parser */
+ DBUG_ASSERT(view->db.str); /* Must be set in the parser */
List_iterator_fast<Item> it(sl->item_list);
Item *item;
while ((item= it++))
{
Item_field *fld= item->field_for_view_update();
- uint priv= (get_column_grant(thd, &view->grant, view->db,
- view->table_name, item->name) &
+ uint priv= (get_column_grant(thd, &view->grant, view->db.str,
+ view->table_name.str, item->name.str) &
VIEW_ANY_ACL);
- if (fld && !fld->field->table->s->tmp_table)
+ if (!fld)
+ continue;
+ TABLE_SHARE *s= fld->field->table->s;
+ const Lex_ident field_name= fld->field->field_name;
+ if (s->tmp_table ||
+ (s->versioned &&
+ (field_name.streq(s->vers_start_field()->field_name) ||
+ field_name.streq(s->vers_end_field()->field_name))))
{
+ continue;
+ }
- final_priv&= fld->have_privileges;
+ final_priv&= fld->have_privileges;
- if (~fld->have_privileges & priv)
- report_item= item;
- }
+ if (~fld->have_privileges & priv)
+ report_item= item;
}
}
@@ -621,8 +628,8 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
{
my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
"create view", thd->security_ctx->priv_user,
- thd->security_ctx->priv_host, report_item->name,
- view->table_name);
+ thd->security_ctx->priv_host, report_item->name.str,
+ view->table_name.str);
res= TRUE;
goto err;
}
@@ -638,19 +645,18 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
*/
if (!res)
- tdc_remove_table(thd, TDC_RT_REMOVE_ALL, view->db, view->table_name, false);
+ tdc_remove_table(thd, TDC_RT_REMOVE_ALL, view->db.str, view->table_name.str, false);
if (!res && mysql_bin_log.is_open())
{
StringBuffer<128> buff(thd->variables.character_set_client);
DBUG_ASSERT(buff.charset()->mbminlen == 1);
- const LEX_STRING command[3]=
- {{ C_STRING_WITH_LEN("CREATE ") },
- { C_STRING_WITH_LEN("ALTER ") },
- { C_STRING_WITH_LEN("CREATE OR REPLACE ") }};
+ const LEX_CSTRING command[3]=
+ {{ STRING_WITH_LEN("CREATE ") },
+ { STRING_WITH_LEN("ALTER ") },
+ { STRING_WITH_LEN("CREATE OR REPLACE ") }};
- buff.append(command[thd->lex->create_view_mode].str,
- command[thd->lex->create_view_mode].length);
+ buff.append(&command[thd->lex->create_view->mode]);
view_store_options(thd, views, &buff);
buff.append(STRING_WITH_LEN("VIEW "));
@@ -659,34 +665,39 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
buff.append(STRING_WITH_LEN("IF NOT EXISTS "));
/* Test if user supplied a db (ie: we did not use thd->db) */
- if (views->db && views->db[0] &&
- (thd->db == NULL || strcmp(views->db, thd->db)))
+ if (views->db.str && views->db.str[0] &&
+ (thd->db.str == NULL || cmp(&views->db, &thd->db)))
{
- append_identifier(thd, &buff, views->db,
- views->db_length);
+ append_identifier(thd, &buff, &views->db);
buff.append('.');
}
- append_identifier(thd, &buff, views->table_name,
- views->table_name_length);
+ append_identifier(thd, &buff, &views->table_name);
if (lex->view_list.elements)
{
- List_iterator_fast<LEX_STRING> names(lex->view_list);
- LEX_STRING *name;
+ List_iterator_fast<LEX_CSTRING> names(lex->view_list);
+ LEX_CSTRING *name;
int i;
for (i= 0; (name= names++); i++)
{
buff.append(i ? ", " : "(");
- append_identifier(thd, &buff, name->str, name->length);
+ append_identifier(thd, &buff, name);
}
buff.append(')');
}
buff.append(STRING_WITH_LEN(" AS "));
+ /* views->source doesn't end with \0 */
buff.append(views->source.str, views->source.length);
int errcode= query_error_code(thd, TRUE);
+ /*
+ Don't log any unsafe warnings for CREATE VIEW as it's safely replicated
+ with statement based replication
+ */
+ thd->reset_unsafe_warnings();
if (thd->binlog_query(THD::STMT_QUERY_TYPE,
- buff.ptr(), buff.length(), FALSE, FALSE, FALSE, errcode))
+ buff.ptr(), buff.length(), FALSE, FALSE, FALSE,
+ errcode) > 0)
res= TRUE;
}
@@ -705,7 +716,6 @@ WSREP_ERROR_LABEL:
goto err_no_relink;
err:
- THD_STAGE_INFO(thd, stage_end);
lex->link_first_table_back(view, link_to_local);
err_no_relink:
unit->cleanup();
@@ -713,20 +723,20 @@ err_no_relink:
}
-static void make_view_filename(LEX_STRING *dir, char *dir_buff,
+static void make_view_filename(LEX_CSTRING *dir, char *dir_buff,
size_t dir_buff_len,
- LEX_STRING *path, char *path_buff,
+ LEX_CSTRING *path, char *path_buff,
size_t path_buff_len,
- LEX_STRING *file,
+ LEX_CSTRING *file,
TABLE_LIST *view)
{
/* print file name */
dir->length= build_table_filename(dir_buff, dir_buff_len - 1,
- view->db, "", "", 0);
+ view->db.str, "", "", 0);
dir->str= dir_buff;
path->length= build_table_filename(path_buff, path_buff_len - 1,
- view->db, view->table_name, reg_ext, 0);
+ view->db.str, view->table_name.str, reg_ext, 0);
path->str= path_buff;
file->str= path->str + dir->length;
@@ -743,37 +753,37 @@ static const int required_view_parameters= 15;
parse()
*/
static File_option view_parameters[]=
-{{{ C_STRING_WITH_LEN("query")},
+{{{ STRING_WITH_LEN("query")},
my_offsetof(TABLE_LIST, select_stmt),
FILE_OPTIONS_ESTRING},
- {{ C_STRING_WITH_LEN("md5")},
+ {{ STRING_WITH_LEN("md5")},
my_offsetof(TABLE_LIST, md5),
FILE_OPTIONS_STRING},
- {{ C_STRING_WITH_LEN("updatable")},
+ {{ STRING_WITH_LEN("updatable")},
my_offsetof(TABLE_LIST, updatable_view),
FILE_OPTIONS_ULONGLONG},
- {{ C_STRING_WITH_LEN("algorithm")},
+ {{ STRING_WITH_LEN("algorithm")},
my_offsetof(TABLE_LIST, algorithm),
FILE_OPTIONS_VIEW_ALGO},
- {{ C_STRING_WITH_LEN("definer_user")},
+ {{ STRING_WITH_LEN("definer_user")},
my_offsetof(TABLE_LIST, definer.user),
FILE_OPTIONS_STRING},
- {{ C_STRING_WITH_LEN("definer_host")},
+ {{ STRING_WITH_LEN("definer_host")},
my_offsetof(TABLE_LIST, definer.host),
FILE_OPTIONS_STRING},
- {{ C_STRING_WITH_LEN("suid")},
+ {{ STRING_WITH_LEN("suid")},
my_offsetof(TABLE_LIST, view_suid),
FILE_OPTIONS_ULONGLONG},
- {{ C_STRING_WITH_LEN("with_check_option")},
+ {{ STRING_WITH_LEN("with_check_option")},
my_offsetof(TABLE_LIST, with_check),
FILE_OPTIONS_ULONGLONG},
- {{ C_STRING_WITH_LEN("timestamp")},
+ {{ STRING_WITH_LEN("timestamp")},
my_offsetof(TABLE_LIST, timestamp),
FILE_OPTIONS_TIMESTAMP},
- {{ C_STRING_WITH_LEN("create-version")},
+ {{ STRING_WITH_LEN("create-version")},
my_offsetof(TABLE_LIST, file_version),
FILE_OPTIONS_ULONGLONG},
- {{ C_STRING_WITH_LEN("source")},
+ {{ STRING_WITH_LEN("source")},
my_offsetof(TABLE_LIST, source),
FILE_OPTIONS_ESTRING},
{{(char*) STRING_WITH_LEN("client_cs_name")},
@@ -785,21 +795,21 @@ static File_option view_parameters[]=
{{(char*) STRING_WITH_LEN("view_body_utf8")},
my_offsetof(TABLE_LIST, view_body_utf8),
FILE_OPTIONS_ESTRING},
- {{ C_STRING_WITH_LEN("mariadb-version")},
+ {{ STRING_WITH_LEN("mariadb-version")},
my_offsetof(TABLE_LIST, mariadb_version),
FILE_OPTIONS_ULONGLONG},
{{NullS, 0}, 0,
FILE_OPTIONS_STRING}
};
-static LEX_STRING view_file_type[]= {{(char*) STRING_WITH_LEN("VIEW") }};
+static LEX_CSTRING view_file_type[]= {{STRING_WITH_LEN("VIEW") }};
int mariadb_fix_view(THD *thd, TABLE_LIST *view, bool wrong_checksum,
bool swap_alg)
{
char dir_buff[FN_REFLEN + 1], path_buff[FN_REFLEN + 1];
- LEX_STRING dir, file, path;
+ LEX_CSTRING dir, file, path;
DBUG_ENTER("mariadb_fix_view");
if (!wrong_checksum && view->mariadb_version)
@@ -839,11 +849,12 @@ int mariadb_fix_view(THD *thd, TABLE_LIST *view, bool wrong_checksum,
(uchar*)view, view_parameters))
{
sql_print_error("View '%-.192s'.'%-.192s': algorithm swap error.",
- view->db, view->table_name);
+ view->db.str, view->table_name.str);
DBUG_RETURN(HA_ADMIN_INTERNAL_ERROR);
}
sql_print_information("View %`s.%`s: the version is set to %llu%s%s",
- view->db, view->table_name, view->mariadb_version,
+ view->db.str, view->table_name.str,
+ view->mariadb_version,
(wrong_checksum ? ", checksum corrected" : ""),
(swap_alg ?
((view->algorithm == VIEW_ALGORITHM_MERGE) ?
@@ -907,7 +918,7 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
char md5[MD5_BUFF_LENGTH];
bool can_be_merged;
char dir_buff[FN_REFLEN + 1], path_buff[FN_REFLEN + 1];
- LEX_STRING dir, file, path;
+ LEX_CSTRING dir, file, path;
int error= 0;
DBUG_ENTER("mysql_register_view");
@@ -929,7 +940,7 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
DBUG_PRINT("info", ("View: %.*s", view_query.length(), view_query.ptr()));
/* fill structure */
- view->source= thd->lex->create_view_select;
+ view->source= thd->lex->create_view->select;
if (!thd->make_lex_string(&view->select_stmt, view_query.ptr(),
view_query.length()))
@@ -954,18 +965,18 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
}
view->md5.length= 32;
can_be_merged= lex->can_be_merged();
- if (lex->create_view_algorithm == VIEW_ALGORITHM_MERGE &&
+ if (lex->create_view->algorithm == VIEW_ALGORITHM_MERGE &&
!lex->can_be_merged())
{
push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_VIEW_MERGE,
ER_THD(thd, ER_WARN_VIEW_MERGE));
- lex->create_view_algorithm= DTYPE_ALGORITHM_UNDEFINED;
+ lex->create_view->algorithm= DTYPE_ALGORITHM_UNDEFINED;
}
- view->algorithm= lex->create_view_algorithm;
+ view->algorithm= lex->create_view->algorithm;
view->definer.user= lex->definer->user;
view->definer.host= lex->definer->host;
- view->view_suid= lex->create_view_suid;
- view->with_check= lex->create_view_check;
+ view->view_suid= lex->create_view->suid;
+ view->with_check= lex->create_view->check;
DBUG_EXECUTE_IF("simulate_register_view_failure",
{
@@ -1009,26 +1020,26 @@ loop_out:
/* check old .frm */
{
char path_buff[FN_REFLEN];
- LEX_STRING path;
+ LEX_CSTRING path;
File_parser *parser;
path.str= path_buff;
fn_format(path_buff, file.str, dir.str, "", MY_UNPACK_FILENAME);
path.length= strlen(path_buff);
- if (ha_table_exists(thd, view->db, view->table_name, NULL))
+ if (ha_table_exists(thd, &view->db, &view->table_name))
{
if (lex->create_info.if_not_exists())
{
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_TABLE_EXISTS_ERROR,
ER_THD(thd, ER_TABLE_EXISTS_ERROR),
- view->table_name);
+ view->table_name.str);
DBUG_RETURN(0);
}
else if (mode == VIEW_CREATE_NEW)
{
- my_error(ER_TABLE_EXISTS_ERROR, MYF(0), view->alias);
+ my_error(ER_TABLE_EXISTS_ERROR, MYF(0), view->alias.str);
error= -1;
goto err;
}
@@ -1041,7 +1052,8 @@ loop_out:
if (!parser->ok() || !is_equal(&view_type, parser->type()))
{
- my_error(ER_WRONG_OBJECT, MYF(0), view->db, view->table_name, "VIEW");
+ my_error(ER_WRONG_OBJECT, MYF(0), view->db.str, view->table_name.str,
+ "VIEW");
error= -1;
goto err;
}
@@ -1055,7 +1067,7 @@ loop_out:
{
if (mode == VIEW_ALTER)
{
- my_error(ER_NO_SUCH_TABLE, MYF(0), view->db, view->alias);
+ my_error(ER_NO_SUCH_TABLE, MYF(0), view->db.str, view->alias.str);
error= -1;
goto err;
}
@@ -1096,11 +1108,11 @@ loop_out:
UNION
*/
if (view->updatable_view &&
- !lex->select_lex.master_unit()->is_union() &&
+ !lex->select_lex.master_unit()->is_unit_op() &&
!(lex->select_lex.table_list.first)->next_local &&
find_table_in_global_list(lex->query_tables->next_global,
- lex->query_tables->db,
- lex->query_tables->table_name))
+ &lex->query_tables->db,
+ &lex->query_tables->table_name))
{
view->updatable_view= 0;
}
@@ -1108,7 +1120,7 @@ loop_out:
if (view->with_check != VIEW_CHECK_NONE &&
!view->updatable_view)
{
- my_error(ER_VIEW_NONUPD_CHECK, MYF(0), view->db, view->table_name);
+ my_error(ER_VIEW_NONUPD_CHECK, MYF(0), view->db.str, view->table_name.str);
error= -1;
goto err;
}
@@ -1152,9 +1164,9 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
bool result, view_is_mergeable;
TABLE_LIST *UNINIT_VAR(view_main_select_tables);
DBUG_ENTER("mysql_make_view");
- DBUG_PRINT("info", ("table: %p (%s)", table, table->table_name));
+ DBUG_PRINT("info", ("table: %p (%s)", table, table->table_name.str));
- if (table->required_type == FRMTYPE_TABLE)
+ if (table->required_type == TABLE_TYPE_NORMAL)
{
my_error(ER_WRONG_OBJECT, MYF(0), share->db.str, share->table_name.str,
"BASE TABLE");
@@ -1196,7 +1208,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),
- table->index_hints->head()->key_name.str, table->table_name);
+ table->index_hints->head()->key_name.str, table->table_name.str);
DBUG_RETURN(TRUE);
}
@@ -1205,12 +1217,12 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
precedent;
precedent= precedent->referencing_view)
{
- if (precedent->view_name.length == table->table_name_length &&
- precedent->view_db.length == table->db_length &&
+ if (precedent->view_name.length == table->table_name.length &&
+ precedent->view_db.length == table->db.length &&
my_strcasecmp(system_charset_info,
- precedent->view_name.str, table->table_name) == 0 &&
+ precedent->view_name.str, table->table_name.str) == 0 &&
my_strcasecmp(system_charset_info,
- precedent->view_db.str, table->db) == 0)
+ precedent->view_db.str, table->db.str) == 0)
{
my_error(ER_VIEW_RECURSIVE, MYF(0),
top_view->view_db.str, top_view->view_name.str);
@@ -1254,7 +1266,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
!table->definer.host.length);
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_VIEW_FRM_NO_USER, ER_THD(thd, ER_VIEW_FRM_NO_USER),
- table->db, table->table_name);
+ table->db.str, table->table_name.str);
get_default_definer(thd, &table->definer, false);
}
@@ -1284,10 +1296,8 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
Save VIEW parameters, which will be wiped out by derived table
processing
*/
- table->view_db.str= table->db;
- table->view_db.length= table->db_length;
- table->view_name.str= table->table_name;
- table->view_name.length= table->table_name_length;
+ table->view_db= table->db;
+ table->view_name= table->table_name;
/*
We don't invalidate a prepared statement when a view changes,
or when someone creates a temporary table.
@@ -1327,18 +1337,19 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
{
char old_db_buf[SAFE_NAME_LEN+1];
- LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) };
+ LEX_CSTRING old_db= { old_db_buf, sizeof(old_db_buf) };
bool dbchanged;
Parser_state parser_state;
if (parser_state.init(thd, table->select_stmt.str,
- table->select_stmt.length))
+ (uint)table->select_stmt.length))
goto err;
/*
Use view db name as thread default database, in order to ensure
that the view is parsed and prepared correctly.
*/
- if ((result= mysql_opt_change_db(thd, &table->view_db, &old_db, 1,
+ if ((result= mysql_opt_change_db(thd, &table->view_db,
+ (LEX_STRING*) &old_db, 1,
&dbchanged)))
goto end;
@@ -1358,7 +1369,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
* MODE_NO_UNSIGNED_SUBTRACTION affect execution
- MODE_NO_DIR_IN_CREATE affect table creation only
- MODE_POSTGRESQL compounded from other modes
- - MODE_ORACLE compounded from other modes
+ - MODE_ORACLE affects Item creation (e.g for CONCAT)
- MODE_MSSQL compounded from other modes
- MODE_DB2 compounded from other modes
- MODE_MAXDB affect only CREATE TABLE parsing
@@ -1373,7 +1384,8 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
+ MODE_NO_BACKSLASH_ESCAPES affect expression parsing
*/
thd->variables.sql_mode&= ~(MODE_PIPES_AS_CONCAT | MODE_ANSI_QUOTES |
- MODE_IGNORE_SPACE | MODE_NO_BACKSLASH_ESCAPES);
+ MODE_IGNORE_SPACE | MODE_NO_BACKSLASH_ESCAPES |
+ MODE_ORACLE);
/* Parse the query. */
@@ -1479,6 +1491,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
privileges of top_view
*/
tbl->grant.want_privilege= SELECT_ACL;
+
/*
After unfolding the view we lose the list of tables referenced in it
(we will have only a list of underlying tables in case of MERGE
@@ -1529,6 +1542,18 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
views with subqueries in select list.
*/
view_main_select_tables= lex->select_lex.table_list.first;
+ /*
+ Mergeable view can be used for inserting, so we move the flag down
+ */
+ if (table->for_insert_data)
+ {
+ for (TABLE_LIST *t= view_main_select_tables;
+ t;
+ t= t->next_local)
+ {
+ t->for_insert_data= TRUE;
+ }
+ }
/*
Let us set proper lock type for tables of the view's main
@@ -1539,7 +1564,9 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
*/
for (tbl= view_main_select_tables; tbl; tbl= tbl->next_local)
{
- tbl->lock_type= table->lock_type;
+ /* We have to keep the lock type for sequence tables */
+ if (!tbl->sequence)
+ tbl->lock_type= table->lock_type;
tbl->mdl_request.set_type(table->mdl_request.type);
tbl->updating= table->updating;
}
@@ -1670,7 +1697,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
We can safely ignore the VIEW's ORDER BY if we merge into union
branch, as order is not important there.
*/
- if (!table->select_lex->master_unit()->is_union() &&
+ if (!table->select_lex->master_unit()->is_unit_op() &&
table->select_lex->order_list.elements == 0)
table->select_lex->order_list.push_back(&lex->select_lex.order_list);
else
@@ -1678,12 +1705,12 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
if (old_lex->sql_command == SQLCOM_SELECT &&
(old_lex->describe & DESCRIBE_EXTENDED) &&
lex->select_lex.order_list.elements &&
- !table->select_lex->master_unit()->is_union())
+ !table->select_lex->master_unit()->is_unit_op())
{
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_VIEW_ORDERBY_IGNORED,
ER_THD(thd, ER_VIEW_ORDERBY_IGNORED),
- table->db, table->table_name);
+ table->db.str, table->table_name.str);
}
}
/*
@@ -1700,7 +1727,6 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
view_select->linkage= DERIVED_TABLE_TYPE;
table->updatable= 0;
table->effective_with_check= VIEW_CHECK_NONE;
- old_lex->subqueries= TRUE;
table->derived= &lex->unit;
}
@@ -1761,7 +1787,7 @@ 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;
- char *wrong_object_db= NULL, *wrong_object_name= NULL;
+ const char *wrong_object_db= NULL, *wrong_object_name= NULL;
bool error= FALSE;
bool some_views_deleted= FALSE;
bool something_wrong= FALSE;
@@ -1773,30 +1799,32 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
TABLES we have to simply prohibit dropping of views.
*/
- if (thd->locked_tables_mode)
+ if (unlikely(thd->locked_tables_mode))
{
my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
DBUG_RETURN(TRUE);
}
- if (lock_table_names(thd, views, 0, thd->variables.lock_wait_timeout, 0))
+ if (unlikely(lock_table_names(thd, views, 0,
+ thd->variables.lock_wait_timeout, 0)))
DBUG_RETURN(TRUE);
for (view= views; view; view= view->next_local)
{
bool not_exist;
build_table_filename(path, sizeof(path) - 1,
- view->db, view->table_name, reg_ext, 0);
+ view->db.str, view->table_name.str, reg_ext, 0);
if ((not_exist= my_access(path, F_OK)) || !dd_frm_is_view(thd, path))
{
char name[FN_REFLEN];
- my_snprintf(name, sizeof(name), "%s.%s", view->db, view->table_name);
+ 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_BAD_TABLE_ERROR,
- ER_THD(thd, ER_BAD_TABLE_ERROR),
+ ER_UNKNOWN_VIEW,
+ ER_THD(thd, ER_UNKNOWN_VIEW),
name);
continue;
}
@@ -1810,13 +1838,13 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
{
if (!wrong_object_name)
{
- wrong_object_db= view->db;
- wrong_object_name= view->table_name;
+ wrong_object_db= view->db.str;
+ wrong_object_name= view->table_name.str;
}
}
continue;
}
- if (mysql_file_delete(key_file_frm, path, MYF(MY_WME)))
+ if (unlikely(mysql_file_delete(key_file_frm, path, MYF(MY_WME))))
error= TRUE;
some_views_deleted= TRUE;
@@ -1825,20 +1853,20 @@ 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, view->table_name,
+ tdc_remove_table(thd, TDC_RT_REMOVE_ALL, view->db.str, view->table_name.str,
FALSE);
query_cache_invalidate3(thd, view, 0);
sp_cache_invalidate();
}
- if (wrong_object_name)
+ if (unlikely(wrong_object_name))
{
my_error(ER_WRONG_OBJECT, MYF(0), wrong_object_db, wrong_object_name,
"VIEW");
}
- if (non_existant_views.length())
+ if (unlikely(non_existant_views.length()))
{
- my_error(ER_BAD_TABLE_ERROR, MYF(0), non_existant_views.c_ptr_safe());
+ my_error(ER_UNKNOWN_VIEW, MYF(0), non_existant_views.c_ptr_safe());
}
something_wrong= error || wrong_object_name || non_existant_views.length();
@@ -1847,11 +1875,12 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
/* if something goes wrong, bin-log with possible error code,
otherwise bin-log with error code cleared.
*/
- if (write_bin_log(thd, !something_wrong, thd->query(), thd->query_length()))
+ if (unlikely(write_bin_log(thd, !something_wrong, thd->query(),
+ thd->query_length())))
something_wrong= 1;
}
- if (something_wrong)
+ if (unlikely(something_wrong))
{
DBUG_RETURN(TRUE);
}
@@ -1910,19 +1939,19 @@ bool check_key_in_view(THD *thd, TABLE_LIST *view)
this operation should not have influence on Field::query_id, to avoid
marking as used fields which are not used
*/
- enum_mark_columns save_mark_used_columns= thd->mark_used_columns;
- thd->mark_used_columns= MARK_COLUMNS_NONE;
- DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
+ enum_column_usage saved_column_usage= thd->column_usage;
+ thd->column_usage= COLUMNS_WRITE;
+ DBUG_PRINT("info", ("thd->column_usage: %d", thd->column_usage));
for (Field_translator *fld= trans; fld < end_of_trans; fld++)
{
- if (!fld->item->fixed && fld->item->fix_fields(thd, &fld->item))
+ if (fld->item->fix_fields_if_needed(thd, &fld->item))
{
- thd->mark_used_columns= save_mark_used_columns;
+ thd->column_usage= saved_column_usage;
DBUG_RETURN(TRUE);
}
}
- thd->mark_used_columns= save_mark_used_columns;
- DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
+ thd->column_usage= saved_column_usage;
+ DBUG_PRINT("info", ("thd->column_usage: %d", thd->column_usage));
}
/* Loop over all keys to see if a unique-not-null key is used */
for (;key_info != key_info_end ; key_info++)
@@ -2000,7 +2029,7 @@ bool check_key_in_view(THD *thd, TABLE_LIST *view)
RETURN
FALSE OK
- TRUE error (is not sent to cliet)
+ TRUE error (is not sent to client)
*/
bool insert_view_fields(THD *thd, List<Item> *list, TABLE_LIST *view)
@@ -2017,10 +2046,18 @@ bool insert_view_fields(THD *thd, List<Item> *list, TABLE_LIST *view)
{
Item_field *fld;
if ((fld= entry->item->field_for_view_update()))
+ {
+ TABLE_SHARE *s= fld->context->table_list->table->s;
+ Lex_ident field_name= fld->field_name;
+ if (s->versioned &&
+ (field_name.streq(s->vers_start_field()->field_name) ||
+ field_name.streq(s->vers_end_field()->field_name)))
+ continue;
list->push_back(fld, thd->mem_root);
+ }
else
{
- my_error(ER_NON_INSERTABLE_TABLE, MYF(0), view->alias, "INSERT");
+ my_error(ER_NON_INSERTABLE_TABLE, MYF(0), view->alias.str, "INSERT");
DBUG_RETURN(TRUE);
}
}
@@ -2028,7 +2065,7 @@ bool insert_view_fields(THD *thd, List<Item> *list, TABLE_LIST *view)
}
/*
- checking view md5 check suum
+ checking view md5 check sum
SINOPSYS
view_checksum()
@@ -2122,11 +2159,11 @@ int view_repair(THD *thd, TABLE_LIST *view, HA_CHECK_OPT *check_opt)
*/
bool
mysql_rename_view(THD *thd,
- const char *new_db,
- const char *new_name,
+ const LEX_CSTRING *new_db,
+ const LEX_CSTRING *new_name,
TABLE_LIST *view)
{
- LEX_STRING pathstr;
+ LEX_CSTRING pathstr;
File_parser *parser;
char path_buff[FN_REFLEN + 1];
bool error= TRUE;
@@ -2134,7 +2171,7 @@ mysql_rename_view(THD *thd,
pathstr.str= (char *) path_buff;
pathstr.length= build_table_filename(path_buff, sizeof(path_buff) - 1,
- view->db, view->table_name,
+ view->db.str, view->table_name.str,
reg_ext, 0);
if ((parser= sql_parse_prepare(&pathstr, thd->mem_root, 1)) &&
@@ -2142,7 +2179,7 @@ mysql_rename_view(THD *thd,
{
TABLE_LIST view_def;
char dir_buff[FN_REFLEN + 1];
- LEX_STRING dir, file;
+ LEX_CSTRING dir, file;
/*
To be PS-friendly we should either to restore state of
@@ -2161,16 +2198,17 @@ mysql_rename_view(THD *thd,
goto err;
/* rename view and it's backups */
- if (rename_in_schema_file(thd, view->db, view->table_name, new_db, new_name))
+ if (rename_in_schema_file(thd, view->db.str, view->table_name.str,
+ new_db->str, new_name->str))
goto err;
dir.str= dir_buff;
dir.length= build_table_filename(dir_buff, sizeof(dir_buff) - 1,
- new_db, "", "", 0);
+ new_db->str, "", "", 0);
pathstr.str= path_buff;
pathstr.length= build_table_filename(path_buff, sizeof(path_buff) - 1,
- new_db, new_name, reg_ext, 0);
+ new_db->str, new_name->str, reg_ext, 0);
file.str= pathstr.str + dir.length;
file.length= pathstr.length - dir.length;
@@ -2179,7 +2217,8 @@ mysql_rename_view(THD *thd,
(uchar*)&view_def, view_parameters))
{
/* restore renamed view in case of error */
- rename_in_schema_file(thd, new_db, new_name, view->db, view->table_name);
+ rename_in_schema_file(thd, new_db->str, new_name->str, view->db.str,
+ view->table_name.str);
goto err;
}
} else
diff --git a/sql/sql_view.h b/sql/sql_view.h
index 9131ee09e64..c1e5dc49da3 100644
--- a/sql/sql_view.h
+++ b/sql/sql_view.h
@@ -53,14 +53,14 @@ extern TYPELIB updatable_views_with_limit_typelib;
bool check_duplicate_names(THD *thd, List<Item>& item_list,
bool gen_unique_view_names);
-bool mysql_rename_view(THD *thd, const char *new_db, const char *new_name,
+bool mysql_rename_view(THD *thd, const LEX_CSTRING *new_db, const LEX_CSTRING *new_name,
TABLE_LIST *view);
void make_valid_column_names(THD *thd, List<Item> &item_list);
#define VIEW_ANY_ACL (SELECT_ACL | UPDATE_ACL | INSERT_ACL | DELETE_ACL)
-extern const LEX_STRING view_type;
+extern const LEX_CSTRING view_type;
void make_valid_column_names(List<Item> &item_list);
diff --git a/sql/sql_window.cc b/sql/sql_window.cc
index 612c6e692fe..abb3937096f 100644
--- a/sql/sql_window.cc
+++ b/sql/sql_window.cc
@@ -1,10 +1,27 @@
+/*
+ Copyright (c) 2016, 2017 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 */
+
+#include "mariadb.h"
+#include "sql_parse.h"
#include "sql_select.h"
#include "sql_list.h"
#include "item_windowfunc.h"
#include "filesort.h"
#include "sql_base.h"
#include "sql_window.h"
-#include "my_dbug.h"
bool
@@ -12,13 +29,13 @@ Window_spec::check_window_names(List_iterator_fast<Window_spec> &it)
{
if (window_names_are_checked)
return false;
- char *name= this->name();
- char *ref_name= window_reference();
+ const char *name= this->name();
+ const char *ref_name= window_reference();
it.rewind();
Window_spec *win_spec;
while((win_spec= it++) && win_spec != this)
{
- char *win_spec_name= win_spec->name();
+ const char *win_spec_name= win_spec->name();
if (!win_spec_name)
break;
if (name && my_strcasecmp(system_charset_info, name, win_spec_name) == 0)
@@ -65,19 +82,32 @@ void
Window_spec::print(String *str, enum_query_type query_type)
{
str->append('(');
+ print_partition(str, query_type);
+ print_order(str, query_type);
+
+ if (window_frame)
+ window_frame->print(str, query_type);
+ str->append(')');
+}
+
+void
+Window_spec::print_partition(String *str, enum_query_type query_type)
+{
if (partition_list->first)
{
str->append(STRING_WITH_LEN(" partition by "));
st_select_lex::print_order(str, partition_list->first, query_type);
}
+}
+
+void
+Window_spec::print_order(String *str, enum_query_type query_type)
+{
if (order_list->first)
{
str->append(STRING_WITH_LEN(" order by "));
st_select_lex::print_order(str, order_list->first, query_type);
}
- if (window_frame)
- window_frame->print(str, query_type);
- str->append(')');
}
bool
@@ -297,9 +327,91 @@ setup_windows(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,
}
}
+ List_iterator_fast<Item_window_func> li(win_funcs);
+ while (Item_window_func * win_func_item= li++)
+ {
+ if (win_func_item->check_result_type_of_order_item())
+ DBUG_RETURN(1);
+ }
DBUG_RETURN(0);
}
+
+/**
+ @brief
+ Find fields common for all partition lists used in window functions
+
+ @param thd The thread handle
+
+ @details
+ This function looks for the field references in the partition lists
+ of all window functions used in this select that are common for
+ all the partition lists. The function returns an ORDER list contained
+ all such references.The list either is specially built by the function
+ or is taken directly from the first window specification.
+
+ @retval
+ pointer to the first element of the ORDER list contained field
+ references common for all partition lists
+ 0 if no such reference is found.
+*/
+
+ORDER *st_select_lex::find_common_window_func_partition_fields(THD *thd)
+{
+ ORDER *ord;
+ Item *item;
+ DBUG_ASSERT(window_funcs.elements);
+ List_iterator_fast<Item_window_func> it(window_funcs);
+ Item_window_func *first_wf= it++;
+ if (!first_wf->window_spec->partition_list)
+ return 0;
+ List<Item> common_fields;
+ uint first_partition_elements= 0;
+ for (ord= first_wf->window_spec->partition_list->first; ord; ord= ord->next)
+ {
+ if ((*ord->item)->real_item()->type() == Item::FIELD_ITEM)
+ common_fields.push_back(*ord->item, thd->mem_root);
+ first_partition_elements++;
+ }
+ if (window_specs.elements == 1 &&
+ common_fields.elements == first_partition_elements)
+ return first_wf->window_spec->partition_list->first;
+ List_iterator<Item> li(common_fields);
+ Item_window_func *wf;
+ while (common_fields.elements && (wf= it++))
+ {
+ if (!wf->window_spec->partition_list)
+ return 0;
+ while ((item= li++))
+ {
+ for (ord= wf->window_spec->partition_list->first; ord; ord= ord->next)
+ {
+ if (item->eq(*ord->item, false))
+ break;
+ }
+ if (!ord)
+ li.remove();
+ }
+ li.rewind();
+ }
+ if (!common_fields.elements)
+ return 0;
+ if (common_fields.elements == first_partition_elements)
+ return first_wf->window_spec->partition_list->first;
+ SQL_I_List<ORDER> res_list;
+ for (ord= first_wf->window_spec->partition_list->first, item= li++;
+ ord; ord= ord->next)
+ {
+ if (item != *ord->item)
+ continue;
+ if (add_to_list(thd, res_list, item, ord->direction))
+ return 0;
+ item= li++;
+ }
+ return res_list.first;
+}
+
+
/////////////////////////////////////////////////////////////////////////////
// Sorting window functions to minimize the number of table scans
// performed during the computation of these functions
@@ -320,7 +432,8 @@ int compare_order_elements(ORDER *ord1, ORDER *ord2)
Item *item2= (*ord2->item)->real_item();
DBUG_ASSERT(item1->type() == Item::FIELD_ITEM &&
item2->type() == Item::FIELD_ITEM);
- ptrdiff_t cmp= ((Item_field *) item1)->field - ((Item_field *) item2)->field;
+ int cmp= ((Item_field *) item1)->field->field_index -
+ ((Item_field *) item2)->field->field_index;
if (cmp == 0)
{
if (ord1->direction == ord2->direction)
@@ -393,8 +506,8 @@ int compare_window_frame_bounds(Window_frame_bound *win_frame_bound1,
return CMP_EQ;
else
{
- res= strcmp(win_frame_bound1->offset->name,
- win_frame_bound2->offset->name);
+ res= strcmp(win_frame_bound1->offset->name.str,
+ win_frame_bound2->offset->name.str);
res= res > 0 ? CMP_GT : CMP_LT;
if (is_bottom_bound)
res= -res;
@@ -654,7 +767,7 @@ public:
void init(READ_RECORD *info)
{
ref_length= info->ref_length;
- if (info->read_record == rr_from_pointers)
+ if (info->read_record_func == rr_from_pointers)
{
io_cache= NULL;
cache_start= info->cache_pos;
@@ -785,7 +898,7 @@ public:
{
Rowid_seq_cursor::init(info);
table= info->table;
- record= info->record;
+ record= info->record();
}
virtual int fetch()
@@ -883,6 +996,8 @@ private:
bool end_of_partition;
};
+
+
/////////////////////////////////////////////////////////////////////////////
/*
@@ -1650,21 +1765,59 @@ public:
/* Walk to the end of the partition, find how many rows there are. */
while (!cursor.next())
num_rows_in_partition++;
+ set_win_funcs_row_count(num_rows_in_partition);
+ }
+ ha_rows get_curr_rownum() const
+ {
+ return cursor.get_rownum();
+ }
+
+protected:
+ void set_win_funcs_row_count(ha_rows num_rows_in_partition)
+ {
List_iterator_fast<Item_sum> it(sum_functions);
Item_sum* item;
while ((item= it++))
+ item->set_partition_row_count(num_rows_in_partition);
+ }
+};
+
+class Frame_unbounded_following_set_count_no_nulls:
+ public Frame_unbounded_following_set_count
+{
+
+public:
+ Frame_unbounded_following_set_count_no_nulls(THD *thd,
+ SQL_I_List<ORDER> *partition_list,
+ SQL_I_List<ORDER> *order_list) :
+ Frame_unbounded_following_set_count(thd,partition_list, order_list)
+ {
+ order_item= order_list->first->item[0];
+ }
+ void next_partition(ha_rows rownum)
+ {
+ ha_rows num_rows_in_partition= 0;
+ if (cursor.fetch())
+ return;
+
+ /* Walk to the end of the partition, find how many rows there are. */
+ do
{
- Item_sum_window_with_row_count* item_with_row_count =
- static_cast<Item_sum_window_with_row_count *>(item);
- item_with_row_count->set_row_count(num_rows_in_partition);
- }
+ if (!order_item->is_null())
+ num_rows_in_partition++;
+ } while (!cursor.next());
+
+ set_win_funcs_row_count(num_rows_in_partition);
}
ha_rows get_curr_rownum() const
{
return cursor.get_rownum();
}
+
+private:
+ Item* order_item;
};
/////////////////////////////////////////////////////////////////////////////
@@ -2405,6 +2558,21 @@ void add_special_frame_cursors(THD *thd, Cursor_manager *cursor_manager,
cursor_manager->add_cursor(fc);
break;
}
+ case Item_sum::PERCENTILE_CONT_FUNC:
+ case Item_sum::PERCENTILE_DISC_FUNC:
+ {
+ fc= new Frame_unbounded_preceding(thd,
+ spec->partition_list,
+ spec->order_list);
+ fc->add_sum_func(item_sum);
+ cursor_manager->add_cursor(fc);
+ fc= new Frame_unbounded_following(thd,
+ spec->partition_list,
+ spec->order_list);
+ fc->add_sum_func(item_sum);
+ cursor_manager->add_cursor(fc);
+ break;
+ }
default:
fc= new Frame_unbounded_preceding(
thd, spec->partition_list, spec->order_list);
@@ -2429,6 +2597,8 @@ static bool is_computed_with_remove(Item_sum::Sumfunctype sum_func)
case Item_sum::NTILE_FUNC:
case Item_sum::FIRST_VALUE_FUNC:
case Item_sum::LAST_VALUE_FUNC:
+ case Item_sum::PERCENTILE_CONT_FUNC:
+ case Item_sum::PERCENTILE_DISC_FUNC:
return false;
default:
return true;
@@ -2459,9 +2629,18 @@ void get_window_functions_required_cursors(
*/
if (item_win_func->requires_partition_size())
{
- fc= new Frame_unbounded_following_set_count(thd,
+ if (item_win_func->only_single_element_order_list())
+ {
+ fc= new Frame_unbounded_following_set_count_no_nulls(thd,
item_win_func->window_spec->partition_list,
item_win_func->window_spec->order_list);
+ }
+ else
+ {
+ fc= new Frame_unbounded_following_set_count(thd,
+ item_win_func->window_spec->partition_list,
+ item_win_func->window_spec->order_list);
+ }
fc->add_sum_func(sum_func);
cursor_manager->add_cursor(fc);
}
@@ -2641,7 +2820,7 @@ bool compute_window_func(THD *thd,
while (true)
{
- if ((err= info.read_record(&info)))
+ if ((err= info.read_record()))
break; // End of file.
/* Remember current row so that we can restore it before computing
@@ -2669,6 +2848,13 @@ bool compute_window_func(THD *thd,
{
cursor_manager->notify_cursors_next_row();
}
+
+ /* Check if we found any error in the window function while adding values
+ through cursors. */
+ if (unlikely(thd->is_error() || thd->is_killed()))
+ break;
+
+
/* Return to current row after notifying cursors for each window
function. */
tbl->file->ha_rnd_pos(tbl->record[0], rowid_buf);
@@ -2960,10 +3146,14 @@ Window_funcs_computation::save_explain_plan(MEM_ROOT *mem_root,
Explain_aggr_window_funcs *xpl= new Explain_aggr_window_funcs;
List_iterator<Window_funcs_sort> it(win_func_sorts);
Window_funcs_sort *srt;
+ if (!xpl)
+ return 0;
while ((srt = it++))
{
Explain_aggr_filesort *eaf=
new Explain_aggr_filesort(mem_root, is_analyze, srt->filesort);
+ if (!eaf)
+ return 0;
xpl->sorts.push_back(eaf, mem_root);
}
return xpl;
@@ -3101,5 +3291,3 @@ bool st_select_lex::add_window_func(Item_window_func *win_func)
}
else
#endif
-
-
diff --git a/sql/sql_window.h b/sql/sql_window.h
index e0c1563e5bb..373b367b211 100644
--- a/sql/sql_window.h
+++ b/sql/sql_window.h
@@ -1,11 +1,23 @@
+/*
+ Copyright (c) 2016, 2017 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_WINDOW_INCLUDED
#define SQL_WINDOW_INCLUDED
-#include "my_global.h"
-#include "item.h"
#include "filesort.h"
-#include "records.h"
class Item_window_func;
@@ -96,7 +108,7 @@ class Window_spec : public Sql_alloc
public:
virtual ~Window_spec() {}
- LEX_STRING *window_ref;
+ LEX_CSTRING *window_ref;
SQL_I_List<ORDER> *partition_list;
@@ -106,7 +118,7 @@ class Window_spec : public Sql_alloc
Window_spec *referenced_win_spec;
- Window_spec(LEX_STRING *win_ref,
+ Window_spec(LEX_CSTRING *win_ref,
SQL_I_List<ORDER> *part_list,
SQL_I_List<ORDER> *ord_list,
Window_frame *win_frame)
@@ -114,11 +126,14 @@ class Window_spec : public Sql_alloc
partition_list(part_list), order_list(ord_list),
window_frame(win_frame), referenced_win_spec(NULL) {}
- virtual char *name() { return NULL; }
+ virtual const char *name() { return NULL; }
bool check_window_names(List_iterator_fast<Window_spec> &it);
- char *window_reference() { return window_ref ? window_ref->str : NULL; }
+ const char *window_reference()
+ {
+ return window_ref ? window_ref->str : NULL;
+ }
void join_partition_and_order_lists()
{
@@ -131,6 +146,8 @@ class Window_spec : public Sql_alloc
}
void print(String *str, enum_query_type query_type);
+ void print_order(String *str, enum_query_type query_type);
+ void print_partition(String *str, enum_query_type query_type);
};
@@ -138,17 +155,17 @@ class Window_def : public Window_spec
{
public:
- LEX_STRING *window_name;
+ LEX_CSTRING *window_name;
- Window_def(LEX_STRING *win_name,
- LEX_STRING *win_ref,
+ Window_def(LEX_CSTRING *win_name,
+ LEX_CSTRING *win_ref,
SQL_I_List<ORDER> *part_list,
SQL_I_List<ORDER> *ord_list,
Window_frame *win_frame)
: Window_spec(win_ref, part_list, ord_list, win_frame),
window_name(win_name) {}
- char *name() { return window_name->str; }
+ const char *name() { return window_name->str; }
};
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 386c86cb3e2..04a19d6922e 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -1,6 +1,6 @@
/*
Copyright (c) 2000, 2015, Oracle and/or its affiliates.
- Copyright (c) 2010, 2016, MariaDB
+ Copyright (c) 2010, 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
@@ -33,11 +33,11 @@
#define Lex (thd->lex)
#define Select Lex->current_select
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "sql_parse.h" /* comp_*_creator */
#include "sql_table.h" /* primary_key_name */
-#include "sql_partition.h" /* mem_alloc_error, partition_info, HASH_PARTITION */
+#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"
@@ -65,6 +65,9 @@
#include "set_var.h"
#include "rpl_mi.h"
#include "lex_token.h"
+#include "sql_lex.h"
+#include "sql_sequence.h"
+#include "my_base.h"
/* this is to get the bison compilation windows warnings out */
#ifdef _MSC_VER
@@ -76,8 +79,8 @@ int yylex(void *yylval, void *yythd);
#define yyoverflow(A,B,C,D,E,F) \
{ \
- ulong val= *(F); \
- if (my_yyoverflow((B), (D), &val)) \
+ size_t val= *(F); \
+ if (unlikely(my_yyoverflow((B), (D), &val))) \
{ \
yyerror(thd, (char*) (A)); \
return 2; \
@@ -96,9 +99,9 @@ int yylex(void *yylval, void *yythd);
} while (0)
#define MYSQL_YYABORT_UNLESS(A) \
- if (!(A)) \
+ if (unlikely(!(A))) \
{ \
- my_parse_error(thd, ER_SYNTAX_ERROR); \
+ thd->parse_error(); \
MYSQL_YYABORT; \
}
@@ -111,43 +114,6 @@ int yylex(void *yylval, void *yythd);
#define YYDEBUG 0
#endif
-/**
- @brief Push an error message into MySQL error stack with line
- and position information.
-
- This function provides semantic action implementers with a way
- to push the famous "You have a syntax error near..." error
- message into the error stack, which is normally produced only if
- a parse error is discovered internally by the Bison generated
- parser.
-*/
-
-static void my_parse_error_intern(THD *thd, const char *err_text,
- const char *yytext)
-{
- Lex_input_stream *lip= &thd->m_parser_state->m_lip;
- if (!yytext)
- {
- if (!(yytext= lip->get_tok_start()))
- yytext= "";
- }
- /* Push an error into the error stack */
- ErrConvString err(yytext, strlen(yytext),
- thd->variables.character_set_client);
- my_error(ER_PARSE_ERROR, MYF(0), err_text, err.ptr(), lip->yylineno);
-}
-
-
-static void my_parse_error(THD *thd, uint err_number, const char *yytext=0)
-{
- return my_parse_error_intern(thd, ER_THD(thd, err_number), yytext);
-}
-
-void LEX::parse_error()
-{
- my_parse_error(thd, ER_SYNTAX_ERROR);
-}
-
/**
@brief Bison callback to report a syntax/OOM error
@@ -164,7 +130,7 @@ void LEX::parse_error()
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 my_parse_error or my_error to
+ 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.
*/
@@ -181,7 +147,7 @@ void MYSQLerror(THD *thd, const char *s)
/* "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);
- my_parse_error_intern(thd, s, 0);
+ thd->parse_error(s, 0);
}
@@ -207,80 +173,6 @@ void turn_parser_debug_on()
}
#endif
-static bool is_native_function(THD *thd, const LEX_STRING *name)
-{
- if (find_native_function_builder(thd, *name))
- return true;
-
- if (is_lex_native_function(name))
- return true;
-
- return false;
-}
-
-
-static sp_head *make_sp_head(THD *thd, sp_name *name,
- enum stored_procedure_type type)
-{
- LEX *lex= thd->lex;
- sp_head *sp;
-
- /* Order is important here: new - reset - init */
- if ((sp= sp_head::create()))
- {
- sp->reset_thd_mem_root(thd);
- sp->init(lex);
- sp->m_type= type;
- if (name)
- sp->init_sp_name(thd, name);
- sp->m_chistics= &lex->sp_chistics;
- lex->sphead= sp;
- }
- bzero(&lex->sp_chistics, sizeof(lex->sp_chistics));
- return sp;
-}
-
-static bool maybe_start_compound_statement(THD *thd)
-{
- if (!thd->lex->sphead)
- {
- if (!make_sp_head(thd, NULL, TYPE_ENUM_PROCEDURE))
- return 1;
-
- Lex->sp_chistics.suid= SP_IS_NOT_SUID;
- Lex->sphead->set_body_start(thd, YYLIP->get_cpp_ptr());
- }
- return 0;
-}
-
-static bool push_sp_label(THD *thd, LEX_STRING label)
-{
- sp_pcontext *ctx= thd->lex->spcont;
- sp_label *lab= ctx->find_label(label);
-
- if (lab)
- {
- my_error(ER_SP_LABEL_REDEFINE, MYF(0), label.str);
- return 1;
- }
- else
- {
- lab= thd->lex->spcont->push_label(thd, label,
- thd->lex->sphead->instructions());
- lab->type= sp_label::ITERATION;
- }
- return 0;
-}
-
-static bool push_sp_empty_label(THD *thd)
-{
- if (maybe_start_compound_statement(thd))
- return 1;
- /* Unlabeled controls get an empty label. */
- thd->lex->spcont->push_label(thd, empty_lex_str,
- thd->lex->sphead->instructions());
- return 0;
-}
/**
Helper action for a case expression statement (the expr in 'CASE expr').
@@ -290,22 +182,20 @@ static bool push_sp_empty_label(THD *thd)
@return 0 on success
*/
-int case_stmt_action_expr(LEX *lex, Item* expr)
+int LEX::case_stmt_action_expr(Item* expr)
{
- sp_head *sp= lex->sphead;
- sp_pcontext *parsing_ctx= lex->spcont;
- int case_expr_id= parsing_ctx->register_case_expr();
+ int case_expr_id= spcont->register_case_expr();
sp_instr_set_case_expr *i;
- if (parsing_ctx->push_case_expr_id(case_expr_id))
+ if (spcont->push_case_expr_id(case_expr_id))
return 1;
- i= new (lex->thd->mem_root)
- sp_instr_set_case_expr(sp->instructions(), parsing_ctx, case_expr_id, expr,
- lex);
+ i= new (thd->mem_root)
+ sp_instr_set_case_expr(sphead->instructions(), spcont, case_expr_id, expr,
+ this);
- sp->add_cont_backpatch(i);
- return sp->add_instr(i);
+ sphead->add_cont_backpatch(i);
+ return sphead->add_instr(i);
}
/**
@@ -316,33 +206,30 @@ int case_stmt_action_expr(LEX *lex, Item* expr)
@param simple true for simple cases, false for searched cases
*/
-int case_stmt_action_when(LEX *lex, Item *when, bool simple)
+int LEX::case_stmt_action_when(Item *when, bool simple)
{
- sp_head *sp= lex->sphead;
- sp_pcontext *ctx= lex->spcont;
- uint ip= sp->instructions();
+ uint ip= sphead->instructions();
sp_instr_jump_if_not *i;
Item_case_expr *var;
Item *expr;
- THD *thd= lex->thd;
if (simple)
{
var= new (thd->mem_root)
- Item_case_expr(thd, ctx->get_current_case_expr_id());
+ Item_case_expr(thd, spcont->get_current_case_expr_id());
-#ifndef DBUG_OFF
+#ifdef DBUG_ASSERT_EXISTS
if (var)
{
- var->m_sp= sp;
+ 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, ctx, expr, lex);
+ 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, ctx, when, lex);
+ i= new (thd->mem_root) sp_instr_jump_if_not(ip, spcont, when, this);
/*
BACKPATCH: Registering forward jump from
@@ -350,10 +237,11 @@ int case_stmt_action_when(LEX *lex, Item *when, bool simple)
(jump_if_not from instruction 2 to 5, 5 to 8 ... in the example)
*/
- return !MY_TEST(i) ||
- sp->push_backpatch(thd, i, ctx->push_label(thd, empty_lex_str, 0)) ||
- sp->add_cont_backpatch(i) ||
- sp->add_instr(i);
+ 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);
}
/**
@@ -362,13 +250,11 @@ int case_stmt_action_when(LEX *lex, Item *when, bool simple)
@param lex the parser lex context
*/
-int case_stmt_action_then(LEX *lex)
+int LEX::case_stmt_action_then()
{
- sp_head *sp= lex->sphead;
- sp_pcontext *ctx= lex->spcont;
- uint ip= sp->instructions();
- sp_instr_jump *i= new (lex->thd->mem_root) sp_instr_jump(ip, ctx);
- if (!MY_TEST(i) || sp->add_instr(i))
+ 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;
/*
@@ -377,7 +263,7 @@ int case_stmt_action_then(LEX *lex)
(jump_if_not from instruction 2 to 5, 5 to 8 ... in the example)
*/
- sp->backpatch(ctx->pop_label());
+ sphead->backpatch(spcont->pop_label());
/*
BACKPATCH: Registering forward jump from
@@ -385,18 +271,7 @@ int case_stmt_action_then(LEX *lex)
(jump from instruction 4 to 12, 7 to 12 ... in the example)
*/
- return sp->push_backpatch(lex->thd, i, ctx->last_label());
-}
-
-static bool
-find_sys_var_null_base(THD *thd, struct sys_var_with_base *tmp)
-{
- tmp->var= find_sys_var(thd, tmp->base_name.str, tmp->base_name.length);
-
- if (tmp->var != NULL)
- tmp->base_name= null_lex_str;
-
- return thd->is_error();
+ return sphead->push_backpatch(thd, i, spcont->last_label());
}
@@ -404,7 +279,6 @@ find_sys_var_null_base(THD *thd, struct sys_var_with_base *tmp)
Helper action for a SET statement.
Used to push a system variable into the assignment list.
- @param thd the current thread
@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
@@ -412,67 +286,29 @@ find_sys_var_null_base(THD *thd, struct sys_var_with_base *tmp)
@return TRUE if error, FALSE otherwise.
*/
-static bool
-set_system_variable(THD *thd, struct sys_var_with_base *tmp,
- enum enum_var_type var_type, Item *val)
+bool
+LEX::set_system_variable(enum enum_var_type var_type,
+ sys_var *sysvar, const LEX_CSTRING *base_name,
+ Item *val)
{
- set_var *var;
- LEX *lex= thd->lex;
+ set_var *setvar;
/* No AUTOCOMMIT from a stored function or trigger. */
- if (lex->spcont && tmp->var == Sys_autocommit_ptr)
- lex->sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT;
+ 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), tmp->var->name.str);
+ my_error(ER_WRONG_TYPE_FOR_VAR, MYF(0), sysvar->name.str);
return TRUE;
}
- if (! (var= new (thd->mem_root)
- set_var(thd, var_type, tmp->var, &tmp->base_name, val)))
+ if (!(setvar= new (thd->mem_root) set_var(thd, var_type, sysvar,
+ base_name, val)))
return TRUE;
- return lex->var_list.push_back(var, thd->mem_root);
-}
-
-
-/**
- Helper action for a SET statement.
- Used to push a SP local variable into the assignment list.
-
- @param thd the current thread
- @param var_type the SP local variable
- @param val the value being assigned to the variable
-
- @return TRUE if error, FALSE otherwise.
-*/
-
-static bool
-set_local_variable(THD *thd, sp_variable *spv, Item *val)
-{
- Item *it;
- LEX *lex= thd->lex;
- sp_instr_set *sp_set;
-
- if (val)
- it= val;
- else if (spv->default_value)
- it= spv->default_value;
- else
- {
- it= new (thd->mem_root) Item_null(thd);
- if (it == NULL)
- return TRUE;
- }
-
- sp_set= new (thd->mem_root)
- sp_instr_set(lex->sphead->instructions(), lex->spcont,
- spv->offset, it, spv->sql_type(),
- lex, TRUE);
-
- return (sp_set == NULL || lex->sphead->add_instr(sp_set));
+ return var_list.push_back(setvar, thd->mem_root);
}
@@ -480,17 +316,14 @@ set_local_variable(THD *thd, sp_variable *spv, Item *val)
Helper action for a SET statement.
Used to SET a field of NEW row.
- @param thd the current thread
@param name the field name
@param val the value being assigned to the row
@return TRUE if error, FALSE otherwise.
*/
-static bool
-set_trigger_new_row(THD *thd, LEX_STRING *name, Item *val)
+bool LEX::set_trigger_new_row(const LEX_CSTRING *name, Item *val)
{
- LEX *lex= thd->lex;
Item_trigger_field *trg_fld;
sp_instr_set_trigger_field *sp_fld;
@@ -498,40 +331,38 @@ set_trigger_new_row(THD *thd, LEX_STRING *name, Item *val)
if (! val)
val= new (thd->mem_root) Item_null(thd);
- DBUG_ASSERT(lex->trg_chistics.action_time == TRG_ACTION_BEFORE &&
- (lex->trg_chistics.event == TRG_EVENT_INSERT ||
- lex->trg_chistics.event == TRG_EVENT_UPDATE));
+ 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, lex->current_context(),
+ Item_trigger_field(thd, current_context(),
Item_trigger_field::NEW_ROW,
- name->str, UPDATE_ACL, FALSE);
+ name, UPDATE_ACL, FALSE);
- if (trg_fld == NULL)
+ if (unlikely(trg_fld == NULL))
return TRUE;
sp_fld= new (thd->mem_root)
- sp_instr_set_trigger_field(lex->sphead->instructions(),
- lex->spcont, trg_fld, val,
- lex);
+ sp_instr_set_trigger_field(sphead->instructions(),
+ spcont, trg_fld, val, this);
- if (sp_fld == NULL)
+ if (unlikely(sp_fld == NULL))
return TRUE;
/*
Let us add this item to list of all Item_trigger_field
objects in trigger.
*/
- lex->trg_table_fields.link_in_list(trg_fld, &trg_fld->next_trg_field);
+ trg_table_fields.link_in_list(trg_fld, &trg_fld->next_trg_field);
- return lex->sphead->add_instr(sp_fld);
+ return sphead->add_instr(sp_fld);
}
/**
Create an object to represent a SP variable in the Item-hierarchy.
- @param thd The current thread.
@param name The SP variable name.
@param spvar The SP variable (optional).
@param start_in_q Start position of the SP variable name in the query.
@@ -543,18 +374,22 @@ set_trigger_new_row(THD *thd, LEX_STRING *name, Item *val)
@return An Item_splocal object representing the SP variable, or NULL on error.
*/
-static Item_splocal*
-create_item_for_sp_var(THD *thd, LEX_STRING name, sp_variable *spvar,
- const char *start_in_q, const char *end_in_q)
+Item_splocal*
+LEX::create_item_for_sp_var(const Lex_ident_cli_st *cname, sp_variable *spvar)
{
+ const Sp_rcontext_handler *rh;
Item_splocal *item;
- LEX *lex= thd->lex;
+ const char *start_in_q= cname->pos();
+ const char *end_in_q= cname->end();
uint pos_in_q, len_in_q;
- sp_pcontext *spc = lex->spcont;
+ Lex_ident_sys name(thd, cname);
+
+ if (name.is_null())
+ return NULL; // EOM
/* If necessary, look for the variable. */
- if (spc && !spvar)
- spvar= spc->find_variable(name, false);
+ if (spcont && !spvar)
+ spvar= find_variable(&name, &rh);
if (!spvar)
{
@@ -562,19 +397,19 @@ create_item_for_sp_var(THD *thd, LEX_STRING name, sp_variable *spvar,
return NULL;
}
- DBUG_ASSERT(spc && spvar);
+ DBUG_ASSERT(spcont && spvar);
/* Position and length of the SP variable name in the query. */
- pos_in_q= (uint)(start_in_q - lex->sphead->m_tmp_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, name, spvar->offset, spvar->sql_type(),
+ Item_splocal(thd, rh, &name, spvar->offset, spvar->type_handler(),
pos_in_q, len_in_q);
-#ifndef DBUG_OFF
+#ifdef DBUG_ASSERT_EXISTS
if (item)
- item->m_sp= lex->sphead;
+ item->m_sp= sphead;
#endif
return item;
@@ -673,59 +508,74 @@ Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal,
@return <code>false</code> if successful, <code>true</code> if an error was
reported. In the latter case parsing should stop.
*/
-bool add_select_to_union_list(LEX *lex, bool is_union_distinct,
- bool is_top_level)
+bool LEX::add_select_to_union_list(bool is_union_distinct,
+ enum sub_select_type type,
+ bool is_top_level)
{
- /*
+ const char *type_name= (type == INTERSECT_TYPE ? "INTERSECT" :
+ (type == EXCEPT_TYPE ? "EXCEPT" : "UNION"));
+ /*
Only the last SELECT can have INTO. Since the grammar won't allow INTO in
a nested SELECT, we make this check only when creating a top-level SELECT.
*/
- if (is_top_level && lex->result)
+ if (is_top_level && result)
{
- my_error(ER_WRONG_USAGE, MYF(0), "UNION", "INTO");
+ my_error(ER_WRONG_USAGE, MYF(0), type_name, "INTO");
return TRUE;
}
- if (lex->current_select->order_list.first && !lex->current_select->braces)
+ if (current_select->order_list.first && !current_select->braces)
{
- my_error(ER_WRONG_USAGE, MYF(0), "UNION", "ORDER BY");
+ my_error(ER_WRONG_USAGE, MYF(0), type_name, "ORDER BY");
return TRUE;
}
- if (lex->current_select->explicit_limit && !lex->current_select->braces)
+ if (current_select->explicit_limit && !current_select->braces)
+ {
+ my_error(ER_WRONG_USAGE, MYF(0), type_name, "LIMIT");
+ return TRUE;
+ }
+ if (current_select->linkage == GLOBAL_OPTIONS_TYPE)
{
- my_error(ER_WRONG_USAGE, MYF(0), "UNION", "LIMIT");
+ thd->parse_error();
return TRUE;
}
- if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE)
+ if (!is_union_distinct && (type == INTERSECT_TYPE || type == EXCEPT_TYPE))
{
- my_parse_error(lex->thd, ER_SYNTAX_ERROR);
+ my_error(ER_WRONG_USAGE, MYF(0), type_name, "ALL");
return TRUE;
}
+ /*
+ Priority implementation, but also trying to keep things as flat
+ as possible */
+ if (type == INTERSECT_TYPE &&
+ (current_select->linkage != INTERSECT_TYPE &&
+ current_select != current_select->master_unit()->first_select())
+ && !(thd->variables.sql_mode & MODE_ORACLE))
+ {
+ /*
+ This and previous SELECTs should go one level down because of
+ priority
+ */
+ SELECT_LEX *prev= exclude_last_select();
+ if (add_unit_in_brackets(prev))
+ return TRUE;
+ return add_select_to_union_list(is_union_distinct, type, 0);
+ }
+ else
+ {
+ check_automatic_up(type);
+ }
/* This counter shouldn't be incremented for UNION parts */
- lex->nest_level--;
- if (mysql_new_select(lex, 0))
+ nest_level--;
+ if (mysql_new_select(this, 0, NULL))
return TRUE;
- mysql_init_select(lex);
- lex->current_select->linkage=UNION_TYPE;
- lex->current_select->with_all_modifier= !is_union_distinct;
+ mysql_init_select(this);
+ current_select->linkage= type;
+ current_select->with_all_modifier= !is_union_distinct;
if (is_union_distinct) /* UNION DISTINCT - remember position */
- lex->current_select->master_unit()->union_distinct=
- lex->current_select;
- return FALSE;
-}
-
-
-static bool add_create_index_prepare(LEX *lex, Table_ident *table)
-{
- lex->sql_command= SQLCOM_CREATE_INDEX;
- if (!lex->current_select->add_table_to_list(lex->thd, table, NULL,
- TL_OPTION_UPDATING,
- TL_READ_NO_INSERT,
- MDL_SHARED_UPGRADABLE))
- return TRUE;
- lex->alter_info.reset();
- lex->alter_info.flags= Alter_info::ALTER_ADD_INDEX;
- lex->option_list= NULL;
+ current_select->master_unit()->union_distinct= current_select;
+ else
+ DBUG_ASSERT(type == UNION_TYPE);
return FALSE;
}
@@ -748,7 +598,7 @@ static bool add_create_index_prepare(LEX *lex, Table_ident *table)
@param no_lookahead True if the parser has no lookahead
*/
-static void sp_create_assignment_lex(THD *thd, bool no_lookahead)
+void sp_create_assignment_lex(THD *thd, bool no_lookahead)
{
LEX *lex= thd->lex;
@@ -786,7 +636,7 @@ static void sp_create_assignment_lex(THD *thd, bool no_lookahead)
@return false if success, true otherwise.
*/
-static bool sp_create_assignment_instr(THD *thd, bool no_lookahead)
+bool sp_create_assignment_instr(THD *thd, bool no_lookahead)
{
LEX *lex= thd->lex;
@@ -839,50 +689,45 @@ static bool sp_create_assignment_instr(THD *thd, bool no_lookahead)
return false;
}
-
-static void add_key_to_list(LEX *lex, LEX_STRING *field_name,
- enum Key::Keytype type, bool check_exists)
+void LEX::add_key_to_list(LEX_CSTRING *field_name,
+ enum Key::Keytype type, bool check_exists)
{
Key *key;
- MEM_ROOT *mem_root= lex->thd->mem_root;
+ MEM_ROOT *mem_root= thd->mem_root;
key= new (mem_root)
- Key(type, null_lex_str, HA_KEY_ALG_UNDEF, false,
+ 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),
+ key->columns.push_back(new (mem_root) Key_part_spec(field_name, 0),
mem_root);
- lex->alter_info.key_list.push_back(key, 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 char *field_name,
- CHARSET_INFO *cs)
+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;
+ field->field_name= *field_name;
/* reset LEX fields that are used in Create_field::set_and_check() */
charset= cs;
}
-void LEX::set_last_field_type(const Lex_field_type_st &type)
-{
- last_field->sql_type= type.field_type();
- last_field->charset= charset;
-
- if (type.length())
- {
- int err;
- last_field->length= my_strtoll10(type.length(), NULL, &err);
- if (err)
- last_field->length= ~0ULL; // safety
- }
- else
- last_field->length= 0;
-
- last_field->decimals= type.dec() ? (uint)atoi(type.dec()) : 0;
-}
bool LEX::set_bincmp(CHARSET_INFO *cs, bool bin)
{
@@ -907,18 +752,16 @@ bool LEX::set_bincmp(CHARSET_INFO *cs, bool bin)
#define bincmp_collation(X,Y) \
do \
{ \
- if (Lex->set_bincmp(X,Y)) \
+ if (unlikely(Lex->set_bincmp(X,Y))) \
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 (!v)
- {
- mem_alloc_error(sizeof(Virtual_column_info));
+ if (unlikely(!v))
return 0;
- }
v->expr= expr;
v->utf8= 0; /* connection charset */
return v;
@@ -930,19 +773,30 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr)
ulong ulong_num;
ulonglong ulonglong_number;
longlong longlong_number;
+ uint sp_instr_addr;
/* structs */
- LEX_STRING lex_str;
- LEX_SYMBOL symbol;
- struct sys_var_with_base variable;
- struct { int vars, conds, hndlrs, curs; } spblock;
+ 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;
/* 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;
@@ -950,27 +804,31 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr)
Item *item;
Item_num *item_num;
Item_param *item_param;
+ Item_basic_constant *item_basic_constant;
Key_part_spec *key_part;
LEX *lex;
- LEX_STRING *lex_str_ptr;
+ sp_assignment_lex *assignment_lex;
+ class sp_lex_cursor *sp_cursor_stmt;
+ LEX_CSTRING *lex_str_ptr;
LEX_USER *lex_user;
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_STRING> *lex_str_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_label *splabel;
class sp_name *spname;
class sp_variable *spvar;
class With_clause *with_clause;
@@ -985,6 +843,8 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr)
st_trg_execution_order trg_execution_order;
/* enums */
+ enum enum_view_suid view_suid;
+ enum sub_select_type unit_type;
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;
@@ -993,7 +853,6 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr)
enum Item_udftype udf_type;
enum Key::Keytype key_type;
enum Statement_information_item::Name stmt_info_item_name;
- enum enum_field_types field_type;
enum enum_filetype filetype;
enum enum_tx_isolation tx_isolation;
enum enum_var_type var_type;
@@ -1012,26 +871,30 @@ 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 Column_definition::enum_column_versioning vers_column_versioning;
+ enum plsql_cursor_attr_t plsql_cursor_attr;
}
%{
-bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
+bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%}
%pure-parser /* We have threads */
%parse-param { THD *thd }
%lex-param { THD *thd }
/*
- Currently there are 102 shift/reduce conflicts.
+ Currently there are 52 shift/reduce conflicts.
We should not introduce new conflicts any more.
*/
-%expect 101
+%expect 51
/*
Comments for TOKENS.
For each token, please include in the same line a comment that contains
the following tags:
- SQL-2011-N : Non Reserved keywird as per SQL-2011
+ 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
@@ -1044,727 +907,967 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
This makes the code grep-able, and helps maintenance.
*/
-
+
+
+/*
+ Reserved keywords and operators
+*/
%token ABORT_SYM /* INTERNAL (used in lex) */
%token ACCESSIBLE_SYM
-%token ACTION /* SQL-2003-N */
%token ADD /* SQL-2003-R */
-%token ADMIN_SYM /* SQL-2003-N */
-%token ADDDATE_SYM /* MYSQL-FUNC */
-%token AFTER_SYM /* SQL-2003-N */
-%token AGAINST
-%token AGGREGATE_SYM
-%token ALGORITHM_SYM
%token ALL /* SQL-2003-R */
%token ALTER /* SQL-2003-R */
-%token ALWAYS_SYM
%token ANALYZE_SYM
%token AND_AND_SYM /* OPERATOR */
%token AND_SYM /* SQL-2003-R */
-%token ANY_SYM /* SQL-2003-R */
%token AS /* SQL-2003-R */
%token ASC /* SQL-2003-N */
-%token ASCII_SYM /* MYSQL-FUNC */
%token ASENSITIVE_SYM /* FUTURE-USE */
-%token AT_SYM /* SQL-2003-R */
-%token ATOMIC_SYM /* SQL-2003-R */
-%token AUTHORS_SYM
-%token AUTOEXTEND_SIZE_SYM
-%token AUTO_INC
-%token AUTO_SYM
-%token AVG_ROW_LENGTH
-%token AVG_SYM /* SQL-2003-N */
-%token BACKUP_SYM
%token BEFORE_SYM /* SQL-2003-N */
-%token BEGIN_SYM /* SQL-2003-R */
%token BETWEEN_SYM /* SQL-2003-R */
%token BIGINT /* SQL-2003-R */
%token BINARY /* SQL-2003-R */
-%token BINLOG_SYM
%token BIN_NUM
%token BIT_AND /* MYSQL-FUNC */
%token BIT_OR /* MYSQL-FUNC */
-%token BIT_SYM /* MYSQL-FUNC */
%token BIT_XOR /* MYSQL-FUNC */
-%token BLOB_SYM /* SQL-2003-R */
-%token BLOCK_SYM
-%token BOOLEAN_SYM /* SQL-2003-R */
-%token BOOL_SYM
+%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 BTREE_SYM
%token BY /* SQL-2003-R */
-%token BYTE_SYM
-%token CACHE_SYM
%token CALL_SYM /* SQL-2003-R */
%token CASCADE /* SQL-2003-N */
-%token CASCADED /* SQL-2003-R */
%token CASE_SYM /* SQL-2003-R */
%token CAST_SYM /* SQL-2003-R */
-%token CATALOG_NAME_SYM /* SQL-2003-N */
-%token CHAIN_SYM /* SQL-2003-N */
%token CHANGE
-%token CHANGED
-%token CHARSET
%token CHAR_SYM /* SQL-2003-R */
-%token CHECKPOINT_SYM
-%token CHECKSUM_SYM
%token CHECK_SYM /* SQL-2003-R */
-%token CIPHER_SYM
-%token CLASS_ORIGIN_SYM /* SQL-2003-N */
-%token CLIENT_SYM
-%token CLOSE_SYM /* SQL-2003-R */
-%token COALESCE /* SQL-2003-N */
-%token CODE_SYM
%token COLLATE_SYM /* SQL-2003-R */
-%token COLLATION_SYM /* SQL-2003-N */
-%token COLUMNS
-%token COLUMN_ADD_SYM
-%token COLUMN_CHECK_SYM
-%token COLUMN_CREATE_SYM
-%token COLUMN_DELETE_SYM
-%token COLUMN_GET_SYM
-%token COLUMN_SYM /* SQL-2003-R */
-%token COLUMN_NAME_SYM /* SQL-2003-N */
-%token COMMENT_SYM
-%token COMMITTED_SYM /* SQL-2003-N */
-%token COMMIT_SYM /* SQL-2003-R */
-%token COMPACT_SYM
-%token COMPLETION_SYM
-%token COMPRESSED_SYM
-%token CONCURRENT
%token CONDITION_SYM /* SQL-2003-R, SQL-2008-R */
-%token CONNECTION_SYM
-%token CONSISTENT_SYM
%token CONSTRAINT /* SQL-2003-R */
-%token CONSTRAINT_CATALOG_SYM /* SQL-2003-N */
-%token CONSTRAINT_NAME_SYM /* SQL-2003-N */
-%token CONSTRAINT_SCHEMA_SYM /* SQL-2003-N */
-%token CONTAINS_SYM /* SQL-2003-N */
-%token CONTEXT_SYM
-%token CONTINUE_SYM /* SQL-2003-R */
-%token CONTRIBUTORS_SYM
+%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 CPU_SYM
%token CREATE /* SQL-2003-R */
%token CROSS /* SQL-2003-R */
-%token CUBE_SYM /* SQL-2003-R */
%token CUME_DIST_SYM
%token CURDATE /* MYSQL-FUNC */
-%token CURRENT_SYM /* SQL-2003-R */
%token CURRENT_USER /* SQL-2003-R */
%token CURRENT_ROLE /* SQL-2003-R */
-%token CURRENT_POS_SYM
%token CURSOR_SYM /* SQL-2003-R */
-%token CURSOR_NAME_SYM /* SQL-2003-N */
%token CURTIME /* MYSQL-FUNC */
%token DATABASE
%token DATABASES
-%token DATAFILE_SYM
-%token DATA_SYM /* SQL-2003-N */
-%token DATETIME
%token DATE_ADD_INTERVAL /* MYSQL-FUNC */
%token DATE_SUB_INTERVAL /* MYSQL-FUNC */
-%token DATE_SYM /* SQL-2003-R */
%token DAY_HOUR_SYM
%token DAY_MICROSECOND_SYM
%token DAY_MINUTE_SYM
%token DAY_SECOND_SYM
-%token DAY_SYM /* SQL-2003-R */
-%token DEALLOCATE_SYM /* SQL-2003-R */
%token DECIMAL_NUM
%token DECIMAL_SYM /* SQL-2003-R */
-%token DECLARE_SYM /* SQL-2003-R */
+%token DECLARE_MARIADB_SYM /* SQL-2003-R */
+%token DECLARE_ORACLE_SYM /* Oracle-R */
%token DEFAULT /* SQL-2003-R */
-%token DEFINER_SYM
-%token DELAYED_SYM
-%token DELAY_KEY_WRITE_SYM
%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 DES_KEY_FILE
%token DETERMINISTIC_SYM /* SQL-2003-R */
-%token DIAGNOSTICS_SYM /* SQL-2003-N */
-%token DIRECTORY_SYM
-%token DISABLE_SYM
-%token DISCARD
-%token DISK_SYM
%token DISTINCT /* SQL-2003-R */
%token DIV_SYM
%token DOUBLE_SYM /* SQL-2003-R */
%token DO_DOMAIN_IDS_SYM
-%token DO_SYM
+%token DOT_DOT_SYM
%token DROP /* SQL-2003-R */
%token DUAL_SYM
-%token DUMPFILE
-%token DUPLICATE_SYM
-%token DYNAMIC_SYM /* SQL-2003-R */
%token EACH_SYM /* SQL-2003-R */
%token ELSE /* SQL-2003-R */
-%token ELSEIF_SYM
-%token ENABLE_SYM
+%token ELSEIF_MARIADB_SYM
+%token ELSIF_ORACLE_SYM /* PLSQL-R */
%token ENCLOSED
-%token END /* SQL-2003-R */
-%token ENDS_SYM
%token END_OF_INPUT /* INTERNAL */
-%token ENGINES_SYM
-%token ENGINE_SYM
-%token ENUM
%token EQUAL_SYM /* OPERATOR */
-%token ERROR_SYM
-%token ERRORS
%token ESCAPED
-%token ESCAPE_SYM /* SQL-2003-R */
-%token EVENTS_SYM
-%token EVENT_SYM
-%token EVERY_SYM /* SQL-2003-N */
-%token EXCHANGE_SYM
-%token EXAMINED_SYM
-%token EXCLUDE_SYM /* SQL-2011-N */
-%token EXECUTE_SYM /* SQL-2003-R */
+%token EXCEPT_SYM /* SQL-2003-R */
%token EXISTS /* SQL-2003-R */
-%token EXIT_SYM
-%token EXPANSION_SYM
-%token EXPORT_SYM
-%token EXTENDED_SYM
-%token EXTENT_SIZE_SYM
%token EXTRACT_SYM /* SQL-2003-N */
%token FALSE_SYM /* SQL-2003-R */
-%token FAST_SYM
-%token FAULTS_SYM
%token FETCH_SYM /* SQL-2003-R */
-%token FILE_SYM
%token FIRST_VALUE_SYM /* SQL-2011 */
-%token FIRST_SYM /* SQL-2003-N */
-%token FIXED_SYM
%token FLOAT_NUM
%token FLOAT_SYM /* SQL-2003-R */
-%token FLUSH_SYM
-%token FOLLOWS_SYM /* MYSQL trigger*/
-%token FOLLOWING_SYM /* SQL-2011-N */
-%token FORCE_SYM
%token FOREIGN /* SQL-2003-R */
%token FOR_SYM /* SQL-2003-R */
-%token FORMAT_SYM
-%token FOUND_SYM /* SQL-2003-R */
+%token FOR_SYSTEM_TIME_SYM /* INTERNAL */
%token FROM
-%token FULL /* SQL-2003-R */
%token FULLTEXT_SYM
-%token FUNCTION_SYM /* SQL-2003-R */
%token GE
-%token GENERAL
-%token GENERATED_SYM
-%token GEOMETRYCOLLECTION
-%token GEOMETRY_SYM
-%token GET_FORMAT /* MYSQL-FUNC */
-%token GET_SYM /* SQL-2003-R */
-%token GLOBAL_SYM /* SQL-2003-R */
+%token GOTO_ORACLE_SYM /* Oracle-R */
%token GRANT /* SQL-2003-R */
-%token GRANTS
%token GROUP_SYM /* SQL-2003-R */
%token GROUP_CONCAT_SYM
%token LAG_SYM /* SQL-2011 */
%token LEAD_SYM /* SQL-2011 */
-%token HANDLER_SYM
-%token HARD_SYM
-%token HASH_SYM
%token HAVING /* SQL-2003-R */
-%token HELP_SYM
%token HEX_NUM
%token HEX_STRING
-%token HIGH_PRIORITY
-%token HOST_SYM
-%token HOSTS_SYM
%token HOUR_MICROSECOND_SYM
%token HOUR_MINUTE_SYM
%token HOUR_SECOND_SYM
-%token HOUR_SYM /* SQL-2003-R */
-%token ID_SYM /* MYSQL */
%token IDENT
-%token IDENTIFIED_SYM
%token IDENT_QUOTED
%token IF_SYM
%token IGNORE_DOMAIN_IDS_SYM
%token IGNORE_SYM
-%token IGNORE_SERVER_IDS_SYM
-%token IMMEDIATE_SYM /* SQL-2003-R */
-%token IMPORT
-%token INDEXES
%token INDEX_SYM
%token INFILE
-%token INITIAL_SIZE_SYM
%token INNER_SYM /* SQL-2003-R */
%token INOUT_SYM /* SQL-2003-R */
%token INSENSITIVE_SYM /* SQL-2003-R */
%token INSERT /* SQL-2003-R */
-%token INSERT_METHOD
-%token INSTALL_SYM
+%token INTERSECT_SYM /* SQL-2003-R */
%token INTERVAL_SYM /* SQL-2003-R */
%token INTO /* SQL-2003-R */
%token INT_SYM /* SQL-2003-R */
-%token INVOKER_SYM
%token IN_SYM /* SQL-2003-R */
-%token IO_SYM
-%token IPC_SYM
%token IS /* SQL-2003-R */
-%token ISOLATION /* SQL-2003-R */
-%token ISSUER_SYM
%token ITERATE_SYM
%token JOIN_SYM /* SQL-2003-R */
-%token JSON_SYM
%token KEYS
-%token KEY_BLOCK_SIZE
%token KEY_SYM /* SQL-2003-N */
%token KILL_SYM
-%token LANGUAGE_SYM /* SQL-2003-R */
-%token LAST_SYM /* SQL-2003-N */
-%token LAST_VALUE
%token LE /* OPERATOR */
%token LEADING /* SQL-2003-R */
-%token LEAVES
%token LEAVE_SYM
%token LEFT /* SQL-2003-R */
-%token LESS_SYM
-%token LEVEL_SYM
%token LEX_HOSTNAME
%token LIKE /* SQL-2003-R */
%token LIMIT
%token LINEAR_SYM
%token LINES
-%token LINESTRING
-%token LIST_SYM
%token LOAD
-%token LOCAL_SYM /* SQL-2003-R */
%token LOCATOR_SYM /* SQL-2003-N */
-%token LOCKS_SYM
%token LOCK_SYM
-%token LOGFILE_SYM
-%token LOGS_SYM
%token LONGBLOB
%token LONGTEXT
%token LONG_NUM
%token LONG_SYM
%token LOOP_SYM
%token LOW_PRIORITY
-%token MASTER_CONNECT_RETRY_SYM
-%token MASTER_DELAY_SYM
-%token MASTER_GTID_POS_SYM
-%token MASTER_HOST_SYM
-%token MASTER_LOG_FILE_SYM
-%token MASTER_LOG_POS_SYM
-%token MASTER_PASSWORD_SYM
-%token MASTER_PORT_SYM
-%token MASTER_SERVER_ID_SYM
-%token MASTER_SSL_CAPATH_SYM
-%token MASTER_SSL_CA_SYM
-%token MASTER_SSL_CERT_SYM
-%token MASTER_SSL_CIPHER_SYM
-%token MASTER_SSL_CRL_SYM
-%token MASTER_SSL_CRLPATH_SYM
-%token MASTER_SSL_KEY_SYM
-%token MASTER_SSL_SYM
%token MASTER_SSL_VERIFY_SERVER_CERT_SYM
-%token MASTER_SYM
-%token MASTER_USER_SYM
-%token MASTER_USE_GTID_SYM
-%token MASTER_HEARTBEAT_PERIOD_SYM
%token MATCH /* SQL-2003-R */
-%token MAX_CONNECTIONS_PER_HOUR
-%token MAX_QUERIES_PER_HOUR
-%token MAX_ROWS
-%token MAX_SIZE_SYM
%token MAX_SYM /* SQL-2003-N */
-%token MAX_UPDATES_PER_HOUR
-%token MAX_STATEMENT_TIME_SYM
-%token MAX_USER_CONNECTIONS_SYM
-%token MAX_VALUE_SYM /* SQL-2003-N */
+%token MAXVALUE_SYM /* SQL-2003-N */
+%token MEDIAN_SYM
%token MEDIUMBLOB
%token MEDIUMINT
%token MEDIUMTEXT
-%token MEDIUM_SYM
-%token MEMORY_SYM
-%token MERGE_SYM /* SQL-2003-R */
-%token MESSAGE_TEXT_SYM /* SQL-2003-N */
-%token MICROSECOND_SYM /* MYSQL-FUNC */
-%token MIGRATE_SYM
%token MINUTE_MICROSECOND_SYM
%token MINUTE_SECOND_SYM
-%token MINUTE_SYM /* SQL-2003-R */
-%token MIN_ROWS
%token MIN_SYM /* SQL-2003-N */
-%token MODE_SYM
%token MODIFIES_SYM /* SQL-2003-R */
-%token MODIFY_SYM
%token MOD_SYM /* SQL-2003-N */
-%token MONTH_SYM /* SQL-2003-R */
-%token MULTILINESTRING
-%token MULTIPOINT
-%token MULTIPOLYGON
-%token MUTEX_SYM
-%token MYSQL_SYM
-%token MYSQL_ERRNO_SYM
-%token NAMES_SYM /* SQL-2003-N */
-%token NAME_SYM /* SQL-2003-N */
-%token NATIONAL_SYM /* SQL-2003-R */
+%token MYSQL_CONCAT_SYM /* OPERATOR */
%token NATURAL /* SQL-2003-R */
%token NCHAR_STRING
-%token NCHAR_SYM /* SQL-2003-R */
%token NE /* OPERATOR */
%token NEG
-%token NEW_SYM /* SQL-2003-R */
-%token NEXT_SYM /* SQL-2003-N */
-%token NODEGROUP_SYM
-%token NONE_SYM /* SQL-2003-R */
%token NOT2_SYM
%token NOT_SYM /* SQL-2003-R */
%token NOW_SYM
-%token NO_SYM /* SQL-2003-R */
-%token NO_WAIT_SYM
%token NO_WRITE_TO_BINLOG
%token NTILE_SYM
%token NULL_SYM /* SQL-2003-R */
%token NUM
-%token NUMBER_SYM /* SQL-2003-N */
%token NUMERIC_SYM /* SQL-2003-R */
%token NTH_VALUE_SYM /* SQL-2011 */
-%token NVARCHAR_SYM
-%token OFFSET_SYM
-%token OLD_PASSWORD_SYM
%token ON /* SQL-2003-R */
-%token ONE_SYM
-%token ONLY_SYM /* SQL-2003-R */
-%token ONLINE_SYM
-%token OPEN_SYM /* SQL-2003-R */
%token OPTIMIZE
-%token OPTIONS_SYM
-%token OPTION /* SQL-2003-N */
%token OPTIONALLY
+%token ORACLE_CONCAT_SYM /* INTERNAL */
%token OR2_SYM
%token ORDER_SYM /* SQL-2003-R */
-%token OR_OR_SYM /* OPERATOR */
%token OR_SYM /* SQL-2003-R */
-%token OTHERS_SYM /* SQL-2011-N */
+%token OTHERS_ORACLE_SYM /* SQL-2011-N, PLSQL-R */
%token OUTER
%token OUTFILE
%token OUT_SYM /* SQL-2003-R */
%token OVER_SYM
-%token OWNER_SYM
-%token PACK_KEYS_SYM
-%token PAGE_SYM
+%token PACKAGE_ORACLE_SYM /* Oracle-R */
%token PAGE_CHECKSUM_SYM
%token PARAM_MARKER
-%token PARSER_SYM
%token PARSE_VCOL_EXPR_SYM
-%token PARTIAL /* SQL-2003-N */
%token PARTITION_SYM /* SQL-2003-R */
-%token PARTITIONS_SYM
-%token PARTITIONING_SYM
-%token PASSWORD_SYM
+%token PERCENT_ORACLE_SYM /* INTERNAL */
%token PERCENT_RANK_SYM
-%token PERSISTENT_SYM
-%token PHASE_SYM
-%token PLUGINS_SYM
-%token PLUGIN_SYM
-%token POINT_SYM
-%token POLYGON
-%token PORT_SYM
+%token PERCENTILE_CONT_SYM
+%token PERCENTILE_DISC_SYM
%token POSITION_SYM /* SQL-2003-N */
-%token PRECEDES_SYM /* MYSQL */
-%token PRECEDING_SYM /* SQL-2011-N */
%token PRECISION /* SQL-2003-R */
-%token PREPARE_SYM /* SQL-2003-R */
-%token PRESERVE_SYM
-%token PREV_SYM
%token PRIMARY_SYM /* SQL-2003-R */
-%token PRIVILEGES /* SQL-2003-N */
%token PROCEDURE_SYM /* SQL-2003-R */
-%token PROCESS
-%token PROCESSLIST_SYM
-%token PROFILE_SYM
-%token PROFILES_SYM
-%token PROXY_SYM
%token PURGE
-%token QUARTER_SYM
-%token QUERY_SYM
-%token QUICK
+%token RAISE_ORACLE_SYM /* PLSQL-R */
%token RANGE_SYM /* SQL-2003-R */
%token RANK_SYM
%token READS_SYM /* SQL-2003-R */
-%token READ_ONLY_SYM
%token READ_SYM /* SQL-2003-N */
%token READ_WRITE_SYM
%token REAL /* SQL-2003-R */
-%token REBUILD_SYM
-%token RECOVER_SYM
%token RECURSIVE_SYM
-%token REDOFILE_SYM
-%token REDO_BUFFER_SIZE_SYM
-%token REDUNDANT_SYM
+%token REF_SYSTEM_ID_SYM
%token REFERENCES /* SQL-2003-R */
%token REGEXP
-%token RELAY
-%token RELAYLOG_SYM
-%token RELAY_LOG_FILE_SYM
-%token RELAY_LOG_POS_SYM
-%token RELAY_THREAD
%token RELEASE_SYM /* SQL-2003-R */
-%token RELOAD
-%token REMOVE_SYM
%token RENAME
-%token REORGANIZE_SYM
-%token REPAIR
-%token REPEATABLE_SYM /* SQL-2003-N */
%token REPEAT_SYM /* MYSQL-FUNC */
%token REPLACE /* MYSQL-FUNC */
-%token REPLICATION
%token REQUIRE_SYM
-%token RESET_SYM
%token RESIGNAL_SYM /* SQL-2003-R */
-%token RESOURCES
-%token RESTORE_SYM
%token RESTRICT
-%token RESUME_SYM
-%token RETURNED_SQLSTATE_SYM /* SQL-2003-N */
%token RETURNING_SYM
-%token RETURNS_SYM /* SQL-2003-R */
-%token RETURN_SYM /* SQL-2003-R */
-%token REVERSE_SYM
+%token 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 ROLE_SYM
-%token ROLLBACK_SYM /* SQL-2003-R */
-%token ROLLUP_SYM /* SQL-2003-R */
-%token ROUTINE_SYM /* SQL-2003-N */
-%token ROW_SYM /* SQL-2003-R */
%token ROWS_SYM /* SQL-2003-R */
-%token ROW_COUNT_SYM /* SQL-2003-N */
-%token ROW_FORMAT_SYM
+%token ROWTYPE_ORACLE_SYM /* PLSQL-R */
%token ROW_NUMBER_SYM
-%token RTREE_SYM
-%token SAVEPOINT_SYM /* SQL-2003-R */
-%token SCHEDULE_SYM
-%token SCHEMA_NAME_SYM /* SQL-2003-N */
%token SECOND_MICROSECOND_SYM
-%token SECOND_SYM /* SQL-2003-R */
-%token SECURITY_SYM /* SQL-2003-N */
%token SELECT_SYM /* SQL-2003-R */
%token SENSITIVE_SYM /* FUTURE-USE */
%token SEPARATOR_SYM
-%token SERIALIZABLE_SYM /* SQL-2003-N */
-%token SERIAL_SYM
-%token SESSION_SYM /* SQL-2003-N */
-%token SERVER_SYM
%token SERVER_OPTIONS
%token SET /* SQL-2003-R */
%token SET_VAR
-%token SHARE_SYM
%token SHIFT_LEFT /* OPERATOR */
%token SHIFT_RIGHT /* OPERATOR */
%token SHOW
-%token SHUTDOWN
%token SIGNAL_SYM /* SQL-2003-R */
-%token SIGNED_SYM
-%token SIMPLE_SYM /* SQL-2003-N */
-%token SLAVE
-%token SLAVES
-%token SLAVE_POS_SYM
-%token SLOW
%token SMALLINT /* SQL-2003-R */
-%token SNAPSHOT_SYM
-%token SOCKET_SYM
-%token SOFT_SYM
-%token SONAME_SYM
-%token SOUNDS_SYM
-%token SOURCE_SYM
%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_BUFFER_RESULT
-%token SQL_CACHE_SYM
-%token SQL_CALC_FOUND_ROWS
-%token SQL_NO_CACHE_SYM
%token SQL_SMALL_RESULT
%token SQL_SYM /* SQL-2003-R */
-%token SQL_THREAD
-%token REF_SYSTEM_ID_SYM
%token SSL_SYM
%token STARTING
-%token STARTS_SYM
-%token START_SYM /* SQL-2003-R */
-%token STATEMENT_SYM
%token STATS_AUTO_RECALC_SYM
%token STATS_PERSISTENT_SYM
%token STATS_SAMPLE_PAGES_SYM
-%token STATUS_SYM
%token STDDEV_SAMP_SYM /* SQL-2003-N */
%token STD_SYM
-%token STOP_SYM
-%token STORAGE_SYM
-%token STORED_SYM
%token STRAIGHT_JOIN
-%token STRING_SYM
-%token SUBCLASS_ORIGIN_SYM /* SQL-2003-N */
-%token SUBDATE_SYM
-%token SUBJECT_SYM
-%token SUBPARTITIONS_SYM
-%token SUBPARTITION_SYM
%token SUBSTRING /* SQL-2003-N */
%token SUM_SYM /* SQL-2003-N */
-%token SUPER_SYM
-%token SUSPEND_SYM
-%token SWAPS_SYM
-%token SWITCHES_SYM
%token SYSDATE
-%token TABLES
-%token TABLESPACE
%token TABLE_REF_PRIORITY
%token TABLE_SYM /* SQL-2003-R */
-%token TABLE_CHECKSUM_SYM
-%token TABLE_NAME_SYM /* SQL-2003-N */
-%token TEMPORARY /* SQL-2003-N */
-%token TEMPTABLE_SYM
%token TERMINATED
%token TEXT_STRING
-%token TEXT_SYM
-%token THAN_SYM
%token THEN_SYM /* SQL-2003-R */
-%token TIES_SYM /* SQL-2011-N */
-%token TIMESTAMP /* SQL-2003-R */
-%token TIMESTAMP_ADD
-%token TIMESTAMP_DIFF
-%token TIME_SYM /* SQL-2003-R */
%token TINYBLOB
%token TINYINT
%token TINYTEXT
%token TO_SYM /* SQL-2003-R */
%token TRAILING /* SQL-2003-R */
-%token TRANSACTION_SYM
-%token TRANSACTIONAL_SYM
-%token TRIGGERS_SYM
%token TRIGGER_SYM /* SQL-2003-R */
%token TRIM /* SQL-2003-N */
%token TRUE_SYM /* SQL-2003-R */
-%token TRUNCATE_SYM
-%token TYPES_SYM
-%token TYPE_SYM /* SQL-2003-N */
-%token UDF_RETURNS_SYM
%token ULONGLONG_NUM
-%token UNBOUNDED_SYM /* SQL-2011-N */
-%token UNCOMMITTED_SYM /* SQL-2003-N */
-%token UNDEFINED_SYM
%token UNDERSCORE_CHARSET
-%token UNDOFILE_SYM
-%token UNDO_BUFFER_SIZE_SYM
%token UNDO_SYM /* FUTURE-USE */
-%token UNICODE_SYM
-%token UNINSTALL_SYM
%token UNION_SYM /* SQL-2003-R */
%token UNIQUE_SYM
-%token UNKNOWN_SYM /* SQL-2003-R */
%token UNLOCK_SYM
%token UNSIGNED
-%token UNTIL_SYM
%token UPDATE_SYM /* SQL-2003-R */
-%token UPGRADE_SYM
%token USAGE /* SQL-2003-N */
-%token USER_SYM /* SQL-2003-R */
-%token USE_FRM
%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 VALUE_SYM /* SQL-2003-R */
+%token VALUES_IN_SYM
+%token VALUES_LESS_SYM
%token VARBINARY
%token VARCHAR /* SQL-2003-R */
-%token VARIABLES
%token VARIANCE_SYM
%token VARYING /* SQL-2003-R */
%token VAR_SAMP_SYM
-%token VIA_SYM
-%token VIEW_SYM /* SQL-2003-N */
-%token VIRTUAL_SYM
-%token WAIT_SYM
-%token WARNINGS
-%token WEEK_SYM
-%token WEIGHT_STRING_SYM
%token WHEN_SYM /* SQL-2003-R */
%token WHERE /* SQL-2003-R */
-%token WINDOW_SYM
%token WHILE_SYM
%token WITH /* SQL-2003-R */
%token WITH_CUBE_SYM /* INTERNAL */
%token WITH_ROLLUP_SYM /* INTERNAL */
-%token WORK_SYM /* SQL-2003-N */
-%token WRAPPER_SYM
-%token WRITE_SYM /* SQL-2003-N */
-%token X509_SYM
-%token XA_SYM
-%token XML_SYM
+%token WITH_SYSTEM_SYM /* INTERNAL */
%token XOR
%token YEAR_MONTH_SYM
-%token YEAR_SYM /* SQL-2003-R */
%token ZEROFILL
-%token IMPOSSIBLE_ACTION /* To avoid warning for yyerrlab1 */
+%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> 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> 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> 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> 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 */
+
+
+/*
+ Give ESCAPE (in LIKE) a very low precedence.
+ This allows the concatenation operator || to be used on the right
+ side of "LIKE" with sql_mode=PIPES_AS_CONCAT (without ORACLE):
+ SELECT 'ab' LIKE 'a'||'b'||'c';
+*/
+%left PREC_BELOW_ESCAPE
+%left ESCAPE_SYM
/* 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_OR_SYM OR_SYM OR2_SYM
+%left OR_SYM OR2_SYM
%left XOR
%left AND_SYM AND_AND_SYM
+
+%left PREC_BELOW_NOT
+%left NOT_SYM
+
%left BETWEEN_SYM CASE_SYM WHEN_SYM THEN_SYM ELSE
-%left '=' EQUAL_SYM GE '>' LE '<' NE IS LIKE REGEXP IN_SYM
+%left '=' EQUAL_SYM GE '>' LE '<' NE IS LIKE SOUNDS_SYM REGEXP IN_SYM
%left '|'
%left '&'
%left SHIFT_LEFT SHIFT_RIGHT
-%left '-' '+'
+%left '-' '+' ORACLE_CONCAT_SYM
%left '*' '/' '%' DIV_SYM MOD_SYM
%left '^'
-%left NEG '~'
-%right NOT_SYM NOT2_SYM
-%right BINARY COLLATE_SYM
-%left INTERVAL_SYM
+%left MYSQL_CONCAT_SYM
+%left NEG '~' NOT2_SYM BINARY
+%left COLLATE_SYM
+
+/*
+ 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
%type <lex_str>
- IDENT IDENT_QUOTED TEXT_STRING DECIMAL_NUM FLOAT_NUM NUM LONG_NUM
+ DECIMAL_NUM FLOAT_NUM NUM LONG_NUM
HEX_NUM HEX_STRING
- LEX_HOSTNAME ULONGLONG_NUM field_ident select_alias ident ident_or_text
- IDENT_sys TEXT_STRING_sys TEXT_STRING_literal
- NCHAR_STRING opt_component key_cache_name
- sp_opt_label BIN_NUM label_ident TEXT_STRING_filesystem ident_or_empty
- opt_constraint constraint opt_ident ident_table_alias
+ 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
+ sp_block_label opt_place opt_db
+
+%type <lex_str>
+ sp_label
+
+%type <ident_sys>
+ IDENT_sys
+ ident
+ label_ident
+ sp_decl_ident
+ ident_set_usual_case
+ ident_or_empty
+ ident_table_alias
+ ident_sysvar_name
+
+%type <lex_string_with_metadata>
+ TEXT_STRING
+ NCHAR_STRING
%type <lex_str_ptr>
opt_table_alias
+%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
+
%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 opt_db
+ remember_name remember_end
remember_tok_start remember_tok_end
wild_and_where
- field_length opt_field_length opt_field_length_default_1
%type <const_simple_string>
- opt_place
+ 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 <field_type> int_type real_type
+%type <type_handler> int_type real_type
%type <Lex_field_type> type_with_opt_collate field_type
+ qualified_field_type
+ 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
@@ -1776,7 +1879,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <num>
order_dir lock_option
udf_type opt_local opt_no_write_to_binlog
- opt_temporary all_or_any opt_distinct
+ opt_temporary all_or_any opt_distinct opt_glimit_clause
opt_ignore_leaves fulltext_options union_option
opt_not
select_derived_init transaction_access_mode_types
@@ -1786,12 +1889,12 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
optional_flush_tables_arguments
opt_time_precision kill_type kill_option int_num
opt_default_time_precision
- case_stmt_body opt_bin_mod
+ 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_recursive opt_format_xid
%type <object_ddl_options>
- create_or_replace
+ create_or_replace
opt_if_not_exists
opt_if_exists
@@ -1810,51 +1913,78 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <ulong_num>
ulong_num real_ulong_num merge_insert_types
- ws_nweights
+ 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
+ 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 text_literal insert_ident order_ident temporal_literal
- simple_ident expr opt_expr opt_else sum_expr in_sum_expr
+ 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
opt_escape
sp_opt_default
- simple_ident_nospvar simple_ident_q
+ 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
+ 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
+
+%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
@@ -1877,7 +2007,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
table_primary_ident table_primary_derived
select_derived derived_table_list
select_derived_union
+ derived_simple_table
derived_query_specification
+ derived_table_value_constructor
%type <date_time_type> date_time_type;
%type <interval> interval
@@ -1895,8 +2027,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <Lex_length_and_dec> precision opt_precision float_options
-%type <symbol> keyword keyword_sp keyword_alias
-
%type <lex_user> user grant_user grant_role user_or_role current_role
admin_option_for_role user_maybe_role
@@ -1912,15 +2042,15 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
opt_load_data_charset
UNDERSCORE_CHARSET
-%type <variable> internal_variable_name
-
%type <select_lex> subselect
get_select_lex get_select_lex_derived
+ simple_table
query_specification
query_term_union_not_ready
query_term_union_ready
query_expression_body
select_paren_derived
+ table_value_constructor
%type <boolfunc2creator> comp_op
@@ -1932,13 +2062,14 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <virtual_column> opt_check_constraint check_constraint virtual_column_func
column_default_expr
+%type <unit_type> unit_type_decl
%type <NONE>
analyze_stmt_command
query verb_clause create change select do drop insert replace insert2
insert_values update delete truncate rename compound_statement
show describe load alter optimize keycache preload flush
- reset purge begin commit rollback savepoint release
+ 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
@@ -1952,6 +2083,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
preload_list preload_list_or_parts preload_keys preload_keys_parts
select_item_list select_item values_list no_braces
opt_limit_clause 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
@@ -1960,7 +2092,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
ref_list opt_match_clause opt_on_update_delete use
opt_delete_options opt_delete_option varchar nchar nvarchar
opt_outer table_list table_name table_alias_ref_list table_alias_ref
- opt_attribute opt_attribute_list attribute column_list column_list_id
+ 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
@@ -1980,11 +2115,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
statement sp_suid
sp_c_chistics sp_a_chistics sp_chistic sp_c_chistic xa
opt_field_or_var_spec fields_or_vars opt_load_data_set_spec
- view_algorithm view_or_trigger_or_sp_or_event
- definer_tail no_definer_tail
- view_suid view_tail view_list_opt view_list view_select
- view_check_option trigger_tail sp_tail sf_tail event_tail
- udf_tail udf_tail2
+ view_list_opt view_list view_select
+ trigger_tail sp_tail sf_tail event_tail
+ udf_tail create_function_tail create_aggregate_function_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
@@ -1992,13 +2125,17 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
keep_gcc_happy
key_using_alg
part_column_list
+ period_for_system_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
+ opt_constraint_no_id
END_OF_INPUT
%type <NONE> call sp_proc_stmts sp_proc_stmts1 sp_proc_stmt
@@ -2008,25 +2145,37 @@ END_OF_INPUT
%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_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> sp_decl_idents sp_handler_type sp_hcond_list
+%type <num> view_algorithm view_check_option
+%type <view_suid> view_suid opt_view_suid
+
+%type <plsql_cursor_attr> plsql_cursor_attr
+
+%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_decls sp_decl
-%type <lex> sp_cursor_stmt
+%type <spblock> sp_decl_handler
+%type <spblock> sp_decls sp_decl sp_decl_body sp_decl_variable_list
%type <spname> sp_name
-%type <splabel> sp_block_content
-%type <spvar> sp_param_name_and_type
+%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
+%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;
@@ -2041,6 +2190,9 @@ END_OF_INPUT
%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
@@ -2049,12 +2201,11 @@ END_OF_INPUT
%type <frame_exclusion> opt_window_frame_exclusion;
%type <window_frame_bound> window_frame_start window_frame_bound;
-
%type <NONE>
'-' '+' '*' '/' '%' '(' ')'
- ',' '!' '{' '}' '&' '|' AND_SYM OR_SYM OR_OR_SYM BETWEEN_SYM CASE_SYM
+ ',' '!' '{' '}' '&' '|' AND_SYM OR_SYM BETWEEN_SYM CASE_SYM
THEN_SYM WHEN_SYM DIV_SYM MOD_SYM OR2_SYM AND_AND_SYM DELETE_SYM
- ROLE_SYM
+ MYSQL_CONCAT_SYM ORACLE_CONCAT_SYM
%type <with_clause> opt_with_clause with_clause
@@ -2062,6 +2213,9 @@ END_OF_INPUT
%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
%%
@@ -2089,8 +2243,8 @@ rule: <-- starts at col 1
query:
END_OF_INPUT
{
- if (!thd->bootstrap &&
- (!(thd->lex->select_lex.options & OPTION_FOUND_COMMENT)))
+ if (likely(!thd->bootstrap) &&
+ unlikely(!(thd->lex->select_lex.options & OPTION_FOUND_COMMENT)))
my_yyabort_error((ER_EMPTY_QUERY, MYF(0)));
thd->lex->sql_command= SQLCOM_EMPTY_QUERY;
@@ -2135,7 +2289,7 @@ opt_end_of_input:
verb_clause:
statement
- | begin
+ | begin_stmt_mariadb
| compound_statement
;
@@ -2175,6 +2329,7 @@ statement:
| preload
| prepare
| purge
+ | raise_stmt_oracle
| release
| rename
| repair
@@ -2217,7 +2372,7 @@ prepare:
PREPARE_SYM ident FROM prepare_src
{
LEX *lex= thd->lex;
- if (lex->table_or_sp_used())
+ if (unlikely(lex->table_or_sp_used()))
my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0),
"PREPARE..FROM"));
lex->sql_command= SQLCOM_PREPARE;
@@ -2245,7 +2400,7 @@ execute:
{}
| EXECUTE_SYM IMMEDIATE_SYM prepare_src
{
- if (Lex->table_or_sp_used())
+ if (unlikely(Lex->table_or_sp_used()))
my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0),
"EXECUTE IMMEDIATE"));
Lex->sql_command= SQLCOM_EXECUTE_IMMEDIATE;
@@ -2259,7 +2414,7 @@ execute_using:
| USING { Lex->expr_allows_subselect= false; }
execute_var_list
{
- if (Lex->table_or_sp_used())
+ if (unlikely(Lex->table_or_sp_used()))
my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0),
"EXECUTE..USING"));
Lex->expr_allows_subselect= true;
@@ -2274,7 +2429,8 @@ execute_var_list:
execute_var_ident:
expr_or_default
{
- if (Lex->prepared_stmt_params.push_back($1, thd->mem_root))
+ if (unlikely(Lex->prepared_stmt_params.push_back($1,
+ thd->mem_root)))
MYSQL_YYABORT;
}
;
@@ -2284,7 +2440,7 @@ execute_var_ident:
help:
HELP_SYM
{
- if (Lex->sphead)
+ if (unlikely(Lex->sphead))
my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "HELP"));
}
ident_or_text
@@ -2337,7 +2493,7 @@ master_def:
if ($3 > MASTER_DELAY_MAX)
{
my_error(ER_MASTER_DELAY_VALUE_OUT_OF_RANGE, MYF(0),
- (uint) $3, (uint) MASTER_DELAY_MAX);
+ (ulong) $3, (ulong) MASTER_DELAY_MAX);
}
else
Lex->mi.sql_delay = $3;
@@ -2384,20 +2540,21 @@ master_def:
| MASTER_HEARTBEAT_PERIOD_SYM '=' NUM_literal
{
Lex->mi.heartbeat_period= (float) $3->val_real();
- if (Lex->mi.heartbeat_period > SLAVE_MAX_HEARTBEAT_PERIOD ||
- Lex->mi.heartbeat_period < 0.0)
+ 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 (Lex->mi.heartbeat_period > slave_net_timeout)
+ 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 (Lex->mi.heartbeat_period < 0.001)
+ if (unlikely(Lex->mi.heartbeat_period < 0.001))
{
- if (Lex->mi.heartbeat_period != 0.0)
+ 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,
@@ -2495,19 +2652,19 @@ master_file_def:
}
| MASTER_USE_GTID_SYM '=' CURRENT_POS_SYM
{
- if (Lex->mi.use_gtid_opt != LEX_MASTER_INFO::LEX_GTID_UNCHANGED)
+ 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 (Lex->mi.use_gtid_opt != LEX_MASTER_INFO::LEX_GTID_UNCHANGED)
+ 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 (Lex->mi.use_gtid_opt != LEX_MASTER_INFO::LEX_GTID_UNCHANGED)
+ 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;
}
@@ -2517,7 +2674,7 @@ optional_connection_name:
/* empty */
{
LEX *lex= thd->lex;
- lex->mi.connection_name= null_lex_str;
+ lex->mi.connection_name= null_clex_str;
}
| connection_name
;
@@ -2527,7 +2684,7 @@ connection_name:
{
Lex->mi.connection_name= $1;
#ifdef HAVE_REPLICATION
- if (check_master_connection_name(&$1))
+ if (unlikely(check_master_connection_name(&$1)))
my_yyabort_error((ER_WRONG_ARGUMENTS, MYF(0), "MASTER_CONNECTION_NAME"));
#endif
}
@@ -2542,11 +2699,13 @@ create:
if (!(lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_create_table()))
MYSQL_YYABORT;
lex->create_info.init();
- if (lex->set_command_with_check(SQLCOM_CREATE_TABLE, $2, $1 | $4))
+ if (unlikely(lex->set_command_with_check(SQLCOM_CREATE_TABLE, $2,
+ $1 | $4)))
MYSQL_YYABORT;
- if (!lex->select_lex.add_table_to_list(thd, $5, NULL,
- TL_OPTION_UPDATING,
- TL_WRITE, MDL_EXCLUSIVE))
+ if (unlikely(!lex->select_lex.add_table_to_list(thd, $5, NULL,
+ TL_OPTION_UPDATING,
+ TL_WRITE,
+ MDL_EXCLUSIVE)))
MYSQL_YYABORT;
lex->alter_info.reset();
/*
@@ -2555,7 +2714,7 @@ create:
*/
lex->query_tables->open_strategy= TABLE_LIST::OPEN_STUB;
lex->create_info.default_table_charset= NULL;
- lex->name= null_lex_str;
+ lex->name= null_clex_str;
lex->create_last_non_select_table= lex->last_table();
}
create_body
@@ -2564,36 +2723,91 @@ create:
lex->current_select= &lex->select_lex;
create_table_set_open_action_and_adjust_tables(lex);
}
+ | create_or_replace opt_temporary SEQUENCE_SYM opt_if_not_exists table_ident
+ {
+ LEX *lex= thd->lex;
+ 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 (unlikely(!lex->select_lex.add_table_to_list(thd, $5, NULL,
+ TL_OPTION_UPDATING,
+ TL_WRITE,
+ MDL_EXCLUSIVE)))
+ MYSQL_YYABORT;
+
+ /*
+ For CREATE TABLE, an non-existing table is not an error.
+ Instruct open_tables() to just take an MDL lock if the
+ table does not exist.
+ */
+ lex->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->select_lex.table_list.first->db.str,
+ lex->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;
+
+ lex->current_select= &lex->select_lex;
+ create_table_set_open_action_and_adjust_tables(lex);
+ }
| create_or_replace opt_unique INDEX_SYM opt_if_not_exists ident
opt_key_algorithm_clause
ON table_ident
{
- if (add_create_index_prepare(Lex, $8))
+ if (unlikely(Lex->add_create_index_prepare($8)))
MYSQL_YYABORT;
- if (Lex->add_create_index($2, $5, $6, $1 | $4))
+ if (unlikely(Lex->add_create_index($2, &$5, $6, $1 | $4)))
MYSQL_YYABORT;
}
- '(' key_list ')' normal_key_options
+ '(' key_list ')' opt_lock_wait_timeout normal_key_options
opt_index_lock_algorithm { }
| create_or_replace fulltext INDEX_SYM opt_if_not_exists ident
ON table_ident
{
- if (add_create_index_prepare(Lex, $7))
+ if (unlikely(Lex->add_create_index_prepare($7)))
MYSQL_YYABORT;
- if (Lex->add_create_index($2, $5, HA_KEY_ALG_UNDEF, $1 | $4))
+ if (unlikely(Lex->add_create_index($2, &$5, HA_KEY_ALG_UNDEF,
+ $1 | $4)))
MYSQL_YYABORT;
}
- '(' key_list ')' fulltext_key_options
+ '(' key_list ')' opt_lock_wait_timeout fulltext_key_options
opt_index_lock_algorithm { }
| create_or_replace spatial INDEX_SYM opt_if_not_exists ident
ON table_ident
{
- if (add_create_index_prepare(Lex, $7))
+ if (unlikely(Lex->add_create_index_prepare($7)))
MYSQL_YYABORT;
- if (Lex->add_create_index($2, $5, HA_KEY_ALG_UNDEF, $1 | $4))
+ if (unlikely(Lex->add_create_index($2, &$5, HA_KEY_ALG_UNDEF,
+ $1 | $4)))
MYSQL_YYABORT;
}
- '(' key_list ')' spatial_key_options
+ '(' key_list ')' opt_lock_wait_timeout spatial_key_options
opt_index_lock_algorithm { }
| create_or_replace DATABASE opt_if_not_exists ident
{
@@ -2603,29 +2817,75 @@ create:
opt_create_database_options
{
LEX *lex=Lex;
- if (lex->set_command_with_check(SQLCOM_CREATE_DB, 0, $1 | $3))
+ if (unlikely(lex->set_command_with_check(SQLCOM_CREATE_DB, 0,
+ $1 | $3)))
MYSQL_YYABORT;
lex->name= $4;
}
- | create_or_replace
+ | create_or_replace definer_opt opt_view_suid VIEW_SYM
+ opt_if_not_exists table_ident
+ {
+ if (unlikely(Lex->add_create_view(thd, $1 | $5,
+ DTYPE_ALGORITHM_UNDEFINED, $3,
+ $6)))
+ MYSQL_YYABORT;
+ }
+ view_list_opt AS view_select
+ { }
+ | create_or_replace view_algorithm definer_opt opt_view_suid VIEW_SYM
+ opt_if_not_exists table_ident
+ {
+ if (unlikely(Lex->add_create_view(thd, $1 | $6, $2, $4, $7)))
+ MYSQL_YYABORT;
+ }
+ view_list_opt AS view_select
+ { }
+ | create_or_replace definer_opt TRIGGER_SYM
+ { Lex->create_info.set($1); }
+ trigger_tail
+ { }
+ | create_or_replace definer_opt PROCEDURE_SYM
+ { Lex->create_info.set($1); }
+ sp_tail
+ { }
+ | create_or_replace definer_opt EVENT_SYM
+ { Lex->create_info.set($1); }
+ event_tail
+ { }
+ | create_or_replace definer FUNCTION_SYM
+ {
+ Lex->create_info.set($1);
+ }
+ sf_tail_not_aggregate
+ { }
+ | create_or_replace definer AGGREGATE_SYM FUNCTION_SYM
{
Lex->create_info.set($1);
- Lex->create_view_mode= ($1.or_replace() ? VIEW_CREATE_OR_REPLACE :
- VIEW_CREATE_NEW);
- Lex->create_view_algorithm= DTYPE_ALGORITHM_UNDEFINED;
- Lex->create_view_suid= TRUE;
}
- view_or_trigger_or_sp_or_event { }
+ sf_tail_aggregate
+ { }
+ | create_or_replace no_definer FUNCTION_SYM
+ { Lex->create_info.set($1); }
+ create_function_tail
+ { }
+ | create_or_replace no_definer AGGREGATE_SYM FUNCTION_SYM
+ {
+ Lex->create_info.set($1);
+ }
+ create_aggregate_function_tail
+ { }
| create_or_replace USER_SYM opt_if_not_exists clear_privileges grant_list
opt_require_clause opt_resource_options
{
- if (Lex->set_command_with_check(SQLCOM_CREATE_USER, $1 | $3))
+ 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 (Lex->set_command_with_check(SQLCOM_CREATE_ROLE, $1 | $3))
+ if (unlikely(Lex->set_command_with_check(SQLCOM_CREATE_ROLE,
+ $1 | $3)))
MYSQL_YYABORT;
}
| CREATE LOGFILE_SYM GROUP_SYM logfile_group_info
@@ -2641,10 +2901,165 @@ create:
{ }
;
+sf_tail_not_aggregate:
+ sf_tail
+ {
+ if (unlikely(Lex->sphead->m_flags & sp_head::HAS_AGGREGATE_INSTR))
+ {
+ my_yyabort_error((ER_NOT_AGGREGATE_FUNCTION, MYF(0)));
+ }
+ Lex->sphead->set_chistics_agg_type(NOT_AGGREGATE);
+ }
+ ;
+
+sf_tail_aggregate:
+ sf_tail
+ {
+ if (unlikely(!(Lex->sphead->m_flags & sp_head::HAS_AGGREGATE_INSTR)))
+ {
+ my_yyabort_error((ER_INVALID_AGGREGATE_FUNCTION, MYF(0)));
+ }
+ Lex->sphead->set_chistics_agg_type(GROUP_AGGREGATE);
+ }
+ ;
+
+create_function_tail:
+ sf_tail_not_aggregate { }
+ | udf_tail { Lex->udf.type= UDFTYPE_FUNCTION; }
+ ;
+
+create_aggregate_function_tail:
+ sf_tail_aggregate
+ { }
+ | udf_tail { Lex->udf.type= UDFTYPE_AGGREGATE; }
+ ;
+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 (Lex->add_create_options_with_check($2))
+ if (unlikely(Lex->add_create_options_with_check($2)))
MYSQL_YYABORT;
Lex->server_options.reset($3);
}
@@ -2696,16 +3111,17 @@ server_option:
;
event_tail:
- remember_name EVENT_SYM opt_if_not_exists sp_name
+ remember_name opt_if_not_exists sp_name
{
LEX *lex=Lex;
lex->stmt_definition_begin= $1;
- if (lex->add_create_options_with_check($3))
+ if (unlikely(lex->add_create_options_with_check($2)))
MYSQL_YYABORT;
- if (!(lex->event_parse_data= Event_parse_data::new_instance(thd)))
+ if (unlikely(!(lex->event_parse_data=
+ Event_parse_data::new_instance(thd))))
MYSQL_YYABORT;
- lex->event_parse_data->identifier= $4;
+ lex->event_parse_data->identifier= $3;
lex->event_parse_data->on_completion=
Event_parse_data::ON_COMPLETION_DROP;
@@ -2766,7 +3182,7 @@ ev_starts:
/* empty */
{
Item *item= new (thd->mem_root) Item_func_now_local(thd, 0);
- if (item == NULL)
+ if (unlikely(item == NULL))
MYSQL_YYABORT;
Lex->event_parse_data->item_starts= item;
}
@@ -2830,13 +3246,14 @@ ev_sql_stmt:
(the nested ALTER EVENT can have anything but DO clause)
- CREATE PROCEDURE ... BEGIN DROP EVENT ... END|
*/
- if (lex->sphead)
+ if (unlikely(lex->sphead))
my_yyabort_error((ER_EVENT_RECURSION_FORBIDDEN, MYF(0)));
- if (!make_sp_head(thd, lex->event_parse_data->identifier, TYPE_ENUM_PROCEDURE))
+ if (unlikely(!lex->make_sp_head(thd,
+ lex->event_parse_data->identifier,
+ &sp_handler_procedure)))
MYSQL_YYABORT;
- lex->sp_chistics.suid= SP_IS_SUID; //always the definer!
lex->sphead->set_body_start(thd, lip->get_cpp_ptr());
}
sp_proc_stmt
@@ -2859,7 +3276,7 @@ clear_privileges:
lex->columns.empty();
lex->grant= lex->grant_tot_col= 0;
lex->all_privileges= 0;
- lex->select_lex.db= 0;
+ lex->select_lex.db= null_clex_str;
lex->ssl_type= SSL_TYPE_NOT_SPECIFIED;
lex->ssl_cipher= lex->x509_subject= lex->x509_issuer= 0;
bzero((char *)&(lex->mqh),sizeof(lex->mqh));
@@ -2869,29 +3286,13 @@ clear_privileges:
sp_name:
ident '.' ident
{
- if (!$1.str || check_db_name(&$1))
- my_yyabort_error((ER_WRONG_DB_NAME, MYF(0), $1.str));
- if (check_routine_name(&$3))
+ if (unlikely(!($$= Lex->make_sp_name(thd, &$1, &$3))))
MYSQL_YYABORT;
- $$= new (thd->mem_root) sp_name($1, $3, true);
- if ($$ == NULL)
- MYSQL_YYABORT;
- $$->init_qname(thd);
}
| ident
{
- LEX *lex= thd->lex;
- LEX_STRING db;
- if (check_routine_name(&$1))
- {
- MYSQL_YYABORT;
- }
- if (lex->copy_db_to(&db.str, &db.length))
- MYSQL_YYABORT;
- $$= new (thd->mem_root) sp_name(db, $1, false);
- if ($$ == NULL)
+ if (unlikely(!($$= Lex->make_sp_name(thd, &$1))))
MYSQL_YYABORT;
- $$->init_qname(thd);
}
;
@@ -2943,12 +3344,8 @@ sp_suid:
call:
CALL_SYM sp_name
{
- LEX *lex = Lex;
-
- lex->sql_command= SQLCOM_CALL;
- lex->spname= $2;
- lex->value_list.empty();
- sp_add_used_routine(lex, thd, $2, TYPE_ENUM_PROCEDURE);
+ if (unlikely(Lex->call_statement_start(thd, $2)))
+ MYSQL_YYABORT;
}
opt_sp_cparam_list {}
;
@@ -2986,34 +3383,47 @@ sp_fdparams:
| sp_param_name_and_type
;
-sp_param_name_and_type:
+sp_param_name:
ident
{
- LEX *lex= Lex;
- sp_pcontext *spc= lex->spcont;
-
- if (spc->find_variable($1, TRUE))
- my_yyabort_error((ER_SP_DUP_PARAM, MYF(0), $1.str));
-
- sp_variable *spvar= spc->add_variable(thd, $1);
+ if (unlikely(!($$= Lex->sp_param_init(&$1))))
+ MYSQL_YYABORT;
+ }
+ ;
- lex->init_last_field(&spvar->field_def, $1.str,
- thd->variables.collation_database);
- $<spvar>$= spvar;
+sp_param_name_and_type:
+ sp_param_name type_with_opt_collate
+ {
+ if (unlikely(Lex->sp_param_fill_definition($$= $1)))
+ MYSQL_YYABORT;
}
- type_with_opt_collate
+ | sp_param_name TYPE_SYM OF_SYM ident '.' ident
{
- LEX *lex= Lex;
- sp_variable *spvar= $<spvar>2;
-
- if (lex->sphead->fill_field_definition(thd, lex, lex->last_field))
- {
+ 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;
+ }
+ | sp_param_name ROW_SYM row_type_body
+ {
+ if (unlikely(Lex->sphead->spvar_fill_row(thd, $$= $1, $3)))
MYSQL_YYABORT;
- }
- spvar->field_def.field_name= spvar->name.str;
- spvar->field_def.pack_flag |= FIELDFLAG_MAYBE_NULL;
-
- $$= spvar;
}
;
@@ -3039,6 +3449,30 @@ sp_opt_inout:
| INOUT_SYM { $$= sp_variable::MODE_INOUT; }
;
+sp_parenthesized_fdparam_list:
+ '('
+ {
+ Lex->sphead->m_param_begin= YYLIP->get_cpp_tok_start() + 1;
+ }
+ sp_fdparam_list
+ ')'
+ {
+ Lex->sphead->m_param_end= YYLIP->get_cpp_tok_start();
+ }
+ ;
+
+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_proc_stmts:
/* Empty */ {}
| sp_proc_stmts sp_proc_stmt ';'
@@ -3052,7 +3486,7 @@ sp_proc_stmts1:
sp_decls:
/* Empty */
{
- $$.vars= $$.conds= $$.hndlrs= $$.curs= 0;
+ $$.init();
}
| sp_decls sp_decl ';'
{
@@ -3060,202 +3494,197 @@ sp_decls:
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 (($2.vars || $2.conds) && ($1.curs || $1.hndlrs))
- my_yyabort_error((ER_SP_VARCOND_AFTER_CURSHNDLR, MYF(0)));
- if ($2.curs && $1.hndlrs)
- my_yyabort_error((ER_SP_CURSOR_AFTER_HANDLER, MYF(0)));
- $$.vars= $1.vars + $2.vars;
- $$.conds= $1.conds + $2.conds;
- $$.hndlrs= $1.hndlrs + $2.hndlrs;
- $$.curs= $1.curs + $2.curs;
+ if (unlikely(Lex->sp_declarations_join(&$$, $1, $2)))
+ MYSQL_YYABORT;
}
;
sp_decl:
- DECLARE_SYM sp_decl_idents
+ DECLARE_MARIADB_SYM sp_decl_body { $$= $2; }
+ ;
+
+
+optionally_qualified_column_ident:
+ sp_decl_ident
{
- LEX *lex= Lex;
- sp_pcontext *pctx= lex->spcont;
+ 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
+ {
+ 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(!($$= new (thd->mem_root) Spvar_definition())))
+ MYSQL_YYABORT;
+ Lex->init_last_field($$, &$1, thd->variables.collation_database);
+ }
+ ;
- // get the last variable:
- uint num_vars= pctx->context_var_count();
- uint var_idx= pctx->var_context2runtime(num_vars - 1);
- sp_variable *spvar= pctx->find_variable(var_idx);
+row_field_definition:
+ row_field_name type_with_opt_collate
+ ;
- lex->sphead->reset_lex(thd);
- pctx->declare_var_boundary($2);
- thd->lex->init_last_field(&spvar->field_def, spvar->name.str,
- thd->variables.collation_database);
+row_field_definition_list:
+ row_field_definition
+ {
+ if (unlikely(!($$= new (thd->mem_root) Row_definition_list())) ||
+ unlikely($$->push_back($1, thd->mem_root)))
+ MYSQL_YYABORT;
+ }
+ | row_field_definition_list ',' row_field_definition
+ {
+ uint unused;
+ if (unlikely($1->find_row_field_by_name(&$3->field_name, &unused)))
+ my_yyabort_error((ER_DUP_FIELDNAME, MYF(0), $3->field_name.str));
+ $$= $1;
+ if (unlikely($$->push_back($3, thd->mem_root)))
+ 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_variable_list:
+ sp_decl_idents_init_vars
type_with_opt_collate
sp_opt_default
{
- LEX *lex= Lex;
- sp_pcontext *pctx= lex->spcont;
- uint num_vars= pctx->context_var_count();
- Item *dflt_value_item= $5;
-
- if (!dflt_value_item)
- {
- dflt_value_item= new (thd->mem_root) Item_null(thd);
- if (dflt_value_item == NULL)
- MYSQL_YYABORT;
- /* QQ Set to the var_type with null_value? */
- }
-
- for (uint i = num_vars-$2 ; i < num_vars ; i++)
- {
- uint var_idx= pctx->var_context2runtime(i);
- sp_variable *spvar= pctx->find_variable(var_idx);
- bool last= i == num_vars - 1;
-
- if (!spvar)
- MYSQL_YYABORT;
-
- if (!last)
- spvar->field_def= *lex->last_field;
-
- spvar->default_value= dflt_value_item;
- spvar->field_def.field_name= spvar->name.str;
-
- if (lex->sphead->fill_field_definition(thd, lex,
- &spvar->field_def))
- {
- MYSQL_YYABORT;
- }
-
- spvar->field_def.pack_flag |= FIELDFLAG_MAYBE_NULL;
-
- /* The last instruction is responsible for freeing LEX. */
-
- sp_instr_set *is= new (lex->thd->mem_root)
- sp_instr_set(lex->sphead->instructions(),
- pctx, var_idx, dflt_value_item,
- $4.field_type(), lex, last);
- if (is == NULL || lex->sphead->add_instr(is))
- MYSQL_YYABORT;
- }
-
- pctx->declare_var_boundary(0);
- if (lex->sphead->restore_lex(thd))
+ if (unlikely(Lex->sp_variable_declarations_finalize(thd, $1,
+ &Lex->last_field[0],
+ $3)))
MYSQL_YYABORT;
- $$.vars= $2;
- $$.conds= $$.hndlrs= $$.curs= 0;
+ $$.init_using_vars($1);
}
- | DECLARE_SYM ident CONDITION_SYM FOR_SYM sp_cond
+ | sp_decl_idents_init_vars
+ TYPE_SYM OF_SYM optionally_qualified_column_ident
+ sp_opt_default
{
- LEX *lex= Lex;
- sp_pcontext *spc= lex->spcont;
+ 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_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);
+ }
+ ;
- if (spc->find_condition($2, TRUE))
- my_yyabort_error((ER_SP_DUP_COND, MYF(0), $2.str));
- if(spc->add_condition(thd, $2, $5))
+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;
}
- | DECLARE_SYM sp_handler_type HANDLER_SYM FOR_SYM
+ | sp_decl_handler
+ | sp_decl_ident CURSOR_SYM
{
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
-
- sp_handler *h= lex->spcont->add_handler(thd,
- (sp_handler::enum_type) $2);
-
- lex->spcont= lex->spcont->push_context(thd,
- sp_pcontext::HANDLER_SCOPE);
-
- sp_pcontext *ctx= lex->spcont;
- sp_instr_hpush_jump *i=
- new (thd->mem_root) sp_instr_hpush_jump(sp->instructions(),
- ctx, h);
-
- if (i == NULL || sp->add_instr(i))
+ 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;
-
- /* For continue handlers, mark end of handler scope. */
- if ($2 == sp_handler::CONTINUE &&
- sp->push_backpatch(thd, i, ctx->last_label()))
+ if (unlikely(Lex->sp_declare_cursor(thd, &$1, $6, param_ctx, true)))
MYSQL_YYABORT;
+ $$.vars= $$.conds= $$.hndlrs= 0;
+ $$.curs= 1;
+ }
+ ;
- if (sp->push_backpatch(thd, i, ctx->push_label(thd, empty_lex_str, 0)))
+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
{
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- sp_pcontext *ctx= lex->spcont;
- sp_label *hlab= lex->spcont->pop_label(); /* After this hdlr */
- sp_instr_hreturn *i;
-
- if ($2 == sp_handler::CONTINUE)
- {
- i= new (thd->mem_root)
- sp_instr_hreturn(sp->instructions(), ctx);
- if (i == NULL ||
- sp->add_instr(i))
- MYSQL_YYABORT;
- }
- else
- { /* EXIT or UNDO handler, just jump to the end of the block */
- i= new (thd->mem_root)
- sp_instr_hreturn(sp->instructions(), ctx);
- if (i == NULL ||
- sp->add_instr(i) ||
- sp->push_backpatch(thd, i, lex->spcont->last_label())) /* Block end */
- MYSQL_YYABORT;
- }
- lex->sphead->backpatch(hlab);
-
- lex->spcont= ctx->pop_context();
-
+ if (unlikely(Lex->sp_handler_declaration_finalize(thd, $1)))
+ MYSQL_YYABORT;
$$.vars= $$.conds= $$.curs= 0;
$$.hndlrs= 1;
}
- | DECLARE_SYM ident CURSOR_SYM FOR_SYM sp_cursor_stmt
- {
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- sp_pcontext *ctx= lex->spcont;
- uint offp;
- sp_instr_cpush *i;
+ ;
- if (ctx->find_cursor($2, &offp, TRUE))
- my_yyabort_error((ER_SP_DUP_CURS, MYF(0), $2.str));
+opt_parenthesized_cursor_formal_parameters:
+ /* Empty */
+ | '(' sp_fdparams ')'
+ ;
- i= new (thd->mem_root)
- sp_instr_cpush(sp->instructions(), ctx, $5,
- ctx->current_cursor_count());
- if (i == NULL || sp->add_instr(i) || ctx->add_cursor($2))
+
+sp_cursor_stmt_lex:
+ {
+ DBUG_ASSERT(thd->lex->sphead);
+ if (unlikely(!($$= new (thd->mem_root)
+ sp_lex_cursor(thd, thd->lex))))
MYSQL_YYABORT;
- $$.vars= $$.conds= $$.hndlrs= 0;
- $$.curs= 1;
}
;
sp_cursor_stmt:
+ sp_cursor_stmt_lex
{
- Lex->sphead->reset_lex(thd);
+ DBUG_ASSERT(thd->free_list == NULL);
+ Lex->sphead->reset_lex(thd, $1);
}
select
{
- LEX *lex= Lex;
-
- DBUG_ASSERT(lex->sql_command == SQLCOM_SELECT);
-
- if (lex->result)
- my_yyabort_error((ER_SP_BAD_CURSOR_SELECT, MYF(0)));
- lex->sp_lex_in_use= TRUE;
- $$= lex;
- if (lex->sphead->restore_lex(thd))
+ DBUG_ASSERT(Lex == $1);
+ if (unlikely($1->stmt_finalize(thd)) ||
+ unlikely($1->sphead->restore_lex(thd)))
MYSQL_YYABORT;
+ $$= $1;
}
;
sp_handler_type:
- EXIT_SYM { $$= sp_handler::EXIT; }
- | CONTINUE_SYM { $$= sp_handler::CONTINUE; }
+ 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 } */
;
@@ -3273,7 +3702,7 @@ sp_hcond_element:
sp_head *sp= lex->sphead;
sp_pcontext *ctx= lex->spcont->parent_context();
- if (ctx->check_duplicate_handler($1))
+ 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();
@@ -3284,10 +3713,10 @@ sp_hcond_element:
sp_cond:
ulong_num
{ /* mysql errno */
- if ($1 == 0)
+ if (unlikely($1 == 0))
my_yyabort_error((ER_WRONG_VALUE, MYF(0), "CONDITION", "0"));
$$= new (thd->mem_root) sp_condition_value($1);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| sqlstate
@@ -3304,10 +3733,11 @@ sqlstate:
allowed to SIGNAL, or declare a handler for the completion
condition.
*/
- if (!is_sqlstate_valid(&$3) || is_sqlstate_completion($3.str))
+ 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 ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
;
@@ -3324,26 +3754,46 @@ sp_hcond:
}
| ident /* CONDITION name */
{
- $$= Lex->spcont->find_condition($1, false);
- if ($$ == NULL)
+ $$= 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 ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| not FOUND_SYM /* SQLSTATEs 02??? */
{
$$= new (thd->mem_root) sp_condition_value(sp_condition_value::NOT_FOUND);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| SQLEXCEPTION_SYM /* All other SQLSTATEs */
{
$$= new (thd->mem_root) sp_condition_value(sp_condition_value::EXCEPTION);
- if ($$ == NULL)
+ 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;
}
;
@@ -3351,13 +3801,7 @@ sp_hcond:
signal_stmt:
SIGNAL_SYM signal_value opt_set_signal_information
{
- LEX *lex= thd->lex;
- Yacc_state *state= & thd->m_parser_state->m_yacc;
-
- lex->sql_command= SQLCOM_SIGNAL;
- lex->m_sql_cmd=
- new (thd->mem_root) Sql_cmd_signal($2, state->m_set_signal_info);
- if (lex->m_sql_cmd == NULL)
+ if (unlikely(Lex->add_signal_statement(thd, $2)))
MYSQL_YYABORT;
}
;
@@ -3369,12 +3813,12 @@ signal_value:
sp_condition_value *cond;
/* SIGNAL foo cannot be used outside of stored programs */
- if (lex->spcont == NULL)
+ if (unlikely(lex->spcont == NULL))
my_yyabort_error((ER_SP_COND_MISMATCH, MYF(0), $1.str));
- cond= lex->spcont->find_condition($1, false);
- if (cond == NULL)
+ 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 (cond->type != sp_condition_value::SQLSTATE)
+ if (unlikely(cond->type != sp_condition_value::SQLSTATE))
my_yyabort_error((ER_SIGNAL_BAD_CONDITION_TYPE, MYF(0)));
$$= cond;
}
@@ -3412,7 +3856,7 @@ signal_information_item_list:
Set_signal_information *info;
info= &thd->m_parser_state->m_yacc.m_set_signal_info;
int index= (int) $3;
- if (info->m_item[index] != NULL)
+ 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;
@@ -3430,14 +3874,14 @@ signal_allowed_expr:
if ($1->type() == Item::FUNC_ITEM)
{
Item_func *item= (Item_func*) $1;
- if (item->functype() == Item_func::SUSERVAR_FUNC)
+ if (unlikely(item->functype() == Item_func::SUSERVAR_FUNC))
{
/*
Don't allow the following syntax:
SIGNAL/RESIGNAL ...
SET <signal condition item name> = @foo := expr
*/
- my_parse_error(thd, ER_SYNTAX_ERROR);
+ thd->parse_error();
MYSQL_YYABORT;
}
}
@@ -3478,14 +3922,7 @@ signal_condition_information_item_name:
resignal_stmt:
RESIGNAL_SYM opt_signal_value opt_set_signal_information
{
- LEX *lex= thd->lex;
- Yacc_state *state= & thd->m_parser_state->m_yacc;
-
- lex->sql_command= SQLCOM_RESIGNAL;
- lex->m_sql_cmd=
- new (thd->mem_root) Sql_cmd_resignal($2,
- state->m_set_signal_info);
- if (lex->m_sql_cmd == NULL)
+ if (unlikely(Lex->add_resignal_statement(thd, $2)))
MYSQL_YYABORT;
}
;
@@ -3500,7 +3937,7 @@ get_diagnostics:
Lex->sql_command= SQLCOM_GET_DIAGNOSTICS;
Lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_get_diagnostics(info);
- if (Lex->m_sql_cmd == NULL)
+ if (unlikely(Lex->m_sql_cmd == NULL))
MYSQL_YYABORT;
}
;
@@ -3516,13 +3953,13 @@ diagnostics_information:
statement_information
{
$$= new (thd->mem_root) Statement_information($1);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| CONDITION_SYM condition_number condition_information
{
$$= new (thd->mem_root) Condition_information($2, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
;
@@ -3531,12 +3968,13 @@ statement_information:
statement_information_item
{
$$= new (thd->mem_root) List<Statement_information_item>;
- if ($$ == NULL || $$->push_back($1, thd->mem_root))
+ if (unlikely($$ == NULL) ||
+ unlikely($$->push_back($1, thd->mem_root)))
MYSQL_YYABORT;
}
| statement_information ',' statement_information_item
{
- if ($1->push_back($3, thd->mem_root))
+ if (unlikely($1->push_back($3, thd->mem_root)))
MYSQL_YYABORT;
$$= $1;
}
@@ -3546,31 +3984,29 @@ statement_information_item:
simple_target_specification '=' statement_information_item_name
{
$$= new (thd->mem_root) Statement_information_item($3, $1);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
;
simple_target_specification:
- ident
+ ident_cli
{
- Lex_input_stream *lip= &thd->m_parser_state->m_lip;
- $$= create_item_for_sp_var(thd, $1, NULL,
- lip->get_tok_start(), lip->get_ptr());
-
- if ($$ == NULL)
+ 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 ($$ == NULL)
+ $$= new (thd->mem_root) Item_func_get_user_var(thd, &$2);
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
;
statement_information_item_name:
- NUMBER_SYM
+ NUMBER_MARIADB_SYM
+ { $$= Statement_information_item::NUMBER; }
+ | NUMBER_ORACLE_SYM
{ $$= Statement_information_item::NUMBER; }
| ROW_COUNT_SYM
{ $$= Statement_information_item::ROW_COUNT; }
@@ -3589,12 +4025,13 @@ condition_information:
condition_information_item
{
$$= new (thd->mem_root) List<Condition_information_item>;
- if ($$ == NULL || $$->push_back($1, thd->mem_root))
+ if (unlikely($$ == NULL) ||
+ unlikely($$->push_back($1, thd->mem_root)))
MYSQL_YYABORT;
}
| condition_information ',' condition_information_item
{
- if ($1->push_back($3, thd->mem_root))
+ if (unlikely($1->push_back($3, thd->mem_root)))
MYSQL_YYABORT;
$$= $1;
}
@@ -3604,7 +4041,7 @@ condition_information_item:
simple_target_specification '=' condition_information_item_name
{
$$= new (thd->mem_root) Condition_information_item($3, $1);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
;
@@ -3638,17 +4075,26 @@ condition_information_item_name:
{ $$= 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:
- ident
+ sp_decl_ident
{
/* NOTE: field definition is filled in sp_decl section. */
LEX *lex= Lex;
sp_pcontext *spc= lex->spcont;
- if (spc->find_variable($1, TRUE))
+ if (unlikely(spc->find_variable(&$1, TRUE)))
my_yyabort_error((ER_SP_DUP_VAR, MYF(0), $1.str));
- spc->add_variable(thd, $1);
+ spc->add_variable(thd, &$1);
$$= 1;
}
| sp_decl_idents ',' ident
@@ -3658,9 +4104,9 @@ sp_decl_idents:
LEX *lex= Lex;
sp_pcontext *spc= lex->spcont;
- if (spc->find_variable($3, TRUE))
+ if (unlikely(spc->find_variable(&$3, TRUE)))
my_yyabort_error((ER_SP_DUP_VAR, MYF(0), $3.str));
- spc->add_variable(thd, $3);
+ spc->add_variable(thd, &$3);
$$= $1 + 1;
}
;
@@ -3690,8 +4136,11 @@ sp_proc_stmt_in_returns_clause:
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
@@ -3707,7 +4156,7 @@ sp_proc_stmt_compound_ok:
sp_proc_stmt_if:
IF_SYM
{
- if (maybe_start_compound_statement(thd))
+ if (unlikely(Lex->maybe_start_compound_statement(thd)))
MYSQL_YYABORT;
Lex->sphead->new_cont_backpatch(NULL);
}
@@ -3731,7 +4180,7 @@ sp_proc_stmt_statement:
sp->m_flags|= sp_get_flags_for_command(lex);
/* "USE db" doesn't work in a procedure */
- if (lex->sql_command == SQLCOM_CHANGE_DB)
+ if (unlikely(lex->sql_command == SQLCOM_CHANGE_DB))
my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "USE"));
/*
Don't add an instruction for SET statements, since all
@@ -3744,7 +4193,7 @@ sp_proc_stmt_statement:
{
sp_instr_stmt *i=new (thd->mem_root)
sp_instr_stmt(sp->instructions(), lex->spcont, lex);
- if (i == NULL)
+ if (unlikely(i == NULL))
MYSQL_YYABORT;
/*
@@ -3756,169 +4205,226 @@ sp_proc_stmt_statement:
i->m_query.length= lip->get_ptr() - sp->m_tmp_query;
else
i->m_query.length= lip->get_tok_start() - sp->m_tmp_query;;
- if (!(i->m_query.str= strmake_root(thd->mem_root,
- sp->m_tmp_query,
- i->m_query.length)) ||
- sp->add_instr(i))
+ if (unlikely(!(i->m_query.str= strmake_root(thd->mem_root,
+ sp->m_tmp_query,
+ i->m_query.length))) ||
+ unlikely(sp->add_instr(i)))
MYSQL_YYABORT;
}
- if (sp->restore_lex(thd))
+ if (unlikely(sp->restore_lex(thd)))
MYSQL_YYABORT;
}
;
+
+RETURN_ALLMODES_SYM:
+ RETURN_MARIADB_SYM
+ | RETURN_ORACLE_SYM
+ ;
+
sp_proc_stmt_return:
- RETURN_SYM
+ RETURN_ALLMODES_SYM
{ Lex->sphead->reset_lex(thd); }
expr
{
LEX *lex= Lex;
sp_head *sp= lex->sphead;
+ 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;
+ }
+ ;
- if (sp->m_type != TYPE_ENUM_FUNCTION)
- my_yyabort_error((ER_SP_BADRETURN, MYF(0)));
-
- sp_instr_freturn *i;
+reset_lex_expr:
+ { Lex->sphead->reset_lex(thd); } expr { $$= $2; }
+ ;
- i= new (thd->mem_root)
- sp_instr_freturn(sp->instructions(), lex->spcont, $3,
- sp->m_return_field_def.sql_type, lex);
- if (i == NULL || sp->add_instr(i))
+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 (unlikely(Lex->sp_exit_statement(thd, $3)) ||
+ unlikely(Lex->sphead->restore_lex(thd)))
MYSQL_YYABORT;
- sp->m_flags|= sp_head::HAS_RETURN;
+ }
+ | EXIT_ORACLE_SYM label_ident WHEN_SYM reset_lex_expr
+ {
+ if (unlikely(Lex->sp_exit_statement(thd, &$2, $4)) ||
+ unlikely(Lex->sphead->restore_lex(thd)))
+ MYSQL_YYABORT;
+ }
+ ;
- if (sp->restore_lex(thd))
+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 (unlikely(Lex->sp_continue_statement(thd, $3)) ||
+ unlikely(Lex->sphead->restore_lex(thd)))
+ MYSQL_YYABORT;
+ }
+ | CONTINUE_ORACLE_SYM label_ident WHEN_SYM reset_lex_expr
+ {
+ if (unlikely(Lex->sp_continue_statement(thd, &$2, $4)) ||
+ unlikely(Lex->sphead->restore_lex(thd)))
MYSQL_YYABORT;
}
;
+
sp_proc_stmt_leave:
LEAVE_SYM label_ident
{
- LEX *lex= Lex;
- sp_head *sp = lex->sphead;
- sp_pcontext *ctx= lex->spcont;
- sp_label *lab= ctx->find_label($2);
-
- if (! lab)
- my_yyabort_error((ER_SP_LILABEL_MISMATCH, MYF(0), "LEAVE", $2.str));
-
- sp_instr_jump *i;
- uint ip= sp->instructions();
- uint n;
- /*
- When jumping to a BEGIN-END block end, the target jump
- points to the block hpop/cpop cleanup instructions,
- so we should exclude the block context here.
- When jumping to something else (i.e., SP_LAB_ITER),
- there are no hpop/cpop at the jump destination,
- so we should include the block context here for cleanup.
- */
- bool exclusive= (lab->type == sp_label::BEGIN);
-
- n= ctx->diff_handlers(lab->ctx, exclusive);
- if (n)
- {
- sp_instr_hpop *hpop= new (thd->mem_root)
- sp_instr_hpop(ip++, ctx, n);
- if (hpop == NULL)
- MYSQL_YYABORT;
- sp->add_instr(hpop);
- }
- n= ctx->diff_cursors(lab->ctx, exclusive);
- if (n)
- {
- sp_instr_cpop *cpop= new (thd->mem_root)
- sp_instr_cpop(ip++, ctx, n);
- if (cpop == NULL)
- MYSQL_YYABORT;
- sp->add_instr(cpop);
- }
- i= new (thd->mem_root) sp_instr_jump(ip, ctx);
- if (i == NULL)
+ if (unlikely(Lex->sp_leave_statement(thd, &$2)))
MYSQL_YYABORT;
- sp->push_backpatch(thd, i, lab); /* Jumping forward */
- sp->add_instr(i);
}
;
sp_proc_stmt_iterate:
ITERATE_SYM label_ident
{
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- sp_pcontext *ctx= lex->spcont;
- sp_label *lab= ctx->find_label($2);
+ if (unlikely(Lex->sp_iterate_statement(thd, &$2)))
+ MYSQL_YYABORT;
+ }
+ ;
- if (! lab || lab->type != sp_label::ITERATION)
- my_yyabort_error((ER_SP_LILABEL_MISMATCH, MYF(0), "ITERATE", $2.str));
+sp_proc_stmt_goto_oracle:
+ GOTO_ORACLE_SYM label_ident
+ {
+ if (unlikely(Lex->sp_goto_statement(thd, &$2)))
+ MYSQL_YYABORT;
+ }
+ ;
- sp_instr_jump *i;
- uint ip= sp->instructions();
- uint n;
+assignment_source_lex:
+ {
+ DBUG_ASSERT(Lex->sphead);
+ if (unlikely(!($$= new (thd->mem_root)
+ sp_assignment_lex(thd, thd->lex))))
+ MYSQL_YYABORT;
+ }
+ ;
- n= ctx->diff_handlers(lab->ctx, FALSE); /* Inclusive the dest. */
- if (n)
- {
- sp_instr_hpop *hpop= new (thd->mem_root)
- sp_instr_hpop(ip++, ctx, n);
- if (hpop == NULL ||
- sp->add_instr(hpop))
- MYSQL_YYABORT;
- }
- n= ctx->diff_cursors(lab->ctx, FALSE); /* Inclusive the dest. */
- if (n)
- {
- sp_instr_cpop *cpop= new (thd->mem_root)
- sp_instr_cpop(ip++, ctx, n);
- if (cpop == NULL ||
- sp->add_instr(cpop))
- MYSQL_YYABORT;
- }
- i= new (thd->mem_root)
- sp_instr_jump(ip, ctx, lab->ip); /* Jump back */
- if (i == NULL ||
- sp->add_instr(i))
+assignment_source_expr:
+ assignment_source_lex
+ {
+ DBUG_ASSERT(thd->free_list == NULL);
+ Lex->sphead->reset_lex(thd, $1);
+ }
+ 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;
+ if (unlikely($$->sphead->restore_lex(thd)))
+ MYSQL_YYABORT;
+ }
+ ;
+
+for_loop_bound_expr:
+ assignment_source_lex
+ {
+ Lex->sphead->reset_lex(thd, $1);
+ }
+ expr
+ {
+ DBUG_ASSERT($1 == thd->lex);
+ $$= $1;
+ $$->sp_lex_in_use= true;
+ $$->set_item_and_free_list($3, NULL);
+ if (unlikely($$->sphead->restore_lex(thd)))
MYSQL_YYABORT;
}
;
+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
+ OPEN_SYM ident opt_parenthesized_cursor_actual_parameters
{
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- uint offset;
- sp_instr_copen *i;
+ if (unlikely(Lex->sp_open_cursor(thd, &$2, $3)))
+ MYSQL_YYABORT;
+ }
+ ;
- if (! 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_copen(sp->instructions(), lex->spcont, offset);
- if (i == NULL ||
- sp->add_instr(i))
+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:
- FETCH_SYM sp_opt_fetch_noise ident INTO
- {
+ sp_proc_stmt_fetch_head sp_fetch_list { }
+ | FETCH_SYM GROUP_SYM NEXT_SYM ROW_SYM
+ {
LEX *lex= Lex;
sp_head *sp= lex->sphead;
- uint offset;
- sp_instr_cfetch *i;
-
- if (! lex->spcont->find_cursor($3, &offset, false))
- my_yyabort_error((ER_SP_CURSOR_MISMATCH, MYF(0), $3.str));
- i= new (thd->mem_root)
- sp_instr_cfetch(sp->instructions(), lex->spcont, offset);
- if (i == NULL ||
- sp->add_instr(i))
+ lex->sphead->m_flags|= sp_head::HAS_AGGREGATE_INSTR;
+ sp_instr_agg_cfetch *i=
+ new (thd->mem_root) sp_instr_agg_cfetch(sp->instructions(),
+ lex->spcont);
+ if (unlikely(i == NULL) ||
+ unlikely(sp->add_instr(i)))
MYSQL_YYABORT;
- }
- sp_fetch_list
- {}
+ }
;
sp_proc_stmt_close:
@@ -3929,22 +4435,16 @@ sp_proc_stmt_close:
uint offset;
sp_instr_cclose *i;
- if (! lex->spcont->find_cursor($2, &offset, false))
+ 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 (i == NULL ||
- sp->add_instr(i))
+ if (unlikely(i == NULL) ||
+ unlikely(sp->add_instr(i)))
MYSQL_YYABORT;
}
;
-sp_opt_fetch_noise:
- /* Empty */
- | NEXT_SYM FROM
- | FROM
- ;
-
sp_fetch_list:
ident
{
@@ -3953,7 +4453,7 @@ sp_fetch_list:
sp_pcontext *spc= lex->spcont;
sp_variable *spv;
- if (!spc || !(spv = spc->find_variable($1, false)))
+ if (unlikely(!spc || !(spv = spc->find_variable(&$1, false))))
my_yyabort_error((ER_SP_UNDECLARED_VAR, MYF(0), $1.str));
/* An SP local variable */
@@ -3967,7 +4467,7 @@ sp_fetch_list:
sp_pcontext *spc= lex->spcont;
sp_variable *spv;
- if (!spc || !(spv = spc->find_variable($3, false)))
+ if (unlikely(!spc || !(spv = spc->find_variable(&$3, false))))
my_yyabort_error((ER_SP_UNDECLARED_VAR, MYF(0), $3.str));
/* An SP local variable */
@@ -3986,12 +4486,12 @@ sp_if:
uint ip= sp->instructions();
sp_instr_jump_if_not *i= new (thd->mem_root)
sp_instr_jump_if_not(ip, ctx, $2, lex);
- if (i == NULL ||
- sp->push_backpatch(thd, i, ctx->push_label(thd, empty_lex_str, 0)) ||
- sp->add_cont_backpatch(i) ||
- sp->add_instr(i))
+ 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;
- if (sp->restore_lex(thd))
+ if (unlikely(sp->restore_lex(thd)))
MYSQL_YYABORT;
}
sp_proc_stmts1
@@ -4000,11 +4500,11 @@ sp_if:
sp_pcontext *ctx= Lex->spcont;
uint ip= sp->instructions();
sp_instr_jump *i= new (thd->mem_root) sp_instr_jump(ip, ctx);
- if (i == NULL ||
- sp->add_instr(i))
+ 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_lex_str, 0));
+ sp->push_backpatch(thd, i, ctx->push_label(thd, &empty_clex_str, 0));
}
sp_elseifs
{
@@ -4016,14 +4516,14 @@ sp_if:
sp_elseifs:
/* Empty */
- | ELSEIF_SYM sp_if
+ | ELSEIF_MARIADB_SYM sp_if
| ELSE sp_proc_stmts1
;
case_stmt_specification:
CASE_SYM
{
- if (maybe_start_compound_statement(thd))
+ if (unlikely(Lex->maybe_start_compound_statement(thd)))
MYSQL_YYABORT;
/**
@@ -4069,7 +4569,7 @@ case_stmt_specification:
BACKPATCH: Creating target label for the jump to after END CASE
(instruction 12 in the example)
*/
- Lex->spcont->push_label(thd, empty_lex_str, Lex->sphead->instructions());
+ Lex->spcont->push_label(thd, &empty_clex_str, Lex->sphead->instructions());
}
case_stmt_body
else_clause_opt
@@ -4094,10 +4594,9 @@ case_stmt_body:
{ Lex->sphead->reset_lex(thd); /* For expr $2 */ }
expr
{
- if (case_stmt_action_expr(Lex, $2))
+ if (unlikely(Lex->case_stmt_action_expr($2)))
MYSQL_YYABORT;
-
- if (Lex->sphead->restore_lex(thd))
+ if (unlikely(Lex->sphead->restore_lex(thd)))
MYSQL_YYABORT;
}
simple_when_clause_list
@@ -4126,17 +4625,16 @@ simple_when_clause:
/* Simple case: <caseval> = <whenval> */
LEX *lex= Lex;
- if (case_stmt_action_when(lex, $3, true))
+ if (unlikely(lex->case_stmt_action_when($3, true)))
MYSQL_YYABORT;
/* For expr $3 */
- if (lex->sphead->restore_lex(thd))
+ if (unlikely(lex->sphead->restore_lex(thd)))
MYSQL_YYABORT;
}
THEN_SYM
sp_proc_stmts1
{
- LEX *lex= Lex;
- if (case_stmt_action_then(lex))
+ if (unlikely(Lex->case_stmt_action_then()))
MYSQL_YYABORT;
}
;
@@ -4149,17 +4647,16 @@ searched_when_clause:
expr
{
LEX *lex= Lex;
- if (case_stmt_action_when(lex, $3, false))
+ if (unlikely(lex->case_stmt_action_when($3, false)))
MYSQL_YYABORT;
/* For expr $3 */
- if (lex->sphead->restore_lex(thd))
+ if (unlikely(lex->sphead->restore_lex(thd)))
MYSQL_YYABORT;
}
THEN_SYM
sp_proc_stmts1
{
- LEX *lex= Lex;
- if (case_stmt_action_then(lex))
+ if (unlikely(Lex->case_stmt_action_then()))
MYSQL_YYABORT;
}
;
@@ -4172,96 +4669,109 @@ else_clause_opt:
uint ip= sp->instructions();
sp_instr_error *i= new (thd->mem_root)
sp_instr_error(ip, lex->spcont, ER_SP_CASE_NOT_FOUND);
- if (i == NULL ||
- sp->add_instr(i))
+ if (unlikely(i == NULL) ||
+ unlikely(sp->add_instr(i)))
MYSQL_YYABORT;
}
| ELSE sp_proc_stmts1
;
+sp_label:
+ label_ident ':' { $$= $1; }
+ ;
+
sp_opt_label:
- /* Empty */ { $$= null_lex_str; }
+ /* Empty */ { $$= null_clex_str; }
| label_ident { $$= $1; }
;
-sp_labeled_block:
- label_ident ':' BEGIN_SYM
+sp_block_label:
+ sp_label
{
- LEX *lex= Lex;
- sp_pcontext *ctx= lex->spcont;
- sp_label *lab= ctx->find_label($1);
+ if (unlikely(Lex->spcont->block_label_declare(&$1)))
+ MYSQL_YYABORT;
+ $$= $1;
+ }
+ ;
- if (lab)
- my_yyabort_error((ER_SP_LABEL_REDEFINE, MYF(0), $1.str));
- lex->name= $1;
+sp_labeled_block:
+ sp_block_label
+ BEGIN_MARIADB_SYM
+ {
+ Lex->sp_block_init(thd, &$1);
}
- sp_block_content sp_opt_label
+ sp_decls
+ sp_proc_stmts
+ END
+ sp_opt_label
{
- if ($6.str)
- {
- if (my_strcasecmp(system_charset_info, $6.str, $5->name.str) != 0)
- my_yyabort_error((ER_SP_LABEL_MISMATCH, MYF(0), $6.str));
- }
+ if (unlikely(Lex->sp_block_finalize(thd, $4, &$7)))
+ MYSQL_YYABORT;
}
;
sp_unlabeled_block:
- BEGIN_SYM
+ BEGIN_MARIADB_SYM
{
- Lex->name= empty_lex_str; // Unlabeled blocks get an empty label
+ Lex->sp_block_init(thd);
}
- sp_block_content
- { }
- ;
-
-sp_unlabeled_block_not_atomic:
- BEGIN_SYM not ATOMIC_SYM /* TODO: BEGIN ATOMIC (not -> opt_not) */
+ sp_decls
+ sp_proc_stmts
+ END
{
- if (maybe_start_compound_statement(thd))
+ if (unlikely(Lex->sp_block_finalize(thd, $3)))
MYSQL_YYABORT;
- Lex->name= empty_lex_str; // Unlabeled blocks get an empty label
}
- sp_block_content
- { }
;
-sp_block_content:
+sp_unlabeled_block_not_atomic:
+ BEGIN_MARIADB_SYM not ATOMIC_SYM /* TODO: BEGIN ATOMIC (not -> opt_not) */
{
- LEX *lex= Lex;
- sp_label *lab= lex->spcont->push_label(thd, lex->name,
- lex->sphead->instructions());
- lab->type= sp_label::BEGIN;
- lex->spcont= lex->spcont->push_context(thd,
- sp_pcontext::REGULAR_SCOPE);
+ if (unlikely(Lex->maybe_start_compound_statement(thd)))
+ MYSQL_YYABORT;
+ Lex->sp_block_init(thd);
}
sp_decls
sp_proc_stmts
END
{
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- sp_pcontext *ctx= lex->spcont;
- sp_instr *i;
+ if (unlikely(Lex->sp_block_finalize(thd, $5)))
+ MYSQL_YYABORT;
+ }
+ ;
- sp->backpatch(ctx->last_label()); /* We always have a label */
- if ($2.hndlrs)
- {
- i= new (thd->mem_root)
- sp_instr_hpop(sp->instructions(), ctx, $2.hndlrs);
- if (i == NULL ||
- sp->add_instr(i))
- MYSQL_YYABORT;
- }
- if ($2.curs)
- {
- i= new (thd->mem_root)
- sp_instr_cpop(sp->instructions(), ctx, $2.curs);
- if (i == NULL ||
- sp->add_instr(i))
- MYSQL_YYABORT;
- }
- lex->spcont= ctx->pop_context();
- $$ = lex->spcont->pop_label();
+/* This adds one shift/reduce conflict */
+opt_sp_for_loop_direction:
+ /* Empty */ { $$= 1; }
+ | REVERSE_SYM { $$= -1; }
+ ;
+
+sp_for_loop_index_and_bounds:
+ ident sp_for_loop_bounds
+ {
+ if (unlikely(Lex->sp_for_loop_declarations(thd, &$$, &$1, $2)))
+ 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;
}
;
@@ -4273,8 +4783,8 @@ loop_body:
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 (i == NULL ||
- lex->sphead->add_instr(i))
+ if (unlikely(i == NULL) ||
+ unlikely(lex->sphead->add_instr(i)))
MYSQL_YYABORT;
}
;
@@ -4283,30 +4793,15 @@ while_body:
expr DO_SYM
{
LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- uint ip= sp->instructions();
- sp_instr_jump_if_not *i= new (thd->mem_root)
- sp_instr_jump_if_not(ip, lex->spcont, $1, lex);
- if (i == NULL ||
- /* Jumping forward */
- sp->push_backpatch(thd, i, lex->spcont->last_label()) ||
- sp->new_cont_backpatch(i) ||
- sp->add_instr(i))
+ if (unlikely(lex->sp_while_loop_expression(thd, $1)))
MYSQL_YYABORT;
- if (sp->restore_lex(thd))
+ if (unlikely(lex->sphead->restore_lex(thd)))
MYSQL_YYABORT;
}
sp_proc_stmts1 END WHILE_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 (i == NULL ||
- lex->sphead->add_instr(i))
+ if (unlikely(Lex->sp_while_loop_finalize(thd)))
MYSQL_YYABORT;
- lex->sphead->do_cont_backpatch();
}
;
@@ -4320,89 +4815,126 @@ repeat_body:
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 (i == NULL ||
- lex->sphead->add_instr(i))
+ if (unlikely(i == NULL) ||
+ unlikely(lex->sphead->add_instr(i)))
MYSQL_YYABORT;
- if (lex->sphead->restore_lex(thd))
+ if (unlikely(lex->sphead->restore_lex(thd)))
MYSQL_YYABORT;
/* We can shortcut the cont_backpatch here */
i->m_cont_dest= ip+1;
}
;
-pop_sp_label:
+pop_sp_loop_label:
sp_opt_label
{
- sp_label *lab;
- Lex->sphead->backpatch(lab= Lex->spcont->pop_label());
- if ($1.str)
- {
- if (my_strcasecmp(system_charset_info, $1.str,
- lab->name.str) != 0)
- my_yyabort_error((ER_SP_LABEL_MISMATCH, MYF(0), $1.str));
- }
- }
- ;
-
-pop_sp_empty_label:
- {
- sp_label *lab;
- Lex->sphead->backpatch(lab= Lex->spcont->pop_label());
- DBUG_ASSERT(lab->name.length == 0);
+ if (unlikely(Lex->sp_pop_loop_label(thd, &$1)))
+ MYSQL_YYABORT;
}
;
sp_labeled_control:
- label_ident ':' LOOP_SYM
+ sp_label LOOP_SYM
{
- if (push_sp_label(thd, $1))
+ if (unlikely(Lex->sp_push_loop_label(thd, &$1)))
MYSQL_YYABORT;
}
- loop_body pop_sp_label
+ loop_body pop_sp_loop_label
{ }
- | label_ident ':' WHILE_SYM
+ | sp_label WHILE_SYM
{
- if (push_sp_label(thd, $1))
+ if (unlikely(Lex->sp_push_loop_label(thd, &$1)))
MYSQL_YYABORT;
Lex->sphead->reset_lex(thd);
}
- while_body pop_sp_label
+ while_body pop_sp_loop_label
{ }
- | label_ident ':' REPEAT_SYM
+ | sp_label FOR_SYM
{
- if (push_sp_label(thd, $1))
+ // 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;
+ }
+ DO_SYM
+ sp_proc_stmts1
+ END FOR_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;
+ }
+ | sp_label REPEAT_SYM
+ {
+ if (unlikely(Lex->sp_push_loop_label(thd, &$1)))
MYSQL_YYABORT;
}
- repeat_body pop_sp_label
+ repeat_body pop_sp_loop_label
{ }
;
sp_unlabeled_control:
LOOP_SYM
{
- if (push_sp_empty_label(thd))
+ if (unlikely(Lex->sp_push_loop_empty_label(thd)))
MYSQL_YYABORT;
}
loop_body
- pop_sp_empty_label
- { }
+ {
+ Lex->sp_pop_loop_empty_label(thd);
+ }
| WHILE_SYM
{
- if (push_sp_empty_label(thd))
+ if (unlikely(Lex->sp_push_loop_empty_label(thd)))
MYSQL_YYABORT;
Lex->sphead->reset_lex(thd);
}
while_body
- pop_sp_empty_label
- { }
+ {
+ 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;
+ }
+ DO_SYM
+ sp_proc_stmts1
+ END FOR_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 (push_sp_empty_label(thd))
+ if (unlikely(Lex->sp_push_loop_empty_label(thd)))
MYSQL_YYABORT;
}
repeat_body
- pop_sp_empty_label
- { }
+ {
+ Lex->sp_pop_loop_empty_label(thd);
+ }
;
trg_action_time:
@@ -4615,7 +5147,7 @@ tablespace_name:
LEX *lex= Lex;
lex->alter_tablespace_info= (new (thd->mem_root)
st_alter_tablespace());
- if (lex->alter_tablespace_info == NULL)
+ if (unlikely(lex->alter_tablespace_info == NULL))
MYSQL_YYABORT;
lex->alter_tablespace_info->tablespace_name= $1.str;
lex->sql_command= SQLCOM_ALTER_TABLESPACE;
@@ -4628,7 +5160,7 @@ logfile_group_name:
LEX *lex= Lex;
lex->alter_tablespace_info= (new (thd->mem_root)
st_alter_tablespace());
- if (lex->alter_tablespace_info == NULL)
+ if (unlikely(lex->alter_tablespace_info == NULL))
MYSQL_YYABORT;
lex->alter_tablespace_info->logfile_group_name= $1.str;
lex->sql_command= SQLCOM_ALTER_TABLESPACE;
@@ -4705,7 +5237,7 @@ opt_ts_nodegroup:
NODEGROUP_SYM opt_equal real_ulong_num
{
LEX *lex= Lex;
- if (lex->alter_tablespace_info->nodegroup_id != UNDEF_NODEGROUP)
+ 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;
}
@@ -4715,7 +5247,7 @@ opt_ts_comment:
COMMENT_SYM opt_equal TEXT_STRING_sys
{
LEX *lex= Lex;
- if (lex->alter_tablespace_info->ts_comment != NULL)
+ 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;
}
@@ -4725,7 +5257,7 @@ opt_ts_engine:
opt_storage ENGINE_SYM opt_equal storage_engines
{
LEX *lex= Lex;
- if (lex->alter_tablespace_info->storage_engine != NULL)
+ 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;
@@ -4746,7 +5278,7 @@ ts_wait:
| NO_WAIT_SYM
{
LEX *lex= Lex;
- if (!(lex->alter_tablespace_info->wait_until_completed))
+ 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;
}
@@ -4756,34 +5288,8 @@ size_number:
real_ulonglong_num { $$= $1;}
| IDENT_sys
{
- ulonglong number;
- uint text_shift_number= 0;
- longlong prefix_number;
- char *start_ptr= $1.str;
- uint str_len= $1.length;
- char *end_ptr= start_ptr + str_len;
- int error;
- prefix_number= my_strtoll10(start_ptr, &end_ptr, &error);
- if ((start_ptr + str_len - 1) == end_ptr)
- {
- switch (end_ptr[0])
- {
- case 'g':
- case 'G': text_shift_number+=30; break;
- case 'm':
- case 'M': text_shift_number+=20; break;
- case 'k':
- case 'K': text_shift_number+=10; break;
- default:
- my_yyabort_error((ER_WRONG_SIZE_NUMBER, MYF(0)));
- }
- if (prefix_number >> 31)
- my_yyabort_error((ER_SIZE_OVERFLOW_ERROR, MYF(0)));
- number= prefix_number << text_shift_number;
- }
- else
- my_yyabort_error((ER_WRONG_SIZE_NUMBER, MYF(0)));
- $$= number;
+ if ($1.to_size_number(&$$))
+ MYSQL_YYABORT;
}
;
@@ -4812,10 +5318,10 @@ create_body:
Lex->create_info.add(DDL_options_st::OPT_LIKE);
TABLE_LIST *src_table= Lex->select_lex.add_table_to_list(thd,
$1, NULL, 0, TL_READ, MDL_SHARED_READ);
- if (! src_table)
+ if (unlikely(! src_table))
MYSQL_YYABORT;
/* CREATE TABLE ... LIKE is not allowed for views. */
- src_table->required_type= FRMTYPE_TABLE;
+ src_table->required_type= TABLE_TYPE_NORMAL;
}
;
@@ -4826,7 +5332,7 @@ create_like:
opt_create_select:
/* empty */ {}
- | opt_duplicate opt_as create_select_query_expression
+ | opt_duplicate opt_as create_select_query_expression opt_versioning_option
;
create_select_query_expression:
@@ -4896,14 +5402,11 @@ partitioning:
{
LEX *lex= Lex;
lex->part_info= new (thd->mem_root) partition_info();
- if (!lex->part_info)
- {
- mem_alloc_error(sizeof(partition_info));
+ if (unlikely(!lex->part_info))
MYSQL_YYABORT;
- }
if (lex->sql_command == SQLCOM_ALTER_TABLE)
{
- lex->alter_info.flags|= Alter_info::ALTER_PARTITION;
+ lex->alter_info.partition_flags|= ALTER_PARTITION_INFO;
}
}
partition
@@ -4913,8 +5416,8 @@ have_partitioning:
/* empty */
{
#ifdef WITH_PARTITION_STORAGE_ENGINE
- LEX_STRING partition_name={C_STRING_WITH_LEN("partition")};
- if (!plugin_is_ready(&partition_name, MYSQL_STORAGE_ENGINE_PLUGIN))
+ 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
@@ -4927,12 +5430,12 @@ have_partitioning:
partition_entry:
PARTITION_SYM
{
- LEX *lex= Lex;
- if (!lex->part_info)
+ if (unlikely(!Lex->part_info))
{
- my_parse_error(thd, ER_PARTITION_ENTRY_ERROR);
+ 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.
@@ -4962,10 +5465,23 @@ part_type_def:
{ Lex->part_info->part_type= RANGE_PARTITION; }
| RANGE_SYM part_column_list
{ Lex->part_info->part_type= RANGE_PARTITION; }
- | LIST_SYM part_func
- { Lex->part_info->part_type= LIST_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:
@@ -4987,7 +5503,7 @@ opt_key_algo:
Lex->part_info->key_algorithm= partition_info::KEY_ALGORITHM_55;
break;
default:
- my_parse_error(thd, ER_SYNTAX_ERROR);
+ thd->parse_error();
MYSQL_YYABORT;
}
}
@@ -5008,12 +5524,10 @@ part_field_item:
{
partition_info *part_info= Lex->part_info;
part_info->num_columns++;
- if (part_info->part_field_list.push_back($1.str, thd->mem_root))
- {
- mem_alloc_error(1);
+ if (unlikely(part_info->part_field_list.push_back($1.str,
+ thd->mem_root)))
MYSQL_YYABORT;
- }
- if (part_info->num_columns > MAX_REF_PARTS)
+ 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"));
}
@@ -5030,21 +5544,21 @@ part_column_list:
part_func:
- '(' remember_name part_func_expr remember_end ')'
+ '(' part_func_expr ')'
{
partition_info *part_info= Lex->part_info;
- if (part_info->set_part_expr(thd, $2 + 1, $3, $4, FALSE))
- { MYSQL_YYABORT; }
+ 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:
- '(' remember_name part_func_expr remember_end ')'
+ '(' part_func_expr ')'
{
- if (Lex->part_info->set_part_expr(thd, $2 + 1, $3, $4, TRUE))
- { MYSQL_YYABORT; }
+ if (unlikely(Lex->part_info->set_part_expr(thd, $2, TRUE)))
+ MYSQL_YYABORT;
}
;
@@ -5055,7 +5569,7 @@ opt_num_parts:
{
uint num_parts= $2;
partition_info *part_info= Lex->part_info;
- if (num_parts == 0)
+ if (unlikely(num_parts == 0))
my_yyabort_error((ER_NO_PARTS_ERROR, MYF(0), "partitions"));
part_info->num_parts= num_parts;
@@ -5087,12 +5601,11 @@ sub_part_field_item:
ident
{
partition_info *part_info= Lex->part_info;
- if (part_info->subpart_field_list.push_back($1.str, thd->mem_root))
- {
- mem_alloc_error(1);
+ if (unlikely(part_info->subpart_field_list.push_back($1.str,
+ thd->mem_root)))
MYSQL_YYABORT;
- }
- if (part_info->subpart_field_list.elements > MAX_REF_PARTS)
+
+ 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"));
}
@@ -5101,9 +5614,9 @@ sub_part_field_item:
part_func_expr:
bit_expr
{
- if (!Lex->safe_to_cache_query)
+ if (unlikely(!Lex->safe_to_cache_query))
{
- my_parse_error(thd, ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR);
+ thd->parse_error(ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR);
MYSQL_YYABORT;
}
$$=$1;
@@ -5116,7 +5629,7 @@ opt_num_subparts:
{
uint num_parts= $2;
LEX *lex= Lex;
- if (num_parts == 0)
+ 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;
@@ -5127,10 +5640,10 @@ part_defs:
/* empty */
{
partition_info *part_info= Lex->part_info;
- if (part_info->part_type == RANGE_PARTITION)
+ if (unlikely(part_info->part_type == RANGE_PARTITION))
my_yyabort_error((ER_PARTITIONS_MUST_BE_DEFINED_ERROR, MYF(0),
"RANGE"));
- if (part_info->part_type == LIST_PARTITION)
+ if (unlikely(part_info->part_type == LIST_PARTITION))
my_yyabort_error((ER_PARTITIONS_MUST_BE_DEFINED_ERROR, MYF(0),
"LIST"));
}
@@ -5140,10 +5653,10 @@ part_defs:
uint count_curr_parts= part_info->partitions.elements;
if (part_info->num_parts != 0)
{
- if (part_info->num_parts !=
- count_curr_parts)
+ if (unlikely(part_info->num_parts !=
+ count_curr_parts))
{
- my_parse_error(thd, ER_PARTITION_WRONG_NO_PART_ERROR);
+ thd->parse_error(ER_PARTITION_WRONG_NO_PART_ERROR);
MYSQL_YYABORT;
}
}
@@ -5166,13 +5679,12 @@ part_definition:
partition_info *part_info= Lex->part_info;
partition_element *p_elem= new (thd->mem_root) partition_element();
- if (!p_elem ||
- part_info->partitions.push_back(p_elem, thd->mem_root))
- {
- mem_alloc_error(sizeof(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;
@@ -5190,7 +5702,7 @@ part_name:
{
partition_info *part_info= Lex->part_info;
partition_element *p_elem= part_info->curr_part_elem;
- if (check_ident_length(&$1))
+ if (unlikely(check_ident_length(&$1)))
MYSQL_YYABORT;
p_elem->partition_name= $1.str;
}
@@ -5203,19 +5715,23 @@ opt_part_values:
partition_info *part_info= lex->part_info;
if (! lex->is_partition_management())
{
- if (part_info->error_if_requires_values())
- MYSQL_YYABORT;
+ 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
+ | VALUES_LESS_SYM THAN_SYM
{
LEX *lex= Lex;
partition_info *part_info= lex->part_info;
if (! lex->is_partition_management())
{
- if (part_info->part_type != RANGE_PARTITION)
+ if (unlikely(part_info->part_type != RANGE_PARTITION))
my_yyabort_error((ER_PARTITION_WRONG_VALUES_ERROR, MYF(0),
"RANGE", "LESS THAN"));
}
@@ -5223,13 +5739,13 @@ opt_part_values:
part_info->part_type= RANGE_PARTITION;
}
part_func_max {}
- | VALUES IN_SYM
+ | VALUES_IN_SYM
{
LEX *lex= Lex;
partition_info *part_info= lex->part_info;
if (! lex->is_partition_management())
{
- if (part_info->part_type != LIST_PARTITION)
+ if (unlikely(part_info->part_type != LIST_PARTITION))
my_yyabort_error((ER_PARTITION_WRONG_VALUES_ERROR, MYF(0),
"LIST", "IN"));
}
@@ -5237,51 +5753,53 @@ opt_part_values:
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 (part_info->part_type != LIST_PARTITION)
+ 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 (part_info->init_column_part(thd))
- {
+ if (unlikely(part_info->init_column_part(thd)))
MYSQL_YYABORT;
- }
- if (part_info->add_max_value(thd))
- {
+ if (unlikely(part_info->add_max_value(thd)))
MYSQL_YYABORT;
- }
}
;
part_func_max:
- MAX_VALUE_SYM
+ MAXVALUE_SYM
{
partition_info *part_info= Lex->part_info;
- if (part_info->num_columns &&
- part_info->num_columns != 1U)
+ if (unlikely(part_info->num_columns &&
+ part_info->num_columns != 1U))
{
part_info->print_debug("Kilroy II", NULL);
- my_parse_error(thd, ER_PARTITION_COLUMN_LIST_ERROR);
+ thd->parse_error(ER_PARTITION_COLUMN_LIST_ERROR);
MYSQL_YYABORT;
}
else
part_info->num_columns= 1U;
- if (part_info->init_column_part(thd))
- {
+ if (unlikely(part_info->init_column_part(thd)))
MYSQL_YYABORT;
- }
- if (part_info->add_max_value(thd))
- {
+ if (unlikely(part_info->add_max_value(thd)))
MYSQL_YYABORT;
- }
}
| part_value_item {}
;
@@ -5295,12 +5813,12 @@ part_values_in:
if (part_info->num_columns != 1U)
{
- if (!lex->is_partition_management() ||
- part_info->num_columns == 0 ||
- part_info->num_columns > MAX_REF_PARTS)
+ 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);
- my_parse_error(thd, ER_PARTITION_COLUMN_LIST_ERROR);
+ thd->parse_error(ER_PARTITION_COLUMN_LIST_ERROR);
MYSQL_YYABORT;
}
/*
@@ -5310,18 +5828,16 @@ part_values_in:
we ADD or REORGANIZE partitions. Also can only happen
for LIST partitions.
*/
- if (part_info->reorganize_into_single_field_col_val(thd))
- {
+ if (unlikely(part_info->reorganize_into_single_field_col_val(thd)))
MYSQL_YYABORT;
- }
}
}
| '(' part_value_list ')'
{
partition_info *part_info= Lex->part_info;
- if (part_info->num_columns < 2U)
+ if (unlikely(part_info->num_columns < 2U))
{
- my_parse_error(thd, ER_ROW_SINGLE_PARTITION_FIELD_ERROR);
+ thd->parse_error(ER_ROW_SINGLE_PARTITION_FIELD_ERROR);
MYSQL_YYABORT;
}
}
@@ -5338,12 +5854,10 @@ 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 (!(part_info->part_type == LIST_PARTITION &&
- part_info->num_columns == 1U) &&
- part_info->init_column_part(thd))
- {
+ 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 {}
')'
@@ -5352,7 +5866,7 @@ part_value_item:
part_info->print_debug(") part_value_item", NULL);
if (part_info->num_columns == 0)
part_info->num_columns= part_info->curr_list_object;
- if (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
@@ -5362,7 +5876,7 @@ part_value_item:
error.
*/
part_info->print_debug("Kilroy I", NULL);
- my_parse_error(thd, ER_PARTITION_COLUMN_LIST_ERROR);
+ thd->parse_error(ER_PARTITION_COLUMN_LIST_ERROR);
MYSQL_YYABORT;
}
part_info->curr_list_object= 0;
@@ -5375,18 +5889,16 @@ part_value_item_list:
;
part_value_expr_item:
- MAX_VALUE_SYM
+ MAXVALUE_SYM
{
partition_info *part_info= Lex->part_info;
- if (part_info->part_type == LIST_PARTITION)
+ if (unlikely(part_info->part_type == LIST_PARTITION))
{
- my_parse_error(thd, ER_MAXVALUE_IN_VALUES_IN);
+ thd->parse_error(ER_MAXVALUE_IN_VALUES_IN);
MYSQL_YYABORT;
}
- if (part_info->add_max_value(thd))
- {
+ if (unlikely(part_info->add_max_value(thd)))
MYSQL_YYABORT;
- }
}
| bit_expr
{
@@ -5394,15 +5906,13 @@ part_value_expr_item:
partition_info *part_info= lex->part_info;
Item *part_expr= $1;
- if (!lex->safe_to_cache_query)
+ if (unlikely(!lex->safe_to_cache_query))
{
- my_parse_error(thd, ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR);
+ thd->parse_error(ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR);
MYSQL_YYABORT;
}
- if (part_info->add_column_list_value(thd, part_expr))
- {
+ if (unlikely(part_info->add_column_list_value(thd, part_expr)))
MYSQL_YYABORT;
- }
}
;
@@ -5411,14 +5921,14 @@ opt_sub_partition:
/* empty */
{
partition_info *part_info= Lex->part_info;
- if (part_info->num_subparts != 0 &&
- !part_info->use_default_subpartitions)
+ 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.
*/
- my_parse_error(thd, ER_PARTITION_WRONG_NO_SUBPART_ERROR);
+ thd->parse_error(ER_PARTITION_WRONG_NO_SUBPART_ERROR);
MYSQL_YYABORT;
}
}
@@ -5427,18 +5937,18 @@ opt_sub_partition:
partition_info *part_info= Lex->part_info;
if (part_info->num_subparts != 0)
{
- if (part_info->num_subparts !=
- part_info->count_curr_subparts)
+ if (unlikely(part_info->num_subparts !=
+ part_info->count_curr_subparts))
{
- my_parse_error(thd, ER_PARTITION_WRONG_NO_SUBPART_ERROR);
+ thd->parse_error(ER_PARTITION_WRONG_NO_SUBPART_ERROR);
MYSQL_YYABORT;
}
}
else if (part_info->count_curr_subparts > 0)
{
- if (part_info->partitions.elements > 1)
+ if (unlikely(part_info->partitions.elements > 1))
{
- my_parse_error(thd, ER_PARTITION_WRONG_NO_SUBPART_ERROR);
+ thd->parse_error(ER_PARTITION_WRONG_NO_SUBPART_ERROR);
MYSQL_YYABORT;
}
part_info->num_subparts= part_info->count_curr_subparts;
@@ -5459,8 +5969,8 @@ sub_part_definition:
partition_element *curr_part= part_info->current_partition;
partition_element *sub_p_elem= new (thd->mem_root)
partition_element(curr_part);
- if (part_info->use_default_subpartitions &&
- part_info->partitions.elements >= 2)
+ if (unlikely(part_info->use_default_subpartitions &&
+ part_info->partitions.elements >= 2))
{
/*
create table t1 (a int)
@@ -5473,15 +5983,14 @@ sub_part_definition:
the second partition (the current partition processed
have already been put into the partitions list.
*/
- my_parse_error(thd, ER_PARTITION_WRONG_NO_SUBPART_ERROR);
+ thd->parse_error(ER_PARTITION_WRONG_NO_SUBPART_ERROR);
MYSQL_YYABORT;
}
- if (!sub_p_elem ||
- curr_part->subpartitions.push_back(sub_p_elem, thd->mem_root))
- {
- mem_alloc_error(sizeof(partition_element));
+ 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;
@@ -5493,7 +6002,7 @@ sub_part_definition:
sub_name:
ident_or_text
{
- if (check_ident_length(&$1))
+ if (unlikely(check_ident_length(&$1)))
MYSQL_YYABORT;
Lex->part_info->curr_part_elem->partition_name= $1.str;
}
@@ -5538,6 +6047,45 @@ opt_part_option:
{ 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
*/
@@ -5717,7 +6265,7 @@ create_table_option:
Lex->create_info.table_options|= HA_OPTION_PACK_KEYS;
break;
default:
- my_parse_error(thd, ER_SYNTAX_ERROR);
+ thd->parse_error();
MYSQL_YYABORT;
}
Lex->create_info.used_fields|= HA_CREATE_USED_PACK_KEYS;
@@ -5738,7 +6286,7 @@ create_table_option:
Lex->create_info.stats_auto_recalc= HA_STATS_AUTO_RECALC_ON;
break;
default:
- my_parse_error(thd, ER_SYNTAX_ERROR);
+ thd->parse_error();
MYSQL_YYABORT;
}
Lex->create_info.used_fields|= HA_CREATE_USED_STATS_AUTO_RECALC;
@@ -5758,7 +6306,7 @@ create_table_option:
Lex->create_info.table_options|= HA_OPTION_STATS_PERSISTENT;
break;
default:
- my_parse_error(thd, ER_SYNTAX_ERROR);
+ thd->parse_error();
MYSQL_YYABORT;
}
Lex->create_info.used_fields|= HA_CREATE_USED_STATS_PERSISTENT;
@@ -5779,9 +6327,9 @@ create_table_option:
larger values. 65535 pages, 16kb each means to sample 1GB, which
is impractical. If at some point this needs to be extended, then
we can store the higher bits from stats_sample_pages in .frm too. */
- if ($3 == 0 || $3 > 0xffff)
+ if (unlikely($3 == 0 || $3 > 0xffff))
{
- my_parse_error(thd, ER_SYNTAX_ERROR);
+ thd->parse_error();
MYSQL_YYABORT;
}
Lex->create_info.stats_sample_pages=$3;
@@ -5884,38 +6432,70 @@ create_table_option:
}
| IDENT_sys equal TEXT_STRING_sys
{
- if ($3.length > ENGINE_OPTION_MAX_LENGTH)
+ if (unlikely($3.length > ENGINE_OPTION_MAX_LENGTH))
my_yyabort_error((ER_VALUE_TOO_LONG, MYF(0), $1.str));
- new (thd->mem_root)
- engine_option_value($1, $3, true, &Lex->create_info.option_list,
- &Lex->option_list_last);
+ (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 ($3.length > ENGINE_OPTION_MAX_LENGTH)
+ if (unlikely($3.length > ENGINE_OPTION_MAX_LENGTH))
my_yyabort_error((ER_VALUE_TOO_LONG, MYF(0), $1.str));
- new (thd->mem_root)
- engine_option_value($1, $3, false, &Lex->create_info.option_list,
- &Lex->option_list_last);
+ (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
{
- new (thd->mem_root)
- engine_option_value($1, $3, &Lex->create_info.option_list,
- &Lex->option_list_last, thd->mem_root);
+ (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
{
- new (thd->mem_root)
- engine_option_value($1, &Lex->create_info.option_list,
- &Lex->option_list_last);
+ (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_TEMPORARY, MYF(0));
+ 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 (Lex->create_info.add_table_option_default_charset($4))
+ if (unlikely(Lex->create_info.add_table_option_default_charset($4)))
MYSQL_YYABORT;
}
;
@@ -5924,13 +6504,11 @@ default_collation:
opt_default COLLATE_SYM opt_equal collation_name_or_default
{
HA_CREATE_INFO *cinfo= &Lex->create_info;
- if ((cinfo->used_fields & HA_CREATE_USED_DEFAULT_CHARSET) &&
- cinfo->default_table_charset && $4 &&
- !($4= merge_charset_and_collation(cinfo->default_table_charset,
- $4)))
- {
+ 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;
@@ -5951,7 +6529,7 @@ known_storage_engines:
ident_or_text
{
plugin_ref plugin;
- if ((plugin= ha_resolve_by_name(thd, &$1, false)))
+ 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));
@@ -5998,6 +6576,7 @@ field_list_item:
column_def { }
| key_def
| constraint_def
+ | period_for_system_time
;
column_def:
@@ -6011,28 +6590,28 @@ key_def:
key_or_index opt_if_not_exists opt_ident opt_USING_key_algorithm
{
Lex->option_list= NULL;
- if (Lex->add_key(Key::MULTIPLE, $3, $4, $2))
+ 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 (Lex->add_key(Key::MULTIPLE, $3, $5, $2))
+ 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 (Lex->add_key($1, $4, HA_KEY_ALG_UNDEF, $3))
+ 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 (Lex->add_key($1, $4, HA_KEY_ALG_UNDEF, $3))
+ if (unlikely(Lex->add_key($1, &$4, HA_KEY_ALG_UNDEF, $3)))
MYSQL_YYABORT;
}
'(' key_list ')' spatial_key_options { }
@@ -6041,7 +6620,7 @@ key_def:
opt_USING_key_algorithm
{
Lex->option_list= NULL;
- if (Lex->add_key($2, $4.str ? $4 : $1, $5, $3))
+ if (unlikely(Lex->add_key($2, $4.str ? &$4 : &$1, $5, $3)))
MYSQL_YYABORT;
}
'(' key_list ')' normal_key_options { }
@@ -6049,16 +6628,17 @@ key_def:
TYPE_SYM btree_or_rtree
{
Lex->option_list= NULL;
- if (Lex->add_key($2, $4.str ? $4 : $1, $6, $3))
+ 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 (Lex->check_add_key($4) ||
- !(Lex->last_key= (new (thd->mem_root)
- Key(Key::MULTIPLE, $1.str ? $1 : $5,
- HA_KEY_ALG_UNDEF, true, $4))))
+ 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;
}
@@ -6066,16 +6646,16 @@ key_def:
{
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,
+ 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 (key == NULL)
+ if (unlikely(key == NULL))
MYSQL_YYABORT;
/*
handle_if_exists_options() expectes the two keys in this order:
@@ -6086,7 +6666,7 @@ key_def:
lex->option_list= NULL;
/* Only used for ALTER TABLE. Ignored otherwise. */
- lex->alter_info.flags|= Alter_info::ADD_FOREIGN_KEY;
+ lex->alter_info.flags|= ALTER_ADD_FOREIGN_KEY;
}
;
@@ -6097,6 +6677,15 @@ constraint_def:
}
;
+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_system_time($4, $6);
+ }
+ ;
+
opt_check_constraint:
/* empty */ { $$= (Virtual_column_info*) 0; }
| check_constraint { $$= $1;}
@@ -6105,12 +6694,9 @@ opt_check_constraint:
check_constraint:
CHECK_SYM '(' expr ')'
{
- Virtual_column_info *v=
- add_virtual_expression(thd, $3);
- if (!v)
- {
+ Virtual_column_info *v= add_virtual_expression(thd, $3);
+ if (unlikely(!v))
MYSQL_YYABORT;
- }
$$= v;
}
;
@@ -6121,7 +6707,7 @@ opt_constraint_no_id:
;
opt_constraint:
- /* empty */ { $$= null_lex_str; }
+ /* empty */ { $$= null_clex_str; }
| constraint { $$= $1; }
;
@@ -6135,43 +6721,44 @@ field_spec:
LEX *lex=Lex;
Create_field *f= new (thd->mem_root) Create_field();
- if (check_string_char_length(&$1, 0, NAME_CHAR_LEN,
- system_charset_info, 1))
+ 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 (!f)
+ if (unlikely(!f))
MYSQL_YYABORT;
- lex->init_last_field(f, $1.str, NULL);
+ 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 ($$->check(thd))
+ 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)
- add_key_to_list(lex, &$1, Key::PRIMARY, Lex->check_exists);
+ lex->add_key_to_list(&$1, Key::PRIMARY, lex->check_exists);
else if ($$->flags & UNIQUE_KEY_FLAG)
- add_key_to_list(lex, &$1, Key::UNIQUE, Lex->check_exists);
+ lex->add_key_to_list(&$1, Key::UNIQUE, lex->check_exists);
}
;
field_type_or_serial:
- field_type { Lex->set_last_field_type($1); } field_def
+ qualified_field_type { Lex->last_field->set_attributes($1, Lex->charset); }
+ field_def
| SERIAL_SYM
{
- Lex_field_type_st type;
- type.set(MYSQL_TYPE_LONGLONG);
- Lex->set_last_field_type(type);
+ Lex->last_field->set_handler(&type_handler_longlong);
Lex->last_field->flags|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG
| UNSIGNED_FLAG | UNIQUE_KEY_FLAG;
}
@@ -6188,15 +6775,37 @@ opt_serial_attribute_list:
| serial_attribute
;
+opt_asrow_attribute:
+ /* empty */ {}
+ | opt_asrow_attribute_list {}
+ ;
+
+opt_asrow_attribute_list:
+ opt_asrow_attribute_list asrow_attribute {}
+ | asrow_attribute
+ ;
field_def:
- opt_attribute
+ /* 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:
@@ -6238,15 +6847,19 @@ vcol_attribute:
{
LEX *lex=Lex;
lex->last_field->flags|= UNIQUE_KEY_FLAG;
- lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX;
+ 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_info::ALTER_ADD_INDEX;
+ 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:
@@ -6262,7 +6875,7 @@ parse_vcol_expr:
expr
{
Virtual_column_info *v= add_virtual_expression(thd, $3);
- if (!v)
+ if (unlikely(!v))
MYSQL_YYABORT;
Lex->last_field->vcol_info= v;
}
@@ -6272,7 +6885,7 @@ parenthesized_expr:
subselect
{
$$= new (thd->mem_root) Item_singlerow_subselect(thd, $1);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| expr
@@ -6280,7 +6893,7 @@ parenthesized_expr:
{
$3->push_front($1, thd->mem_root);
$$= new (thd->mem_root) Item_row(thd, *$3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
;
@@ -6290,10 +6903,8 @@ virtual_column_func:
{
Virtual_column_info *v=
add_virtual_expression(thd, $2);
- if (!v)
- {
+ if (unlikely(!v))
MYSQL_YYABORT;
- }
$$= v;
}
;
@@ -6304,71 +6915,127 @@ column_default_expr:
virtual_column_func
| expr_or_literal
{
- if (!($$= add_virtual_expression(thd, $1)))
+ 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
+ ;
+
+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(MYSQL_TYPE_FLOAT, $2);
+ $$.set(&type_handler_float, $2);
if ($2.length() && !$2.dec())
{
int err;
ulonglong tmp_length= my_strtoll10($2.length(), NULL, &err);
- if (err || tmp_length > PRECISION_FOR_DOUBLE)
+ if (unlikely(err || tmp_length > PRECISION_FOR_DOUBLE))
my_yyabort_error((ER_WRONG_FIELD_SPEC, MYF(0),
- Lex->last_field->field_name));
+ Lex->last_field->field_name.str));
if (tmp_length > PRECISION_FOR_FLOAT)
- $$.set(MYSQL_TYPE_DOUBLE);
+ $$.set(&type_handler_double);
else
- $$.set(MYSQL_TYPE_FLOAT);
+ $$.set(&type_handler_float);
}
}
| BIT_SYM opt_field_length_default_1
{
- $$.set(MYSQL_TYPE_BIT, $2);
+ $$.set(&type_handler_bit, $2);
}
| BOOL_SYM
{
- $$.set(MYSQL_TYPE_TINY, "1");
+ $$.set(&type_handler_tiny, "1");
}
| BOOLEAN_SYM
{
- $$.set(MYSQL_TYPE_TINY, "1");
+ $$.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);
}
- | char opt_field_length_default_1 opt_binary
+ | 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(MYSQL_TYPE_STRING, $2);
+ $$.set(&type_handler_string, $2);
}
| nchar opt_field_length_default_1 opt_bin_mod
{
- $$.set(MYSQL_TYPE_STRING, $2);
+ $$.set(&type_handler_string, $2);
bincmp_collation(national_charset_info, $3);
}
| BINARY opt_field_length_default_1
{
Lex->charset=&my_charset_bin;
- $$.set(MYSQL_TYPE_STRING, $2);
+ $$.set(&type_handler_string, $2);
}
- | varchar field_length opt_binary
+ | varchar field_length opt_binary_and_compression
{
- $$.set(MYSQL_TYPE_VARCHAR, $2);
+ $$.set(&type_handler_varchar, $2);
}
- | nvarchar field_length opt_bin_mod
+ | VARCHAR2_ORACLE_SYM field_length opt_binary_and_compression
{
- $$.set(MYSQL_TYPE_VARCHAR, $2);
- bincmp_collation(national_charset_info, $3);
+ $$.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
+ | VARBINARY field_length opt_compressed
{
Lex->charset=&my_charset_bin;
- $$.set(MYSQL_TYPE_VARCHAR, $2);
+ $$.set(&type_handler_varchar, $2);
+ }
+ | RAW_ORACLE_SYM field_length opt_compressed
+ {
+ Lex->charset= &my_charset_bin;
+ $$.set(&type_handler_varchar, $2);
}
- | YEAR_SYM opt_field_length field_options
+ ;
+
+field_type_temporal:
+ YEAR_SYM opt_field_length field_options
{
if ($2)
{
@@ -6384,98 +7051,105 @@ field_type:
buff, "YEAR(4)");
}
}
- $$.set(MYSQL_TYPE_YEAR, $2);
+ $$.set(&type_handler_year, $2);
}
- | DATE_SYM
- { $$.set(MYSQL_TYPE_DATE); }
+ | DATE_SYM { $$.set(&type_handler_newdate); }
| TIME_SYM opt_field_length
- { $$.set(opt_mysql56_temporal_format ?
- MYSQL_TYPE_TIME2 : MYSQL_TYPE_TIME, $2); }
+ {
+ $$.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
{
- if (thd->variables.sql_mode & MODE_MAXDB)
- $$.set(opt_mysql56_temporal_format ?
- MYSQL_TYPE_DATETIME2 : MYSQL_TYPE_DATETIME, $2);
- else
- {
- /*
- Unlike other types TIMESTAMP fields are NOT NULL by default.
- Unless --explicit-defaults-for-timestamp is given.
- */
- if (!opt_explicit_defaults_for_timestamp)
- Lex->last_field->flags|= NOT_NULL_FLAG;
- $$.set(opt_mysql56_temporal_format ? MYSQL_TYPE_TIMESTAMP2
- : MYSQL_TYPE_TIMESTAMP, $2);
- }
+ $$.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(opt_mysql56_temporal_format ?
- MYSQL_TYPE_DATETIME2 : MYSQL_TYPE_DATETIME, $2); }
- | TINYBLOB
+ {
+ $$.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(MYSQL_TYPE_TINY_BLOB);
+ $$.set(&type_handler_blob, $2);
}
- | BLOB_SYM opt_field_length
+ | BLOB_ORACLE_SYM opt_compressed
{
Lex->charset=&my_charset_bin;
- $$.set(MYSQL_TYPE_BLOB, $2);
+ $$.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(MYSQL_TYPE_GEOMETRY, $2);
+ $$.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
+ | MEDIUMBLOB opt_compressed
{
Lex->charset=&my_charset_bin;
- $$.set(MYSQL_TYPE_MEDIUM_BLOB);
+ $$.set(&type_handler_medium_blob);
}
- | LONGBLOB
+ | LONGBLOB opt_compressed
{
Lex->charset=&my_charset_bin;
- $$.set(MYSQL_TYPE_LONG_BLOB);
+ $$.set(&type_handler_long_blob);
}
- | LONG_SYM VARBINARY
+ | LONG_SYM VARBINARY opt_compressed
{
Lex->charset=&my_charset_bin;
- $$.set(MYSQL_TYPE_MEDIUM_BLOB);
- }
- | LONG_SYM varchar opt_binary
- { $$.set(MYSQL_TYPE_MEDIUM_BLOB); }
- | TINYTEXT opt_binary
- { $$.set(MYSQL_TYPE_TINY_BLOB); }
- | TEXT_SYM opt_field_length opt_binary
- { $$.set(MYSQL_TYPE_BLOB, $2); }
- | MEDIUMTEXT opt_binary
- { $$.set(MYSQL_TYPE_MEDIUM_BLOB); }
- | LONGTEXT opt_binary
- { $$.set(MYSQL_TYPE_LONG_BLOB); }
- | DECIMAL_SYM float_options field_options
- { $$.set(MYSQL_TYPE_NEWDECIMAL, $2);}
- | NUMERIC_SYM float_options field_options
- { $$.set(MYSQL_TYPE_NEWDECIMAL, $2);}
- | FIXED_SYM float_options field_options
- { $$.set(MYSQL_TYPE_NEWDECIMAL, $2);}
- | ENUM '(' string_list ')' opt_binary
- { $$.set(MYSQL_TYPE_ENUM); }
- | SET '(' string_list ')' opt_binary
- { $$.set(MYSQL_TYPE_SET); }
- | LONG_SYM opt_binary
- { $$.set(MYSQL_TYPE_MEDIUM_BLOB); }
- | JSON_SYM
+ $$.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(MYSQL_TYPE_LONG_BLOB);
+ $$.set(&type_handler_long_blob);
}
;
+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; }
@@ -6510,23 +7184,22 @@ nvarchar:
;
int_type:
- INT_SYM { $$=MYSQL_TYPE_LONG; }
- | TINYINT { $$=MYSQL_TYPE_TINY; }
- | SMALLINT { $$=MYSQL_TYPE_SHORT; }
- | MEDIUMINT { $$=MYSQL_TYPE_INT24; }
- | BIGINT { $$=MYSQL_TYPE_LONGLONG; }
+ 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 ?
- MYSQL_TYPE_FLOAT : MYSQL_TYPE_DOUBLE;
+ static_cast<const Type_handler *>(&type_handler_float) :
+ static_cast<const Type_handler *>(&type_handler_double);
}
- | DOUBLE_SYM
- { $$=MYSQL_TYPE_DOUBLE; }
- | DOUBLE_SYM PRECISION
- { $$=MYSQL_TYPE_DOUBLE; }
+ | DOUBLE_SYM { $$= &type_handler_double; }
+ | DOUBLE_SYM PRECISION { $$= &type_handler_double; }
;
srid_option:
@@ -6580,13 +7253,9 @@ opt_precision:
| precision { $$= $1; }
;
-opt_attribute:
- /* empty */ {}
- | opt_attribute_list {}
- ;
-opt_attribute_list:
- opt_attribute_list attribute {}
+attribute_list:
+ attribute_list attribute {}
| attribute
;
@@ -6596,20 +7265,20 @@ attribute:
| ON UPDATE_SYM NOW_SYM opt_default_time_precision
{
Item *item= new (thd->mem_root) Item_func_now_local(thd, $4);
- if (item == NULL)
+ 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_info::ALTER_ADD_INDEX;
+ lex->alter_info.flags|= ALTER_ADD_INDEX;
}
| COLLATE_SYM collation_name
{
- if (Lex->charset && !my_charset_same(Lex->charset,$2))
+ 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;
@@ -6617,41 +7286,111 @@ attribute:
| serial_attribute
;
-serial_attribute:
- not NULL_SYM { Lex->last_field->flags|= NOT_NULL_FLAG; }
+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_info::ALTER_ADD_INDEX;
+ lex->alter_info.flags|= ALTER_ADD_INDEX;
}
| vcol_attribute
+ ;
+
+serial_attribute:
+ asrow_attribute
| IDENT_sys equal TEXT_STRING_sys
{
- if ($3.length > ENGINE_OPTION_MAX_LENGTH)
+ if (unlikely($3.length > ENGINE_OPTION_MAX_LENGTH))
my_yyabort_error((ER_VALUE_TOO_LONG, MYF(0), $1.str));
- new (thd->mem_root)
- engine_option_value($1, $3, true, &Lex->last_field->option_list,
- &Lex->option_list_last);
+ (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 ($3.length > ENGINE_OPTION_MAX_LENGTH)
+ if (unlikely($3.length > ENGINE_OPTION_MAX_LENGTH))
my_yyabort_error((ER_VALUE_TOO_LONG, MYF(0), $1.str));
- new (thd->mem_root)
- engine_option_value($1, $3, false, &Lex->last_field->option_list,
- &Lex->option_list_last);
+ (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
{
- new (thd->mem_root)
- engine_option_value($1, $3, &Lex->last_field->option_list,
- &Lex->option_list_last, thd->mem_root);
+ (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
{
- new (thd->mem_root)
- engine_option_value($1, &Lex->last_field->option_list, &Lex->option_list_last);
+ (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;
}
;
@@ -6659,14 +7398,14 @@ serial_attribute:
type_with_opt_collate:
field_type opt_collate
{
- $$= $1;
+ Lex->map_data_type(Lex_ident_sys(), &($$= $1));
if ($2)
{
- if (!(Lex->charset= merge_charset_and_collation(Lex->charset, $2)))
+ if (unlikely(!(Lex->charset= merge_charset_and_collation(Lex->charset, $2))))
MYSQL_YYABORT;
}
- Lex->set_last_field_type($1);
+ Lex->last_field->set_attributes($$, Lex->charset);
}
;
@@ -6678,7 +7417,7 @@ charset:
charset_name:
ident_or_text
{
- if (!($$=get_charset_by_csname($1.str,MY_CS_PRIMARY,MYF(0))))
+ 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; }
@@ -6697,8 +7436,9 @@ opt_load_data_charset:
old_or_new_charset_name:
ident_or_text
{
- if (!($$=get_charset_by_csname($1.str,MY_CS_PRIMARY,MYF(0))) &&
- !($$=get_old_charset_by_name($1.str)))
+ 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; }
@@ -6712,7 +7452,7 @@ old_or_new_charset_name_or_default:
collation_name:
ident_or_text
{
- if (!($$= mysqld_collation_get_by_name($1.str)))
+ if (unlikely(!($$= mysqld_collation_get_by_name($1.str))))
MYSQL_YYABORT;
}
;
@@ -6737,14 +7477,18 @@ charset_or_alias:
| ASCII_SYM { $$= &my_charset_latin1; }
| UNICODE_SYM
{
- if (!($$= get_charset_by_csname("ucs2", MY_CS_PRIMARY,MYF(0))))
+ 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); }
- | BYTE_SYM { bincmp_collation(&my_charset_bin, 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); }
@@ -6758,9 +7502,9 @@ opt_bin_mod:
ws_nweights:
'(' real_ulong_num
{
- if ($2 == 0)
+ if (unlikely($2 == 0))
{
- my_parse_error(thd, ER_SYNTAX_ERROR);
+ thd->parse_error();
MYSQL_YYABORT;
}
}
@@ -6848,15 +7592,15 @@ opt_ref_list:
ref_list:
ref_list ',' ident
{
- Key_part_spec *key= new (thd->mem_root) Key_part_spec($3, 0);
- if (key == NULL)
+ 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 (key == NULL)
+ 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();
@@ -7011,35 +7755,39 @@ key_using_alg:
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.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 ($3.length > ENGINE_OPTION_MAX_LENGTH)
+ if (unlikely($3.length > ENGINE_OPTION_MAX_LENGTH))
my_yyabort_error((ER_VALUE_TOO_LONG, MYF(0), $1.str));
- new (thd->mem_root)
- engine_option_value($1, $3, true, &Lex->option_list,
- &Lex->option_list_last);
+ (void) new (thd->mem_root)
+ engine_option_value($1, $3, true, &Lex->option_list,
+ &Lex->option_list_last);
}
| IDENT_sys equal ident
{
- if ($3.length > ENGINE_OPTION_MAX_LENGTH)
+ if (unlikely($3.length > ENGINE_OPTION_MAX_LENGTH))
my_yyabort_error((ER_VALUE_TOO_LONG, MYF(0), $1.str));
- new (thd->mem_root)
- engine_option_value($1, $3, false, &Lex->option_list,
- &Lex->option_list_last);
+ (void) new (thd->mem_root)
+ engine_option_value($1, $3, false, &Lex->option_list,
+ &Lex->option_list_last);
}
| IDENT_sys equal real_ulonglong_num
{
- new (thd->mem_root)
- engine_option_value($1, $3, &Lex->option_list,
- &Lex->option_list_last, thd->mem_root);
+ (void) new (thd->mem_root)
+ engine_option_value($1, $3, &Lex->option_list,
+ &Lex->option_list_last, thd->mem_root);
}
| IDENT_sys equal DEFAULT
{
- new (thd->mem_root)
- engine_option_value($1, &Lex->option_list, &Lex->option_list_last);
+ (void) new (thd->mem_root)
+ engine_option_value($1, &Lex->option_list,
+ &Lex->option_list_last);
}
;
@@ -7056,7 +7804,7 @@ fulltext_key_opt:
all_key_opt
| WITH PARSER_SYM IDENT_sys
{
- if (plugin_is_ready(&$3, MYSQL_FTPARSER_PLUGIN))
+ 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));
@@ -7083,31 +7831,26 @@ key_list:
key_part:
ident
{
- $$= new (thd->mem_root) Key_part_spec($1, 0);
- if ($$ == NULL)
+ $$= new (thd->mem_root) Key_part_spec(&$1, 0);
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| ident '(' NUM ')'
{
int key_part_len= atoi($3.str);
- if (!key_part_len)
+ 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 ($$ == NULL)
+ $$= new (thd->mem_root) Key_part_spec(&$1, (uint) key_part_len);
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
;
opt_ident:
- /* empty */ { $$= null_lex_str; }
+ /* empty */ { $$= null_clex_str; }
| field_ident { $$= $1; }
;
-opt_component:
- /* empty */ { $$= null_lex_str; }
- | '.' ident { $$= $2; }
- ;
-
string_list:
text_string
{ Lex->last_field->interval_list.push_back($1, thd->mem_root); }
@@ -7122,8 +7865,8 @@ string_list:
alter:
ALTER
{
- Lex->name= null_lex_str;
- Lex->only_view= FALSE;
+ Lex->name= null_clex_str;
+ Lex->table_type= TABLE_TYPE_UNKNOWN;
Lex->sql_command= SQLCOM_ALTER_TABLE;
Lex->duplicates= DUP_ERROR;
Lex->select_lex.init_order();
@@ -7134,23 +7877,24 @@ alter:
Lex->create_info.storage_media= HA_SM_DEFAULT;
DBUG_ASSERT(!Lex->m_sql_cmd);
}
- alter_options TABLE_SYM table_ident
+ alter_options TABLE_SYM table_ident opt_lock_wait_timeout
{
- if (!Lex->select_lex.add_table_to_list(thd, $5, NULL,
- TL_OPTION_UPDATING,
- TL_READ_NO_INSERT,
- MDL_SHARED_UPGRADABLE))
+ if (unlikely(!Lex->select_lex.add_table_to_list(thd, $5, NULL,
+ TL_OPTION_UPDATING,
+ TL_READ_NO_INSERT,
+ MDL_SHARED_UPGRADABLE)))
MYSQL_YYABORT;
Lex->select_lex.db= (Lex->select_lex.table_list.first)->db;
Lex->create_last_non_select_table= Lex->last_table();
+ Lex->mark_first_table_as_inserting();
}
alter_commands
{
- if (!Lex->m_sql_cmd)
+ 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 (Lex->m_sql_cmd == NULL)
+ if (unlikely(Lex->m_sql_cmd == NULL))
MYSQL_YYABORT;
}
}
@@ -7165,13 +7909,13 @@ alter:
lex->sql_command=SQLCOM_ALTER_DB;
lex->name= $3;
if (lex->name.str == NULL &&
- lex->copy_db_to(&lex->name.str, &lex->name.length))
+ unlikely(lex->copy_db_to(&lex->name)))
MYSQL_YYABORT;
}
| ALTER DATABASE ident UPGRADE_SYM DATA_SYM DIRECTORY_SYM NAME_SYM
{
LEX *lex= Lex;
- if (lex->sphead)
+ 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;
@@ -7180,9 +7924,9 @@ alter:
{
LEX *lex= Lex;
- if (lex->sphead)
+ if (unlikely(lex->sphead))
my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "PROCEDURE"));
- bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
+ lex->sp_chistics.init();
}
sp_a_chistics
{
@@ -7195,9 +7939,9 @@ alter:
{
LEX *lex= Lex;
- if (lex->sphead)
+ if (unlikely(lex->sphead))
my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "FUNCTION"));
- bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
+ lex->sp_chistics.init();
}
sp_a_chistics
{
@@ -7206,31 +7950,24 @@ alter:
lex->sql_command= SQLCOM_ALTER_FUNCTION;
lex->spname= $3;
}
- | ALTER view_algorithm definer_opt
+ | ALTER view_algorithm definer_opt opt_view_suid VIEW_SYM table_ident
{
- LEX *lex= Lex;
-
- if (lex->sphead)
- my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "ALTER VIEW"));
- lex->create_view_mode= VIEW_ALTER;
+ if (unlikely(Lex->add_alter_view(thd, $2, $4, $6)))
+ MYSQL_YYABORT;
}
- view_tail
+ view_list_opt AS view_select
{}
- | ALTER definer_opt
+ | 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.
*/
{
- LEX *lex= Lex;
-
- if (lex->sphead)
- my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "ALTER VIEW"));
- lex->create_view_algorithm= VIEW_ALGORITHM_INHERIT;
- lex->create_view_mode= VIEW_ALTER;
+ if (unlikely(Lex->add_alter_view(thd, VIEW_ALGORITHM_INHERIT, $3, $5)))
+ MYSQL_YYABORT;
}
- view_tail
+ view_list_opt AS view_select
{}
| ALTER definer_opt remember_name EVENT_SYM sp_name
{
@@ -7242,7 +7979,7 @@ alter:
Event_parse_data.
*/
- if (!(Lex->event_parse_data= Event_parse_data::new_instance(thd)))
+ if (unlikely(!(Lex->event_parse_data= Event_parse_data::new_instance(thd))))
MYSQL_YYABORT;
Lex->event_parse_data->identifier= $5;
@@ -7255,9 +7992,9 @@ alter:
opt_ev_comment
opt_ev_sql_stmt
{
- if (!($7 || $8 || $9 || $10 || $11))
+ if (unlikely(!($7 || $8 || $9 || $10 || $11)))
{
- my_parse_error(thd, ER_SYNTAX_ERROR);
+ thd->parse_error();
MYSQL_YYABORT;
}
/*
@@ -7300,6 +8037,34 @@ alter:
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);
+ }
+ table_ident
+ {
+ LEX *lex= Lex;
+ if (unlikely(!(lex->create_info.seq_create_info=
+ new (thd->mem_root) sequence_definition())) ||
+ unlikely(!lex->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;
+ }
;
ev_alter_on_schedule_completion:
@@ -7328,8 +8093,8 @@ opt_ev_sql_stmt:
;
ident_or_empty:
- /* empty */ { $$= null_lex_str; }
- | ident { $$= $1; }
+ /* empty */ { $$= Lex_ident_sys(); }
+ | ident
;
alter_commands:
@@ -7339,7 +8104,7 @@ alter_commands:
Lex->m_sql_cmd= new (thd->mem_root)
Sql_cmd_discard_import_tablespace(
Sql_cmd_discard_import_tablespace::DISCARD_TABLESPACE);
- if (Lex->m_sql_cmd == NULL)
+ if (unlikely(Lex->m_sql_cmd == NULL))
MYSQL_YYABORT;
}
| IMPORT TABLESPACE
@@ -7347,7 +8112,7 @@ alter_commands:
Lex->m_sql_cmd= new (thd->mem_root)
Sql_cmd_discard_import_tablespace(
Sql_cmd_discard_import_tablespace::IMPORT_TABLESPACE);
- if (Lex->m_sql_cmd == NULL)
+ if (unlikely(Lex->m_sql_cmd == NULL))
MYSQL_YYABORT;
}
| alter_list
@@ -7366,7 +8131,7 @@ alter_commands:
| add_partition_rule
| DROP PARTITION_SYM opt_if_exists alt_part_name_list
{
- Lex->alter_info.flags|= Alter_info::ALTER_DROP_PARTITION;
+ Lex->alter_info.partition_flags|= ALTER_PARTITION_DROP;
DBUG_ASSERT(!Lex->if_exists());
Lex->create_info.add($3);
}
@@ -7374,7 +8139,7 @@ alter_commands:
all_or_alt_part_name_list
{
LEX *lex= Lex;
- lex->alter_info.flags|= Alter_info::ALTER_REBUILD_PARTITION;
+ lex->alter_info.partition_flags|= ALTER_PARTITION_REBUILD;
lex->no_write_to_binlog= $3;
}
| OPTIMIZE PARTITION_SYM opt_no_write_to_binlog
@@ -7386,7 +8151,7 @@ alter_commands:
DBUG_ASSERT(!lex->m_sql_cmd);
lex->m_sql_cmd= new (thd->mem_root)
Sql_cmd_alter_table_optimize_partition();
- if (lex->m_sql_cmd == NULL)
+ if (unlikely(lex->m_sql_cmd == NULL))
MYSQL_YYABORT;
}
opt_no_write_to_binlog
@@ -7399,7 +8164,7 @@ alter_commands:
DBUG_ASSERT(!lex->m_sql_cmd);
lex->m_sql_cmd= new (thd->mem_root)
Sql_cmd_alter_table_analyze_partition();
- if (lex->m_sql_cmd == NULL)
+ if (unlikely(lex->m_sql_cmd == NULL))
MYSQL_YYABORT;
}
| CHECK_SYM PARTITION_SYM all_or_alt_part_name_list
@@ -7409,7 +8174,7 @@ alter_commands:
DBUG_ASSERT(!lex->m_sql_cmd);
lex->m_sql_cmd= new (thd->mem_root)
Sql_cmd_alter_table_check_partition();
- if (lex->m_sql_cmd == NULL)
+ if (unlikely(lex->m_sql_cmd == NULL))
MYSQL_YYABORT;
}
opt_mi_check_type
@@ -7422,14 +8187,14 @@ alter_commands:
DBUG_ASSERT(!lex->m_sql_cmd);
lex->m_sql_cmd= new (thd->mem_root)
Sql_cmd_alter_table_repair_partition();
- if (lex->m_sql_cmd == NULL)
+ 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.flags|= Alter_info::ALTER_COALESCE_PARTITION;
+ lex->alter_info.partition_flags|= ALTER_PARTITION_COALESCE;
lex->no_write_to_binlog= $3;
lex->alter_info.num_parts= $4;
}
@@ -7440,7 +8205,7 @@ alter_commands:
DBUG_ASSERT(!lex->m_sql_cmd);
lex->m_sql_cmd= new (thd->mem_root)
Sql_cmd_alter_table_truncate_partition();
- if (lex->m_sql_cmd == NULL)
+ if (unlikely(lex->m_sql_cmd == NULL))
MYSQL_YYABORT;
}
| reorg_partition_rule
@@ -7448,24 +8213,21 @@ alter_commands:
WITH TABLE_SYM table_ident have_partitioning
{
LEX *lex= thd->lex;
- size_t dummy;
- lex->select_lex.db=$6->db.str;
- if (lex->select_lex.db == NULL &&
- lex->copy_db_to(&lex->select_lex.db, &dummy))
- {
+ lex->select_lex.db= $6->db;
+ if (lex->select_lex.db.str == NULL &&
+ unlikely(lex->copy_db_to(&lex->select_lex.db)))
MYSQL_YYABORT;
- }
lex->name= $6->table;
- lex->alter_info.flags|= Alter_info::ALTER_EXCHANGE_PARTITION;
- if (!lex->select_lex.add_table_to_list(thd, $6, NULL,
- TL_OPTION_UPDATING,
- TL_READ_NO_INSERT,
- MDL_SHARED_NO_WRITE))
+ lex->alter_info.partition_flags|= ALTER_PARTITION_EXCHANGE;
+ if (unlikely(!lex->select_lex.add_table_to_list(thd, $6, NULL,
+ TL_OPTION_UPDATING,
+ TL_READ_NO_INSERT,
+ MDL_SHARED_NO_WRITE)))
MYSQL_YYABORT;
DBUG_ASSERT(!lex->m_sql_cmd);
lex->m_sql_cmd= new (thd->mem_root)
Sql_cmd_alter_table_exchange_partition();
- if (lex->m_sql_cmd == NULL)
+ if (unlikely(lex->m_sql_cmd == NULL))
MYSQL_YYABORT;
}
;
@@ -7473,14 +8235,14 @@ alter_commands:
remove_partitioning:
REMOVE_SYM PARTITIONING_SYM
{
- Lex->alter_info.flags|= Alter_info::ALTER_REMOVE_PARTITIONING;
+ Lex->alter_info.partition_flags|= ALTER_PARTITION_REMOVE;
}
;
all_or_alt_part_name_list:
ALL
{
- Lex->alter_info.flags|= Alter_info::ALTER_ALL_PARTITION;
+ Lex->alter_info.partition_flags|= ALTER_PARTITION_ALL;
}
| alt_part_name_list
;
@@ -7491,12 +8253,10 @@ add_partition_rule:
{
LEX *lex= Lex;
lex->part_info= new (thd->mem_root) partition_info();
- if (!lex->part_info)
- {
- mem_alloc_error(sizeof(partition_info));
+ if (unlikely(!lex->part_info))
MYSQL_YYABORT;
- }
- lex->alter_info.flags|= Alter_info::ALTER_ADD_PARTITION;
+
+ 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;
@@ -7523,11 +8283,9 @@ reorg_partition_rule:
{
LEX *lex= Lex;
lex->part_info= new (thd->mem_root) partition_info();
- if (!lex->part_info)
- {
- mem_alloc_error(sizeof(partition_info));
+ if (unlikely(!lex->part_info))
MYSQL_YYABORT;
- }
+
lex->no_write_to_binlog= $3;
}
reorg_parts_rule
@@ -7536,11 +8294,11 @@ reorg_partition_rule:
reorg_parts_rule:
/* empty */
{
- Lex->alter_info.flags|= Alter_info::ALTER_TABLE_REORG;
+ Lex->alter_info.partition_flags|= ALTER_PARTITION_TABLE_REORG;
}
| alt_part_name_list
{
- Lex->alter_info.flags|= Alter_info::ALTER_REORGANIZE_PARTITION;
+ Lex->alter_info.partition_flags|= ALTER_PARTITION_REORGANIZE;
}
INTO '(' part_def_list ')'
{
@@ -7557,12 +8315,9 @@ alt_part_name_list:
alt_part_name_item:
ident
{
- if (Lex->alter_info.partition_names.push_back($1.str,
- thd->mem_root))
- {
- mem_alloc_error(1);
+ if (unlikely(Lex->alter_info.partition_names.push_back($1.str,
+ thd->mem_root)))
MYSQL_YYABORT;
- }
}
;
@@ -7577,50 +8332,53 @@ alter_list:
add_column:
ADD opt_column opt_if_not_exists_table_element
- {
- LEX *lex=Lex;
- lex->alter_info.flags|= Alter_info::ALTER_ADD_COLUMN;
- }
;
alter_list_item:
add_column column_def opt_place
{
- Lex->create_last_non_select_table= Lex->last_table();
+ 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_info::ALTER_ADD_INDEX;
+ Lex->alter_info.flags|= ALTER_ADD_INDEX;
+ }
+ | ADD period_for_system_time
+ {
+ Lex->alter_info.flags|= ALTER_ADD_PERIOD;
}
| add_column '(' create_field_list ')'
{
- Lex->alter_info.flags|= Alter_info::ALTER_ADD_COLUMN |
- Alter_info::ALTER_ADD_INDEX;
+ 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_info::ALTER_ADD_CHECK_CONSTRAINT;
+ Lex->alter_info.flags|= ALTER_ADD_CHECK_CONSTRAINT;
}
| ADD CONSTRAINT IF_SYM not EXISTS field_ident check_constraint
{
- Lex->alter_info.flags|= Alter_info::ALTER_ADD_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_info::ALTER_CHANGE_COLUMN |
- Alter_info::ALTER_RENAME_COLUMN);
+ Lex->alter_info.flags|= ALTER_CHANGE_COLUMN | ALTER_RENAME_COLUMN;
Lex->create_last_non_select_table= Lex->last_table();
- $5->change= $4.str;
+ $5->change= $4;
$5->after= $6;
}
| MODIFY_SYM opt_column opt_if_exists_table_element
field_spec opt_place
{
- Lex->alter_info.flags|= Alter_info::ALTER_CHANGE_COLUMN;
+ Lex->alter_info.flags|= ALTER_CHANGE_COLUMN;
Lex->create_last_non_select_table= Lex->last_table();
$4->change= $4->field_name;
$4->after= $5;
@@ -7630,10 +8388,10 @@ alter_list_item:
LEX *lex=Lex;
Alter_drop *ad= (new (thd->mem_root)
Alter_drop(Alter_drop::COLUMN, $4.str, $3));
- if (ad == NULL)
+ if (unlikely(ad == NULL))
MYSQL_YYABORT;
lex->alter_info.drop_list.push_back(ad, thd->mem_root);
- lex->alter_info.flags|= Alter_info::ALTER_DROP_COLUMN;
+ lex->alter_info.flags|= ALTER_PARSER_DROP_COLUMN;
}
| DROP CONSTRAINT opt_if_exists_table_element field_ident
{
@@ -7641,20 +8399,20 @@ alter_list_item:
Alter_drop *ad= (new (thd->mem_root)
Alter_drop(Alter_drop::CHECK_CONSTRAINT,
$4.str, $3));
- if (ad == NULL)
+ if (unlikely(ad == NULL))
MYSQL_YYABORT;
lex->alter_info.drop_list.push_back(ad, thd->mem_root);
- lex->alter_info.flags|= Alter_info::ALTER_DROP_CHECK_CONSTRAINT;
+ 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 (ad == NULL)
+ if (unlikely(ad == NULL))
MYSQL_YYABORT;
lex->alter_info.drop_list.push_back(ad, thd->mem_root);
- lex->alter_info.flags|= Alter_info::DROP_FOREIGN_KEY;
+ lex->alter_info.flags|= ALTER_DROP_FOREIGN_KEY;
}
| DROP opt_constraint_no_id PRIMARY_SYM KEY_SYM
{
@@ -7662,67 +8420,57 @@ alter_list_item:
Alter_drop *ad= (new (thd->mem_root)
Alter_drop(Alter_drop::KEY, primary_key_name,
FALSE));
- if (ad == NULL)
+ if (unlikely(ad == NULL))
MYSQL_YYABORT;
lex->alter_info.drop_list.push_back(ad, thd->mem_root);
- lex->alter_info.flags|= Alter_info::ALTER_DROP_INDEX;
+ 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 (ad == NULL)
+ if (unlikely(ad == NULL))
MYSQL_YYABORT;
lex->alter_info.drop_list.push_back(ad, thd->mem_root);
- lex->alter_info.flags|= Alter_info::ALTER_DROP_INDEX;
+ 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_info::ALTER_KEYS_ONOFF;
+ 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_info::ALTER_KEYS_ONOFF;
+ lex->alter_info.flags|= ALTER_KEYS_ONOFF;
}
- | ALTER opt_column field_ident SET DEFAULT column_default_expr
+ | ALTER opt_column opt_if_exists_table_element field_ident SET DEFAULT column_default_expr
{
- LEX *lex=Lex;
- Alter_column *ac= new (thd->mem_root) Alter_column($3.str,$6);
- if (ac == NULL)
+ if (unlikely(Lex->add_alter_list($4.str, $7, $3)))
MYSQL_YYABORT;
- lex->alter_info.alter_list.push_back(ac, thd->mem_root);
- lex->alter_info.flags|= Alter_info::ALTER_CHANGE_COLUMN_DEFAULT;
}
- | ALTER opt_column field_ident DROP DEFAULT
+ | ALTER opt_column opt_if_exists_table_element field_ident DROP DEFAULT
{
- LEX *lex=Lex;
- Alter_column *ac= (new (thd->mem_root)
- Alter_column($3.str, (Virtual_column_info*) 0));
- if (ac == NULL)
+ if (unlikely(Lex->add_alter_list($4.str, (Virtual_column_info*) 0,
+ $3)))
MYSQL_YYABORT;
- lex->alter_info.alter_list.push_back(ac, thd->mem_root);
- lex->alter_info.flags|= Alter_info::ALTER_CHANGE_COLUMN_DEFAULT;
}
| RENAME opt_to table_ident
{
LEX *lex=Lex;
- size_t dummy;
- lex->select_lex.db=$3->db.str;
- if (lex->select_lex.db == NULL &&
- lex->copy_db_to(&lex->select_lex.db, &dummy))
- {
+ lex->select_lex.db= $3->db;
+ if (lex->select_lex.db.str == NULL &&
+ unlikely(lex->copy_db_to(&lex->select_lex.db)))
MYSQL_YYABORT;
- }
- if (check_table_name($3->table.str,$3->table.length, FALSE) ||
- ($3->db.str && check_db_name(&$3->db)))
+ 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_info::ALTER_RENAME;
+ lex->alter_info.flags|= ALTER_RENAME;
}
| CONVERT_SYM TO_SYM charset charset_name_or_default opt_collate
{
@@ -7731,29 +8479,43 @@ alter_list_item:
$4= thd->variables.collation_database;
}
$5= $5 ? $5 : $4;
- if (!my_charset_same($4,$5))
+ if (unlikely(!my_charset_same($4,$5)))
my_yyabort_error((ER_COLLATION_CHARSET_MISMATCH, MYF(0),
$5->name, $4->csname));
- if (Lex->create_info.add_alter_list_item_convert_to_charset($5))
+ if (unlikely(Lex->create_info.add_alter_list_item_convert_to_charset($5)))
MYSQL_YYABORT;
- Lex->alter_info.flags|= Alter_info::ALTER_OPTIONS;
+ Lex->alter_info.flags|= ALTER_OPTIONS;
}
| create_table_options_space_separated
{
LEX *lex=Lex;
- lex->alter_info.flags|= Alter_info::ALTER_OPTIONS;
+ lex->alter_info.flags|= ALTER_OPTIONS;
}
| FORCE_SYM
{
- Lex->alter_info.flags|= Alter_info::ALTER_RECREATE;
+ Lex->alter_info.flags|= ALTER_RECREATE;
}
| alter_order_clause
{
LEX *lex=Lex;
- lex->alter_info.flags|= Alter_info::ALTER_ORDER;
+ 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;
+ }
;
opt_index_lock_algorithm:
@@ -7767,12 +8529,12 @@ opt_index_lock_algorithm:
alter_algorithm_option:
ALGORITHM_SYM opt_equal DEFAULT
{
- Lex->alter_info.requested_algorithm=
- Alter_info::ALTER_TABLE_ALGORITHM_DEFAULT;
+ Lex->alter_info.set_requested_algorithm(
+ Alter_info::ALTER_TABLE_ALGORITHM_DEFAULT);
}
| ALGORITHM_SYM opt_equal ident
{
- if (Lex->alter_info.set_requested_algorithm(&$3))
+ if (unlikely(Lex->alter_info.set_requested_algorithm(&$3)))
my_yyabort_error((ER_UNKNOWN_ALTER_ALGORITHM, MYF(0), $3.str));
}
;
@@ -7785,13 +8547,13 @@ alter_lock_option:
}
| LOCK_SYM opt_equal ident
{
- if (Lex->alter_info.set_requested_lock(&$3))
+ if (unlikely(Lex->alter_info.set_requested_lock(&$3)))
my_yyabort_error((ER_UNKNOWN_ALTER_LOCK, MYF(0), $3.str));
}
;
opt_column:
- /* empty */ {}
+ /* empty */ {} %prec PREC_BELOW_IDENTIFIER_OPT_SPECIAL_CASE
| COLUMN_SYM {}
;
@@ -7831,16 +8593,17 @@ opt_restrict:
;
opt_place:
- /* empty */ { $$= NULL; }
+ /* empty */ { $$= null_clex_str; }
| AFTER_SYM ident
{
- $$= $2.str;
- Lex->alter_info.flags |= Alter_info::ALTER_COLUMN_ORDER;
+ $$= $2;
+ Lex->alter_info.flags |= ALTER_COLUMN_ORDER;
}
| FIRST_SYM
{
- $$= first_keyword;
- Lex->alter_info.flags |= Alter_info::ALTER_COLUMN_ORDER;
+ $$.str= first_keyword;
+ $$.length= 5; /* Length of "first" */
+ Lex->alter_info.flags |= ALTER_COLUMN_ORDER;
}
;
@@ -7891,10 +8654,10 @@ start:
LEX *lex= Lex;
lex->sql_command= SQLCOM_BEGIN;
/* READ ONLY and READ WRITE are mutually exclusive. */
- if (($3 & MYSQL_START_TRANS_OPT_READ_WRITE) &&
- ($3 & MYSQL_START_TRANS_OPT_READ_ONLY))
+ if (unlikely(($3 & MYSQL_START_TRANS_OPT_READ_WRITE) &&
+ ($3 & MYSQL_START_TRANS_OPT_READ_ONLY)))
{
- my_parse_error(thd, ER_SYNTAX_ERROR);
+ thd->parse_error();
MYSQL_YYABORT;
}
lex->start_transaction_opt= $3;
@@ -7960,10 +8723,10 @@ slave_until:
| UNTIL_SYM slave_until_opts
{
LEX *lex=Lex;
- if (((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)))
+ 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
@@ -7997,7 +8760,9 @@ opt_checksum_type:
repair_table_or_view:
table_or_tables table_list opt_mi_repair_type
- | VIEW_SYM { Lex->only_view= TRUE; } table_list opt_view_repair_type
+ | VIEW_SYM
+ { Lex->table_type= TABLE_TYPE_VIEW; }
+ table_list opt_view_repair_type
;
repair:
@@ -8016,7 +8781,7 @@ repair:
LEX* lex= thd->lex;
DBUG_ASSERT(!lex->m_sql_cmd);
lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_repair_table();
- if (lex->m_sql_cmd == NULL)
+ if (unlikely(lex->m_sql_cmd == NULL))
MYSQL_YYABORT;
}
;
@@ -8058,7 +8823,7 @@ analyze:
LEX* lex= thd->lex;
DBUG_ASSERT(!lex->m_sql_cmd);
lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_analyze_table();
- if (lex->m_sql_cmd == NULL)
+ if (unlikely(lex->m_sql_cmd == NULL))
MYSQL_YYABORT;
}
;
@@ -8094,7 +8859,7 @@ persistent_column_stat_spec:
{
LEX* lex= thd->lex;
lex->column_list= new (thd->mem_root) List<LEX_STRING>;
- if (lex->column_list == NULL)
+ if (unlikely(lex->column_list == NULL))
MYSQL_YYABORT;
}
table_column_list
@@ -8107,7 +8872,7 @@ persistent_index_stat_spec:
{
LEX* lex= thd->lex;
lex->index_list= new (thd->mem_root) List<LEX_STRING>;
- if (lex->index_list == NULL)
+ if (unlikely(lex->index_list == NULL))
MYSQL_YYABORT;
}
table_index_list
@@ -8172,7 +8937,9 @@ binlog_base64_event:
check_view_or_table:
table_or_tables table_list opt_mi_check_type
- | VIEW_SYM { Lex->only_view= TRUE; } table_list opt_view_check_type
+ | VIEW_SYM
+ { Lex->table_type= TABLE_TYPE_VIEW; }
+ table_list opt_view_check_type
;
check: CHECK_SYM
@@ -8188,11 +8955,11 @@ check: CHECK_SYM
check_view_or_table
{
LEX* lex= thd->lex;
- if (lex->sphead)
+ 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 (lex->m_sql_cmd == NULL)
+ if (unlikely(lex->m_sql_cmd == NULL))
MYSQL_YYABORT;
}
;
@@ -8232,12 +8999,12 @@ optimize:
/* Will be overridden during execution. */
YYPS->m_lock_type= TL_UNLOCK;
}
- table_list
+ 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 (lex->m_sql_cmd == NULL)
+ if (unlikely(lex->m_sql_cmd == NULL))
MYSQL_YYABORT;
}
;
@@ -8264,14 +9031,14 @@ rename:
rename_list:
user TO_SYM user
{
- if (Lex->users_list.push_back($1, thd->mem_root) ||
- Lex->users_list.push_back($3, thd->mem_root))
+ 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 (Lex->users_list.push_back($3, thd->mem_root) ||
- Lex->users_list.push_back($5, thd->mem_root))
+ if (unlikely(Lex->users_list.push_back($3, thd->mem_root) ||
+ Lex->users_list.push_back($5, thd->mem_root)))
MYSQL_YYABORT;
}
;
@@ -8282,14 +9049,16 @@ table_to_table_list:
;
table_to_table:
- table_ident TO_SYM table_ident
+ table_ident opt_lock_wait_timeout TO_SYM table_ident
{
LEX *lex=Lex;
SELECT_LEX *sl= lex->current_select;
- if (!sl->add_table_to_list(thd, $1,NULL,TL_OPTION_UPDATING,
- TL_IGNORE, MDL_EXCLUSIVE) ||
- !sl->add_table_to_list(thd, $3,NULL,TL_OPTION_UPDATING,
- TL_IGNORE, MDL_EXCLUSIVE))
+ 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;
}
;
@@ -8320,9 +9089,10 @@ keycache_list:
assign_to_keycache:
table_ident cache_keys_spec
{
- if (!Select->add_table_to_list(thd, $1, NULL, 0, TL_READ,
- MDL_SHARED_READ,
- Select->pop_index_hints()))
+ if (unlikely(!Select->add_table_to_list(thd, $1, NULL, 0, TL_READ,
+ MDL_SHARED_READ,
+ Select->
+ pop_index_hints())))
MYSQL_YYABORT;
}
;
@@ -8330,9 +9100,10 @@ assign_to_keycache:
assign_to_keycache_parts:
table_ident adm_partition cache_keys_spec
{
- if (!Select->add_table_to_list(thd, $1, NULL, 0, TL_READ,
- MDL_SHARED_READ,
- Select->pop_index_hints()))
+ if (unlikely(!Select->add_table_to_list(thd, $1, NULL, 0, TL_READ,
+ MDL_SHARED_READ,
+ Select->
+ pop_index_hints())))
MYSQL_YYABORT;
}
;
@@ -8366,9 +9137,10 @@ preload_list:
preload_keys:
table_ident cache_keys_spec opt_ignore_leaves
{
- if (!Select->add_table_to_list(thd, $1, NULL, $3, TL_READ,
- MDL_SHARED_READ,
- Select->pop_index_hints()))
+ if (unlikely(!Select->add_table_to_list(thd, $1, NULL, $3, TL_READ,
+ MDL_SHARED_READ,
+ Select->
+ pop_index_hints())))
MYSQL_YYABORT;
}
;
@@ -8376,9 +9148,10 @@ preload_keys:
preload_keys_parts:
table_ident adm_partition cache_keys_spec opt_ignore_leaves
{
- if (!Select->add_table_to_list(thd, $1, NULL, $4, TL_READ,
- MDL_SHARED_READ,
- Select->pop_index_hints()))
+ if (unlikely(!Select->add_table_to_list(thd, $1, NULL, $4, TL_READ,
+ MDL_SHARED_READ,
+ Select->
+ pop_index_hints())))
MYSQL_YYABORT;
}
;
@@ -8386,7 +9159,7 @@ preload_keys_parts:
adm_partition:
PARTITION_SYM have_partitioning
{
- Lex->alter_info.flags|= Alter_info::ALTER_ADMIN_PARTITION;
+ Lex->alter_info.partition_flags|= ALTER_PARTITION_ADMIN;
}
'(' all_or_alt_part_name_list ')'
;
@@ -8427,6 +9200,9 @@ select:
select_init:
SELECT_SYM select_options_and_item_list select_init3
+ | table_value_constructor
+ | table_value_constructor union_list
+ | table_value_constructor union_order_or_limit
| '(' select_paren ')'
| '(' select_paren ')' union_list
| '(' select_paren ')' union_order_or_limit
@@ -8434,6 +9210,9 @@ select_init:
union_list_part2:
SELECT_SYM select_options_and_item_list select_init3_union_query_term
+ | table_value_constructor
+ | table_value_constructor union_list
+ | table_value_constructor union_order_or_limit
| '(' select_paren_union_query_term ')'
| '(' select_paren_union_query_term ')' union_list
| '(' select_paren_union_query_term ')' union_order_or_limit
@@ -8441,6 +9220,14 @@ union_list_part2:
select_paren:
{
+ Lex->current_select->set_braces(true);
+ }
+ table_value_constructor select_part3
+ {
+ DBUG_ASSERT(Lex->current_select->braces);
+ }
+ |
+ {
/*
In order to correctly parse UNION's global ORDER BY we need to
set braces before parsing the clause.
@@ -8455,6 +9242,12 @@ select_paren:
| '(' select_paren ')'
;
+select_parent_union_query_term_proper:
+ SELECT_SYM select_options_and_item_list select_part3_union_query_term
+ opt_select_lock_type
+ | table_value_constructor select_part3_union_query_term
+ ;
+
select_paren_union_query_term:
{
/*
@@ -8463,14 +9256,19 @@ select_paren_union_query_term:
*/
Lex->current_select->set_braces(true);
}
- SELECT_SYM select_options_and_item_list select_part3_union_query_term
- opt_select_lock_type
+ select_parent_union_query_term_proper
{
DBUG_ASSERT(Lex->current_select->braces);
}
| '(' select_paren_union_query_term ')'
;
+select_parent_view_proper:
+ SELECT_SYM select_options_and_item_list select_part3_view
+ opt_select_lock_type
+ | table_value_constructor select_part3_view
+ ;
+
select_paren_view:
{
/*
@@ -8479,8 +9277,7 @@ select_paren_view:
*/
Lex->current_select->set_braces(true);
}
- SELECT_SYM select_options_and_item_list select_part3_view
- opt_select_lock_type
+ select_parent_view_proper
{
DBUG_ASSERT(Lex->current_select->braces);
}
@@ -8492,6 +9289,15 @@ select_paren_derived:
{
Lex->current_select->set_braces(true);
}
+ table_value_constructor
+ {
+ DBUG_ASSERT(Lex->current_select->braces);
+ $$= Lex->current_select->master_unit()->first_select();
+ }
+ |
+ {
+ Lex->current_select->set_braces(true);
+ }
SELECT_SYM select_part2_derived
opt_table_expression
opt_order_clause
@@ -8645,11 +9451,76 @@ select_options:
/* empty*/
| select_option_list
{
- if (Select->options & SELECT_DISTINCT && Select->options & SELECT_ALL)
+ 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= create_temporal_literal(thd, $2.str, $2.length, YYCSCL,
+ MYSQL_TYPE_DATETIME, 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);
+ }
+ ;
+
+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
@@ -8663,11 +9534,11 @@ select_option:
Allow this flag only on the first top-level SELECT statement, if
SQL_CACHE wasn't specified, and only once per query.
*/
- if (Lex->current_select != &Lex->select_lex)
+ if (unlikely(Lex->current_select != &Lex->select_lex))
my_yyabort_error((ER_CANT_USE_OPTION_HERE, MYF(0), "SQL_NO_CACHE"));
- if (Lex->select_lex.sql_cache == SELECT_LEX::SQL_CACHE)
+ if (unlikely(Lex->select_lex.sql_cache == SELECT_LEX::SQL_CACHE))
my_yyabort_error((ER_WRONG_USAGE, MYF(0), "SQL_CACHE", "SQL_NO_CACHE"));
- if (Lex->select_lex.sql_cache == SELECT_LEX::SQL_NO_CACHE)
+ if (unlikely(Lex->select_lex.sql_cache == SELECT_LEX::SQL_NO_CACHE))
my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "SQL_NO_CACHE"));
Lex->safe_to_cache_query=0;
@@ -8680,11 +9551,11 @@ select_option:
Allow this flag only on the first top-level SELECT statement, if
SQL_NO_CACHE wasn't specified, and only once per query.
*/
- if (Lex->current_select != &Lex->select_lex)
+ if (unlikely(Lex->current_select != &Lex->select_lex))
my_yyabort_error((ER_CANT_USE_OPTION_HERE, MYF(0), "SQL_CACHE"));
- if (Lex->select_lex.sql_cache == SELECT_LEX::SQL_NO_CACHE)
+ if (unlikely(Lex->select_lex.sql_cache == SELECT_LEX::SQL_NO_CACHE))
my_yyabort_error((ER_WRONG_USAGE, MYF(0), "SQL_NO_CACHE", "SQL_CACHE"));
- if (Lex->select_lex.sql_cache == SELECT_LEX::SQL_CACHE)
+ if (unlikely(Lex->select_lex.sql_cache == SELECT_LEX::SQL_CACHE))
my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "SQL_CACHE"));
Lex->safe_to_cache_query=1;
@@ -8695,14 +9566,14 @@ select_option:
opt_select_lock_type:
/* empty */
- | FOR_SYM UPDATE_SYM
+ | FOR_SYM UPDATE_SYM opt_lock_wait_timeout
{
LEX *lex=Lex;
lex->current_select->lock_type= TL_WRITE;
lex->current_select->set_lock_for_tables(TL_WRITE, false);
lex->safe_to_cache_query=0;
}
- | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM
+ | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM opt_lock_wait_timeout
{
LEX *lex=Lex;
lex->current_select->lock_type= TL_READ_WITH_SHARED_LOCKS;
@@ -8719,36 +9590,36 @@ select_item_list:
{
Item *item= new (thd->mem_root)
Item_field(thd, &thd->lex->current_select->context,
- NULL, NULL, "*");
- if (item == NULL)
+ NULL, NULL, &star_clex_str);
+ if (unlikely(item == NULL))
MYSQL_YYABORT;
- if (add_item_to_list(thd, item))
+ if (unlikely(add_item_to_list(thd, item)))
MYSQL_YYABORT;
(thd->lex->current_select->with_wild)++;
}
;
select_item:
- remember_name table_wild remember_end
+ remember_name select_sublist_qualified_asterisk remember_end
{
- if (add_item_to_list(thd, $2))
+ if (unlikely(add_item_to_list(thd, $2)))
MYSQL_YYABORT;
}
| remember_name expr remember_end select_alias
{
DBUG_ASSERT($1 < $3);
- if (add_item_to_list(thd, $2))
+ if (unlikely(add_item_to_list(thd, $2)))
MYSQL_YYABORT;
if ($4.str)
{
- if (Lex->sql_command == SQLCOM_CREATE_VIEW &&
- check_column_name($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)
+ else if (!$2->name.str || $2->name.str == item_empty_name)
{
$2->set_name(thd, $1, (uint) ($3 - $1), thd->charset());
}
@@ -8775,12 +9646,12 @@ remember_name:
remember_end:
{
- $$= (char*) YYLIP->get_cpp_tok_end();
+ $$= (char*) YYLIP->get_cpp_tok_end_rtrim();
}
;
select_alias:
- /* empty */ { $$=null_lex_str;}
+ /* empty */ { $$=null_clex_str;}
| AS ident { $$=$2; }
| AS TEXT_STRING_sys { $$=$2; }
| ident { $$=$1; }
@@ -8853,7 +9724,7 @@ expr:
{
/* X OR Y */
$$= new (thd->mem_root) Item_cond_or(thd, $1, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
}
@@ -8861,7 +9732,7 @@ expr:
{
/* XOR is a proprietary extension */
$$= new (thd->mem_root) Item_func_xor(thd, $1, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| expr and expr %prec AND_SYM
@@ -8903,84 +9774,84 @@ expr:
{
/* X AND Y */
$$= new (thd->mem_root) Item_cond_and(thd, $1, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
}
| NOT_SYM expr %prec NOT_SYM
{
$$= negate_expression(thd, $2);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| bool_pri IS TRUE_SYM %prec IS
{
$$= new (thd->mem_root) Item_func_istrue(thd, $1);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| bool_pri IS not TRUE_SYM %prec IS
{
$$= new (thd->mem_root) Item_func_isnottrue(thd, $1);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| bool_pri IS FALSE_SYM %prec IS
{
$$= new (thd->mem_root) Item_func_isfalse(thd, $1);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| bool_pri IS not FALSE_SYM %prec IS
{
$$= new (thd->mem_root) Item_func_isnotfalse(thd, $1);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| bool_pri IS UNKNOWN_SYM %prec IS
{
$$= new (thd->mem_root) Item_func_isnull(thd, $1);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| bool_pri IS not UNKNOWN_SYM %prec IS
{
$$= new (thd->mem_root) Item_func_isnotnull(thd, $1);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
- | bool_pri
+ | bool_pri %prec PREC_BELOW_NOT
;
bool_pri:
bool_pri IS NULL_SYM %prec IS
{
$$= new (thd->mem_root) Item_func_isnull(thd, $1);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| bool_pri IS not NULL_SYM %prec IS
{
$$= new (thd->mem_root) Item_func_isnotnull(thd, $1);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| bool_pri EQUAL_SYM predicate %prec EQUAL_SYM
{
$$= new (thd->mem_root) Item_func_equal(thd, $1, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| bool_pri comp_op predicate %prec '='
{
$$= (*$2)(0)->create(thd, $1, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| bool_pri comp_op all_or_any '(' subselect ')' %prec '='
{
$$= all_any_subquery_creator(thd, $1, $2, $3, $5);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| predicate
@@ -8990,22 +9861,22 @@ predicate:
bit_expr IN_SYM '(' subselect ')'
{
$$= new (thd->mem_root) Item_in_subselect(thd, $1, $4);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| bit_expr not IN_SYM '(' subselect ')'
{
Item *item= new (thd->mem_root) Item_in_subselect(thd, $1, $5);
- if (item == NULL)
+ if (unlikely(item == NULL))
MYSQL_YYABORT;
$$= negate_expression(thd, item);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| bit_expr IN_SYM '(' expr ')'
{
$$= handle_sql2003_note184_exception(thd, $1, true, $4);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| bit_expr IN_SYM '(' expr ',' expr_list ')'
@@ -9013,13 +9884,13 @@ predicate:
$6->push_front($4, thd->mem_root);
$6->push_front($1, thd->mem_root);
$$= new (thd->mem_root) Item_func_in(thd, *$6);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| bit_expr not IN_SYM '(' expr ')'
{
$$= handle_sql2003_note184_exception(thd, $1, false, $5);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| bit_expr not IN_SYM '(' expr ',' expr_list ')'
@@ -9027,21 +9898,21 @@ predicate:
$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 (item == NULL)
+ if (unlikely(item == NULL))
MYSQL_YYABORT;
$$= item->neg_transformer(thd);
}
| bit_expr BETWEEN_SYM bit_expr AND_SYM predicate
{
$$= new (thd->mem_root) Item_func_between(thd, $1, $3, $5);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| bit_expr not BETWEEN_SYM bit_expr AND_SYM predicate
{
Item_func_between *item;
item= new (thd->mem_root) Item_func_between(thd, $1, $4, $6);
- if (item == NULL)
+ if (unlikely(item == NULL))
MYSQL_YYABORT;
$$= item->neg_transformer(thd);
}
@@ -9049,131 +9920,157 @@ predicate:
{
Item *item1= new (thd->mem_root) Item_func_soundex(thd, $1);
Item *item4= new (thd->mem_root) Item_func_soundex(thd, $4);
- if ((item1 == NULL) || (item4 == NULL))
+ if (unlikely(item1 == NULL) || unlikely(item4 == NULL))
MYSQL_YYABORT;
$$= new (thd->mem_root) Item_func_eq(thd, item1, item4);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
- | bit_expr LIKE simple_expr opt_escape
+ | bit_expr LIKE bit_expr opt_escape
{
$$= new (thd->mem_root) Item_func_like(thd, $1, $3, $4,
Lex->escape_used);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
- | bit_expr not LIKE simple_expr opt_escape
+ | bit_expr not LIKE bit_expr opt_escape
{
Item *item= new (thd->mem_root) Item_func_like(thd, $1, $4, $5,
Lex->escape_used);
- if (item == NULL)
+ if (unlikely(item == NULL))
MYSQL_YYABORT;
$$= item->neg_transformer(thd);
}
| bit_expr REGEXP bit_expr
{
$$= new (thd->mem_root) Item_func_regex(thd, $1, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| bit_expr not REGEXP bit_expr
{
Item *item= new (thd->mem_root) Item_func_regex(thd, $1, $4);
- if (item == NULL)
+ if (unlikely(item == NULL))
MYSQL_YYABORT;
$$= negate_expression(thd, item);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
- | bit_expr
+ | 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 ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| bit_expr '&' bit_expr %prec '&'
{
$$= new (thd->mem_root) Item_func_bit_and(thd, $1, $3);
- if ($$ == NULL)
+ 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 ($$ == NULL)
+ 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 ($$ == NULL)
+ 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 ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| bit_expr '-' bit_expr %prec '-'
{
$$= new (thd->mem_root) Item_func_minus(thd, $1, $3);
- if ($$ == NULL)
+ 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 ($$ == NULL)
+ 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 ($$ == NULL)
+ 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 ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| bit_expr '/' bit_expr %prec '/'
{
$$= new (thd->mem_root) Item_func_div(thd, $1, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| bit_expr '%' bit_expr %prec '%'
{
$$= new (thd->mem_root) Item_func_mod(thd, $1, $3);
- if ($$ == NULL)
+ 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 ($$ == NULL)
+ 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 ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| bit_expr '^' bit_expr
{
$$= new (thd->mem_root) Item_func_bit_xor(thd, $1, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
- | simple_expr
+ | mysql_concatenation_expr %prec '^'
;
or:
@@ -9260,7 +10157,7 @@ dyncall_create_element:
LEX *lex= Lex;
$$= (DYNCALL_CREATE_DEF *)
alloc_root(thd->mem_root, sizeof(DYNCALL_CREATE_DEF));
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
$$->key= $1;
$$->value= $3;
@@ -9281,7 +10178,7 @@ dyncall_create_list:
dyncall_create_element
{
$$= new (thd->mem_root) List<DYNCALL_CREATE_DEF>;
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
$$->push_back($1, thd->mem_root);
}
@@ -9292,6 +10189,34 @@ dyncall_create_list:
}
;
+
+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.
@@ -9331,124 +10256,138 @@ column_default_non_parenthesized_expr:
| variable
| sum_expr
| window_func_expr
+ | inverse_distribution_function
| ROW_SYM '(' expr ',' expr_list ')'
{
$5->push_front($3, thd->mem_root);
$$= new (thd->mem_root) Item_row(thd, *$5);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| EXISTS '(' subselect ')'
{
$$= new (thd->mem_root) Item_exists_subselect(thd, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| '{' ident expr '}'
{
- $$= NULL;
- /*
- If "expr" is reasonably short pure ASCII string literal,
- try to parse known ODBC style date, time or timestamp literals,
- e.g:
- SELECT {d'2001-01-01'};
- SELECT {t'10:20:30'};
- SELECT {ts'2001-01-01 10:20:30'};
- */
- if ($3->type() == Item::STRING_ITEM)
- {
- Item_string *item= (Item_string *) $3;
- enum_field_types type= item->odbc_temporal_literal_type(&$2);
- if (type != MYSQL_TYPE_STRING)
- {
- $$= create_temporal_literal(thd, item->val_str(NULL),
- type, false);
- }
- }
- if ($$ == NULL)
- $$= $3;
+ 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 (i1 == NULL)
+ if (unlikely(i1 == NULL))
MYSQL_YYABORT;
Select->add_ftfunc_to_list(thd, i1);
$$= i1;
}
| CAST_SYM '(' expr AS cast_type ')'
{
- LEX *lex= Lex;
- $$= create_func_cast(thd, $3, $5.type(), $5.length(), $5.dec(),
- lex->charset);
- if ($$ == NULL)
+ if (unlikely(!($$= $5.create_typecast_item(thd, $3, Lex->charset))))
MYSQL_YYABORT;
}
- | CASE_SYM opt_expr when_list opt_else END
+ | CASE_SYM when_list_opt_else END
{
- $$= new (thd->mem_root) Item_func_case(thd, *$3, $2, $4);
- if ($$ == NULL)
+ 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 ')'
{
- $$= create_func_cast(thd, $3, $5.type(), $5.length(), $5.dec(),
- Lex->charset);
- if ($$ == NULL)
+ 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 ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| DEFAULT '(' simple_ident ')'
{
Item_splocal *il= $3->get_item_splocal();
- if (il)
+ 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 ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
+ Lex->default_used= TRUE;
}
- | VALUES '(' simple_ident_nospvar ')'
+ | VALUE_SYM '(' simple_ident_nospvar ')'
{
$$= new (thd->mem_root) Item_insert_value(thd, Lex->current_context(),
$3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
- ;
-
-simple_expr:
- column_default_non_parenthesized_expr
- | simple_expr COLLATE_SYM ident_or_text %prec NEG
+ | NEXT_SYM VALUE_SYM FOR_SYM table_ident
{
- Item *i1= new (thd->mem_root) Item_string(thd, $3.str,
- $3.length,
- thd->charset());
- if (i1 == NULL)
+ if (unlikely(!($$= Lex->create_item_func_nextval(thd, $4))))
MYSQL_YYABORT;
- $$= new (thd->mem_root) Item_func_set_collation(thd, $1, i1);
- if ($$ == NULL)
+ }
+ | 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; }
- | BINARY simple_expr %prec NEG
+ ;
+
+string_factor_expr:
+ primary_expr
+ | string_factor_expr COLLATE_SYM collation_name
{
- $$= create_func_cast(thd, $2, ITEM_CAST_CHAR, NULL, NULL,
- &my_charset_bin);
- if ($$ == NULL)
+ if (unlikely(!($$= new (thd->mem_root) Item_func_set_collation(thd, $1, $3))))
MYSQL_YYABORT;
}
- | simple_expr OR_OR_SYM simple_expr
+ ;
+
+simple_expr:
+ string_factor_expr %prec NEG
+ | BINARY simple_expr
{
- $$= new (thd->mem_root) Item_func_concat(thd, $1, $3);
- if ($$ == NULL)
+ 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
@@ -9458,30 +10397,48 @@ simple_expr:
| '-' simple_expr %prec NEG
{
$$= $2->neg(thd);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| '~' simple_expr %prec NEG
{
$$= new (thd->mem_root) Item_func_bit_neg(thd, $2);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| not2 simple_expr %prec NEG
{
$$= negate_expression(thd, $2);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
- | INTERVAL_SYM expr interval '+' expr %prec INTERVAL_SYM
- /* we cannot put interval before - */
+ ;
+
+mysql_concatenation_expr:
+ simple_expr
+ | mysql_concatenation_expr MYSQL_CONCAT_SYM simple_expr
{
- $$= new (thd->mem_root) Item_date_add_interval(thd, $5, $2, $3, 0);
- if ($$ == NULL)
+ $$= 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_add_time(thd, $3, $5, 1, 0);
+ if (unlikely($$ == NULL))
+ MYSQL_YYABORT;
+ }
+ ;
/*
Function call syntax using official SQL 2003 keywords.
Because the function name is an official token,
@@ -9492,20 +10449,20 @@ function_call_keyword:
CHAR_SYM '(' expr_list ')'
{
$$= new (thd->mem_root) Item_func_char(thd, *$3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| CHAR_SYM '(' expr_list USING charset_name ')'
{
$$= new (thd->mem_root) Item_func_char(thd, *$3, $5);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| CURRENT_USER optional_braces
{
$$= new (thd->mem_root) Item_func_current_user(thd,
Lex->current_context());
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
Lex->safe_to_cache_query= 0;
@@ -9514,7 +10471,7 @@ function_call_keyword:
{
$$= new (thd->mem_root) Item_func_current_role(thd,
Lex->current_context());
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
Lex->safe_to_cache_query= 0;
@@ -9522,154 +10479,111 @@ function_call_keyword:
| DATE_SYM '(' expr ')'
{
$$= new (thd->mem_root) Item_date_typecast(thd, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| DAY_SYM '(' expr ')'
{
$$= new (thd->mem_root) Item_func_dayofmonth(thd, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| HOUR_SYM '(' expr ')'
{
$$= new (thd->mem_root) Item_func_hour(thd, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| INSERT '(' expr ',' expr ',' expr ',' expr ')'
{
$$= new (thd->mem_root) Item_func_insert(thd, $3, $5, $7, $9);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
- | INTERVAL_SYM '(' expr ',' expr ')' %prec INTERVAL_SYM
+ | INTERVAL_SYM '(' expr ',' expr ')'
{
List<Item> *list= new (thd->mem_root) List<Item>;
- if (list == NULL)
+ 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;
- list->push_front($5, thd->mem_root);
- list->push_front($3, thd->mem_root);
Item_row *item= new (thd->mem_root) Item_row(thd, *list);
- if (item == NULL)
+ if (unlikely(item == NULL))
MYSQL_YYABORT;
$$= new (thd->mem_root) Item_func_interval(thd, item);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
- | INTERVAL_SYM '(' expr ',' expr ',' expr_list ')' %prec INTERVAL_SYM
+ | 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 (item == NULL)
+ if (unlikely(item == NULL))
MYSQL_YYABORT;
$$= new (thd->mem_root) Item_func_interval(thd, item);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| LEFT '(' expr ',' expr ')'
{
$$= new (thd->mem_root) Item_func_left(thd, $3, $5);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| MINUTE_SYM '(' expr ')'
{
$$= new (thd->mem_root) Item_func_minute(thd, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| MONTH_SYM '(' expr ')'
{
$$= new (thd->mem_root) Item_func_month(thd, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| RIGHT '(' expr ',' expr ')'
{
$$= new (thd->mem_root) Item_func_right(thd, $3, $5);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| SECOND_SYM '(' expr ')'
{
$$= new (thd->mem_root) Item_func_second(thd, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
- | TIME_SYM '(' expr ')'
+ | SQL_SYM PERCENT_ORACLE_SYM ROWCOUNT_SYM
{
- $$= new (thd->mem_root) Item_time_typecast(thd, $3,
- AUTO_SEC_PART_DIGITS);
- if ($$ == NULL)
+ $$= 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;
}
- | TIMESTAMP '(' expr ')'
+ | TIME_SYM '(' expr ')'
{
- $$= new (thd->mem_root) Item_datetime_typecast(thd, $3,
+ $$= new (thd->mem_root) Item_time_typecast(thd, $3,
AUTO_SEC_PART_DIGITS);
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
- | TIMESTAMP '(' expr ',' expr ')'
- {
- $$= new (thd->mem_root) Item_func_add_time(thd, $3, $5, 1, 0);
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
- | TRIM '(' expr ')'
- {
- $$= new (thd->mem_root) Item_func_trim(thd, $3);
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
- | TRIM '(' LEADING expr FROM expr ')'
- {
- $$= new (thd->mem_root) Item_func_ltrim(thd, $6, $4);
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
- | TRIM '(' TRAILING expr FROM expr ')'
- {
- $$= new (thd->mem_root) Item_func_rtrim(thd, $6, $4);
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
- | TRIM '(' BOTH expr FROM expr ')'
- {
- $$= new (thd->mem_root) Item_func_trim(thd, $6, $4);
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
- | TRIM '(' LEADING FROM expr ')'
- {
- $$= new (thd->mem_root) Item_func_ltrim(thd, $5);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
- | TRIM '(' TRAILING FROM expr ')'
+ | function_call_keyword_timestamp
{
- $$= new (thd->mem_root) Item_func_rtrim(thd, $5);
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
- | TRIM '(' BOTH FROM expr ')'
- {
- $$= new (thd->mem_root) Item_func_trim(thd, $5);
- if ($$ == NULL)
- MYSQL_YYABORT;
+ $$= $1;
}
- | TRIM '(' expr FROM expr ')'
+ | TRIM '(' trim_operands ')'
{
- $$= new (thd->mem_root) Item_func_trim(thd, $5, $3);
- if ($$ == NULL)
+ if (unlikely(!($$= $3.make_item_func_trim(thd))))
MYSQL_YYABORT;
}
| USER_SYM '(' ')'
{
$$= new (thd->mem_root) Item_func_user(thd);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
Lex->safe_to_cache_query=0;
@@ -9677,7 +10591,7 @@ function_call_keyword:
| YEAR_SYM '(' expr ')'
{
$$= new (thd->mem_root) Item_func_year(thd, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
;
@@ -9699,103 +10613,121 @@ function_call_nonkeyword:
{
$$= new (thd->mem_root) Item_date_add_interval(thd, $3, $5,
INTERVAL_DAY, 0);
- if ($$ == NULL)
+ 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 ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| CURDATE optional_braces
{
$$= new (thd->mem_root) Item_func_curdate_local(thd);
- if ($$ == NULL)
+ 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 ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
Lex->safe_to_cache_query=0;
}
| DATE_ADD_INTERVAL '(' expr ',' INTERVAL_SYM expr interval ')'
- %prec INTERVAL_SYM
{
$$= new (thd->mem_root) Item_date_add_interval(thd, $3, $6, $7, 0);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| DATE_SUB_INTERVAL '(' expr ',' INTERVAL_SYM expr interval ')'
- %prec INTERVAL_SYM
{
$$= new (thd->mem_root) Item_date_add_interval(thd, $3, $6, $7, 1);
- if ($$ == NULL)
+ 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 ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| GET_FORMAT '(' date_time_type ',' expr ')'
{
$$= new (thd->mem_root) Item_func_get_format(thd, $3, $5);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| NOW_SYM opt_time_precision
{
$$= new (thd->mem_root) Item_func_now_local(thd, $2);
- if ($$ == NULL)
+ 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 ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| SUBDATE_SYM '(' expr ',' expr ')'
{
$$= new (thd->mem_root) Item_date_add_interval(thd, $3, $5,
INTERVAL_DAY, 1);
- if ($$ == NULL)
+ 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 ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| SUBSTRING '(' expr ',' expr ',' expr ')'
{
- $$= new (thd->mem_root) Item_func_substr(thd, $3, $5, $7);
- if ($$ == NULL)
+ if (unlikely(!($$= Lex->make_item_func_substr(thd, $3, $5, $7))))
MYSQL_YYABORT;
}
| SUBSTRING '(' expr ',' expr ')'
{
- $$= new (thd->mem_root) Item_func_substr(thd, $3, $5);
- if ($$ == NULL)
+ if (unlikely(!($$= Lex->make_item_func_substr(thd, $3, $5))))
MYSQL_YYABORT;
}
| SUBSTRING '(' expr FROM expr FOR_SYM expr ')'
{
- $$= new (thd->mem_root) Item_func_substr(thd, $3, $5, $7);
- if ($$ == NULL)
+ if (unlikely(!($$= Lex->make_item_func_substr(thd, $3, $5, $7))))
MYSQL_YYABORT;
}
| SUBSTRING '(' expr FROM expr ')'
{
- $$= new (thd->mem_root) Item_func_substr(thd, $3, $5);
- if ($$ == NULL)
+ if (unlikely(!($$= Lex->make_item_func_substr(thd, $3, $5))))
MYSQL_YYABORT;
}
| SYSDATE opt_time_precision
@@ -9812,40 +10744,45 @@ function_call_nonkeyword:
$$= new (thd->mem_root) Item_func_sysdate_local(thd, $2);
else
$$= new (thd->mem_root) Item_func_now_local(thd, $2);
- if ($$ == NULL)
+ 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 ($$ == NULL)
+ 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 ($$ == NULL)
+ 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 ($$ == NULL)
+ 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 ($$ == NULL)
+ 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 ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
Lex->safe_to_cache_query=0;
}
@@ -9853,38 +10790,38 @@ function_call_nonkeyword:
COLUMN_ADD_SYM '(' expr ',' dyncall_create_list ')'
{
$$= create_func_dyncol_add(thd, $3, *$5);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
|
COLUMN_DELETE_SYM '(' expr ',' expr_list ')'
{
$$= create_func_dyncol_delete(thd, $3, *$5);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
|
COLUMN_CHECK_SYM '(' expr ')'
{
$$= new (thd->mem_root) Item_func_dyncol_check(thd, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
|
COLUMN_CREATE_SYM '(' dyncall_create_list ')'
{
$$= create_func_dyncol_create(thd, *$3);
- if ($$ == NULL)
+ 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(),
+ $$= create_func_dyncol_get(thd, $3, $5, $7.type_handler(),
$7.length(), $7.dec(),
lex->charset);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
;
@@ -9898,50 +10835,50 @@ function_call_conflict:
ASCII_SYM '(' expr ')'
{
$$= new (thd->mem_root) Item_func_ascii(thd, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| CHARSET '(' expr ')'
{
$$= new (thd->mem_root) Item_func_charset(thd, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| COALESCE '(' expr_list ')'
{
$$= new (thd->mem_root) Item_func_coalesce(thd, *$3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| COLLATION_SYM '(' expr ')'
{
$$= new (thd->mem_root) Item_func_collation(thd, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| DATABASE '(' ')'
{
$$= new (thd->mem_root) Item_func_database(thd);
- if ($$ == NULL)
+ 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 ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| FORMAT_SYM '(' expr ',' expr ')'
{
$$= new (thd->mem_root) Item_func_format(thd, $3, $5);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| FORMAT_SYM '(' expr ',' expr ',' expr ')'
{
$$= new (thd->mem_root) Item_func_format(thd, $3, $5, $7);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
/* LAST_VALUE here conflicts with the definition for window functions.
@@ -9950,76 +10887,75 @@ function_call_conflict:
| LAST_VALUE '(' expr ')'
{
List<Item> *list= new (thd->mem_root) List<Item>;
- if (list == NULL)
+ if (unlikely(list == NULL))
MYSQL_YYABORT;
list->push_back($3, thd->mem_root);
$$= new (thd->mem_root) Item_func_last_value(thd, *list);
- if ($$ == NULL)
+ 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 ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| MICROSECOND_SYM '(' expr ')'
{
$$= new (thd->mem_root) Item_func_microsecond(thd, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| MOD_SYM '(' expr ',' expr ')'
{
$$= new (thd->mem_root) Item_func_mod(thd, $3, $5);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| OLD_PASSWORD_SYM '(' expr ')'
{
$$= new (thd->mem_root)
Item_func_password(thd, $3, Item_func_password::OLD);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| PASSWORD_SYM '(' expr ')'
{
Item* i1;
i1= new (thd->mem_root) Item_func_password(thd, $3);
- if (i1 == NULL)
+ if (unlikely(i1 == NULL))
MYSQL_YYABORT;
$$= i1;
}
| QUARTER_SYM '(' expr ')'
{
$$= new (thd->mem_root) Item_func_quarter(thd, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| REPEAT_SYM '(' expr ',' expr ')'
{
$$= new (thd->mem_root) Item_func_repeat(thd, $3, $5);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| REPLACE '(' expr ',' expr ',' expr ')'
{
- $$= new (thd->mem_root) Item_func_replace(thd, $3, $5, $7);
- if ($$ == NULL)
+ 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 ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| ROW_COUNT_SYM '(' ')'
{
$$= new (thd->mem_root) Item_func_row_count(thd);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
Lex->safe_to_cache_query= 0;
@@ -10027,25 +10963,25 @@ function_call_conflict:
| TRUNCATE_SYM '(' expr ',' expr ')'
{
$$= new (thd->mem_root) Item_func_round(thd, $3, $5, 1);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| WEEK_SYM '(' expr ')'
{
$$= new (thd->mem_root) Item_func_week(thd, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| WEEK_SYM '(' expr ',' expr ')'
{
$$= new (thd->mem_root) Item_func_week(thd, $3, $5);
- if ($$ == NULL)
+ 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 ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| WEIGHT_STRING_SYM '(' expr AS CHAR_SYM ws_nweights opt_ws_levels ')'
@@ -10053,26 +10989,26 @@ function_call_conflict:
$$= new (thd->mem_root)
Item_func_weight_string(thd, $3, 0, $6,
$7 | MY_STRXFRM_PAD_WITH_SPACE);
- if ($$ == NULL)
+ 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 (item == NULL)
+ if (unlikely(item == NULL))
MYSQL_YYABORT;
$$= new (thd->mem_root)
Item_func_weight_string(thd, item, 0, $6,
MY_STRXFRM_PAD_WITH_SPACE);
- if ($$ == NULL)
+ 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 ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| geometry_function
@@ -10080,7 +11016,7 @@ function_call_conflict:
#ifdef HAVE_SPATIAL
$$= $1;
/* $1 may be NULL, GEOM_NEW not tested for out of memory */
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
#else
my_yyabort_error((ER_FEATURE_DISABLED, MYF(0), sym_group_geom.name,
@@ -10142,6 +11078,11 @@ geometry_function:
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));
+ }
;
/*
@@ -10163,9 +11104,9 @@ function_call_generic:
(udf= find_udf($1.str, $1.length)) &&
udf->type == UDFTYPE_AGGREGATE)
{
- if (lex->current_select->inc_in_sum_expr())
+ if (unlikely(lex->current_select->inc_in_sum_expr()))
{
- my_parse_error(thd, ER_SYNTAX_ERROR);
+ thd->parse_error();
MYSQL_YYABORT;
}
}
@@ -10178,10 +11119,8 @@ function_call_generic:
Create_func *builder;
Item *item= NULL;
- if (check_routine_name(&$1))
- {
+ if (unlikely(check_routine_name(&$1)))
MYSQL_YYABORT;
- }
/*
Implementation note:
@@ -10192,10 +11131,10 @@ function_call_generic:
This will be revised with WL#2128 (SQL PATH)
*/
- builder= find_native_function_builder(thd, $1);
+ builder= find_native_function_builder(thd, &$1);
if (builder)
{
- item= builder->create_func(thd, $1, $4);
+ item= builder->create_func(thd, &$1, $4);
}
else
{
@@ -10217,49 +11156,17 @@ function_call_generic:
{
builder= find_qualified_function_builder(thd);
DBUG_ASSERT(builder);
- item= builder->create_func(thd, $1, $4);
+ item= builder->create_func(thd, &$1, $4);
}
}
- if (! ($$= item))
- {
+ if (unlikely(! ($$= item)))
MYSQL_YYABORT;
- }
}
- | ident '.' ident '(' opt_expr_list ')'
+ | ident_cli '.' ident_cli '(' opt_expr_list ')'
{
- Create_qfunc *builder;
- Item *item= NULL;
-
- /*
- The following in practice calls:
- <code>Create_sp_func::create()</code>
- and builds a stored function.
-
- However, it's important to maintain the interface between the
- parser and the implementation in item_create.cc clean,
- since this will change with WL#2128 (SQL PATH):
- - INFORMATION_SCHEMA.version() is the SQL 99 syntax for the native
- function version(),
- - MySQL.version() is the SQL 2003 syntax for the native function
- version() (a vendor can specify any schema).
- */
-
- if (!$1.str || check_db_name(&$1))
- my_yyabort_error((ER_WRONG_DB_NAME, MYF(0), $1.str));
- if (check_routine_name(&$3))
- {
- MYSQL_YYABORT;
- }
-
- builder= find_qualified_function_builder(thd);
- DBUG_ASSERT(builder);
- item= builder->create_with_db(thd, $1, $3, true, $5);
-
- if (! ($$= item))
- {
+ if (unlikely(!($$= Lex->make_item_func_call_generic(thd, &$1, &$3, $5))))
MYSQL_YYABORT;
- }
}
;
@@ -10289,7 +11196,7 @@ udf_expr_list:
udf_expr
{
$$= new (thd->mem_root) List<Item>;
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
$$->push_back($1, thd->mem_root);
}
@@ -10331,46 +11238,46 @@ sum_expr:
AVG_SYM '(' in_sum_expr ')'
{
$$= new (thd->mem_root) Item_sum_avg(thd, $3, FALSE);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| AVG_SYM '(' DISTINCT in_sum_expr ')'
{
$$= new (thd->mem_root) Item_sum_avg(thd, $4, TRUE);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| BIT_AND '(' in_sum_expr ')'
{
$$= new (thd->mem_root) Item_sum_and(thd, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| BIT_OR '(' in_sum_expr ')'
{
$$= new (thd->mem_root) Item_sum_or(thd, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| BIT_XOR '(' in_sum_expr ')'
{
$$= new (thd->mem_root) Item_sum_xor(thd, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| COUNT_SYM '(' opt_all '*' ')'
{
Item *item= new (thd->mem_root) Item_int(thd, (int32) 0L, 1);
- if (item == NULL)
+ if (unlikely(item == NULL))
MYSQL_YYABORT;
$$= new (thd->mem_root) Item_sum_count(thd, item);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| COUNT_SYM '(' in_sum_expr ')'
{
$$= new (thd->mem_root) Item_sum_count(thd, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| COUNT_SYM '(' DISTINCT
@@ -10380,13 +11287,13 @@ sum_expr:
')'
{
$$= new (thd->mem_root) Item_sum_count(thd, *$5);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| MIN_SYM '(' in_sum_expr ')'
{
$$= new (thd->mem_root) Item_sum_min(thd, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
/*
@@ -10397,70 +11304,76 @@ sum_expr:
| MIN_SYM '(' DISTINCT in_sum_expr ')'
{
$$= new (thd->mem_root) Item_sum_min(thd, $4);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| MAX_SYM '(' in_sum_expr ')'
{
$$= new (thd->mem_root) Item_sum_max(thd, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| MAX_SYM '(' DISTINCT in_sum_expr ')'
{
$$= new (thd->mem_root) Item_sum_max(thd, $4);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| STD_SYM '(' in_sum_expr ')'
{
$$= new (thd->mem_root) Item_sum_std(thd, $3, 0);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| VARIANCE_SYM '(' in_sum_expr ')'
{
$$= new (thd->mem_root) Item_sum_variance(thd, $3, 0);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| STDDEV_SAMP_SYM '(' in_sum_expr ')'
{
$$= new (thd->mem_root) Item_sum_std(thd, $3, 1);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| VAR_SAMP_SYM '(' in_sum_expr ')'
{
$$= new (thd->mem_root) Item_sum_variance(thd, $3, 1);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| SUM_SYM '(' in_sum_expr ')'
{
$$= new (thd->mem_root) Item_sum_sum(thd, $3, FALSE);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| SUM_SYM '(' DISTINCT in_sum_expr ')'
{
$$= new (thd->mem_root) Item_sum_sum(thd, $4, TRUE);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| GROUP_CONCAT_SYM '(' opt_distinct
{ Select->in_sum_expr++; }
expr_list opt_gorder_clause
- opt_gconcat_separator
+ 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);
- if ($$ == NULL)
- MYSQL_YYABORT;
+ 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();
}
@@ -10470,25 +11383,25 @@ window_func_expr:
window_func OVER_SYM window_name
{
$$= new (thd->mem_root) Item_window_func(thd, (Item_sum *) $1, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
- if (Select->add_window_func((Item_window_func *) $$))
+ if (unlikely(Select->add_window_func((Item_window_func *) $$)))
MYSQL_YYABORT;
}
|
window_func OVER_SYM window_spec
{
LEX *lex= Lex;
- if (Select->add_window_spec(thd, lex->win_ref,
- Select->group_list,
- Select->order_list,
- lex->win_frame))
+ 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 ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
- if (Select->add_window_func((Item_window_func *) $$))
+ if (unlikely(Select->add_window_func((Item_window_func *) $$)))
MYSQL_YYABORT;
}
;
@@ -10506,63 +11419,63 @@ simple_window_func:
ROW_NUMBER_SYM '(' ')'
{
$$= new (thd->mem_root) Item_sum_row_number(thd);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
|
RANK_SYM '(' ')'
{
$$= new (thd->mem_root) Item_sum_rank(thd);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
|
DENSE_RANK_SYM '(' ')'
{
$$= new (thd->mem_root) Item_sum_dense_rank(thd);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
|
PERCENT_RANK_SYM '(' ')'
{
$$= new (thd->mem_root) Item_sum_percent_rank(thd);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
|
CUME_DIST_SYM '(' ')'
{
$$= new (thd->mem_root) Item_sum_cume_dist(thd);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
|
NTILE_SYM '(' expr ')'
{
$$= new (thd->mem_root) Item_sum_ntile(thd, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
|
FIRST_VALUE_SYM '(' expr ')'
{
$$= new (thd->mem_root) Item_sum_first_value(thd, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
|
LAST_VALUE '(' expr ')'
{
$$= new (thd->mem_root) Item_sum_last_value(thd, $3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
|
NTH_VALUE_SYM '(' expr ',' expr ')'
{
$$= new (thd->mem_root) Item_sum_nth_value(thd, $3, $5);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
|
@@ -10570,17 +11483,17 @@ simple_window_func:
{
/* No second argument defaults to 1. */
Item* item_offset= new (thd->mem_root) Item_uint(thd, 1);
- if (item_offset == NULL)
+ if (unlikely(item_offset == NULL))
MYSQL_YYABORT;
$$= new (thd->mem_root) Item_sum_lead(thd, $3, item_offset);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
|
LEAD_SYM '(' expr ',' expr ')'
{
$$= new (thd->mem_root) Item_sum_lead(thd, $3, $5);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
|
@@ -10588,26 +11501,94 @@ simple_window_func:
{
/* No second argument defaults to 1. */
Item* item_offset= new (thd->mem_root) Item_uint(thd, 1);
- if (item_offset == NULL)
+ if (unlikely(item_offset == NULL))
MYSQL_YYABORT;
$$= new (thd->mem_root) Item_sum_lag(thd, $3, item_offset);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
|
LAG_SYM '(' expr ',' expr ')'
{
$$= new (thd->mem_root) Item_sum_lag(thd, $3, $5);
- if ($$ == NULL)
+ 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_STRING *) thd->memdup(&$1, sizeof(LEX_STRING));
- if ($$ == NULL)
+ $$= (LEX_CSTRING *) thd->memdup(&$1, sizeof(LEX_CSTRING));
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
;
@@ -10615,7 +11596,7 @@ window_name:
variable:
'@'
{
- if (! Lex->parsing_options.allows_variable)
+ if (unlikely(! Lex->parsing_options.allows_variable))
my_yyabort_error((ER_VIEW_SELECT_VARIABLE, MYF(0)));
}
variable_aux
@@ -10628,8 +11609,8 @@ 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 ($$ == NULL)
+ $$= 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);
@@ -10637,24 +11618,21 @@ variable_aux:
}
| ident_or_text
{
- $$= new (thd->mem_root) Item_func_get_user_var(thd, $1);
- if ($$ == NULL)
+ $$= 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_or_text opt_component
+ | '@' opt_var_ident_type ident_sysvar_name
{
- /* disallow "SELECT @@global.global.variable" */
- if ($3.str && $4.str && check_reserved_words(&$3))
- {
- my_parse_error(thd, ER_SYNTAX_ERROR);
+ if (unlikely(!($$= Lex->make_item_sysvar(thd, $2, &$3))))
MYSQL_YYABORT;
- }
- if (!($$= get_system_var(thd, $2, $3, $4)))
+ }
+ | '@' opt_var_ident_type ident_sysvar_name '.' ident
+ {
+ if (unlikely(!($$= Lex->make_item_sysvar(thd, $2, &$3, &$5))))
MYSQL_YYABORT;
- if (!((Item_func_get_system_var*) $$)->is_written_to_binlog())
- Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_VARIABLE);
}
;
@@ -10667,7 +11645,7 @@ opt_gconcat_separator:
/* empty */
{
$$= new (thd->mem_root) String(",", 1, &my_charset_latin1);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| SEPARATOR_SYM text_string { $$ = $2; }
@@ -10680,18 +11658,66 @@ opt_gorder_clause:
gorder_list:
gorder_list ',' order_ident order_dir
- { if (add_gorder_to_list(thd, $3,(bool) $4)) MYSQL_YYABORT; }
+ {
+ if (unlikely(add_gorder_to_list(thd, $3,(bool) $4)))
+ MYSQL_YYABORT;
+ }
| order_ident order_dir
- { if (add_gorder_to_list(thd, $1,(bool) $2)) MYSQL_YYABORT; }
+ {
+ 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 (lex->current_select->inc_in_sum_expr())
+ if (unlikely(lex->current_select->inc_in_sum_expr()))
{
- my_parse_error(thd, ER_SYNTAX_ERROR);
+ thd->parse_error();
MYSQL_YYABORT;
}
}
@@ -10704,34 +11730,43 @@ in_sum_expr:
cast_type:
BINARY opt_field_length
- { $$.set(ITEM_CAST_CHAR, $2); Lex->charset= &my_charset_bin; }
+ { $$.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(ITEM_CAST_CHAR, $2); }
+ { $$.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(ITEM_CAST_CHAR, $2, 0);
+ $$.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(ITEM_CAST_SIGNED_INT); }
- | SIGNED_SYM { $$.set(ITEM_CAST_SIGNED_INT); }
- | SIGNED_SYM INT_SYM { $$.set(ITEM_CAST_SIGNED_INT); }
- | UNSIGNED { $$.set(ITEM_CAST_UNSIGNED_INT); }
- | UNSIGNED INT_SYM { $$.set(ITEM_CAST_UNSIGNED_INT); }
- | DECIMAL_SYM float_options { $$.set(ITEM_CAST_DECIMAL, $2); }
- | DOUBLE_SYM opt_precision { $$.set(ITEM_CAST_DOUBLE, $2); }
+ 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(ITEM_CAST_DATE); }
- | TIME_SYM opt_field_length { $$.set(ITEM_CAST_TIME, 0, $2); }
- | DATETIME opt_field_length { $$.set(ITEM_CAST_DATETIME, 0, $2); }
+ 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); }
;
opt_expr_list:
@@ -10743,9 +11778,9 @@ expr_list:
expr
{
$$= new (thd->mem_root) List<Item>;
- if ($$ == NULL)
+ if (unlikely($$ == NULL) ||
+ unlikely($$->push_back($1, thd->mem_root)))
MYSQL_YYABORT;
- $$->push_back($1, thd->mem_root);
}
| expr_list ',' expr
{
@@ -10763,9 +11798,9 @@ ident_list:
simple_ident
{
$$= new (thd->mem_root) List<Item>;
- if ($$ == NULL)
+ if (unlikely($$ == NULL) ||
+ unlikely($$->push_back($1, thd->mem_root)))
MYSQL_YYABORT;
- $$->push_back($1, thd->mem_root);
}
| ident_list ',' simple_ident
{
@@ -10774,33 +11809,54 @@ ident_list:
}
;
-opt_expr:
- /* empty */ { $$= NULL; }
- | expr { $$= $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;
+ }
;
-opt_else:
- /* empty */ { $$= NULL; }
- | ELSE expr { $$= $2; }
+when_list_opt_else:
+ when_list
+ | when_list ELSE expr
+ {
+ if (unlikely($1->push_back($3, thd->mem_root)))
+ MYSQL_YYABORT;
+ $$= $1;
+ }
;
-when_list:
- WHEN_SYM expr THEN_SYM expr
+decode_when_list_oracle:
+ expr ',' expr
{
$$= new (thd->mem_root) List<Item>;
- if ($$ == NULL)
+ if (unlikely($$ == NULL) ||
+ unlikely($$->push_back($1, thd->mem_root)) ||
+ unlikely($$->push_back($3, thd->mem_root)))
MYSQL_YYABORT;
- $$->push_back($2, thd->mem_root);
- $$->push_back($4, thd->mem_root);
+
}
- | when_list WHEN_SYM expr THEN_SYM expr
+ | decode_when_list_oracle ',' expr
{
- $1->push_back($3, thd->mem_root);
- $1->push_back($5, thd->mem_root);
$$= $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:
@@ -10808,9 +11864,9 @@ table_ref:
| join_table
{
LEX *lex= Lex;
- if (!($$= lex->current_select->nest_last_join(thd)))
+ if (unlikely(!($$= lex->current_select->nest_last_join(thd))))
{
- my_parse_error(thd, ER_SYNTAX_ERROR);
+ thd->parse_error();
MYSQL_YYABORT;
}
}
@@ -10866,7 +11922,7 @@ join_table:
{
MYSQL_YYABORT_UNLESS($1 && $3);
/* Change the current name resolution context to a local context. */
- if (push_new_name_resolution_context(thd, $1, $3))
+ if (unlikely(push_new_name_resolution_context(thd, $1, $3)))
MYSQL_YYABORT;
Select->parsing_place= IN_ON;
}
@@ -10883,7 +11939,7 @@ join_table:
MYSQL_YYABORT_UNLESS($1 && $3);
}
'(' using_list ')'
- {
+ {
$3->straight=$2;
add_join_natural($1,$3,$7,Select);
$$=$3;
@@ -10901,7 +11957,7 @@ join_table:
{
MYSQL_YYABORT_UNLESS($1 && $5);
/* Change the current name resolution context to a local context. */
- if (push_new_name_resolution_context(thd, $1, $5))
+ if (unlikely(push_new_name_resolution_context(thd, $1, $5)))
MYSQL_YYABORT;
Select->parsing_place= IN_ON;
}
@@ -10937,14 +11993,14 @@ join_table:
{
MYSQL_YYABORT_UNLESS($1 && $5);
/* Change the current name resolution context to a local context. */
- if (push_new_name_resolution_context(thd, $1, $5))
+ if (unlikely(push_new_name_resolution_context(thd, $1, $5)))
MYSQL_YYABORT;
Select->parsing_place= IN_ON;
}
expr
{
LEX *lex= Lex;
- if (!($$= lex->current_select->convert_right_join()))
+ if (unlikely(!($$= lex->current_select->convert_right_join())))
MYSQL_YYABORT;
add_join_on(thd, $$, $8);
$1->on_context= Lex->pop_context();
@@ -10957,7 +12013,7 @@ join_table:
USING '(' using_list ')'
{
LEX *lex= Lex;
- if (!($$= lex->current_select->convert_right_join()))
+ if (unlikely(!($$= lex->current_select->convert_right_join())))
MYSQL_YYABORT;
add_join_natural($$,$5,$9,Select);
}
@@ -10966,7 +12022,7 @@ join_table:
MYSQL_YYABORT_UNLESS($1 && $6);
add_join_natural($6,$1,NULL,Select);
LEX *lex= Lex;
- if (!($$= lex->current_select->convert_right_join()))
+ if (unlikely(!($$= lex->current_select->convert_right_join())))
MYSQL_YYABORT;
}
;
@@ -11014,19 +12070,24 @@ table_factor:
table_primary_ident:
{
+ DBUG_ASSERT(Select);
SELECT_LEX *sel= Select;
sel->table_join_options= 0;
}
- table_ident opt_use_partition opt_table_alias opt_key_definition
+ table_ident opt_use_partition opt_for_system_time_clause opt_table_alias opt_key_definition
{
- if (!($$= Select->add_table_to_list(thd, $2, $4,
- Select->get_table_join_options(),
- YYPS->m_lock_type,
- YYPS->m_mdl_type,
- Select->pop_index_hints(),
- $3)))
+ if (unlikely(!($$= Select->add_table_to_list(thd, $2, $5,
+ Select->get_table_join_options(),
+ YYPS->m_lock_type,
+ YYPS->m_mdl_type,
+ Select->
+ pop_index_hints(),
+ $3))))
MYSQL_YYABORT;
- Select->add_joined_table($$);
+ TABLE_LIST *tl= $$;
+ Select->add_joined_table(tl);
+ if ($4)
+ tl->vers_conditions= Lex->vers_conditions;
}
;
@@ -11049,11 +12110,11 @@ table_primary_ident:
*/
table_primary_derived:
- '(' get_select_lex select_derived_union ')' opt_table_alias
+ '(' get_select_lex select_derived_union ')' opt_for_system_time_clause opt_table_alias
{
/* Use $2 instead of Lex->current_select as derived table will
alter value of Lex->current_select. */
- if (!($3 || $5) && $2->embedding &&
+ if (!($3 || $6) && $2->embedding &&
!$2->embedding->nested_join->join_list.elements)
{
/* we have a derived table ($3 == NULL) but no alias,
@@ -11068,32 +12129,29 @@ table_primary_derived:
are no outer parentheses, add_table_to_list() will throw
error in this case */
LEX *lex=Lex;
+ lex->check_automatic_up(UNSPECIFIED_TYPE);
SELECT_LEX *sel= lex->current_select;
SELECT_LEX_UNIT *unit= sel->master_unit();
lex->current_select= sel= unit->outer_select();
Table_ident *ti= new (thd->mem_root) Table_ident(unit);
- if (ti == NULL)
+ if (unlikely(ti == NULL))
MYSQL_YYABORT;
- if (!($$= sel->add_table_to_list(thd,
- ti, $5, 0,
- TL_READ, MDL_SHARED_READ)))
-
+ if (unlikely(!($$= sel->add_table_to_list(thd,
+ ti, $6, 0,
+ TL_READ,
+ MDL_SHARED_READ))))
MYSQL_YYABORT;
sel->add_joined_table($$);
lex->pop_context();
lex->nest_level--;
}
- /*else if (($3->select_lex &&
- $3->select_lex->master_unit()->is_union() &&
- ($3->select_lex->master_unit()->first_select() ==
- $3->select_lex || !$3->lifted)) || $5)*/
- else if ($5 != NULL)
+ else if (unlikely($6 != NULL))
{
/*
Tables with or without joins within parentheses cannot
have aliases, and we ruled out derived tables above.
*/
- my_parse_error(thd, ER_SYNTAX_ERROR);
+ thd->parse_error();
MYSQL_YYABORT;
}
else
@@ -11111,25 +12169,33 @@ table_primary_derived:
if ($$ && $$->derived &&
!$$->derived->first_select()->next_select())
$$->select_lex->add_where_field($$->derived->first_select());
+ if ($5)
+ {
+ MYSQL_YYABORT_UNLESS(!$3);
+ $$->vers_conditions= Lex->vers_conditions;
+ }
}
/* Represents derived table with WITH clause */
| '(' get_select_lex subselect_start
with_clause query_expression_body
- subselect_end ')' opt_table_alias
+ subselect_end ')' opt_for_system_time_clause opt_table_alias
{
LEX *lex=Lex;
SELECT_LEX *sel= $2;
SELECT_LEX_UNIT *unit= $5->master_unit();
Table_ident *ti= new (thd->mem_root) Table_ident(unit);
- if (ti == NULL)
+ if (unlikely(ti == NULL))
MYSQL_YYABORT;
$5->set_with_clause($4);
lex->current_select= sel;
- if (!($$= sel->add_table_to_list(lex->thd,
- ti, $8, 0,
- TL_READ, MDL_SHARED_READ)))
+ if (unlikely(!($$= sel->add_table_to_list(lex->thd,
+ ti, $9, 0,
+ TL_READ,
+ MDL_SHARED_READ))))
MYSQL_YYABORT;
sel->add_joined_table($$);
+ if ($8)
+ $$->vers_conditions= Lex->vers_conditions;
}
;
@@ -11156,24 +12222,24 @@ select_derived_union:
select_derived
| select_derived union_order_or_limit
{
- if ($1)
+ if (unlikely($1))
{
- my_parse_error(thd, ER_SYNTAX_ERROR);
+ thd->parse_error();
MYSQL_YYABORT;
}
}
| select_derived union_head_non_top
{
- if ($1)
+ if (unlikely($1))
{
- my_parse_error(thd, ER_SYNTAX_ERROR);
+ thd->parse_error();
MYSQL_YYABORT;
}
}
union_list_derived_part2
- | derived_query_specification opt_select_lock_type
- | derived_query_specification order_or_limit opt_select_lock_type
- | derived_query_specification opt_select_lock_type union_list_derived
+ | derived_simple_table opt_select_lock_type
+ | derived_simple_table order_or_limit opt_select_lock_type
+ | derived_simple_table opt_select_lock_type union_list_derived
;
union_list_derived_part2:
@@ -11218,16 +12284,20 @@ select_derived:
/* for normal joins, $2 != NULL and end_nested_join() != NULL,
for derived tables, both must equal NULL */
- if (!($$= $1->end_nested_join(lex->thd)) && $2)
+ if (unlikely(!($$= $1->end_nested_join(lex->thd)) && $2))
MYSQL_YYABORT;
- if (!$2 && $$)
+ if (unlikely(!$2 && $$))
{
- my_parse_error(thd, ER_SYNTAX_ERROR);
+ thd->parse_error();
MYSQL_YYABORT;
}
}
;
+derived_simple_table:
+ derived_query_specification { $$= $1; }
+ | derived_table_value_constructor { $$= $1; }
+ ;
/*
Similar to query_specification, but for derived tables.
Example: the inner parenthesized SELECT in this query:
@@ -11242,18 +12312,32 @@ derived_query_specification:
}
;
+derived_table_value_constructor:
+ VALUES
+ {
+ Lex->tvc_start();
+ }
+ values_list
+ {
+ if (Lex->tvc_finalize_derived())
+ MYSQL_YYABORT;
+ $$= NULL;
+ }
+ ;
+
+
select_derived2:
{
LEX *lex= Lex;
lex->derived_tables|= DERIVED_SUBQUERY;
- if (!lex->expr_allows_subselect ||
- lex->sql_command == (int)SQLCOM_PURGE)
+ if (unlikely(!lex->expr_allows_subselect ||
+ lex->sql_command == (int)SQLCOM_PURGE))
{
- my_parse_error(thd, ER_SYNTAX_ERROR);
+ thd->parse_error();
MYSQL_YYABORT;
}
if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE ||
- mysql_new_select(lex, 1))
+ unlikely(mysql_new_select(lex, 1, NULL)))
MYSQL_YYABORT;
mysql_init_select(lex);
lex->current_select->linkage= DERIVED_TABLE_TYPE;
@@ -11274,7 +12358,7 @@ get_select_lex_derived:
get_select_lex
{
LEX *lex= Lex;
- if ($1->init_nested_join(lex->thd))
+ if (unlikely($1->init_nested_join(lex->thd)))
MYSQL_YYABORT;
}
;
@@ -11347,7 +12431,7 @@ key_usage_element:
ident
{ Select->add_index_hint(thd, $1.str, $1.length); }
| PRIMARY_SYM
- { Select->add_index_hint(thd, (char *)"PRIMARY", 7); }
+ { Select->add_index_hint(thd, "PRIMARY", 7); }
;
key_usage_list:
@@ -11358,12 +12442,12 @@ key_usage_list:
using_list:
ident
{
- if (!($$= new (thd->mem_root) List<String>))
+ 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 (s == NULL)
+ if (unlikely(unlikely(s == NULL)))
MYSQL_YYABORT;
$$->push_back(s, thd->mem_root);
}
@@ -11372,9 +12456,10 @@ using_list:
String *s= new (thd->mem_root) String((const char *) $3.str,
$3.length,
system_charset_info);
- if (s == NULL)
+ if (unlikely(unlikely(s == NULL)))
+ MYSQL_YYABORT;
+ if (unlikely($1->push_back(s, thd->mem_root)))
MYSQL_YYABORT;
- $1->push_back(s, thd->mem_root);
$$= $1;
}
;
@@ -11423,8 +12508,8 @@ opt_table_alias:
/* empty */ { $$=0; }
| table_alias ident_table_alias
{
- $$= (LEX_STRING*) thd->memdup(&$2,sizeof(LEX_STRING));
- if ($$ == NULL)
+ $$= (LEX_CSTRING*) thd->memdup(&$2,sizeof(LEX_STRING));
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
;
@@ -11472,13 +12557,13 @@ opt_escape:
Lex->escape_used= TRUE;
$$= $2;
}
- | /* empty */
+ | /* empty */ %prec PREC_BELOW_ESCAPE
{
Lex->escape_used= FALSE;
$$= ((thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES) ?
new (thd->mem_root) Item_string_ascii(thd, "", 0) :
new (thd->mem_root) Item_string_ascii(thd, "\\", 1));
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
;
@@ -11494,9 +12579,15 @@ opt_group_clause:
group_list:
group_list ',' order_ident order_dir
- { if (add_group_to_list(thd, $3,(bool) $4)) MYSQL_YYABORT; }
+ {
+ if (unlikely(add_group_to_list(thd, $3,(bool) $4)))
+ MYSQL_YYABORT;
+ }
| order_ident order_dir
- { if (add_group_to_list(thd, $1,(bool) $2)) MYSQL_YYABORT; }
+ {
+ if (unlikely(add_group_to_list(thd, $1,(bool) $2)))
+ MYSQL_YYABORT;
+ }
;
olap_opt:
@@ -11511,7 +12602,7 @@ olap_opt:
SQL-2003: GROUP BY ... CUBE(col1, col2, col3)
*/
LEX *lex=Lex;
- if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE)
+ if (unlikely(lex->current_select->linkage == GLOBAL_OPTIONS_TYPE))
my_yyabort_error((ER_WRONG_USAGE, MYF(0), "WITH CUBE",
"global union parameters"));
lex->current_select->olap= CUBE_TYPE;
@@ -11528,7 +12619,7 @@ olap_opt:
SQL-2003: GROUP BY ... ROLLUP(col1, col2, col3)
*/
LEX *lex= Lex;
- if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE)
+ if (unlikely(lex->current_select->linkage == GLOBAL_OPTIONS_TYPE))
my_yyabort_error((ER_WRONG_USAGE, MYF(0), "WITH ROLLUP",
"global union parameters"));
lex->current_select->olap= ROLLUP_TYPE;
@@ -11556,10 +12647,10 @@ window_def:
window_name AS window_spec
{
LEX *lex= Lex;
- if (Select->add_window_def(thd, $1, lex->win_ref,
- Select->group_list,
- Select->order_list,
- lex->win_frame ))
+ if (unlikely(Select->add_window_def(thd, $1, lex->win_ref,
+ Select->group_list,
+ Select->order_list,
+ lex->win_frame)))
MYSQL_YYABORT;
}
;
@@ -11576,8 +12667,8 @@ opt_window_ref:
/* empty */ {}
| ident
{
- thd->lex->win_ref= (LEX_STRING *) thd->memdup(&$1, sizeof(LEX_STRING));
- if (thd->lex->win_ref == NULL)
+ thd->lex->win_ref= (LEX_CSTRING *) thd->memdup(&$1, sizeof(LEX_CSTRING));
+ if (unlikely(thd->lex->win_ref == NULL))
MYSQL_YYABORT;
}
;
@@ -11602,7 +12693,7 @@ opt_window_frame_clause:
lex->frame_top_bound,
lex->frame_bottom_bound,
$3);
- if (lex->win_frame == NULL)
+ if (unlikely(lex->win_frame == NULL))
MYSQL_YYABORT;
}
;
@@ -11620,7 +12711,7 @@ window_frame_extent:
lex->frame_bottom_bound=
new (thd->mem_root)
Window_frame_bound(Window_frame_bound::CURRENT, NULL);
- if (lex->frame_bottom_bound == NULL)
+ if (unlikely(lex->frame_bottom_bound == NULL))
MYSQL_YYABORT;
}
| BETWEEN_SYM window_frame_bound AND_SYM window_frame_bound
@@ -11636,21 +12727,21 @@ window_frame_start:
{
$$= new (thd->mem_root)
Window_frame_bound(Window_frame_bound::PRECEDING, NULL);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| CURRENT_SYM ROW_SYM
{
$$= new (thd->mem_root)
Window_frame_bound(Window_frame_bound::CURRENT, NULL);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| literal PRECEDING_SYM
{
$$= new (thd->mem_root)
Window_frame_bound(Window_frame_bound::PRECEDING, $1);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
;
@@ -11661,14 +12752,14 @@ window_frame_bound:
{
$$= new (thd->mem_root)
Window_frame_bound(Window_frame_bound::FOLLOWING, NULL);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| literal FOLLOWING_SYM
{
$$= new (thd->mem_root)
Window_frame_bound(Window_frame_bound::FOLLOWING, $1);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
;
@@ -11681,7 +12772,9 @@ opt_window_frame_exclusion:
{ $$= Window_frame::EXCL_GROUP; }
| EXCLUDE_SYM TIES_SYM
{ $$= Window_frame::EXCL_TIES; }
- | EXCLUDE_SYM NO_SYM OTHERS_SYM
+ | EXCLUDE_SYM NO_SYM OTHERS_MARIADB_SYM
+ { $$= Window_frame::EXCL_NONE; }
+ | EXCLUDE_SYM NO_SYM OTHERS_ORACLE_SYM
{ $$= Window_frame::EXCL_NONE; }
;
@@ -11702,7 +12795,7 @@ alter_order_item:
simple_ident_nospvar order_dir
{
bool ascending= ($2 == 1) ? true : false;
- if (add_order_to_list(thd, $1, ascending))
+ if (unlikely(add_order_to_list(thd, $1, ascending)))
MYSQL_YYABORT;
}
;
@@ -11722,9 +12815,9 @@ order_clause:
LEX *lex=Lex;
SELECT_LEX *sel= lex->current_select;
SELECT_LEX_UNIT *unit= sel-> master_unit();
- if (sel->linkage != GLOBAL_OPTIONS_TYPE &&
- sel->olap != UNSPECIFIED_OLAP_TYPE &&
- (sel->linkage != UNION_TYPE || sel->braces))
+ if (unlikely(sel->linkage != GLOBAL_OPTIONS_TYPE &&
+ sel->olap != UNSPECIFIED_OLAP_TYPE &&
+ (sel->linkage != UNION_TYPE || sel->braces)))
{
my_error(ER_WRONG_USAGE, MYF(0),
"CUBE/ROLLUP", "ORDER BY");
@@ -11738,17 +12831,17 @@ order_clause:
executed in the same way as the query
SELECT ... ORDER BY order_list
unless the SELECT construct contains ORDER BY or LIMIT clauses.
- Otherwise we create a fake SELECT_LEX if it has not been created
- yet.
+ Otherwise we create a fake SELECT_LEX if it has not been
+ created yet.
*/
SELECT_LEX *first_sl= unit->first_select();
- if (!unit->is_union() &&
- (first_sl->order_list.elements ||
- first_sl->select_limit) &&
- unit->add_fake_select_lex(thd))
+ if (unlikely(!unit->is_unit_op() &&
+ (first_sl->order_list.elements ||
+ first_sl->select_limit) &&
+ unit->add_fake_select_lex(thd)))
MYSQL_YYABORT;
}
- if (sel->master_unit()->is_union() && !sel->braces)
+ if (sel->master_unit()->is_unit_op() && !sel->braces)
{
/*
At this point we don't know yet whether this is the last
@@ -11768,9 +12861,15 @@ order_clause:
order_list:
order_list ',' order_ident order_dir
- { if (add_order_to_list(thd, $3,(bool) $4)) MYSQL_YYABORT; }
+ {
+ if (unlikely(add_order_to_list(thd, $3,(bool) $4)))
+ MYSQL_YYABORT;
+ }
| order_ident order_dir
- { if (add_order_to_list(thd, $1,(bool) $2)) MYSQL_YYABORT; }
+ {
+ if (unlikely(add_order_to_list(thd, $1,(bool) $2)))
+ MYSQL_YYABORT;
+ }
;
order_dir:
@@ -11788,7 +12887,7 @@ limit_clause_init:
LIMIT
{
SELECT_LEX *sel= Select;
- if (sel->master_unit()->is_union() && !sel->braces)
+ if (sel->master_unit()->is_unit_op() && !sel->braces)
{
/* Move LIMIT that belongs to UNION to fake_select_lex */
Lex->current_select= sel->master_unit()->fake_select_lex;
@@ -11841,61 +12940,36 @@ limit_options:
;
limit_option:
- ident
- {
- Item_splocal *splocal;
- LEX *lex= thd->lex;
- Lex_input_stream *lip= & thd->m_parser_state->m_lip;
- sp_variable *spv;
- sp_pcontext *spc = lex->spcont;
- if (spc && (spv = spc->find_variable($1, false)))
- {
- uint pos_in_query= 0;
- uint len_in_query= 0;
- if (!lex->clone_spec_offset)
- {
- pos_in_query= (uint)(lip->get_tok_start() -
- lex->sphead->m_tmp_query);
- len_in_query= (uint)(lip->get_ptr() -
- lip->get_tok_start());
- }
- splocal= new (thd->mem_root)
- Item_splocal(thd, $1, spv->offset, spv->sql_type(),
- pos_in_query, len_in_query);
- if (splocal == NULL)
+ 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;
-#ifndef DBUG_OFF
- splocal->m_sp= lex->sphead;
-#endif
- lex->safe_to_cache_query=0;
}
- else
- my_yyabort_error((ER_SP_UNDECLARED_VAR, MYF(0), $1.str));
- if (splocal->type() != Item::INT_ITEM)
- my_yyabort_error((ER_WRONG_SPVAR_TYPE_IN_LIMIT, MYF(0)));
- splocal->limit_clause_param= TRUE;
- $$= splocal;
- }
| param_marker
- {
- $1->limit_clause_param= TRUE;
- }
+ {
+ $1->limit_clause_param= TRUE;
+ }
| ULONGLONG_NUM
{
$$= new (thd->mem_root) Item_uint(thd, $1.str, $1.length);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| LONG_NUM
{
$$= new (thd->mem_root) Item_uint(thd, $1.str, $1.length);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| NUM
{
$$= new (thd->mem_root) Item_uint(thd, $1.str, $1.length);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
;
@@ -11921,23 +12995,27 @@ delete_limit_clause:
Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
sel->explicit_limit= 1;
}
- | LIMIT ROWS_SYM EXAMINED_SYM { my_parse_error(thd, ER_SYNTAX_ERROR); MYSQL_YYABORT; }
- | LIMIT limit_option ROWS_SYM EXAMINED_SYM { my_parse_error(thd, ER_SYNTAX_ERROR); MYSQL_YYABORT; }
+ | LIMIT ROWS_SYM EXAMINED_SYM { thd->parse_error(); MYSQL_YYABORT; }
+ | LIMIT limit_option ROWS_SYM EXAMINED_SYM { thd->parse_error(); MYSQL_YYABORT; }
+ ;
+
+opt_plus:
+ /* empty */
+ | '+'
;
int_num:
- NUM { int error; $$= (int) my_strtoll10($1.str, (char**) 0, &error); }
+ opt_plus NUM { int error; $$= (int) my_strtoll10($2.str, (char**) 0, &error); }
| '-' NUM { int error; $$= -(int) my_strtoll10($2.str, (char**) 0, &error); }
- | '-' LONG_NUM { int error; $$= -(int) my_strtoll10($2.str, (char**) 0, &error); }
;
ulong_num:
- NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); }
+ opt_plus NUM { int error; $$= (ulong) my_strtoll10($2.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); }
- | DECIMAL_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); }
- | FLOAT_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); }
+ | 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:
@@ -11948,12 +13026,19 @@ real_ulong_num:
| 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:
- NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); }
- | ULONGLONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); }
- | LONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); }
- | DECIMAL_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); }
- | FLOAT_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); }
+ 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:
@@ -11966,7 +13051,7 @@ real_ulonglong_num:
dec_num_error:
dec_num
- { my_parse_error(thd, ER_ONLY_INTEGERS_ALLOWED); }
+ { thd->parse_error(ER_ONLY_INTEGERS_ALLOWED); }
;
dec_num:
@@ -11979,6 +13064,12 @@ choice:
| DEFAULT { $$= HA_CHOICE_UNDEF; }
;
+bool:
+ ulong_num { $$= $1 != 0; }
+ | TRUE_SYM { $$= 1; }
+ | FALSE_SYM { $$= 0; }
+ ;
+
procedure_clause:
PROCEDURE_SYM ident /* Procedure name */
{
@@ -11991,10 +13082,10 @@ 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.str);
- if (item == NULL)
+ NULL, NULL, &$2);
+ if (unlikely(item == NULL))
MYSQL_YYABORT;
- if (add_proc_to_list(thd, item))
+ if (unlikely(add_proc_to_list(thd, item)))
MYSQL_YYABORT;
Lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
@@ -12026,9 +13117,9 @@ procedure_list2:
procedure_item:
remember_name expr remember_end
{
- if (add_proc_to_list(thd, $2))
+ if (unlikely(add_proc_to_list(thd, $2)))
MYSQL_YYABORT;
- if (!$2->name)
+ if (!$2->name.str || $2->name.str == item_empty_name)
$2->set_name(thd, $1, (uint) ($3 - $1), thd->charset());
}
;
@@ -12037,7 +13128,8 @@ select_var_list_init:
{
LEX *lex=Lex;
if (!lex->describe &&
- (!(lex->result= new (thd->mem_root) select_dumpvar(thd))))
+ unlikely((!(lex->result= new (thd->mem_root)
+ select_dumpvar(thd)))))
MYSQL_YYABORT;
}
select_var_list
@@ -12053,7 +13145,7 @@ select_var_ident: select_outvar
{
if (Lex->result)
{
- if ($1 == NULL)
+ if (unlikely($1 == NULL))
MYSQL_YYABORT;
((select_dumpvar *)Lex->result)->var_list.push_back($1, thd->mem_root);
}
@@ -12071,18 +13163,17 @@ select_var_ident: select_outvar
select_outvar:
'@' ident_or_text
{
- $$ = Lex->result ? new (thd->mem_root) my_var_user($2) : NULL;
+ $$ = Lex->result ? new (thd->mem_root) my_var_user(&$2) : NULL;
}
| ident_or_text
{
- sp_variable *t;
-
- if (!Lex->spcont || !(t= Lex->spcont->find_variable($1, false)))
- my_yyabort_error((ER_SP_UNDECLARED_VAR, MYF(0), $1.str));
- $$ = Lex->result ? (new (thd->mem_root)
- my_var_sp($1, t->offset, t->sql_type(),
- Lex->sphead)) :
- NULL;
+ 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;
}
;
@@ -12095,10 +13186,11 @@ into_destination:
{
LEX *lex= Lex;
lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
- if (!(lex->exchange=
- new (thd->mem_root) sql_exchange($2.str, 0)) ||
- !(lex->result=
- new (thd->mem_root) select_export(thd, lex->exchange)))
+ 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
@@ -12110,10 +13202,12 @@ into_destination:
if (!lex->describe)
{
lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
- if (!(lex->exchange= new (thd->mem_root) sql_exchange($2.str,1)))
+ if (unlikely(!(lex->exchange=
+ new (thd->mem_root) sql_exchange($2.str,1))))
MYSQL_YYABORT;
- if (!(lex->result=
- new (thd->mem_root) select_dump(thd, lex->exchange)))
+ if (unlikely(!(lex->result=
+ new (thd->mem_root)
+ select_dump(thd, lex->exchange))))
MYSQL_YYABORT;
}
}
@@ -12152,23 +13246,23 @@ drop:
YYPS->m_lock_type= TL_UNLOCK;
YYPS->m_mdl_type= MDL_EXCLUSIVE;
}
- table_list opt_restrict
+ table_list opt_lock_wait_timeout opt_restrict
{}
- | DROP INDEX_SYM opt_if_exists_table_element ident ON table_ident {}
+ | DROP INDEX_SYM 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, $4.str, $3));
- if (ad == NULL)
+ if (unlikely(ad == NULL))
MYSQL_YYABORT;
lex->sql_command= SQLCOM_DROP_INDEX;
lex->alter_info.reset();
- lex->alter_info.flags= Alter_info::ALTER_DROP_INDEX;
+ lex->alter_info.flags= ALTER_DROP_INDEX;
lex->alter_info.drop_list.push_back(ad, thd->mem_root);
- if (!lex->current_select->add_table_to_list(thd, $6, NULL,
- TL_OPTION_UPDATING,
- TL_READ_NO_INSERT,
- MDL_SHARED_UPGRADABLE))
+ if (unlikely(!lex->current_select->
+ add_table_to_list(thd, $6, NULL, TL_OPTION_UPDATING,
+ TL_READ_NO_INSERT,
+ MDL_SHARED_UPGRADABLE)))
MYSQL_YYABORT;
}
| DROP DATABASE opt_if_exists ident
@@ -12181,37 +13275,35 @@ drop:
{
LEX *lex= thd->lex;
sp_name *spname;
- if ($4.str && check_db_name(&$4))
- my_yyabort_error((ER_WRONG_DB_NAME, MYF(0), $4.str));
- if (lex->sphead)
+ 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 (spname == NULL)
+ spname= new (thd->mem_root) sp_name(&$4, &$6, true);
+ if (unlikely(spname == NULL))
MYSQL_YYABORT;
- spname->init_qname(thd);
lex->spname= spname;
}
| DROP FUNCTION_SYM opt_if_exists ident
{
LEX *lex= thd->lex;
- LEX_STRING db= {0, 0};
+ LEX_CSTRING db= {0, 0};
sp_name *spname;
- if (lex->sphead)
+ if (unlikely(lex->sphead))
my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "FUNCTION"));
- if (thd->db && lex->copy_db_to(&db.str, &db.length))
+ 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 (spname == NULL)
+ spname= new (thd->mem_root) sp_name(&db, &$4, false);
+ if (unlikely(spname == NULL))
MYSQL_YYABORT;
- spname->init_qname(thd);
lex->spname= spname;
}
| DROP PROCEDURE_SYM opt_if_exists sp_name
{
LEX *lex=Lex;
- if (lex->sphead)
+ 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;
@@ -12259,6 +13351,17 @@ drop:
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:
@@ -12269,10 +13372,10 @@ table_list:
table_name:
table_ident
{
- if (!Select->add_table_to_list(thd, $1, NULL,
- TL_OPTION_UPDATING,
- YYPS->m_lock_type,
- YYPS->m_mdl_type))
+ if (unlikely(!Select->add_table_to_list(thd, $1, NULL,
+ TL_OPTION_UPDATING,
+ YYPS->m_lock_type,
+ YYPS->m_mdl_type)))
MYSQL_YYABORT;
}
;
@@ -12280,12 +13383,12 @@ table_name:
table_name_with_opt_use_partition:
table_ident opt_use_partition
{
- if (!Select->add_table_to_list(thd, $1, NULL,
- TL_OPTION_UPDATING,
- YYPS->m_lock_type,
- YYPS->m_mdl_type,
- NULL,
- $2))
+ 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;
}
;
@@ -12298,10 +13401,12 @@ table_alias_ref_list:
table_alias_ref:
table_ident_opt_wild
{
- if (!Select->add_table_to_list(thd, $1, NULL,
- TL_OPTION_UPDATING | TL_OPTION_ALIAS,
+ if (unlikely(!Select->
+ add_table_to_list(thd, $1, NULL,
+ (TL_OPTION_UPDATING |
+ TL_OPTION_ALIAS),
YYPS->m_lock_type,
- YYPS->m_mdl_type))
+ YYPS->m_mdl_type)))
MYSQL_YYABORT;
}
;
@@ -12353,7 +13458,9 @@ insert:
Lex->current_select= &Lex->select_lex;
}
insert_field_spec opt_insert_update
- {}
+ {
+ Lex->mark_first_table_as_inserting();
+ }
;
replace:
@@ -12370,7 +13477,9 @@ replace:
Lex->current_select= &Lex->select_lex;
}
insert_field_spec
- {}
+ {
+ Lex->mark_first_table_as_inserting();
+ }
;
insert_lock_option:
@@ -12386,10 +13495,9 @@ insert_lock_option:
| LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; }
| DELAYED_SYM
{
- Lex->keyword_delayed_begin_offset= (uint)(YYLIP->get_tok_start() -
- thd->query());
- Lex->keyword_delayed_end_offset= Lex->keyword_delayed_begin_offset +
- YYLIP->yyLength() + 1;
+ // 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; }
@@ -12399,10 +13507,8 @@ replace_lock_option:
opt_low_priority { $$= $1; }
| DELAYED_SYM
{
- Lex->keyword_delayed_begin_offset= (uint)(YYLIP->get_tok_start() -
- thd->query());
- Lex->keyword_delayed_end_offset= Lex->keyword_delayed_begin_offset +
- YYLIP->yyLength() + 1;
+ Lex->keyword_delayed_begin_offset= (uint)($1.pos() - thd->query());
+ Lex->keyword_delayed_end_offset= (uint)($1.end() - thd->query());
$$= TL_WRITE_DELAYED;
}
;
@@ -12429,8 +13535,9 @@ insert_field_spec:
| SET
{
LEX *lex=Lex;
- if (!(lex->insert_list= new (thd->mem_root) List_item) ||
- lex->many_values.push_back(lex->insert_list, thd->mem_root))
+ 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;
}
ident_eq_list
@@ -12450,7 +13557,7 @@ insert_values:
values_list:
values_list ',' no_braces
- | no_braces
+ | no_braces_with_names
;
ident_eq_list:
@@ -12462,8 +13569,8 @@ ident_eq_value:
simple_ident_nospvar equal expr_or_default
{
LEX *lex=Lex;
- if (lex->field_list.push_back($1, thd->mem_root) ||
- lex->insert_list->push_back($3, thd->mem_root))
+ if (unlikely(lex->field_list.push_back($1, thd->mem_root)) ||
+ unlikely(lex->insert_list->push_back($3, thd->mem_root)))
MYSQL_YYABORT;
}
;
@@ -12478,16 +13585,42 @@ opt_equal:
| equal {}
;
+opt_with:
+ opt_equal {}
+ | WITH {}
+ ;
+
+opt_by:
+ opt_equal {}
+ | BY {}
+ ;
+
no_braces:
'('
{
- if (!(Lex->insert_list= new (thd->mem_root) List_item))
- MYSQL_YYABORT;
+ if (unlikely(!(Lex->insert_list= new (thd->mem_root) List_item)))
+ MYSQL_YYABORT;
}
opt_values ')'
{
LEX *lex=Lex;
- if (lex->many_values.push_back(lex->insert_list, thd->mem_root))
+ 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;
}
;
@@ -12497,31 +13630,55 @@ opt_values:
| values
;
+opt_values_with_names:
+ /* empty */ {}
+ | values_with_names
+ ;
+
values:
values ',' expr_or_default
{
- if (Lex->insert_list->push_back($3, thd->mem_root))
+ if (unlikely(Lex->insert_list->push_back($3, thd->mem_root)))
MYSQL_YYABORT;
}
| expr_or_default
{
- if (Lex->insert_list->push_back($1, thd->mem_root))
+ 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_value(thd, Lex->current_context());
- if ($$ == NULL)
+ $$= new (thd->mem_root) Item_default_specification(thd);
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| IGNORE_SYM
{
- $$= new (thd->mem_root) Item_ignore_value(thd, Lex->current_context());
- if ($$ == NULL)
+ $$= new (thd->mem_root) Item_ignore_specification(thd);
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
;
@@ -12529,7 +13686,14 @@ expr_or_default:
opt_insert_update:
/* empty */
| ON DUPLICATE_SYM { Lex->duplicates= DUP_UPDATE; }
- KEY_SYM UPDATE_SYM insert_update_list
+ KEY_SYM UPDATE_SYM
+ {
+ Select->parsing_place= IN_UPDATE_ON_DUP_KEY;
+ }
+ insert_update_list
+ {
+ Select->parsing_place= NO_MATTER;
+ }
;
/* Update rows in a table */
@@ -12548,11 +13712,11 @@ update:
SELECT_LEX *slex= &Lex->select_lex;
if (slex->table_list.elements > 1)
Lex->sql_command= SQLCOM_UPDATE_MULTI;
- else if (slex->get_table_list()->derived)
+ else if (unlikely(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, "UPDATE");
+ slex->get_table_list()->alias.str, "UPDATE");
MYSQL_YYABORT;
}
/*
@@ -12573,7 +13737,8 @@ update_list:
update_elem:
simple_ident_nospvar equal expr_or_default
{
- if (add_item_to_list(thd, $1) || add_value_to_list(thd, $3))
+ if (unlikely(add_item_to_list(thd, $1)) ||
+ unlikely(add_value_to_list(thd, $3)))
MYSQL_YYABORT;
}
;
@@ -12587,8 +13752,8 @@ insert_update_elem:
simple_ident_nospvar equal expr_or_default
{
LEX *lex= Lex;
- if (lex->update_list.push_back($1, thd->mem_root) ||
- lex->value_list.push_back($3, thd->mem_root))
+ if (unlikely(lex->update_list.push_back($1, thd->mem_root)) ||
+ unlikely(lex->value_list.push_back($3, thd->mem_root)))
MYSQL_YYABORT;
}
;
@@ -12612,23 +13777,48 @@ delete:
lex->ignore= 0;
lex->select_lex.init_order();
}
- opt_delete_options single_multi
+ delete_part2
+ ;
+
+opt_delete_system_time:
+ /* empty */
+ {
+ Lex->vers_conditions.init(SYSTEM_TIME_HISTORY);
+ }
+ | 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;
+ }
;
-single_multi:
+delete_single_table:
FROM table_ident opt_use_partition
{
- if (!Select->add_table_to_list(thd, $2, NULL, TL_OPTION_UPDATING,
+ if (unlikely(!Select->
+ add_table_to_list(thd, $2, NULL, TL_OPTION_UPDATING,
YYPS->m_lock_type,
YYPS->m_mdl_type,
NULL,
- $3))
+ $3)))
MYSQL_YYABORT;
YYPS->m_lock_type= TL_READ_DEFAULT;
YYPS->m_mdl_type= MDL_SHARED_READ;
}
- opt_where_clause opt_order_clause
- delete_limit_clause {}
+ ;
+
+single_multi:
+ delete_single_table
+ opt_where_clause
+ opt_order_clause
+ delete_limit_clause
opt_select_expressions {}
| table_wild_list
{
@@ -12638,7 +13828,7 @@ single_multi:
}
FROM join_table_list opt_where_clause
{
- if (multi_delete_set_locks_and_link_aux_tables(Lex))
+ if (unlikely(multi_delete_set_locks_and_link_aux_tables(Lex)))
MYSQL_YYABORT;
}
| FROM table_alias_ref_list
@@ -12649,7 +13839,7 @@ single_multi:
}
USING join_table_list opt_where_clause
{
- if (multi_delete_set_locks_and_link_aux_tables(Lex))
+ if (unlikely(multi_delete_set_locks_and_link_aux_tables(Lex)))
MYSQL_YYABORT;
}
;
@@ -12667,28 +13857,32 @@ table_wild_list:
table_wild_one:
ident opt_wild
{
- Table_ident *ti= new (thd->mem_root) Table_ident($1);
- if (ti == NULL)
+ Table_ident *ti= new (thd->mem_root) Table_ident(&$1);
+ if (unlikely(ti == NULL))
MYSQL_YYABORT;
- if (!Select->add_table_to_list(thd,
+ if (unlikely(!Select->
+ add_table_to_list(thd,
ti,
NULL,
- TL_OPTION_UPDATING | TL_OPTION_ALIAS,
+ (TL_OPTION_UPDATING |
+ TL_OPTION_ALIAS),
YYPS->m_lock_type,
- YYPS->m_mdl_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 (ti == NULL)
+ Table_ident *ti= new (thd->mem_root) Table_ident(thd, &$1, &$3, 0);
+ if (unlikely(ti == NULL))
MYSQL_YYABORT;
- if (!Select->add_table_to_list(thd,
+ if (unlikely(!Select->
+ add_table_to_list(thd,
ti,
NULL,
- TL_OPTION_UPDATING | TL_OPTION_ALIAS,
+ (TL_OPTION_UPDATING |
+ TL_OPTION_ALIAS),
YYPS->m_lock_type,
- YYPS->m_mdl_type))
+ YYPS->m_mdl_type)))
MYSQL_YYABORT;
}
;
@@ -12710,7 +13904,7 @@ opt_delete_option:
;
truncate:
- TRUNCATE_SYM opt_table_sym
+ TRUNCATE_SYM
{
LEX* lex= Lex;
lex->sql_command= SQLCOM_TRUNCATE;
@@ -12721,12 +13915,12 @@ truncate:
YYPS->m_lock_type= TL_WRITE;
YYPS->m_mdl_type= MDL_EXCLUSIVE;
}
- table_name
+ 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 (lex->m_sql_cmd == NULL)
+ if (unlikely(lex->m_sql_cmd == NULL))
MYSQL_YYABORT;
}
;
@@ -12801,7 +13995,7 @@ show:
{
LEX *lex=Lex;
lex->wild=0;
- lex->ident=null_lex_str;
+ lex->ident= null_clex_str;
mysql_init_select(lex);
lex->current_select->parsing_place= SELECT_LIST;
lex->create_info.init();
@@ -12817,7 +14011,7 @@ show_param:
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_DATABASES;
- if (prepare_schema_table(thd, lex, 0, SCH_SCHEMATA))
+ if (unlikely(prepare_schema_table(thd, lex, 0, SCH_SCHEMATA)))
MYSQL_YYABORT;
}
| opt_full TABLES opt_db wild_and_where
@@ -12825,7 +14019,7 @@ show_param:
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_TABLES;
lex->select_lex.db= $3;
- if (prepare_schema_table(thd, lex, 0, SCH_TABLE_NAMES))
+ if (unlikely(prepare_schema_table(thd, lex, 0, SCH_TABLE_NAMES)))
MYSQL_YYABORT;
}
| opt_full TRIGGERS_SYM opt_db wild_and_where
@@ -12833,7 +14027,7 @@ show_param:
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_TRIGGERS;
lex->select_lex.db= $3;
- if (prepare_schema_table(thd, lex, 0, SCH_TRIGGERS))
+ if (unlikely(prepare_schema_table(thd, lex, 0, SCH_TRIGGERS)))
MYSQL_YYABORT;
}
| EVENTS_SYM opt_db wild_and_where
@@ -12841,7 +14035,7 @@ show_param:
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_EVENTS;
lex->select_lex.db= $2;
- if (prepare_schema_table(thd, lex, 0, SCH_EVENTS))
+ if (unlikely(prepare_schema_table(thd, lex, 0, SCH_EVENTS)))
MYSQL_YYABORT;
}
| TABLE_SYM STATUS_SYM opt_db wild_and_where
@@ -12849,7 +14043,7 @@ show_param:
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_TABLE_STATUS;
lex->select_lex.db= $3;
- if (prepare_schema_table(thd, lex, 0, SCH_TABLES))
+ if (unlikely(prepare_schema_table(thd, lex, 0, SCH_TABLES)))
MYSQL_YYABORT;
}
| OPEN_SYM TABLES opt_db wild_and_where
@@ -12857,27 +14051,27 @@ show_param:
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_OPEN_TABLES;
lex->select_lex.db= $3;
- if (prepare_schema_table(thd, lex, 0, SCH_OPEN_TABLES))
+ if (unlikely(prepare_schema_table(thd, lex, 0, SCH_OPEN_TABLES)))
MYSQL_YYABORT;
}
| PLUGINS_SYM
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_PLUGINS;
- if (prepare_schema_table(thd, lex, 0, SCH_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 (prepare_schema_table(thd, Lex, 0, SCH_ALL_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 (prepare_schema_table(thd, Lex, 0, SCH_ALL_PLUGINS))
+ if (unlikely(prepare_schema_table(thd, Lex, 0, SCH_ALL_PLUGINS)))
MYSQL_YYABORT;
}
| ENGINE_SYM known_storage_engines show_engine_param
@@ -12888,9 +14082,9 @@ show_param:
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_FIELDS;
- if ($5)
- $4->change_db($5);
- if (prepare_schema_table(thd, lex, $4, SCH_COLUMNS))
+ if ($5.str)
+ $4->change_db(&$5);
+ if (unlikely(prepare_schema_table(thd, lex, $4, SCH_COLUMNS)))
MYSQL_YYABORT;
}
| master_or_binary LOGS_SYM
@@ -12916,16 +14110,16 @@ show_param:
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_KEYS;
- if ($4)
- $3->change_db($4);
- if (prepare_schema_table(thd, lex, $3, SCH_STATISTICS))
+ 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 (prepare_schema_table(thd, lex, 0, SCH_ENGINES))
+ if (unlikely(prepare_schema_table(thd, lex, 0, SCH_ENGINES)))
MYSQL_YYABORT;
}
| AUTHORS_SYM
@@ -12944,9 +14138,15 @@ show_param:
lex->sql_command= SQLCOM_SHOW_PRIVILEGES;
}
| COUNT_SYM '(' '*' ')' WARNINGS
- { (void) create_select_for_variable("warning_count"); }
+ {
+ LEX_CSTRING var= {STRING_WITH_LEN("warning_count")};
+ (void) create_select_for_variable(thd, &var);
+ }
| COUNT_SYM '(' '*' ')' ERRORS
- { (void) create_select_for_variable("error_count"); }
+ {
+ LEX_CSTRING var= {STRING_WITH_LEN("error_count")};
+ (void) create_select_for_variable(thd, &var);
+ }
| WARNINGS opt_limit_clause
{ Lex->sql_command = SQLCOM_SHOW_WARNS;}
| ERRORS opt_limit_clause
@@ -12957,7 +14157,7 @@ show_param:
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_PROFILE;
- if (prepare_schema_table(thd, lex, NULL, SCH_PROFILES) != 0)
+ if (unlikely(prepare_schema_table(thd, lex, NULL, SCH_PROFILES)))
MYSQL_YYABORT;
}
| opt_var_type STATUS_SYM wild_and_where
@@ -12965,7 +14165,7 @@ show_param:
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_STATUS;
lex->option_type= $1;
- if (prepare_schema_table(thd, lex, 0, SCH_SESSION_STATUS))
+ if (unlikely(prepare_schema_table(thd, lex, 0, SCH_SESSION_STATUS)))
MYSQL_YYABORT;
}
| opt_full PROCESSLIST_SYM
@@ -12975,27 +14175,28 @@ show_param:
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_VARIABLES;
lex->option_type= $1;
- if (prepare_schema_table(thd, lex, 0, SCH_SESSION_VARIABLES))
+ 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 (prepare_schema_table(thd, lex, 0, SCH_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 (prepare_schema_table(thd, lex, 0, SCH_COLLATIONS))
+ if (unlikely(prepare_schema_table(thd, lex, 0, SCH_COLLATIONS)))
MYSQL_YYABORT;
}
| GRANTS
{
Lex->sql_command= SQLCOM_SHOW_GRANTS;
- if (!(Lex->grant_user= (LEX_USER*)thd->alloc(sizeof(LEX_USER))))
+ if (unlikely(!(Lex->grant_user=
+ (LEX_USER*)thd->alloc(sizeof(LEX_USER)))))
MYSQL_YYABORT;
Lex->grant_user->user= current_user_and_current_role;
}
@@ -13014,7 +14215,7 @@ show_param:
{
LEX *lex= Lex;
lex->sql_command = SQLCOM_SHOW_CREATE;
- if (!lex->select_lex.add_table_to_list(thd, $3, NULL,0))
+ if (unlikely(!lex->select_lex.add_table_to_list(thd, $3, NULL,0)))
MYSQL_YYABORT;
lex->create_info.storage_media= HA_SM_DEFAULT;
}
@@ -13022,9 +14223,17 @@ show_param:
{
LEX *lex= Lex;
lex->sql_command = SQLCOM_SHOW_CREATE;
- if (!lex->select_lex.add_table_to_list(thd, $3, NULL, 0))
+ if (unlikely(!lex->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 (unlikely(!lex->select_lex.add_table_to_list(thd, $3, NULL, 0)))
MYSQL_YYABORT;
- lex->only_view= 1;
+ lex->table_type= TABLE_TYPE_SEQUENCE;
}
| MASTER_SYM STATUS_SYM
{
@@ -13038,7 +14247,7 @@ show_param:
| SLAVE STATUS_SYM
{
LEX *lex= thd->lex;
- lex->mi.connection_name= null_lex_str;
+ lex->mi.connection_name= null_clex_str;
lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
lex->verbose= 0;
}
@@ -13061,6 +14270,18 @@ show_param:
lex->sql_command = SQLCOM_SHOW_CREATE_FUNC;
lex->spname= $3;
}
+ | CREATE PACKAGE_MARIADB_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 TRIGGER_SYM sp_name
{
LEX *lex= Lex;
@@ -13070,7 +14291,8 @@ show_param:
| CREATE USER_SYM
{
Lex->sql_command= SQLCOM_SHOW_CREATE_USER;
- if (!(Lex->grant_user= (LEX_USER*)thd->alloc(sizeof(LEX_USER))))
+ if (unlikely(!(Lex->grant_user=
+ (LEX_USER*)thd->alloc(sizeof(LEX_USER)))))
MYSQL_YYABORT;
Lex->grant_user->user= current_user;
}
@@ -13083,14 +14305,28 @@ show_param:
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_STATUS_PROC;
- if (prepare_schema_table(thd, lex, 0, SCH_PROCEDURES))
+ 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 (prepare_schema_table(thd, lex, 0, SCH_PROCEDURES))
+ if (unlikely(prepare_schema_table(thd, lex, 0, SCH_PROCEDURES)))
+ MYSQL_YYABORT;
+ }
+ | PACKAGE_MARIADB_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;
+ 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
@@ -13103,6 +14339,11 @@ show_param:
Lex->sql_command= SQLCOM_SHOW_FUNC_CODE;
Lex->spname= $3;
}
+ | PACKAGE_MARIADB_SYM BODY_MARIADB_SYM CODE_SYM sp_name
+ {
+ Lex->sql_command= SQLCOM_SHOW_PACKAGE_BODY_CODE;
+ Lex->spname= $4;
+ }
| CREATE EVENT_SYM sp_name
{
Lex->spname= $3;
@@ -13111,7 +14352,7 @@ show_param:
| describe_command FOR_SYM expr
{
Lex->sql_command= SQLCOM_SHOW_EXPLAIN;
- if (prepare_schema_table(thd, Lex, 0, SCH_EXPLAIN))
+ if (unlikely(prepare_schema_table(thd, Lex, 0, SCH_EXPLAIN)))
MYSQL_YYABORT;
add_value_to_list(thd, $3);
}
@@ -13120,18 +14361,18 @@ show_param:
LEX *lex= Lex;
bool in_plugin;
lex->sql_command= SQLCOM_SHOW_GENERIC;
- ST_SCHEMA_TABLE *table= find_schema_table(thd, $1.str, &in_plugin);
- if (!table || !table->old_format || !in_plugin)
+ ST_SCHEMA_TABLE *table= find_schema_table(thd, &$1, &in_plugin);
+ if (unlikely(!table || !table->old_format || !in_plugin))
{
- my_parse_error(thd, ER_SYNTAX_ERROR, $2);
+ thd->parse_error(ER_SYNTAX_ERROR, $2);
MYSQL_YYABORT;
}
- if (lex->wild && table->idx_field1 < 0)
+ if (unlikely(lex->wild && table->idx_field1 < 0))
{
- my_parse_error(thd, ER_SYNTAX_ERROR, $3);
+ thd->parse_error(ER_SYNTAX_ERROR, $3);
MYSQL_YYABORT;
}
- if (make_schema_select(thd, Lex->current_select, table))
+ if (unlikely(make_schema_select(thd, Lex->current_select, table)))
MYSQL_YYABORT;
}
;
@@ -13156,8 +14397,8 @@ opt_storage:
;
opt_db:
- /* empty */ { $$= 0; }
- | from_or_in ident { $$= $2.str; }
+ /* empty */ { $$= null_clex_str; }
+ | from_or_in ident { $$= $2; }
;
opt_full:
@@ -13186,7 +14427,7 @@ wild_and_where:
{
Lex->wild= new (thd->mem_root) String($3.str, $3.length,
system_charset_info);
- if (Lex->wild == NULL)
+ if (unlikely(Lex->wild == NULL))
MYSQL_YYABORT;
$$= $2;
}
@@ -13207,9 +14448,9 @@ describe:
mysql_init_select(lex);
lex->current_select->parsing_place= SELECT_LIST;
lex->sql_command= SQLCOM_SHOW_FIELDS;
- lex->select_lex.db= 0;
+ lex->select_lex.db= null_clex_str;
lex->verbose= 0;
- if (prepare_schema_table(thd, lex, $2, SCH_COLUMNS))
+ if (unlikely(prepare_schema_table(thd, lex, $2, SCH_COLUMNS)))
MYSQL_YYABORT;
}
opt_describe_column
@@ -13255,12 +14496,13 @@ opt_format_json:
/* empty */ {}
| FORMAT_SYM '=' ident_or_text
{
- if (!my_strcasecmp(system_charset_info, $3.str, "JSON"))
+ if (lex_string_eq(&$3, STRING_WITH_LEN("JSON")))
Lex->explain_json= true;
- else if (!my_strcasecmp(system_charset_info, $3.str, "TRADITIONAL"))
+ 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), $3.str));
+ my_yyabort_error((ER_UNKNOWN_EXPLAIN_FORMAT, MYF(0), "EXPLAIN",
+ $3.str));
}
;
@@ -13272,7 +14514,7 @@ opt_describe_column:
Lex->wild= new (thd->mem_root) String((const char*) $1.str,
$1.length,
system_charset_info);
- if (Lex->wild == NULL)
+ if (unlikely(Lex->wild == NULL))
MYSQL_YYABORT;
}
;
@@ -13316,8 +14558,10 @@ opt_flush_lock:
for (; tables; tables= tables->next_global)
{
tables->mdl_request.set_type(MDL_SHARED_NO_WRITE);
- tables->required_type= FRMTYPE_TABLE; /* Don't try to flush views. */
- tables->open_type= OT_BASE_ONLY; /* Ignore temporary tables. */
+ /* Don't try to flush views. */
+ tables->required_type= TABLE_TYPE_NORMAL;
+ /* Ignore temporary tables. */
+ tables->open_type= OT_BASE_ONLY;
}
}
;
@@ -13327,9 +14571,10 @@ flush_lock:
{ Lex->type|= REFRESH_READ_LOCK | $4; }
| FOR_SYM
{
- if (Lex->query_tables == NULL) // Table list can't be empty
+ if (unlikely(Lex->query_tables == NULL))
{
- my_parse_error(thd, ER_NO_TABLES_USED);
+ // Table list can't be empty
+ thd->parse_error(ER_NO_TABLES_USED);
MYSQL_YYABORT;
}
Lex->type|= REFRESH_FOR_EXPORT;
@@ -13356,7 +14601,7 @@ flush_option:
| RELAY LOGS_SYM optional_connection_name
{
LEX *lex= Lex;
- if (lex->type & REFRESH_RELAY_LOG)
+ 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;
@@ -13370,14 +14615,14 @@ flush_option:
| LOGS_SYM
{
Lex->type|= REFRESH_LOG;
- Lex->relay_log_connection_name= empty_lex_str;
+ Lex->relay_log_connection_name= empty_clex_str;
}
| STATUS_SYM
{ Lex->type|= REFRESH_STATUS; }
| SLAVE optional_connection_name
{
LEX *lex= Lex;
- if (lex->type & REFRESH_SLAVE)
+ 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;
@@ -13391,15 +14636,16 @@ flush_option:
| IDENT_sys remember_tok_start
{
Lex->type|= REFRESH_GENERIC;
- ST_SCHEMA_TABLE *table= find_schema_table(thd, $1.str);
- if (!table || !table->reset_table)
+ ST_SCHEMA_TABLE *table= find_schema_table(thd, &$1);
+ if (unlikely(!table || !table->reset_table))
{
- my_parse_error(thd, ER_SYNTAX_ERROR, $2);
+ thd->parse_error(ER_SYNTAX_ERROR, $2);
MYSQL_YYABORT;
}
- Lex->view_list.push_back((LEX_STRING*)
- thd->memdup(&$1, sizeof(LEX_STRING)),
- thd->mem_root);
+ if (unlikely(Lex->view_list.push_back((LEX_CSTRING*)
+ thd->memdup(&$1, sizeof(LEX_CSTRING)),
+ thd->mem_root)))
+ MYSQL_YYABORT;
}
;
@@ -13568,7 +14814,7 @@ use:
{
LEX *lex=Lex;
lex->sql_command=SQLCOM_CHANGE_DB;
- lex->select_lex.db= $2.str;
+ lex->select_lex.db= $2;
}
;
@@ -13580,7 +14826,7 @@ load:
LEX *lex= thd->lex;
mysql_init_select(lex);
- if (lex->sphead)
+ if (unlikely(lex->sphead))
{
my_error(ER_SP_BADSTATEMENT, MYF(0),
$2 == FILETYPE_CSV ? "LOAD DATA" : "LOAD XML");
@@ -13594,14 +14840,17 @@ load:
lex->local_file= $5;
lex->duplicates= DUP_ERROR;
lex->ignore= 0;
- if (!(lex->exchange= new (thd->mem_root) sql_exchange($7.str, 0, $2)))
+ 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 (!Select->add_table_to_list(thd, $12, NULL, TL_OPTION_UPDATING,
- $4, MDL_SHARED_WRITE, NULL, $13))
+ 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();
@@ -13613,7 +14862,9 @@ 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
- {}
+ {
+ Lex->mark_first_table_as_inserting();
+ }
;
data_or_xml:
@@ -13740,8 +14991,8 @@ field_or_var:
simple_ident_nospvar {$$= $1;}
| '@' ident_or_text
{
- $$= new (thd->mem_root) Item_user_var_as_out_param(thd, $2);
- if ($$ == NULL)
+ $$= new (thd->mem_root) Item_user_var_as_out_param(thd, &$2);
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
;
@@ -13760,8 +15011,8 @@ load_data_set_elem:
simple_ident_nospvar equal remember_name expr_or_default remember_end
{
LEX *lex= Lex;
- if (lex->update_list.push_back($1, thd->mem_root) ||
- lex->value_list.push_back($4, thd->mem_root))
+ 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());
}
@@ -13772,62 +15023,23 @@ load_data_set_elem:
text_literal:
TEXT_STRING
{
- LEX_STRING tmp;
- CHARSET_INFO *cs_con= thd->variables.collation_connection;
- CHARSET_INFO *cs_cli= thd->variables.character_set_client;
- uint repertoire= thd->lex->text_string_is_7bit &&
- my_charset_is_ascii_based(cs_cli) ?
- MY_REPERTOIRE_ASCII : MY_REPERTOIRE_UNICODE30;
- if (thd->charset_is_collation_connection ||
- (repertoire == MY_REPERTOIRE_ASCII &&
- my_charset_is_ascii_based(cs_con)))
- tmp= $1;
- else
- {
- if (thd->convert_string(&tmp, cs_con, $1.str, $1.length, cs_cli))
- MYSQL_YYABORT;
- }
- $$= new (thd->mem_root) Item_string(thd, tmp.str, tmp.length,
- cs_con,
- DERIVATION_COERCIBLE,
- repertoire);
- if ($$ == NULL)
+ if (unlikely(!($$= thd->make_string_literal($1))))
MYSQL_YYABORT;
}
| NCHAR_STRING
{
- uint repertoire= Lex->text_string_is_7bit ?
- MY_REPERTOIRE_ASCII : MY_REPERTOIRE_UNICODE30;
- DBUG_ASSERT(my_charset_is_ascii_based(national_charset_info));
- $$= new (thd->mem_root) Item_string(thd, $1.str, $1.length,
- national_charset_info,
- DERIVATION_COERCIBLE,
- repertoire);
- if ($$ == NULL)
+ if (unlikely(!($$= thd->make_string_literal_nchar($1))))
MYSQL_YYABORT;
}
| UNDERSCORE_CHARSET TEXT_STRING
{
- $$= new (thd->mem_root) Item_string_with_introducer(thd, $2.str,
- $2.length, $1);
- if ($$ == NULL)
+ if (unlikely(!($$= thd->make_string_literal_charset($2, $1))))
MYSQL_YYABORT;
}
| text_literal TEXT_STRING_literal
{
- Item_string* item= (Item_string*) $1;
- item->append($2.str, $2.length);
- if (!(item->collation.repertoire & MY_REPERTOIRE_EXTENDED))
- {
- /*
- If the string has been pure ASCII so far,
- check the new part.
- */
- CHARSET_INFO *cs= thd->variables.collation_connection;
- item->collation.repertoire|= my_string_repertoire(cs,
- $2.str,
- $2.length);
- }
+ if (unlikely(!($$= $1->make_string_literal_concat(thd, &$2))))
+ MYSQL_YYABORT;
}
;
@@ -13837,7 +15049,7 @@ text_string:
$$= new (thd->mem_root) String($1.str,
$1.length,
thd->variables.collation_connection);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| hex_or_bin_String { $$= $1; }
@@ -13849,7 +15061,7 @@ hex_or_bin_String:
{
Item *tmp= new (thd->mem_root) Item_hex_hybrid(thd, $1.str,
$1.length);
- if (tmp == NULL)
+ if (unlikely(tmp == NULL))
MYSQL_YYABORT;
/*
it is OK only emulate fix_fields, because we need only
@@ -13862,7 +15074,7 @@ hex_or_bin_String:
{
Item *tmp= new (thd->mem_root) Item_hex_string(thd, $1.str,
$1.length);
- if (tmp == NULL)
+ if (unlikely(tmp == NULL))
MYSQL_YYABORT;
tmp->quick_fix_field();
$$= tmp->val_str((String*) 0);
@@ -13871,7 +15083,7 @@ hex_or_bin_String:
{
Item *tmp= new (thd->mem_root) Item_bin_string(thd, $1.str,
$1.length);
- if (tmp == NULL)
+ if (unlikely(tmp == NULL))
MYSQL_YYABORT;
/*
it is OK only emulate fix_fields, because we need only
@@ -13885,26 +15097,23 @@ hex_or_bin_String:
param_marker:
PARAM_MARKER
{
- LEX *lex= thd->lex;
- Lex_input_stream *lip= YYLIP;
- Item_param *item;
- bool rc;
- if (! lex->parsing_options.allows_variable)
- my_yyabort_error((ER_VIEW_SELECT_VARIABLE, MYF(0)));
- const char *query_start= lex->sphead && !lex->clone_spec_offset ?
- lex->sphead->m_tmp_query : lip->get_buf();
- item= new (thd->mem_root) Item_param(thd,
- (uint)(lip->get_tok_start() -
- query_start));
- if (!($$= item))
- MYSQL_YYABORT;
- if (!lex->clone_spec_offset)
- rc= lex->param_list.push_back(item, thd->mem_root);
- else
- rc= item->add_as_clone(thd);
- if (rc)
- my_yyabort_error((ER_OUT_OF_RESOURCES, MYF(0)));
-
+ 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;
}
;
@@ -13931,38 +15140,38 @@ literal:
*/
YYLIP->reduce_digest_token(TOK_GENERIC_VALUE, NULL_SYM);
$$= new (thd->mem_root) Item_null(thd);
- if ($$ == NULL)
+ 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 ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| TRUE_SYM
{
$$= new (thd->mem_root) Item_bool(thd, (char*) "TRUE",1);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| HEX_NUM
{
$$= new (thd->mem_root) Item_hex_hybrid(thd, $1.str, $1.length);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| HEX_STRING
{
$$= new (thd->mem_root) Item_hex_string(thd, $1.str, $1.length);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| BIN_NUM
{
$$= new (thd->mem_root) Item_bin_string(thd, $1.str, $1.length);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| UNDERSCORE_CHARSET hex_or_bin_String
@@ -13975,7 +15184,8 @@ literal:
item_str= new (thd->mem_root)
Item_string_with_introducer(thd, NULL, $2->ptr(), $2->length(),
$1);
- if (!item_str || !item_str->check_well_formed_result(true))
+ if (unlikely(!item_str ||
+ !item_str->check_well_formed_result(true)))
MYSQL_YYABORT;
$$= item_str;
@@ -13990,7 +15200,7 @@ NUM_literal:
Item_int(thd, $1.str,
(longlong) my_strtoll10($1.str, NULL, &error),
$1.length);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| LONG_NUM
@@ -14000,31 +15210,27 @@ NUM_literal:
Item_int(thd, $1.str,
(longlong) my_strtoll10($1.str, NULL, &error),
$1.length);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| ULONGLONG_NUM
{
$$= new (thd->mem_root) Item_uint(thd, $1.str, $1.length);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| DECIMAL_NUM
{
$$= new (thd->mem_root) Item_decimal(thd, $1.str, $1.length,
thd->charset());
- if (($$ == NULL) || (thd->is_error()))
- {
+ if (unlikely($$ == NULL) || unlikely(thd->is_error()))
MYSQL_YYABORT;
- }
}
| FLOAT_NUM
{
$$= new (thd->mem_root) Item_float(thd, $1.str, $1.length);
- if (($$ == NULL) || (thd->is_error()))
- {
+ if (unlikely($$ == NULL) || unlikely(thd->is_error()))
MYSQL_YYABORT;
- }
}
;
@@ -14032,20 +15238,26 @@ NUM_literal:
temporal_literal:
DATE_SYM TEXT_STRING
{
- if (!($$= create_temporal_literal(thd, $2.str, $2.length, YYCSCL,
- MYSQL_TYPE_DATE, true)))
+ if (unlikely(!($$= create_temporal_literal(thd, $2.str, $2.length,
+ YYCSCL,
+ MYSQL_TYPE_DATE,
+ true))))
MYSQL_YYABORT;
}
| TIME_SYM TEXT_STRING
{
- if (!($$= create_temporal_literal(thd, $2.str, $2.length, YYCSCL,
- MYSQL_TYPE_TIME, true)))
+ if (unlikely(!($$= create_temporal_literal(thd, $2.str, $2.length,
+ YYCSCL,
+ MYSQL_TYPE_TIME,
+ true))))
MYSQL_YYABORT;
}
| TIMESTAMP TEXT_STRING
{
- if (!($$= create_temporal_literal(thd, $2.str, $2.length, YYCSCL,
- MYSQL_TYPE_DATETIME, true)))
+ if (unlikely(!($$= create_temporal_literal(thd, $2.str, $2.length,
+ YYCSCL,
+ MYSQL_TYPE_DATETIME,
+ true))))
MYSQL_YYABORT;
}
;
@@ -14065,7 +15277,7 @@ with_clause:
{
With_clause *with_clause=
new With_clause($2, Lex->curr_with_clause);
- if (with_clause == NULL)
+ if (unlikely(with_clause == NULL))
MYSQL_YYABORT;
Lex->derived_tables|= DERIVED_WITH;
Lex->curr_with_clause= with_clause;
@@ -14095,8 +15307,8 @@ with_list_element:
query_name
opt_with_column_list
{
- $2= new List<LEX_STRING> (Lex->with_column_list);
- if ($2 == NULL)
+ $2= new List<LEX_CSTRING> (Lex->with_column_list);
+ if (unlikely($2 == NULL))
MYSQL_YYABORT;
Lex->with_column_list.empty();
}
@@ -14107,10 +15319,11 @@ with_list_element:
: thd->query();
char *spec_start= $6 + 1;
With_element *elem= new With_element($1, *$2, $7->master_unit());
- if (elem == NULL || Lex->curr_with_clause->add_with_element(elem))
+ if (unlikely(elem == NULL) ||
+ unlikely(Lex->curr_with_clause->add_with_element(elem)))
MYSQL_YYABORT;
if (elem->set_unparsed_spec(thd, spec_start, $8,
- (uint) (spec_start - query_start)))
+ spec_start - query_start))
MYSQL_YYABORT;
}
;
@@ -14127,13 +15340,13 @@ opt_with_column_list:
with_column_list:
ident
{
- Lex->with_column_list.push_back((LEX_STRING*)
- thd->memdup(&$1, sizeof(LEX_STRING)));
+ 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_STRING*)
- thd->memdup(&$3, sizeof(LEX_STRING)));
+ Lex->with_column_list.push_back((LEX_CSTRING*)
+ thd->memdup(&$3, sizeof(LEX_CSTRING)));
}
;
@@ -14141,8 +15354,8 @@ with_column_list:
query_name:
ident
{
- $$= (LEX_STRING *) thd->memdup(&$1, sizeof(LEX_STRING));
- if ($$ == NULL)
+ $$= (LEX_CSTRING *) thd->memdup(&$1, sizeof(LEX_CSTRING));
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
;
@@ -14161,24 +15374,26 @@ insert_ident:
table_wild:
ident '.' '*'
{
- SELECT_LEX *sel= Select;
- $$= new (thd->mem_root) Item_field(thd, Lex->current_context(),
- NullS, $1.str, "*");
- if ($$ == NULL)
+ if (unlikely(!($$= Lex->create_item_qualified_asterisk(thd, &$1))))
MYSQL_YYABORT;
- sel->with_wild++;
}
| ident '.' ident '.' '*'
{
- SELECT_LEX *sel= Select;
- const char* schema= thd->client_capabilities & CLIENT_NO_SCHEMA ?
- NullS : $1.str;
- $$= new (thd->mem_root) Item_field(thd, Lex->current_context(),
- schema,
- $3.str,"*");
- if ($$ == NULL)
+ 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;
- sel->with_wild++;
}
;
@@ -14186,204 +15401,61 @@ order_ident:
expr { $$=$1; }
;
+
simple_ident:
- ident
+ ident_cli
{
- LEX *lex= thd->lex;
- Lex_input_stream *lip= YYLIP;
- sp_variable *spv;
- sp_pcontext *spc = lex->spcont;
- if (spc && (spv = spc->find_variable($1, false)))
- {
- /* We're compiling a stored procedure and found a variable */
- if (! lex->parsing_options.allows_variable)
- my_yyabort_error((ER_VIEW_SELECT_VARIABLE, MYF(0)));
-
- Item_splocal *splocal;
- uint pos_in_query= 0;
- uint len_in_query= 0;
- if (!lex->clone_spec_offset)
- {
- pos_in_query= (uint)(lip->get_tok_start_prev() -
- lex->sphead->m_tmp_query);
- len_in_query= (uint)(lip->get_tok_end() -
- lip->get_tok_start_prev());
- }
- splocal= new (thd->mem_root)
- Item_splocal(thd, $1, spv->offset, spv->sql_type(),
- pos_in_query, len_in_query);
- if (splocal == NULL)
- MYSQL_YYABORT;
-#ifndef DBUG_OFF
- splocal->m_sp= lex->sphead;
-#endif
- $$= splocal;
- lex->safe_to_cache_query=0;
- }
- else
- {
- SELECT_LEX *sel=Select;
- if ((sel->parsing_place != IN_HAVING) ||
- (sel->get_in_sum_expr() > 0))
- {
- $$= new (thd->mem_root) Item_field(thd, Lex->current_context(),
- NullS, NullS, $1.str);
- }
- else
- {
- $$= new (thd->mem_root) Item_ref(thd, Lex->current_context(),
- NullS, NullS, $1.str);
- }
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
+ 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_q { $$= $1; }
;
simple_ident_nospvar:
ident
{
- SELECT_LEX *sel=Select;
- if ((sel->parsing_place != IN_HAVING) ||
- (sel->get_in_sum_expr() > 0))
- {
- $$= new (thd->mem_root) Item_field(thd, Lex->current_context(),
- NullS, NullS, $1.str);
- }
- else
- {
- $$= new (thd->mem_root) Item_ref(thd, Lex->current_context(),
- NullS, NullS, $1.str);
- }
- if ($$ == NULL)
+ if (unlikely(!($$= Lex->create_item_ident_nosp(thd, &$1))))
MYSQL_YYABORT;
}
- | simple_ident_q { $$= $1; }
- ;
-
-simple_ident_q:
- ident '.' ident
+ | ident '.' ident
{
- LEX *lex= thd->lex;
-
- /*
- FIXME This will work ok in simple_ident_nospvar case because
- we can't meet simple_ident_nospvar in trigger now. But it
- should be changed in future.
- */
- if (lex->sphead && lex->sphead->m_type == TYPE_ENUM_TRIGGER &&
- (!my_strcasecmp(system_charset_info, $1.str, "NEW") ||
- !my_strcasecmp(system_charset_info, $1.str, "OLD")))
- {
- Item_trigger_field *trg_fld;
- bool new_row= ($1.str[0]=='N' || $1.str[0]=='n');
-
- if (lex->trg_chistics.event == TRG_EVENT_INSERT &&
- !new_row)
- my_yyabort_error((ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "OLD", "on INSERT"));
-
- if (lex->trg_chistics.event == TRG_EVENT_DELETE &&
- new_row)
- my_yyabort_error((ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "NEW", "on DELETE"));
-
- DBUG_ASSERT(!new_row ||
- (lex->trg_chistics.event == TRG_EVENT_INSERT ||
- lex->trg_chistics.event == TRG_EVENT_UPDATE));
- const bool tmp_read_only=
- !(new_row && lex->trg_chistics.action_time == TRG_ACTION_BEFORE);
- trg_fld= new (thd->mem_root)
- Item_trigger_field(thd, Lex->current_context(),
- new_row ?
- Item_trigger_field::NEW_ROW:
- Item_trigger_field::OLD_ROW,
- $3.str,
- SELECT_ACL,
- tmp_read_only);
- if (trg_fld == NULL)
- MYSQL_YYABORT;
-
- /*
- Let us add this item to list of all Item_trigger_field objects
- in trigger.
- */
- lex->trg_table_fields.link_in_list(trg_fld,
- &trg_fld->next_trg_field);
-
- $$= trg_fld;
- }
- else
- {
- SELECT_LEX *sel= lex->current_select;
- if (sel->no_table_names_allowed)
- {
- my_error(ER_TABLENAME_NOT_ALLOWED_HERE,
- MYF(0), $1.str, thd->where);
- }
- if ((sel->parsing_place != IN_HAVING) ||
- (sel->get_in_sum_expr() > 0))
- {
- $$= new (thd->mem_root) Item_field(thd, Lex->current_context(),
- NullS, $1.str, $3.str);
- }
- else
- {
- $$= new (thd->mem_root) Item_ref(thd, Lex->current_context(),
- NullS, $1.str, $3.str);
- }
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
+ 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 *lex= thd->lex;
- SELECT_LEX *sel= lex->current_select;
- if (sel->no_table_names_allowed)
- {
- my_error(ER_TABLENAME_NOT_ALLOWED_HERE,
- MYF(0), $2.str, thd->where);
- }
- if ((sel->parsing_place != IN_HAVING) ||
- (sel->get_in_sum_expr() > 0))
- {
- $$= new (thd->mem_root) Item_field(thd, Lex->current_context(),
- NullS, $2.str, $4.str);
-
- }
- else
- {
- $$= new (thd->mem_root) Item_ref(thd, Lex->current_context(),
- NullS, $2.str, $4.str);
- }
- if ($$ == NULL)
+ Lex_ident_sys none;
+ if (unlikely(!($$= Lex->create_item_ident(thd, &none, &$2, &$4))))
MYSQL_YYABORT;
}
| ident '.' ident '.' ident
{
- LEX *lex= thd->lex;
- SELECT_LEX *sel= lex->current_select;
- const char* schema= (thd->client_capabilities & CLIENT_NO_SCHEMA ?
- NullS : $1.str);
- if (sel->no_table_names_allowed)
- {
- my_error(ER_TABLENAME_NOT_ALLOWED_HERE,
- MYF(0), $3.str, thd->where);
- }
- if ((sel->parsing_place != IN_HAVING) ||
- (sel->get_in_sum_expr() > 0))
- {
- $$= new (thd->mem_root) Item_field(thd, Lex->current_context(),
- schema,
- $3.str, $5.str);
- }
- else
- {
- $$= new (thd->mem_root) Item_ref(thd, Lex->current_context(),
- schema,
- $3.str, $5.str);
- }
- if ($$ == NULL)
+ if (unlikely(!($$= Lex->create_item_ident(thd, &$1, &$3, &$5))))
MYSQL_YYABORT;
}
;
@@ -14393,17 +15465,19 @@ field_ident:
| ident '.' ident '.' ident
{
TABLE_LIST *table= Select->table_list.first;
- if (my_strcasecmp(table_alias_charset, $1.str, table->db))
+ if (unlikely(my_strcasecmp(table_alias_charset, $1.str,
+ table->db.str)))
my_yyabort_error((ER_WRONG_DB_NAME, MYF(0), $1.str));
- if (my_strcasecmp(table_alias_charset, $3.str,
- table->table_name))
+ 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 (my_strcasecmp(table_alias_charset, $1.str, table->alias))
+ if (unlikely(my_strcasecmp(table_alias_charset, $1.str,
+ table->alias.str)))
my_yyabort_error((ER_WRONG_TABLE_NAME, MYF(0), $1.str));
$$=$3;
}
@@ -14413,21 +15487,21 @@ field_ident:
table_ident:
ident
{
- $$= new (thd->mem_root) Table_ident($1);
- if ($$ == NULL)
+ $$= 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 ($$ == NULL)
+ $$= 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 ($$ == NULL)
+ $$= new (thd->mem_root) Table_ident(&$2);
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
;
@@ -14435,14 +15509,14 @@ table_ident:
table_ident_opt_wild:
ident opt_wild
{
- $$= new (thd->mem_root) Table_ident($1);
- if ($$ == NULL)
+ $$= 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 ($$ == NULL)
+ $$= new (thd->mem_root) Table_ident(thd, &$1, &$3, 0);
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
;
@@ -14450,112 +15524,104 @@ table_ident_opt_wild:
table_ident_nodb:
ident
{
- LEX_STRING db={(char*) any_db,3};
- $$= new (thd->mem_root) Table_ident(thd, db, $1, 0);
- if ($$ == NULL)
+ LEX_CSTRING db={(char*) any_db,3};
+ $$= new (thd->mem_root) Table_ident(thd, &db, &$1, 0);
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
;
-IDENT_sys:
- IDENT { $$= $1; }
+IDENT_cli:
+ IDENT
| IDENT_QUOTED
+ ;
+
+ident_cli:
+ IDENT
+ | IDENT_QUOTED
+ | keyword_ident { $$= $1; }
+ ;
+
+IDENT_sys:
+ IDENT_cli
{
- if (thd->charset_is_system_charset)
- {
- CHARSET_INFO *cs= system_charset_info;
- uint wlen= Well_formed_prefix(cs, $1.str, $1.length).length();
- if (wlen < $1.length)
- {
- ErrConvString err($1.str, $1.length, &my_charset_bin);
- my_error(ER_INVALID_CHARACTER_STRING, MYF(0),
- cs->csname, err.ptr());
- MYSQL_YYABORT;
- }
- $$= $1;
- }
- else
- {
- if (thd->convert_with_error(system_charset_info, &$$,
- thd->charset(), $1.str, $1.length))
- MYSQL_YYABORT;
- }
+ if (unlikely(thd->to_ident_sys_alloc(&$$, &$1)))
+ MYSQL_YYABORT;
}
;
TEXT_STRING_sys:
TEXT_STRING
{
- if (thd->charset_is_system_charset)
- $$= $1;
- else
- {
- if (thd->convert_string(&$$, system_charset_info,
- $1.str, $1.length, thd->charset()))
- MYSQL_YYABORT;
- }
+ if (thd->make_text_string_sys(&$$, &$1))
+ MYSQL_YYABORT;
}
;
TEXT_STRING_literal:
TEXT_STRING
{
- if (thd->charset_is_collation_connection)
- $$= $1;
- else
- {
- if (thd->convert_string(&$$, thd->variables.collation_connection,
- $1.str, $1.length, thd->charset()))
- MYSQL_YYABORT;
- }
+ if (thd->make_text_string_connection(&$$, &$1))
+ MYSQL_YYABORT;
}
;
TEXT_STRING_filesystem:
TEXT_STRING
{
- if (thd->charset_is_character_set_filesystem)
- $$= $1;
- else
- {
- if (thd->convert_string(&$$,
- thd->variables.character_set_filesystem,
- $1.str, $1.length, thd->charset()))
- MYSQL_YYABORT;
- }
+ if (thd->make_text_string_filesystem(&$$, &$1))
+ MYSQL_YYABORT;
}
;
ident_table_alias:
- IDENT_sys { $$= $1; }
- | keyword_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
{
- $$.str= thd->strmake($1.str, $1.length);
- if ($$.str == NULL)
+ if (unlikely($$.copy_sys(thd, &$1)))
MYSQL_YYABORT;
- $$.length= $1.length;
}
;
+
ident:
- IDENT_sys { $$=$1; }
- | keyword
+ IDENT_sys
+ | keyword_ident
{
- $$.str= thd->strmake($1.str, $1.length);
- if ($$.str == NULL)
+ if (unlikely($$.copy_keyword(thd, &$1)))
MYSQL_YYABORT;
- $$.length= $1.length;
}
;
label_ident:
- IDENT_sys { $$=$1; }
- | keyword_sp
+ IDENT_sys
+ | keyword_label
{
- $$.str= thd->strmake($1.str, $1.length);
- if ($$.str == NULL)
+ if (unlikely($$.copy_keyword(thd, &$1)))
MYSQL_YYABORT;
- $$.length= $1.length;
}
;
@@ -14568,28 +15634,28 @@ ident_or_text:
user_maybe_role:
ident_or_text
{
- if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
+ if (unlikely(!($$=(LEX_USER*) thd->alloc(sizeof(LEX_USER)))))
MYSQL_YYABORT;
$$->user = $1;
- $$->host= null_lex_str; // User or Role, see get_current_user()
+ $$->host= null_clex_str; // User or Role, see get_current_user()
$$->reset_auth();
- if (check_string_char_length(&$$->user, ER_USERNAME,
- username_char_length,
- system_charset_info, 0))
+ 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 (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
+ if (unlikely(!($$=(LEX_USER*) thd->alloc(sizeof(LEX_USER)))))
MYSQL_YYABORT;
$$->user = $1; $$->host=$3;
$$->reset_auth();
- if (check_string_char_length(&$$->user, ER_USERNAME,
- username_char_length,
- system_charset_info, 0) ||
- check_host_name(&$$->host))
+ 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])
{
@@ -14598,7 +15664,7 @@ user_maybe_role:
It's OK to use in-place lowercase as long as
the character set is utf8.
*/
- my_casedn_str(system_charset_info, $$->host.str);
+ my_casedn_str(system_charset_info, (char*) $$->host.str);
}
else
{
@@ -14611,11 +15677,11 @@ user_maybe_role:
}
| CURRENT_USER optional_braces
{
- if (!($$=(LEX_USER*)thd->calloc(sizeof(LEX_USER))))
+ if (unlikely(!($$=(LEX_USER*)thd->calloc(sizeof(LEX_USER)))))
MYSQL_YYABORT;
$$->user= current_user;
- $$->plugin= empty_lex_str;
- $$->auth= empty_lex_str;
+ $$->plugin= empty_clex_str;
+ $$->auth= empty_clex_str;
}
;
@@ -14630,405 +15696,593 @@ user: user_maybe_role
;
/* Keywords which we allow as table aliases. */
-keyword_alias:
- keyword_sp {}
- | ASCII_SYM {}
- | BACKUP_SYM {}
- | BEGIN_SYM {}
- | BINLOG_SYM {}
- | BYTE_SYM {}
- | CACHE_SYM {}
- | CHARSET {}
- | CHECKSUM_SYM {}
- | CHECKPOINT_SYM {}
- | CLOSE_SYM {}
- | COLUMN_ADD_SYM {}
- | COLUMN_CHECK_SYM {}
- | COLUMN_CREATE_SYM {}
- | COLUMN_DELETE_SYM {}
- | COLUMN_GET_SYM {}
- | COMMENT_SYM {}
- | COMMIT_SYM {}
- | CONTAINS_SYM {}
- | DEALLOCATE_SYM {}
- | DO_SYM {}
- | END {}
- | EXAMINED_SYM {}
- | EXCLUDE_SYM {}
- | EXECUTE_SYM {}
- | FLUSH_SYM {}
- | FOLLOWS_SYM {}
- | FOLLOWING_SYM {}
- | FORMAT_SYM {}
- | GET_SYM {}
- | HANDLER_SYM {}
- | HELP_SYM {}
- | HOST_SYM {}
- | INSTALL_SYM {}
- | LANGUAGE_SYM {}
- | NO_SYM {}
- | OPEN_SYM {}
- | OPTION {}
- | OPTIONS_SYM {}
- | OTHERS_SYM {}
- | OWNER_SYM {}
- | PARSER_SYM {}
- | PORT_SYM {}
- | PRECEDES_SYM {}
- | PRECEDING_SYM {}
- | PREPARE_SYM {}
- | REMOVE_SYM {}
- | REPAIR {}
- | RESET_SYM {}
- | RESTORE_SYM {}
- | ROLLBACK_SYM {}
- | SAVEPOINT_SYM {}
- | SECURITY_SYM {}
- | SERVER_SYM {}
- | SHUTDOWN {}
- | SIGNED_SYM {}
- | SOCKET_SYM {}
- | SLAVE {}
- | SLAVES {}
- | SONAME_SYM {}
- | START_SYM {}
- | STOP_SYM {}
- | STORED_SYM {}
- | TIES_SYM {}
- | TRUNCATE_SYM {}
- | UNICODE_SYM {}
- | UNINSTALL_SYM {}
- | UNBOUNDED_SYM {}
- | WRAPPER_SYM {}
- | XA_SYM {}
- | UPGRADE_SYM {}
+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
;
-
/* Keyword that we allow for identifiers (except SP labels) */
-keyword: keyword_alias | WINDOW_SYM {};
+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
+ | WINDOW_SYM
+ ;
/*
- * Keywords that we allow for labels in SPs.
- * Anything that's the beginning of a statement or characteristics
- * must be in keyword above, otherwise we get (harmful) shift/reduce
- * conflicts.
- */
-keyword_sp:
- ACTION {}
- | 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 {}
- | BIT_SYM {}
- | BLOCK_SYM {}
- | BOOL_SYM {}
- | BOOLEAN_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 {}
- | COMPRESSED_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 {}
+ 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
+ ;
+
+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
+ | WINDOW_SYM
+ ;
+
+keyword_sp_decl:
+ 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
+ | 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
+ | 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_MARIADB_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
+ | 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 {}
- | DATA_SYM {}
- | DATAFILE_SYM {}
- | DATETIME {}
- | DATE_SYM {}
- | DAY_SYM {}
- | DEFINER_SYM {}
- | DELAY_KEY_WRITE_SYM {}
- | DES_KEY_FILE {}
- | DIAGNOSTICS_SYM {}
- | DIRECTORY_SYM {}
- | DISABLE_SYM {}
- | DISCARD {}
- | DISK_SYM {}
- | DUMPFILE {}
- | DUPLICATE_SYM {}
- | DYNAMIC_SYM {}
- | ENDS_SYM {}
- | ENUM {}
- | ENGINE_SYM {}
- | ENGINES_SYM {}
- | ERROR_SYM {}
- | ERRORS {}
- | ESCAPE_SYM {}
- | EVENT_SYM {}
- | EVENTS_SYM {}
- | EVERY_SYM {}
- | EXCHANGE_SYM {}
- | EXPANSION_SYM {}
- | EXPORT_SYM {}
- | EXTENDED_SYM {}
- | EXTENT_SIZE_SYM {}
- | FAULTS_SYM {}
- | FAST_SYM {}
- | FOUND_SYM {}
- | ENABLE_SYM {}
- | FULL {}
- | FILE_SYM {}
- | FIRST_SYM {}
- | FIXED_SYM {}
- | GENERAL {}
- | GENERATED_SYM {}
- | GEOMETRY_SYM {}
- | GEOMETRYCOLLECTION {}
- | GET_FORMAT {}
- | GRANTS {}
- | GLOBAL_SYM {}
- | HASH_SYM {}
- | HARD_SYM {}
- | HOSTS_SYM {}
- | HOUR_SYM {}
- | ID_SYM {}
- | IDENTIFIED_SYM {}
- | IGNORE_SERVER_IDS_SYM {}
- | IMMEDIATE_SYM {} /* SQL-2003-R */
- | INVOKER_SYM {}
- | IMPORT {}
- | INDEXES {}
- | INITIAL_SIZE_SYM {}
- | IO_SYM {}
- | IPC_SYM {}
- | ISOLATION {}
- | ISSUER_SYM {}
- | JSON_SYM {}
- | INSERT_METHOD {}
- | KEY_BLOCK_SIZE {}
- | LAST_VALUE {}
- | LAST_SYM {}
- | LEAVES {}
- | LESS_SYM {}
- | LEVEL_SYM {}
- | LINESTRING {}
- | LIST_SYM {}
- | LOCAL_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 {}
- | MEDIUM_SYM {}
- | MEMORY_SYM {}
- | MERGE_SYM {}
- | MESSAGE_TEXT_SYM {}
- | MICROSECOND_SYM {}
- | MIGRATE_SYM {}
- | MINUTE_SYM {}
- | MIN_ROWS {}
- | MODIFY_SYM {}
- | MODE_SYM {}
- | MONTH_SYM {}
- | MULTILINESTRING {}
- | MULTIPOINT {}
- | MULTIPOLYGON {}
- | MUTEX_SYM {}
- | MYSQL_SYM {}
- | MYSQL_ERRNO_SYM {}
- | NAME_SYM {}
- | NAMES_SYM {}
- | NATIONAL_SYM {}
- | NCHAR_SYM {}
- | NEXT_SYM {}
- | NEW_SYM {}
- | NO_WAIT_SYM {}
- | NODEGROUP_SYM {}
- | NONE_SYM {}
- | NUMBER_SYM {}
- | NVARCHAR_SYM {}
- | OFFSET_SYM {}
- | OLD_PASSWORD_SYM {}
- | ONE_SYM {}
- | ONLINE_SYM {}
- | ONLY_SYM {}
- | PACK_KEYS_SYM {}
- | PAGE_SYM {}
- | PARTIAL {}
- | PARTITIONING_SYM {}
- | PARTITIONS_SYM {}
- | PASSWORD_SYM {}
- | PERSISTENT_SYM {}
- | PHASE_SYM {}
- | PLUGIN_SYM {}
- | PLUGINS_SYM {}
- | POINT_SYM {}
- | POLYGON {}
- | PRESERVE_SYM {}
- | PREV_SYM {}
- | PRIVILEGES {}
- | PROCESS {}
- | PROCESSLIST_SYM {}
- | PROFILE_SYM {}
- | PROFILES_SYM {}
- | PROXY_SYM {}
- | QUARTER_SYM {}
- | QUERY_SYM {}
- | QUICK {}
- | 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 {}
- | RESUME_SYM {}
- | RETURNED_SQLSTATE_SYM {}
- | RETURNS_SYM {}
- | REVERSE_SYM {}
- | ROLE_SYM {}
- | ROLLUP_SYM {}
- | ROUTINE_SYM {}
- | ROW_COUNT_SYM {}
- | ROW_FORMAT_SYM {}
- | ROW_SYM {}
- | RTREE_SYM {}
- | SCHEDULE_SYM {}
- | SCHEMA_NAME_SYM {}
- | SECOND_SYM {}
- | SERIAL_SYM {}
- | SERIALIZABLE_SYM {}
- | SESSION_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 {}
- | 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 {}
- | TABLE_NAME_SYM {}
- | TABLES {}
- | TABLE_CHECKSUM_SYM {}
- | TABLESPACE {}
- | TEMPORARY {}
- | TEMPTABLE_SYM {}
- | TEXT_SYM {}
- | THAN_SYM {}
- | TRANSACTION_SYM {}
- | TRANSACTIONAL_SYM {}
- | TRIGGERS_SYM {}
- | TIMESTAMP {}
- | TIMESTAMP_ADD {}
- | TIMESTAMP_DIFF {}
- | TIME_SYM {}
- | TYPES_SYM {}
- | TYPE_SYM {}
- | UDF_RETURNS_SYM {}
- | FUNCTION_SYM {}
- | UNCOMMITTED_SYM {}
- | UNDEFINED_SYM {}
- | UNDO_BUFFER_SIZE_SYM {}
- | UNDOFILE_SYM {}
- | UNKNOWN_SYM {}
- | UNTIL_SYM {}
- | USER_SYM {}
- | USE_FRM {}
- | VARIABLES {}
- | VIEW_SYM {}
- | VIRTUAL_SYM {}
- | VALUE_SYM {}
- | WARNINGS {}
- | WAIT_SYM {}
- | WEEK_SYM {}
- | WEIGHT_STRING_SYM {}
- | WORK_SYM {}
- | X509_SYM {}
- | XML_SYM {}
- | YEAR_SYM {}
- | VIA_SYM {}
+ | 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
+ | 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
+ | 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
+ | 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
+ | FUNCTION_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
;
/*
@@ -15042,27 +16296,20 @@ set:
SET
{
LEX *lex=Lex;
- lex->sql_command= SQLCOM_SET_OPTION;
- mysql_init_select(lex);
- lex->option_type=OPT_SESSION;
+ lex->set_stmt_init();
lex->var_list.empty();
- lex->autocommit= 0;
sp_create_assignment_lex(thd, yychar == YYEMPTY);
}
start_option_value_list
{}
| SET STATEMENT_SYM
{
- LEX *lex= Lex;
- mysql_init_select(lex);
- lex->option_type= OPT_SESSION;
- lex->sql_command= SQLCOM_SET_OPTION;
- lex->autocommit= 0;
+ Lex->set_stmt_init();
}
set_stmt_option_value_following_option_type_list
{
LEX *lex= Lex;
- if (lex->table_or_sp_used())
+ 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();
@@ -15085,7 +16332,7 @@ set_stmt_option_value_following_option_type_list:
start_option_value_list:
option_value_no_option_type
{
- if (sp_create_assignment_instr(thd, yychar == YYEMPTY))
+ if (unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
MYSQL_YYABORT;
}
option_value_list_continued
@@ -15095,7 +16342,7 @@ start_option_value_list:
}
transaction_characteristics
{
- if (sp_create_assignment_instr(thd, yychar == YYEMPTY))
+ if (unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
MYSQL_YYABORT;
}
| option_type
@@ -15110,14 +16357,14 @@ start_option_value_list:
start_option_value_list_following_option_type:
option_value_following_option_type
{
- if (sp_create_assignment_instr(thd, yychar == YYEMPTY))
- MYSQL_YYABORT;
+ if (unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
+ MYSQL_YYABORT;
}
option_value_list_continued
| TRANSACTION_SYM transaction_characteristics
{
- if (sp_create_assignment_instr(thd, yychar == YYEMPTY))
- MYSQL_YYABORT;
+ if (unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
+ MYSQL_YYABORT;
}
;
@@ -15134,8 +16381,8 @@ option_value_list:
}
option_value
{
- if (sp_create_assignment_instr(thd, yychar == YYEMPTY))
- MYSQL_YYABORT;
+ if (unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
+ MYSQL_YYABORT;
}
| option_value_list ','
{
@@ -15143,8 +16390,8 @@ option_value_list:
}
option_value
{
- if (sp_create_assignment_instr(thd, yychar == YYEMPTY))
- MYSQL_YYABORT;
+ if (unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
+ MYSQL_YYABORT;
}
;
@@ -15180,82 +16427,58 @@ opt_var_ident_type:
/* Option values with preceding option_type. */
option_value_following_option_type:
- internal_variable_name equal set_expr_or_default
+ ident equal set_expr_or_default
{
- LEX *lex= Lex;
-
- if ($1.var && $1.var != trg_new_row_fake_var)
- {
- /* It is a system variable. */
- if (set_system_variable(thd, &$1, lex->option_type, $3))
- MYSQL_YYABORT;
- }
- else
- {
- /*
- Not in trigger assigning value to new row,
- and option_type preceding local variable is illegal.
- */
- my_parse_error(thd, ER_SYNTAX_ERROR);
+ 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:
- internal_variable_name equal set_expr_or_default
+ ident_set_usual_case equal set_expr_or_default
{
- LEX *lex= Lex;
-
- if ($1.var == trg_new_row_fake_var)
- {
- /* We are in trigger and assigning value to field of new row */
- if (set_trigger_new_row(thd, &$1.base_name, $3))
- MYSQL_YYABORT;
- }
- else if ($1.var)
- {
- /* It is a system variable. */
- if (set_system_variable(thd, &$1, lex->option_type, $3))
- MYSQL_YYABORT;
- }
- else
- {
- sp_pcontext *spc= lex->spcont;
- sp_variable *spv= spc->find_variable($1.base_name, false);
-
- /* It is a local variable. */
- if (set_local_variable(thd, spv, $3))
- MYSQL_YYABORT;
- }
+ 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
{
- Item_func_set_user_var *item;
- item= new (thd->mem_root) Item_func_set_user_var(thd, $2, $4);
- if (item == NULL)
+ if (unlikely(Lex->set_user_variable(thd, &$2, $4)))
MYSQL_YYABORT;
- set_var_user *var= new (thd->mem_root) set_var_user(item);
- if (var == NULL)
+ }
+ | '@' '@' opt_var_ident_type ident_sysvar_name equal set_expr_or_default
+ {
+ if (unlikely(Lex->set_system_variable($3, &$4, $6)))
MYSQL_YYABORT;
- Lex->var_list.push_back(var, thd->mem_root);
}
- | '@' '@' opt_var_ident_type internal_variable_name equal set_expr_or_default
+ | '@' '@' opt_var_ident_type ident_sysvar_name '.' ident equal set_expr_or_default
{
- struct sys_var_with_base tmp= $4;
- if (tmp.var == trg_new_row_fake_var)
- {
- my_error(ER_UNKNOWN_SYSTEM_VARIABLE, MYF(0), 3, "NEW");
+ if (unlikely(Lex->set_system_variable(thd, $3, &$4, &$6, $8)))
MYSQL_YYABORT;
- }
- /* Lookup if necessary: must be a system variable. */
- if (tmp.var == NULL)
- {
- if (find_sys_var_null_base(thd, &tmp))
- MYSQL_YYABORT;
- }
- if (set_system_variable(thd, &tmp, $3, $6))
+ }
+ | '@' '@' 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
@@ -15268,7 +16491,7 @@ option_value_no_option_type:
set_var_collation_client(cs2,
thd->variables.collation_database,
cs2));
- if (var == NULL)
+ if (unlikely(var == NULL))
MYSQL_YYABORT;
lex->var_list.push_back(var, thd->mem_root);
}
@@ -15276,15 +16499,11 @@ option_value_no_option_type:
{
LEX *lex= Lex;
sp_pcontext *spc= lex->spcont;
- LEX_STRING names;
-
- names.str= (char *)"names";
- names.length= 5;
- if (spc && spc->find_variable(names, false))
+ 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
- my_parse_error(thd, ER_SYNTAX_ERROR);
-
+ thd->parse_error();
MYSQL_YYABORT;
}
| NAMES_SYM charset_name_or_default opt_collate
@@ -15294,7 +16513,7 @@ option_value_no_option_type:
CHARSET_INFO *cs3;
cs2= $2 ? $2 : global_system_variables.character_set_client;
cs3= $3 ? $3 : cs2;
- if (!my_charset_same(cs2, cs3))
+ if (unlikely(!my_charset_same(cs2, cs3)))
{
my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0),
cs3->name, cs2->csname);
@@ -15302,23 +16521,24 @@ option_value_no_option_type:
}
set_var_collation_client *var;
var= new (thd->mem_root) set_var_collation_client(cs3, cs3, cs3);
- if (var == NULL)
+ if (unlikely(var == NULL) ||
+ unlikely(lex->var_list.push_back(var, thd->mem_root)))
MYSQL_YYABORT;
- lex->var_list.push_back(var, thd->mem_root);
}
| DEFAULT ROLE_SYM grant_role
{
LEX *lex = Lex;
LEX_USER *user;
- if (!(user=(LEX_USER *) thd->calloc(sizeof(LEX_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 (var == NULL)
+ if (unlikely(var == NULL) ||
+ unlikely(lex->var_list.push_back(var, thd->mem_root)))
MYSQL_YYABORT;
- lex->var_list.push_back(var, thd->mem_root);
+
thd->lex->autocommit= TRUE;
if (lex->sphead)
lex->sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT;
@@ -15328,9 +16548,9 @@ option_value_no_option_type:
LEX *lex = Lex;
set_var_default_role *var= (new (thd->mem_root)
set_var_default_role($5, $3->user));
- if (var == NULL)
+ if (unlikely(var == NULL) ||
+ unlikely(lex->var_list.push_back(var, thd->mem_root)))
MYSQL_YYABORT;
- lex->var_list.push_back(var, thd->mem_root);
thd->lex->autocommit= TRUE;
if (lex->sphead)
lex->sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT;
@@ -15339,18 +16559,23 @@ option_value_no_option_type:
{
LEX *lex = Lex;
set_var_role *var= new (thd->mem_root) set_var_role($2);
- if (var == NULL)
+ 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;
- lex->var_list.push_back(var, thd->mem_root);
}
| 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 (var == NULL)
+ if (unlikely(var == NULL) ||
+ unlikely(lex->var_list.push_back(var, thd->mem_root)))
MYSQL_YYABORT;
- lex->var_list.push_back(var, thd->mem_root);
lex->autocommit= TRUE;
if (lex->sphead)
lex->sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT;
@@ -15358,83 +16583,6 @@ option_value_no_option_type:
;
-internal_variable_name:
- ident
- {
- sp_pcontext *spc= thd->lex->spcont;
- sp_variable *spv;
-
- /* Best effort lookup for system variable. */
- if (!spc || !(spv = spc->find_variable($1, false)))
- {
- struct sys_var_with_base tmp= {NULL, $1};
-
- /* Not an SP local variable */
- if (find_sys_var_null_base(thd, &tmp))
- MYSQL_YYABORT;
-
- $$= tmp;
- }
- else
- {
- /*
- Possibly an SP local variable (or a shadowed sysvar).
- Will depend on the context of the SET statement.
- */
- $$.var= NULL;
- $$.base_name= $1;
- }
- }
- | ident '.' ident
- {
- LEX *lex= Lex;
- if (check_reserved_words(&$1))
- {
- my_parse_error(thd, ER_SYNTAX_ERROR);
- MYSQL_YYABORT;
- }
- if (lex->sphead && lex->sphead->m_type == TYPE_ENUM_TRIGGER &&
- (!my_strcasecmp(system_charset_info, $1.str, "NEW") ||
- !my_strcasecmp(system_charset_info, $1.str, "OLD")))
- {
- if ($1.str[0]=='O' || $1.str[0]=='o')
- my_yyabort_error((ER_TRG_CANT_CHANGE_ROW, MYF(0), "OLD", ""));
- if (lex->trg_chistics.event == TRG_EVENT_DELETE)
- {
- my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0),
- "NEW", "on DELETE");
- MYSQL_YYABORT;
- }
- if (lex->trg_chistics.action_time == TRG_ACTION_AFTER)
- my_yyabort_error((ER_TRG_CANT_CHANGE_ROW, MYF(0), "NEW", "after "));
- /* This special combination will denote field of NEW row */
- $$.var= trg_new_row_fake_var;
- $$.base_name= $3;
- }
- else
- {
- sys_var *tmp=find_sys_var(thd, $3.str, $3.length);
- if (!tmp)
- MYSQL_YYABORT;
- if (!tmp->is_struct())
- my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), $3.str);
- $$.var= tmp;
- $$.base_name= $1;
- }
- }
- | DEFAULT '.' ident
- {
- sys_var *tmp=find_sys_var(thd, $3.str, $3.length);
- if (!tmp)
- MYSQL_YYABORT;
- if (!tmp->is_struct())
- my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), $3.str);
- $$.var= tmp;
- $$.base_name.str= (char*) "default";
- $$.base_name.length= 7;
- }
- ;
-
transaction_characteristics:
transaction_access_mode
| isolation_level
@@ -15447,16 +16595,17 @@ transaction_access_mode:
{
LEX *lex=Lex;
Item *item= new (thd->mem_root) Item_int(thd, (int32) $1);
- if (item == NULL)
+ 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_lex_str,
+ &null_clex_str,
item));
- if (var == NULL)
+ if (unlikely(var == NULL))
+ MYSQL_YYABORT;
+ if (unlikely(lex->var_list.push_back(var, thd->mem_root)))
MYSQL_YYABORT;
- lex->var_list.push_back(var, thd->mem_root);
}
;
@@ -15465,16 +16614,16 @@ isolation_level:
{
LEX *lex=Lex;
Item *item= new (thd->mem_root) Item_int(thd, (int32) $3);
- if (item == NULL)
+ 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_lex_str,
+ &null_clex_str,
item));
- if (var == NULL)
+ if (unlikely(var == NULL) ||
+ unlikely(lex->var_list.push_back(var, thd->mem_root)))
MYSQL_YYABORT;
- lex->var_list.push_back(var, thd->mem_root);
}
;
@@ -15495,15 +16644,16 @@ opt_for_user:
{
LEX *lex= thd->lex;
sp_pcontext *spc= lex->spcont;
- LEX_STRING pw= { C_STRING_WITH_LEN("password") };
+ LEX_CSTRING pw= { STRING_WITH_LEN("password") };
- if (spc && spc->find_variable(pw, false))
+ if (unlikely(spc && spc->find_variable(&pw, false)))
my_yyabort_error((ER_SP_BAD_VAR_SHADOW, MYF(0), pw.str));
- if (!(lex->definer= (LEX_USER*) thd->calloc(sizeof(LEX_USER))))
+ if (unlikely(!(lex->definer= (LEX_USER*)
+ thd->calloc(sizeof(LEX_USER)))))
MYSQL_YYABORT;
lex->definer->user= current_user;
- lex->definer->plugin= empty_lex_str;
- lex->definer->auth= empty_lex_str;
+ lex->definer->plugin= empty_clex_str;
+ lex->definer->auth= empty_clex_str;
}
| FOR_SYM user equal { Lex->definer= $2; }
;
@@ -15526,19 +16676,19 @@ set_expr_or_default:
| ON
{
$$=new (thd->mem_root) Item_string_sys(thd, "ON", 2);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| ALL
{
$$=new (thd->mem_root) Item_string_sys(thd, "ALL", 3);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
| BINARY
{
$$=new (thd->mem_root) Item_string_sys(thd, "binary", 6);
- if ($$ == NULL)
+ if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
;
@@ -15550,14 +16700,31 @@ lock:
{
LEX *lex= Lex;
- if (lex->sphead)
+ if (unlikely(lex->sphead))
my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "LOCK"));
lex->sql_command= SQLCOM_LOCK_TABLES;
}
- table_lock_list
+ 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 { }
@@ -15572,7 +16739,7 @@ table_lock:
table_ident opt_table_alias lock_option
{
thr_lock_type lock_type= (thr_lock_type) $3;
- bool lock_for_write= lock_type >= TL_WRITE_ALLOW_WRITE;
+ 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
@@ -15580,8 +16747,9 @@ table_lock:
? MDL_SHARED_WRITE
: MDL_SHARED_NO_READ_WRITE;
- if (!Select->add_table_to_list(thd, $1, $2, table_options,
- lock_type, mdl_type))
+ if (unlikely(!Select->
+ add_table_to_list(thd, $1, $2, table_options,
+ lock_type, mdl_type)))
MYSQL_YYABORT;
}
;
@@ -15603,7 +16771,7 @@ unlock:
{
LEX *lex= Lex;
- if (lex->sphead)
+ if (unlikely(lex->sphead))
my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "UNLOCK"));
lex->sql_command= SQLCOM_UNLOCK_TABLES;
}
@@ -15619,43 +16787,46 @@ handler:
HANDLER_SYM table_ident OPEN_SYM opt_table_alias
{
LEX *lex= Lex;
- if (lex->sphead)
+ 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, $2, $4, 0))
+ if (unlikely(!lex->current_select->add_table_to_list(thd, $2, $4,
+ 0)))
MYSQL_YYABORT;
}
| HANDLER_SYM table_ident_nodb CLOSE_SYM
{
LEX *lex= Lex;
- if (lex->sphead)
+ 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, $2, 0, 0))
+ if (unlikely(!lex->current_select->add_table_to_list(thd, $2, 0,
+ 0)))
MYSQL_YYABORT;
}
| HANDLER_SYM table_ident_nodb READ_SYM
{
LEX *lex=Lex;
- if (lex->sphead)
+ if (unlikely(lex->sphead))
my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "HANDLER"));
lex->expr_allows_subselect= FALSE;
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 (one == NULL)
+ 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, $2, 0, 0))
+ if (unlikely(!lex->current_select->add_table_to_list(thd, $2, 0,
+ 0)))
MYSQL_YYABORT;
}
handler_read_or_scan opt_where_clause opt_limit_clause
{
Lex->expr_allows_subselect= TRUE;
/* Stored functions are not supported for HANDLER READ. */
- if (Lex->uses_stored_routines())
+ if (unlikely(Lex->uses_stored_routines()))
{
my_error(ER_NOT_SUPPORTED_YET, MYF(0),
"stored functions in HANDLER ... READ");
@@ -15665,7 +16836,7 @@ handler:
;
handler_read_or_scan:
- handler_scan_function { Lex->ident= null_lex_str; }
+ handler_scan_function { Lex->ident= null_clex_str; }
| ident handler_rkey_function { Lex->ident= $1; }
;
@@ -15684,7 +16855,7 @@ handler_rkey_function:
LEX *lex=Lex;
lex->ha_read_mode = RKEY;
lex->ha_rkey_mode=$1;
- if (!(lex->insert_list= new (thd->mem_root) List_item))
+ if (unlikely(!(lex->insert_list= new (thd->mem_root) List_item)))
MYSQL_YYABORT;
}
'(' values ')'
@@ -15715,25 +16886,29 @@ revoke_command:
}
| grant_privileges ON FUNCTION_SYM grant_ident FROM user_and_role_list
{
- LEX *lex= Lex;
- if (lex->columns.elements)
- {
- my_parse_error(thd, ER_SYNTAX_ERROR);
+ if (unlikely(Lex->add_grant_command(thd, SQLCOM_REVOKE,
+ TYPE_ENUM_FUNCTION)))
MYSQL_YYABORT;
- }
- lex->sql_command= SQLCOM_REVOKE;
- lex->type= TYPE_ENUM_FUNCTION;
}
| grant_privileges ON PROCEDURE_SYM grant_ident FROM user_and_role_list
{
- LEX *lex= Lex;
- if (lex->columns.elements)
- {
- my_parse_error(thd, ER_SYNTAX_ERROR);
+ 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;
- }
- lex->sql_command= SQLCOM_REVOKE;
- lex->type= TYPE_ENUM_PROCEDURE;
}
| ALL opt_privileges ',' GRANT OPTION FROM user_and_role_list
{
@@ -15749,7 +16924,7 @@ revoke_command:
| admin_option_for_role FROM user_and_role_list
{
Lex->sql_command= SQLCOM_REVOKE_ROLE;
- if (Lex->users_list.push_front($1, thd->mem_root))
+ if (unlikely(Lex->users_list.push_front($1, thd->mem_root)))
MYSQL_YYABORT;
}
;
@@ -15777,26 +16952,30 @@ grant_command:
| grant_privileges ON FUNCTION_SYM grant_ident TO_SYM grant_list
opt_require_clause opt_grant_options
{
- LEX *lex= Lex;
- if (lex->columns.elements)
- {
- my_parse_error(thd, ER_SYNTAX_ERROR);
+ if (unlikely(Lex->add_grant_command(thd, SQLCOM_GRANT,
+ TYPE_ENUM_FUNCTION)))
MYSQL_YYABORT;
- }
- lex->sql_command= SQLCOM_GRANT;
- lex->type= TYPE_ENUM_FUNCTION;
}
| grant_privileges ON PROCEDURE_SYM grant_ident TO_SYM grant_list
opt_require_clause opt_grant_options
{
- LEX *lex= Lex;
- if (lex->columns.elements)
- {
- my_parse_error(thd, ER_SYNTAX_ERROR);
+ 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;
- }
- lex->sql_command= SQLCOM_GRANT;
- lex->type= TYPE_ENUM_PROCEDURE;
}
| PROXY_SYM ON user TO_SYM grant_list opt_grant_option
{
@@ -15810,7 +16989,7 @@ grant_command:
LEX *lex= Lex;
lex->sql_command= SQLCOM_GRANT_ROLE;
/* The first role is the one that is granted */
- if (Lex->users_list.push_front($1, thd->mem_root))
+ if (unlikely(Lex->users_list.push_front($1, thd->mem_root)))
MYSQL_YYABORT;
}
@@ -15829,12 +17008,12 @@ opt_with_admin_option:
role_list:
grant_role
{
- if (Lex->users_list.push_back($1, thd->mem_root))
+ if (unlikely(Lex->users_list.push_back($1, thd->mem_root)))
MYSQL_YYABORT;
}
| role_list ',' grant_role
{
- if (Lex->users_list.push_back($3, thd->mem_root))
+ if (unlikely(Lex->users_list.push_back($3, thd->mem_root)))
MYSQL_YYABORT;
}
;
@@ -15842,7 +17021,7 @@ role_list:
current_role:
CURRENT_ROLE optional_braces
{
- if (!($$=(LEX_USER*) thd->calloc(sizeof(LEX_USER))))
+ if (unlikely(!($$=(LEX_USER*) thd->calloc(sizeof(LEX_USER)))))
MYSQL_YYABORT;
$$->user= current_role;
$$->reset_auth();
@@ -15855,18 +17034,18 @@ 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.str[$1.length] = '\0';
- if ($1.length == 0)
+ ((char*) $1.str)[$1.length] = '\0';
+ if (unlikely($1.length == 0))
my_yyabort_error((ER_INVALID_ROLE, MYF(0), ""));
- if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
+ if (unlikely(!($$=(LEX_USER*) thd->alloc(sizeof(LEX_USER)))))
MYSQL_YYABORT;
- $$->user = $1;
- $$->host= empty_lex_str;
+ $$->user= $1;
+ $$->host= empty_clex_str;
$$->reset_auth();
- if (check_string_char_length(&$$->user, ER_USERNAME,
- username_char_length,
- cs, 0))
+ if (unlikely(check_string_char_length(&$$->user, ER_USERNAME,
+ username_char_length,
+ cs, 0)))
MYSQL_YYABORT;
}
| current_role
@@ -15935,6 +17114,7 @@ object_privilege:
| 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:
@@ -15951,21 +17131,21 @@ require_list_element:
SUBJECT_SYM TEXT_STRING
{
LEX *lex=Lex;
- if (lex->x509_subject)
+ if (unlikely(lex->x509_subject))
my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "SUBJECT"));
lex->x509_subject=$2.str;
}
| ISSUER_SYM TEXT_STRING
{
LEX *lex=Lex;
- if (lex->x509_issuer)
+ if (unlikely(lex->x509_issuer))
my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "ISSUER"));
lex->x509_issuer=$2.str;
}
| CIPHER_SYM TEXT_STRING
{
LEX *lex=Lex;
- if (lex->ssl_cipher)
+ if (unlikely(lex->ssl_cipher))
my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "CIPHER"));
lex->ssl_cipher=$2.str;
}
@@ -15975,37 +17155,37 @@ grant_ident:
'*'
{
LEX *lex= Lex;
- size_t dummy;
- if (lex->copy_db_to(&lex->current_select->db, &dummy))
+ if (unlikely(lex->copy_db_to(&lex->current_select->db)))
MYSQL_YYABORT;
if (lex->grant == GLOBAL_ACLS)
lex->grant = DB_ACLS & ~GRANT_ACL;
- else if (lex->columns.elements)
+ else if (unlikely(lex->columns.elements))
my_yyabort_error((ER_ILLEGAL_GRANT_FOR_TABLE, MYF(0)));
}
| ident '.' '*'
{
LEX *lex= Lex;
- lex->current_select->db = $1.str;
+ lex->current_select->db= $1;
if (lex->grant == GLOBAL_ACLS)
lex->grant = DB_ACLS & ~GRANT_ACL;
- else if (lex->columns.elements)
+ else if (unlikely(lex->columns.elements))
my_yyabort_error((ER_ILLEGAL_GRANT_FOR_TABLE, MYF(0)));
}
| '*' '.' '*'
{
LEX *lex= Lex;
- lex->current_select->db = NULL;
+ lex->current_select->db= null_clex_str;
if (lex->grant == GLOBAL_ACLS)
lex->grant= GLOBAL_ACLS & ~GRANT_ACL;
- else if (lex->columns.elements)
+ else if (unlikely(lex->columns.elements))
my_yyabort_error((ER_ILLEGAL_GRANT_FOR_TABLE, MYF(0)));
}
| table_ident
{
LEX *lex=Lex;
- if (!lex->current_select->add_table_to_list(thd, $1,NULL,
- TL_OPTION_UPDATING))
+ if (unlikely(!lex->current_select->
+ add_table_to_list(thd, $1,NULL,
+ TL_OPTION_UPDATING)))
MYSQL_YYABORT;
if (lex->grant == GLOBAL_ACLS)
lex->grant = TABLE_ACLS & ~GRANT_ACL;
@@ -16015,12 +17195,12 @@ grant_ident:
user_list:
user
{
- if (Lex->users_list.push_back($1, thd->mem_root))
+ if (unlikely(Lex->users_list.push_back($1, thd->mem_root)))
MYSQL_YYABORT;
}
| user_list ',' user
{
- if (Lex->users_list.push_back($3, thd->mem_root))
+ if (unlikely(Lex->users_list.push_back($3, thd->mem_root)))
MYSQL_YYABORT;
}
;
@@ -16028,12 +17208,12 @@ user_list:
grant_list:
grant_user
{
- if (Lex->users_list.push_back($1, thd->mem_root))
+ if (unlikely(Lex->users_list.push_back($1, thd->mem_root)))
MYSQL_YYABORT;
}
| grant_list ',' grant_user
{
- if (Lex->users_list.push_back($3, thd->mem_root))
+ if (unlikely(Lex->users_list.push_back($3, thd->mem_root)))
MYSQL_YYABORT;
}
;
@@ -16041,12 +17221,12 @@ grant_list:
user_and_role_list:
user_or_role
{
- if (Lex->users_list.push_back($1, thd->mem_root))
+ if (unlikely(Lex->users_list.push_back($1, thd->mem_root)))
MYSQL_YYABORT;
}
| user_and_role_list ',' user_or_role
{
- if (Lex->users_list.push_back($3, thd->mem_root))
+ if (unlikely(Lex->users_list.push_back($3, thd->mem_root)))
MYSQL_YYABORT;
}
;
@@ -16059,7 +17239,7 @@ grant_user:
{
$$= $1;
$1->pwtext= $4;
- if (Lex->sql_command == SQLCOM_REVOKE)
+ if (unlikely(Lex->sql_command == SQLCOM_REVOKE))
MYSQL_YYABORT;
}
| user IDENTIFIED_SYM BY PASSWORD_SYM TEXT_STRING
@@ -16071,7 +17251,7 @@ grant_user:
{
$$= $1;
$1->plugin= $4;
- $1->auth= empty_lex_str;
+ $1->auth= empty_clex_str;
}
| user IDENTIFIED_SYM via_or_with ident_or_text using_or_as TEXT_STRING_sys
{
@@ -16101,7 +17281,7 @@ column_list_id:
ident
{
String *new_str= new (thd->mem_root) String((const char*) $1.str,$1.length,system_charset_info);
- if (new_str == NULL)
+ if (unlikely(new_str == NULL))
MYSQL_YYABORT;
List_iterator <LEX_COLUMN> iter(Lex->columns);
class LEX_COLUMN *point;
@@ -16119,7 +17299,7 @@ column_list_id:
{
LEX_COLUMN *col= (new (thd->mem_root)
LEX_COLUMN(*new_str,lex->which_columns));
- if (col == NULL)
+ if (unlikely(col == NULL))
MYSQL_YYABORT;
lex->columns.push_back(col, thd->mem_root);
}
@@ -16210,8 +17390,8 @@ grant_option:
| resource_option {}
;
-begin:
- BEGIN_SYM
+begin_stmt_mariadb:
+ BEGIN_MARIADB_SYM
{
LEX *lex=Lex;
lex->sql_command = SQLCOM_BEGIN;
@@ -16253,11 +17433,6 @@ opt_release:
| NO_SYM RELEASE_SYM { $$= TVL_NO; }
;
-opt_savepoint:
- /* empty */ {}
- | SAVEPOINT_SYM {}
- ;
-
commit:
COMMIT_SYM opt_work opt_chain opt_release
{
@@ -16280,13 +17455,18 @@ rollback:
lex->tx_chain= $3;
lex->tx_release= $4;
}
- | ROLLBACK_SYM opt_work
- TO_SYM opt_savepoint ident
+ | 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:
@@ -16311,6 +17491,14 @@ release:
UNIONS : glue selects together
*/
+unit_type_decl:
+ UNION_SYM
+ { $$= UNION_TYPE; }
+ | INTERSECT_SYM
+ { $$= INTERSECT_TYPE; }
+ | EXCEPT_SYM
+ { $$= EXCEPT_TYPE; }
+ ;
union_clause:
/* empty */ {}
@@ -16318,9 +17506,9 @@ union_clause:
;
union_list:
- UNION_SYM union_option
+ unit_type_decl union_option
{
- if (add_select_to_union_list(Lex, (bool)$2, TRUE))
+ if (unlikely(Lex->add_select_to_union_list((bool)$2, $1, TRUE)))
MYSQL_YYABORT;
}
union_list_part2
@@ -16334,9 +17522,9 @@ union_list:
;
union_list_view:
- UNION_SYM union_option
+ unit_type_decl union_option
{
- if (add_select_to_union_list(Lex, (bool)$2, TRUE))
+ if (unlikely(Lex->add_select_to_union_list((bool)$2, $1, TRUE)))
MYSQL_YYABORT;
}
query_expression_body_view
@@ -16375,9 +17563,9 @@ order_or_limit:
Start a UNION, for non-top level query expressions.
*/
union_head_non_top:
- UNION_SYM union_option
+ unit_type_decl union_option
{
- if (add_select_to_union_list(Lex, (bool)$2, FALSE))
+ if (unlikely(Lex->add_select_to_union_list((bool)$2, $1, FALSE)))
MYSQL_YYABORT;
}
;
@@ -16388,6 +17576,24 @@ union_option:
| ALL { $$=0; }
;
+simple_table:
+ query_specification { $$= $1; }
+ | table_value_constructor { $$= $1; }
+ ;
+
+table_value_constructor:
+ VALUES
+ {
+ Lex->tvc_start();
+ }
+ values_list
+ {
+ $$= Lex->current_select;
+ if (Lex->tvc_finalize())
+ MYSQL_YYABORT;
+ }
+ ;
+
/*
Corresponds to the SQL Standard
<query specification> ::=
@@ -16405,13 +17611,13 @@ query_specification:
;
query_term_union_not_ready:
- query_specification order_or_limit opt_select_lock_type { $$= $1; }
+ simple_table order_or_limit opt_select_lock_type { $$= $1; }
| '(' select_paren_derived ')' union_order_or_limit { $$= $2; }
;
query_term_union_ready:
- query_specification opt_select_lock_type { $$= $1; }
- | '(' select_paren_derived ')' { $$= $2; }
+ simple_table opt_select_lock_type { $$= $1; }
+ | '(' select_paren_derived ')' { $$= $2; }
;
query_expression_body:
@@ -16432,10 +17638,10 @@ subselect:
subselect_start:
{
LEX *lex=Lex;
- if (!lex->expr_allows_subselect ||
- lex->sql_command == (int)SQLCOM_PURGE)
+ if (unlikely(!lex->expr_allows_subselect ||
+ lex->sql_command == (int)SQLCOM_PURGE))
{
- my_parse_error(thd, ER_SYNTAX_ERROR);
+ thd->parse_error();
MYSQL_YYABORT;
}
/*
@@ -16445,7 +17651,7 @@ subselect_start:
(SELECT .. ) UNION ... becomes
SELECT * FROM ((SELECT ...) UNION ...)
*/
- if (mysql_new_select(Lex, 1))
+ if (unlikely(mysql_new_select(Lex, 1, NULL)))
MYSQL_YYABORT;
}
;
@@ -16454,6 +17660,7 @@ subselect_end:
{
LEX *lex=Lex;
+ lex->check_automatic_up(UNSPECIFIED_TYPE);
lex->pop_context();
SELECT_LEX *child= lex->current_select;
lex->current_select = lex->current_select->return_after_parsing();
@@ -16491,7 +17698,7 @@ query_expression_option:
STRAIGHT_JOIN { Select->options|= SELECT_STRAIGHT_JOIN; }
| HIGH_PRIORITY
{
- if (check_simple_select())
+ if (unlikely(Lex->check_simple_select(&$1)))
MYSQL_YYABORT;
YYPS->m_lock_type= TL_READ_HIGH_PRIORITY;
YYPS->m_mdl_type= MDL_SHARED_READ;
@@ -16502,13 +17709,13 @@ query_expression_option:
| SQL_BIG_RESULT { Select->options|= SELECT_BIG_RESULT; }
| SQL_BUFFER_RESULT
{
- if (check_simple_select())
+ if (unlikely(Lex->check_simple_select(&$1)))
MYSQL_YYABORT;
Select->options|= OPTION_BUFFER_RESULT;
}
| SQL_CALC_FOUND_ROWS
{
- if (check_simple_select())
+ if (unlikely(Lex->check_simple_select(&$1)))
MYSQL_YYABORT;
Select->options|= OPTION_FOUND_ROWS;
}
@@ -16517,38 +17724,6 @@ query_expression_option:
/**************************************************************************
- CREATE VIEW | TRIGGER | PROCEDURE statements.
-
-**************************************************************************/
-
-view_or_trigger_or_sp_or_event:
- definer definer_tail
- {}
- | no_definer no_definer_tail
- {}
- | view_algorithm definer_opt view_tail
- {}
- ;
-
-definer_tail:
- view_tail
- | trigger_tail
- | sp_tail
- | sf_tail
- | event_tail
- ;
-
-no_definer_tail:
- view_tail
- | trigger_tail
- | sp_tail
- | sf_tail
- | udf_tail
- | event_tail
- ;
-
-/**************************************************************************
-
DEFINER clause support.
**************************************************************************/
@@ -16589,39 +17764,19 @@ definer:
**************************************************************************/
view_algorithm:
- ALGORITHM_SYM '=' UNDEFINED_SYM
- { Lex->create_view_algorithm= DTYPE_ALGORITHM_UNDEFINED; }
- | ALGORITHM_SYM '=' MERGE_SYM
- { Lex->create_view_algorithm= VIEW_ALGORITHM_MERGE; }
- | ALGORITHM_SYM '=' TEMPTABLE_SYM
- { Lex->create_view_algorithm= VIEW_ALGORITHM_TMPTABLE; }
+ ALGORITHM_SYM '=' UNDEFINED_SYM { $$= DTYPE_ALGORITHM_UNDEFINED; }
+ | ALGORITHM_SYM '=' MERGE_SYM { $$= VIEW_ALGORITHM_MERGE; }
+ | ALGORITHM_SYM '=' TEMPTABLE_SYM { $$= VIEW_ALGORITHM_TMPTABLE; }
;
-view_suid:
- /* empty */
- { Lex->create_view_suid= VIEW_SUID_DEFAULT; }
- | SQL_SYM SECURITY_SYM DEFINER_SYM
- { Lex->create_view_suid= VIEW_SUID_DEFINER; }
- | SQL_SYM SECURITY_SYM INVOKER_SYM
- { Lex->create_view_suid= VIEW_SUID_INVOKER; }
+opt_view_suid:
+ /* empty */ { $$= VIEW_SUID_DEFAULT; }
+ | view_suid { $$= $1; }
;
-view_tail:
- view_suid VIEW_SYM opt_if_not_exists table_ident
- {
- LEX *lex= thd->lex;
- if (lex->add_create_options_with_check($3))
- MYSQL_YYABORT;
- lex->sql_command= SQLCOM_CREATE_VIEW;
- /* first table in list is target VIEW name */
- if (!lex->select_lex.add_table_to_list(thd, $4, NULL,
- TL_OPTION_UPDATING,
- TL_IGNORE,
- MDL_EXCLUSIVE))
- MYSQL_YYABORT;
- lex->query_tables->open_strategy= TABLE_LIST::OPEN_STUB;
- }
- view_list_opt AS view_select
+view_suid:
+ SQL_SYM SECURITY_SYM DEFINER_SYM { $$= VIEW_SUID_DEFINER; }
+ | SQL_SYM SECURITY_SYM INVOKER_SYM { $$= VIEW_SUID_INVOKER; }
;
view_list_opt:
@@ -16633,14 +17788,14 @@ view_list_opt:
view_list:
ident
{
- Lex->view_list.push_back((LEX_STRING*)
- thd->memdup(&$1, sizeof(LEX_STRING)),
+ 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_STRING*)
- thd->memdup(&$3, sizeof(LEX_STRING)),
+ Lex->view_list.push_back((LEX_CSTRING*)
+ thd->memdup(&$3, sizeof(LEX_CSTRING)),
thd->mem_root);
}
;
@@ -16649,18 +17804,18 @@ view_select:
{
LEX *lex= Lex;
lex->parsing_options.allows_variable= FALSE;
- lex->create_view_select.str= (char *) YYLIP->get_cpp_ptr();
+ lex->create_view->select.str= (char *) YYLIP->get_cpp_ptr();
}
opt_with_clause query_expression_body_view view_check_option
{
LEX *lex= Lex;
- size_t len= YYLIP->get_cpp_ptr() - lex->create_view_select.str;
- uint not_used;
- void *create_view_select= thd->memdup(lex->create_view_select.str, len);
- lex->create_view_select.length= len;
- lex->create_view_select.str= (char *) create_view_select;
- trim_whitespace(thd->charset(), &lex->create_view_select,
- &not_used);
+ size_t len= YYLIP->get_cpp_ptr() - lex->create_view->select.str;
+ void *create_view_select= thd->memdup(lex->create_view->select.str, len);
+ lex->create_view->select.length= len;
+ lex->create_view->select.str= (char *) create_view_select;
+ trim_whitespace(thd->charset(),
+ &lex->create_view->select);
+ lex->create_view->check= $4;
lex->parsing_options.allows_variable= TRUE;
lex->current_select->set_with_clause($2);
}
@@ -16672,20 +17827,19 @@ view_select:
*/
query_expression_body_view:
SELECT_SYM select_options_and_item_list select_init3_view
+ | table_value_constructor
+ | table_value_constructor union_order_or_limit
+ | table_value_constructor union_list_view
| '(' select_paren_view ')'
| '(' select_paren_view ')' union_order_or_limit
| '(' select_paren_view ')' union_list_view
;
view_check_option:
- /* empty */
- { Lex->create_view_check= VIEW_CHECK_NONE; }
- | WITH CHECK_SYM OPTION
- { Lex->create_view_check= VIEW_CHECK_CASCADED; }
- | WITH CASCADED CHECK_SYM OPTION
- { Lex->create_view_check= VIEW_CHECK_CASCADED; }
- | WITH LOCAL_SYM CHECK_SYM OPTION
- { Lex->create_view_check= VIEW_CHECK_LOCAL; }
+ /* 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; }
;
/**************************************************************************
@@ -16717,25 +17871,24 @@ trigger_follows_precedes_clause:
;
trigger_tail:
- TRIGGER_SYM
remember_name
opt_if_not_exists
{
- if (Lex->add_create_options_with_check($3))
+ if (unlikely(Lex->add_create_options_with_check($2)))
MYSQL_YYABORT;
}
sp_name
trg_action_time
trg_event
ON
- remember_name /* $9 */
- { /* $10 */
+ remember_name /* $8 */
+ { /* $9 */
Lex->raw_trg_on_table_name_begin= YYLIP->get_tok_start();
}
- table_ident /* $11 */
+ table_ident /* $10 */
FOR_SYM
- remember_name /* $13 */
- { /* $14 */
+ remember_name /* $12 */
+ { /* $13 */
Lex->raw_trg_on_table_name_end= YYLIP->get_tok_start();
}
EACH_SYM
@@ -16743,28 +17896,28 @@ trigger_tail:
{
Lex->trg_chistics.ordering_clause_begin= YYLIP->get_cpp_ptr();
}
- trigger_follows_precedes_clause /* $18 */
- { /* $19 */
+ trigger_follows_precedes_clause /* $17 */
+ { /* $18 */
LEX *lex= thd->lex;
Lex_input_stream *lip= YYLIP;
- if (lex->sphead)
+ if (unlikely(lex->sphead))
my_yyabort_error((ER_SP_NO_RECURSIVE_CREATE, MYF(0), "TRIGGER"));
- lex->stmt_definition_begin= $2;
- lex->ident.str= $9;
- lex->ident.length= $13 - $9;
- lex->spname= $5;
- (*static_cast<st_trg_execution_order*>(&lex->trg_chistics))= ($18);
+ 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 (!make_sp_head(thd, $5, TYPE_ENUM_TRIGGER))
+ if (unlikely(!lex->make_sp_head(thd, $4, &sp_handler_trigger)))
MYSQL_YYABORT;
lex->sphead->set_body_start(thd, lip->get_cpp_tok_start());
}
- sp_proc_stmt /* $20 */
- { /* $21 */
+ sp_proc_stmt /* $19 */
+ { /* $20 */
LEX *lex= Lex;
sp_head *sp= lex->sphead;
@@ -16772,7 +17925,7 @@ trigger_tail:
sp->set_stmt_end(thd);
sp->restore_thd_mem_root(thd);
- if (sp->is_not_allowed_in_function("trigger"))
+ if (unlikely(sp->is_not_allowed_in_function("trigger")))
MYSQL_YYABORT;
/*
@@ -16780,11 +17933,11 @@ trigger_tail:
sp_proc_stmt alternatives are not saving/restoring LEX, so
lex->query_tables can be wiped out.
*/
- if (!lex->select_lex.add_table_to_list(thd, $11,
- (LEX_STRING*) 0,
- TL_OPTION_UPDATING,
- TL_READ_NO_INSERT,
- MDL_SHARED_NO_WRITE))
+ if (unlikely(!lex->select_lex.
+ add_table_to_list(thd, $10, (LEX_CSTRING*) 0,
+ TL_OPTION_UPDATING,
+ TL_READ_NO_INSERT,
+ MDL_SHARED_NO_WRITE)))
MYSQL_YYABORT;
}
;
@@ -16796,162 +17949,82 @@ trigger_tail:
**************************************************************************/
udf_tail:
- AGGREGATE_SYM udf_tail2 { thd->lex->udf.type= UDFTYPE_AGGREGATE; }
- | udf_tail2 { thd->lex->udf.type= UDFTYPE_FUNCTION; }
- ;
-
-udf_tail2:
- FUNCTION_SYM opt_if_not_exists ident
+ opt_if_not_exists ident
RETURNS_SYM udf_type SONAME_SYM TEXT_STRING_sys
{
LEX *lex= thd->lex;
- if (lex->add_create_options_with_check($2))
+ if (unlikely(lex->add_create_options_with_check($1)))
MYSQL_YYABORT;
- if (is_native_function(thd, & $3))
- my_yyabort_error((ER_NATIVE_FCT_NAME_COLLISION, MYF(0), $3.str));
+ if (unlikely(is_native_function(thd, & $2)))
+ my_yyabort_error((ER_NATIVE_FCT_NAME_COLLISION, MYF(0), $2.str));
lex->sql_command= SQLCOM_CREATE_FUNCTION;
- lex->udf.name= $3;
- lex->udf.returns= (Item_result) $5;
- lex->udf.dl= $7.str;
+ lex->udf.name= $2;
+ lex->udf.returns= (Item_result) $4;
+ lex->udf.dl= $6.str;
}
;
-sf_tail:
- FUNCTION_SYM /* $1 */
- opt_if_not_exists /* $2 */
- sp_name /* $3 */
- '(' /* $4 */
- { /* $5 */
- LEX *lex= Lex;
- Lex_input_stream *lip= YYLIP;
- const char* tmp_param_begin;
-
- if (lex->add_create_options_with_check($2))
- MYSQL_YYABORT;
- lex->spname= $3;
-
- if (lex->sphead)
- my_yyabort_error((ER_SP_NO_RECURSIVE_CREATE, MYF(0), "FUNCTION"));
-
- if (!make_sp_head(thd, $3, TYPE_ENUM_FUNCTION))
- MYSQL_YYABORT;
- tmp_param_begin= lip->get_cpp_tok_start();
- tmp_param_begin++;
- lex->sphead->m_param_begin= tmp_param_begin;
- }
- sp_fdparam_list /* $6 */
- ')' /* $7 */
- { /* $8 */
- Lex->sphead->m_param_end= YYLIP->get_cpp_tok_start();
- }
- RETURNS_SYM /* $9 */
- { /* $10 */
+sf_return_type:
+ RETURNS_SYM
+ {
LEX *lex= Lex;
- lex->init_last_field(&lex->sphead->m_return_field_def, NULL,
+ lex->init_last_field(&lex->sphead->m_return_field_def,
+ &empty_clex_str,
thd->variables.collation_database);
}
- type_with_opt_collate /* $11 */
- { /* $12 */
- if (Lex->sphead->fill_field_definition(thd, Lex, Lex->last_field))
+ type_with_opt_collate
+ {
+ if (unlikely(Lex->sphead->fill_field_definition(thd,
+ Lex->last_field)))
MYSQL_YYABORT;
}
- sp_c_chistics /* $13 */
- { /* $14 */
+ ;
+
+sf_tail:
+ opt_if_not_exists
+ sp_name
+ {
+ Lex->sql_command= SQLCOM_CREATE_SPFUNCTION;
+ if (unlikely(!Lex->make_sp_head_no_recursive(thd, $1, $2,
+ &sp_handler_function)))
+ MYSQL_YYABORT;
+ }
+ sp_parenthesized_fdparam_list
+ sf_return_type
+ sp_c_chistics
+ {
LEX *lex= thd->lex;
Lex_input_stream *lip= YYLIP;
+ lex->sphead->set_chistics(lex->sp_chistics);
lex->sphead->set_body_start(thd, lip->get_cpp_tok_start());
}
- sp_proc_stmt_in_returns_clause /* $15 */
+ sp_proc_stmt_in_returns_clause
{
- LEX *lex= thd->lex;
- sp_head *sp= lex->sphead;
-
- if (sp->is_not_allowed_in_function("function"))
+ if (unlikely(Lex->sp_body_finalize_function(thd)))
MYSQL_YYABORT;
-
- lex->sql_command= SQLCOM_CREATE_SPFUNCTION;
- sp->set_stmt_end(thd);
- if (!(sp->m_flags & sp_head::HAS_RETURN))
- my_yyabort_error((ER_SP_NORETURN, MYF(0), sp->m_qname.str));
- if (is_native_function(thd, & sp->m_name))
- {
- /*
- This warning will be printed when
- [1] A client query is parsed,
- [2] A stored function is loaded by db_load_routine.
- Printing the warning for [2] is intentional, to cover the
- following scenario:
- - A user define a SF 'foo' using MySQL 5.N
- - An application uses select foo(), and works.
- - MySQL 5.{N+1} defines a new native function 'foo', as
- part of a new feature.
- - MySQL 5.{N+1} documentation is updated, and should mention
- that there is a potential incompatible change in case of
- existing stored function named 'foo'.
- - The user deploys 5.{N+1}. At this point, 'select foo()'
- means something different, and the user code is most likely
- broken (it's only safe if the code is 'select db.foo()').
- With a warning printed when the SF is loaded (which has to
- occur before the call), the warning will provide a hint
- explaining the root cause of a later failure of 'select foo()'.
- With no warning printed, the user code will fail with no
- apparent reason.
- Printing a warning each time db_load_routine is executed for
- an ambiguous function is annoying, since that can happen a lot,
- but in practice should not happen unless there *are* name
- collisions.
- If a collision exists, it should not be silenced but fixed.
- */
- push_warning_printf(thd,
- Sql_condition::WARN_LEVEL_NOTE,
- ER_NATIVE_FCT_NAME_COLLISION,
- ER_THD(thd, ER_NATIVE_FCT_NAME_COLLISION),
- sp->m_name.str);
- }
- sp->restore_thd_mem_root(thd);
}
;
sp_tail:
- PROCEDURE_SYM opt_if_not_exists sp_name
+ opt_if_not_exists sp_name
{
- if (Lex->add_create_options_with_check($2))
+ Lex->sql_command= SQLCOM_CREATE_PROCEDURE;
+ if (unlikely(!Lex->make_sp_head_no_recursive(thd, $1, $2,
+ &sp_handler_procedure)))
MYSQL_YYABORT;
-
- if (Lex->sphead)
- my_yyabort_error((ER_SP_NO_RECURSIVE_CREATE, MYF(0), "PROCEDURE"));
-
- if (!make_sp_head(thd, $3, TYPE_ENUM_PROCEDURE))
- MYSQL_YYABORT;
- Lex->spname= $3;
- }
- '('
- {
- const char* tmp_param_begin;
-
- tmp_param_begin= YYLIP->get_cpp_tok_start();
- tmp_param_begin++;
- Lex->sphead->m_param_begin= tmp_param_begin;
- }
- sp_pdparam_list
- ')'
- {
- Lex->sphead->m_param_end= YYLIP->get_cpp_tok_start();
}
+ sp_parenthesized_pdparam_list
sp_c_chistics
{
+ Lex->sphead->set_chistics(Lex->sp_chistics);
Lex->sphead->set_body_start(thd, YYLIP->get_cpp_tok_start());
}
sp_proc_stmt
{
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
-
- sp->set_stmt_end(thd);
- lex->sql_command= SQLCOM_CREATE_PROCEDURE;
- sp->restore_thd_mem_root(thd);
+ if (unlikely(Lex->sp_body_finalize_procedure(thd)))
+ MYSQL_YYABORT;
}
;
@@ -16978,9 +18051,27 @@ xa:
{
Lex->sql_command = SQLCOM_XA_ROLLBACK;
}
- | XA_SYM RECOVER_SYM
+ | 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;
+ }
}
;
@@ -16988,28 +18079,29 @@ xid:
text_string
{
MYSQL_YYABORT_UNLESS($1->length() <= MAXGTRIDSIZE);
- if (!(Lex->xid=(XID *)thd->alloc(sizeof(XID))))
+ 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 (!(Lex->xid=(XID *)thd->alloc(sizeof(XID))))
+ 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 (!(Lex->xid=(XID *)thd->alloc(sizeof(XID))))
+ 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_SYM {}
+ BEGIN_MARIADB_SYM {}
+ | BEGIN_ORACLE_SYM {}
| START_SYM {}
;
@@ -17049,7 +18141,7 @@ install:
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_INSTALL_PLUGIN;
- lex->comment= null_lex_str;
+ lex->comment= null_clex_str;
lex->ident= $3;
}
;
@@ -17065,7 +18157,7 @@ uninstall:
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_UNINSTALL_PLUGIN;
- lex->comment= null_lex_str;
+ lex->comment= null_clex_str;
lex->ident= $3;
}
;
diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy
new file mode 100644
index 00000000000..a1eb74771ef
--- /dev/null
+++ b/sql/sql_yacc_ora.yy
@@ -0,0 +1,18270 @@
+/*
+ Copyright (c) 2000, 2015, Oracle and/or its affiliates.
+ Copyright (c) 2010, 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 */
+
+/* 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"
+
+/* 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
+
+
+/**
+ @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;
+
+ /* 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;
+ 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;
+ 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_view_suid view_suid;
+ enum sub_select_type unit_type;
+ 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 }
+/*
+ Currently there are 55 shift/reduce conflicts.
+ We should not introduce new conflicts any more.
+*/
+%expect 54
+
+/*
+ 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 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 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> 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> 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> 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> 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 */
+
+
+/*
+ Give ESCAPE (in LIKE) a very low precedence.
+ This allows the concatenation operator || to be used on the right
+ side of "LIKE" with sql_mode=PIPES_AS_CONCAT (without ORACLE):
+ SELECT 'ab' LIKE 'a'||'b'||'c';
+*/
+%left PREC_BELOW_ESCAPE
+%left ESCAPE_SYM
+
+/* 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
+%left NOT_SYM
+
+%left BETWEEN_SYM CASE_SYM WHEN_SYM THEN_SYM ELSE
+%left '=' EQUAL_SYM GE '>' LE '<' NE IS 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
+%left NEG '~' NOT2_SYM BINARY
+%left COLLATE_SYM
+
+/*
+ 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
+
+%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
+
+%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 remember_tok_end
+ 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
+ select_derived_init transaction_access_mode_types
+ opt_natural_language_mode opt_query_expansion
+ opt_ev_status opt_ev_on_completion ev_on_completion opt_ev_comment
+ ev_alter_on_schedule_completion opt_ev_rename_to opt_ev_sql_stmt
+ optional_flush_tables_arguments
+ opt_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
+
+%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
+ opt_escape
+ 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
+
+%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_derived
+ select_derived derived_table_list
+ select_derived_union
+ derived_simple_table
+ derived_query_specification
+ derived_table_value_constructor
+%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 <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
+ get_select_lex get_select_lex_derived
+ simple_table
+ query_specification
+ query_term_union_not_ready
+ query_term_union_ready
+ query_expression_body
+ select_paren_derived
+ table_value_constructor
+
+%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_type> unit_type_decl
+
+%type <NONE>
+ analyze_stmt_command
+ query verb_clause create change select 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
+ opt_limit_clause 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
+ union_clause union_list
+ subselect_start opt_and charset
+ subselect_end select_var_list select_var_list_init help
+ opt_extended_describe shutdown
+ opt_format_json
+ prepare prepare_src 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 sp_tail sf_tail event_tail
+ udf_tail create_function_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
+ 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
+ sf_tail_standalone
+ 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 <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 <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> opt_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 (likely(!thd->bootstrap) &&
+ unlikely(!(thd->lex->select_lex.options & OPTION_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
+ | 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
+ | set
+ | set_assign
+ | signal_stmt
+ | show
+ | shutdown
+ | slave
+ | start
+ | truncate
+ | uninstall
+ | unlock
+ | update
+ | use
+ | xa
+ ;
+
+deallocate:
+ deallocate_or_drop PREPARE_SYM ident
+ {
+ LEX *lex= thd->lex;
+ lex->sql_command= SQLCOM_DEALLOCATE_PREPARE;
+ lex->prepared_stmt_name= $3;
+ }
+ ;
+
+deallocate_or_drop:
+ DEALLOCATE_SYM
+ | DROP
+ ;
+
+prepare:
+ PREPARE_SYM ident FROM prepare_src
+ {
+ LEX *lex= thd->lex;
+ if (unlikely(lex->table_or_sp_used()))
+ my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0),
+ "PREPARE..FROM"));
+ lex->sql_command= SQLCOM_PREPARE;
+ lex->prepared_stmt_name= $2;
+ }
+ ;
+
+prepare_src:
+ { Lex->expr_allows_subselect= false; }
+ expr
+ {
+ Lex->prepared_stmt_code= $2;
+ Lex->expr_allows_subselect= true;
+ }
+ ;
+
+execute:
+ EXECUTE_SYM ident
+ {
+ LEX *lex= thd->lex;
+ lex->sql_command= SQLCOM_EXECUTE;
+ lex->prepared_stmt_name= $2;
+ }
+ execute_using
+ {}
+ | EXECUTE_SYM IMMEDIATE_SYM prepare_src
+ {
+ if (unlikely(Lex->table_or_sp_used()))
+ my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0),
+ "EXECUTE IMMEDIATE"));
+ Lex->sql_command= SQLCOM_EXECUTE_IMMEDIATE;
+ }
+ execute_using
+ {}
+ ;
+
+execute_using:
+ /* nothing */
+ | USING { Lex->expr_allows_subselect= false; }
+ execute_var_list
+ {
+ if (unlikely(Lex->table_or_sp_used()))
+ my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0),
+ "EXECUTE..USING"));
+ Lex->expr_allows_subselect= true;
+ }
+ ;
+
+execute_var_list:
+ execute_var_list ',' execute_var_ident
+ | execute_var_ident
+ ;
+
+execute_var_ident:
+ expr_or_default
+ {
+ if (unlikely(Lex->prepared_stmt_params.push_back($1,
+ 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 table_ident
+ {
+ LEX *lex= thd->lex;
+ if (!(lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_create_table()))
+ MYSQL_YYABORT;
+ lex->create_info.init();
+ if (unlikely(lex->set_command_with_check(SQLCOM_CREATE_TABLE, $2,
+ $1 | $4)))
+ MYSQL_YYABORT;
+ if (unlikely(!lex->select_lex.add_table_to_list(thd, $5, NULL,
+ TL_OPTION_UPDATING,
+ TL_WRITE,
+ MDL_EXCLUSIVE)))
+ 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;
+ lex->current_select= &lex->select_lex;
+ create_table_set_open_action_and_adjust_tables(lex);
+ }
+ | create_or_replace opt_temporary SEQUENCE_SYM opt_if_not_exists table_ident
+ {
+ LEX *lex= thd->lex;
+ 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 (unlikely(!lex->select_lex.add_table_to_list(thd, $5, NULL,
+ TL_OPTION_UPDATING,
+ TL_WRITE,
+ MDL_EXCLUSIVE)))
+ MYSQL_YYABORT;
+
+ /*
+ For CREATE TABLE, an non-existing table is not an error.
+ Instruct open_tables() to just take an MDL lock if the
+ table does not exist.
+ */
+ lex->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->select_lex.table_list.first->db.str,
+ lex->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;
+
+ lex->current_select= &lex->select_lex;
+ create_table_set_open_action_and_adjust_tables(lex);
+ }
+ | create_or_replace opt_unique INDEX_SYM opt_if_not_exists ident
+ opt_key_algorithm_clause
+ ON table_ident
+ {
+ if (unlikely(Lex->add_create_index_prepare($8)))
+ MYSQL_YYABORT;
+ if (unlikely(Lex->add_create_index($2, &$5, $6, $1 | $4)))
+ MYSQL_YYABORT;
+ }
+ '(' key_list ')' opt_lock_wait_timeout normal_key_options
+ opt_index_lock_algorithm { }
+ | create_or_replace fulltext INDEX_SYM opt_if_not_exists ident
+ ON table_ident
+ {
+ if (unlikely(Lex->add_create_index_prepare($7)))
+ MYSQL_YYABORT;
+ if (unlikely(Lex->add_create_index($2, &$5, HA_KEY_ALG_UNDEF,
+ $1 | $4)))
+ MYSQL_YYABORT;
+ }
+ '(' key_list ')' opt_lock_wait_timeout fulltext_key_options
+ opt_index_lock_algorithm { }
+ | create_or_replace spatial INDEX_SYM opt_if_not_exists ident
+ ON table_ident
+ {
+ if (unlikely(Lex->add_create_index_prepare($7)))
+ MYSQL_YYABORT;
+ if (unlikely(Lex->add_create_index($2, &$5, HA_KEY_ALG_UNDEF,
+ $1 | $4)))
+ MYSQL_YYABORT;
+ }
+ '(' key_list ')' opt_lock_wait_timeout spatial_key_options
+ opt_index_lock_algorithm { }
+ | 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 (unlikely(Lex->add_create_view(thd, $1 | $5,
+ DTYPE_ALGORITHM_UNDEFINED, $3,
+ $6)))
+ MYSQL_YYABORT;
+ }
+ view_list_opt AS view_select
+ { }
+ | create_or_replace view_algorithm definer_opt opt_view_suid VIEW_SYM
+ opt_if_not_exists table_ident
+ {
+ if (unlikely(Lex->add_create_view(thd, $1 | $6, $2, $4, $7)))
+ MYSQL_YYABORT;
+ }
+ view_list_opt AS view_select
+ { }
+ | create_or_replace definer_opt TRIGGER_SYM
+ { Lex->create_info.set($1); }
+ trigger_tail
+ { }
+ | create_or_replace definer_opt PROCEDURE_SYM
+ { Lex->create_info.set($1); }
+ sp_tail_standalone
+ { }
+ | create_or_replace definer_opt EVENT_SYM
+ { Lex->create_info.set($1); }
+ event_tail
+ { }
+ | create_or_replace definer FUNCTION_SYM
+ { Lex->create_info.set($1); }
+ sf_tail_standalone
+ { }
+ | create_or_replace no_definer FUNCTION_SYM
+ { Lex->create_info.set($1); }
+ create_function_tail
+ { }
+ | create_or_replace no_definer AGGREGATE_SYM FUNCTION_SYM
+ {
+ Lex->create_info.set($1);
+ Lex->udf.type= UDFTYPE_AGGREGATE;
+ }
+ udf_tail
+ { }
+ | create_or_replace USER_SYM opt_if_not_exists clear_privileges grant_list
+ opt_require_clause opt_resource_options
+ {
+ 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_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_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)))
+ MYSQL_YYABORT;
+ $1->sphead->get_package()->m_current_routine= $2;
+ (void) is_native_function_with_warn(thd, &$3);
+ }
+ opt_sp_parenthesized_fdparam_list
+ 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)))
+ 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(Lex->sphead->m_flags & sp_head::HAS_AGGREGATE_INSTR))
+ {
+ my_yyabort_error((ER_NOT_AGGREGATE_FUNCTION, MYF(0)));
+ }
+ Lex->sphead->set_chistics_agg_type(NOT_AGGREGATE);
+ 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;
+ }
+ ;
+
+create_function_tail:
+ sf_tail_standalone { }
+ | udf_tail { Lex->udf.type= UDFTYPE_FUNCTION; }
+ ;
+
+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)))
+ MYSQL_YYABORT;
+
+ lex->sphead->set_body_start(thd, lip->get_cpp_ptr());
+ }
+ sp_proc_stmt
+ {
+ LEX *lex= thd->lex;
+
+ /* return back to the original memory root ASAP */
+ lex->sphead->set_stmt_end(thd);
+ lex->sphead->restore_thd_mem_root(thd);
+
+ lex->event_parse_data->body_changed= TRUE;
+ }
+ ;
+
+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->select_lex.db= null_clex_str;
+ lex->ssl_type= SSL_TYPE_NOT_SPECIFIED;
+ lex->ssl_cipher= lex->x509_subject= lex->x509_issuer= 0;
+ bzero((char *)&(lex->mqh),sizeof(lex->mqh));
+ }
+ ;
+
+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 */
+ | sp_fdparams
+ ;
+
+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_fdparam_list:
+ '('
+ {
+ Lex->sphead->m_param_begin= YYLIP->get_cpp_tok_start() + 1;
+ }
+ sp_fdparam_list
+ ')'
+ {
+ Lex->sphead->m_param_end= YYLIP->get_cpp_tok_start();
+ }
+ ;
+
+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_parenthesized_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 (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(!($$= new (thd->mem_root) Spvar_definition())))
+ MYSQL_YYABORT;
+ Lex->init_last_field($$, &$1, thd->variables.collation_database);
+ }
+ ;
+
+row_field_definition:
+ row_field_name type_with_opt_collate
+ ;
+
+row_field_definition_list:
+ row_field_definition
+ {
+ if (unlikely(!($$= new (thd->mem_root) Row_definition_list())) ||
+ unlikely($$->push_back($1, thd->mem_root)))
+ MYSQL_YYABORT;
+ }
+ | row_field_definition_list ',' row_field_definition
+ {
+ uint unused;
+ if (unlikely($1->find_row_field_by_name(&$3->field_name, &unused)))
+ my_yyabort_error((ER_DUP_FIELDNAME, MYF(0), $3->field_name.str));
+ $$= $1;
+ if (unlikely($$->push_back($3, thd->mem_root)))
+ 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);
+ }
+ select
+ {
+ DBUG_ASSERT(Lex == $1);
+ 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 (unlikely(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);
+ lex->sphead->m_tmp_query= lip->get_tok_start();
+ }
+ sp_statement
+ {
+ LEX *lex= thd->lex;
+ Lex_input_stream *lip= YYLIP;
+ sp_head *sp= lex->sphead;
+
+ sp->m_flags|= sp_get_flags_for_command(lex);
+ /* "USE db" doesn't work in a procedure */
+ if (unlikely(lex->sql_command == SQLCOM_CHANGE_DB))
+ my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "USE"));
+ /*
+ Don't add an instruction for SET statements, since all
+ instructions for them were already added during processing
+ of "set" rule.
+ */
+ DBUG_ASSERT(lex->sql_command != SQLCOM_SET_OPTION ||
+ lex->var_list.is_empty());
+ if (lex->sql_command != SQLCOM_SET_OPTION)
+ {
+ sp_instr_stmt *i=new (thd->mem_root)
+ sp_instr_stmt(sp->instructions(), lex->spcont, lex);
+ if (unlikely(i == NULL))
+ MYSQL_YYABORT;
+
+ /*
+ Extract the query statement from the tokenizer. The
+ end is either lex->ptr, if there was no lookahead,
+ lex->tok_end otherwise.
+ */
+ if (yychar == YYEMPTY)
+ i->m_query.length= lip->get_ptr() - sp->m_tmp_query;
+ else
+ i->m_query.length= lip->get_tok_start() - sp->m_tmp_query;;
+ if (unlikely(!(i->m_query.str= strmake_root(thd->mem_root,
+ sp->m_tmp_query,
+ i->m_query.length))) ||
+ unlikely(sp->add_instr(i)))
+ MYSQL_YYABORT;
+ }
+ if (unlikely(sp->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); }
+ expr
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ 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); } expr { $$= $2; }
+ ;
+
+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 (unlikely(Lex->sp_exit_statement(thd, $3)) ||
+ unlikely(Lex->sphead->restore_lex(thd)))
+ MYSQL_YYABORT;
+ }
+ | EXIT_ORACLE_SYM label_ident WHEN_SYM reset_lex_expr
+ {
+ if (unlikely(Lex->sp_exit_statement(thd, &$2, $4)) ||
+ unlikely(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 (unlikely(Lex->sp_continue_statement(thd, $3)) ||
+ unlikely(Lex->sphead->restore_lex(thd)))
+ MYSQL_YYABORT;
+ }
+ | CONTINUE_ORACLE_SYM label_ident WHEN_SYM reset_lex_expr
+ {
+ if (unlikely(Lex->sp_continue_statement(thd, &$2, $4)) ||
+ unlikely(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);
+ }
+ 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;
+ if (unlikely($$->sphead->restore_lex(thd)))
+ MYSQL_YYABORT;
+ }
+ ;
+
+for_loop_bound_expr:
+ assignment_source_lex
+ {
+ Lex->sphead->reset_lex(thd, $1);
+ }
+ expr
+ {
+ DBUG_ASSERT($1 == thd->lex);
+ $$= $1;
+ $$->sp_lex_in_use= true;
+ $$->set_item_and_free_list($3, NULL);
+ if (unlikely($$->sphead->restore_lex(thd)))
+ MYSQL_YYABORT;
+ }
+ ;
+
+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 { }
+ ;
+
+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;
+
+ if (unlikely(!spc || !(spv = spc->find_variable(&$1, false))))
+ 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;
+
+ if (unlikely(!spc || !(spv = spc->find_variable(&$3, false))))
+ 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); }
+ 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;
+ 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 */ }
+ expr
+ {
+ if (unlikely(Lex->case_stmt_action_expr($2)))
+ MYSQL_YYABORT;
+ if (unlikely(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 */
+ }
+ expr
+ {
+ /* Simple case: <caseval> = <whenval> */
+
+ LEX *lex= Lex;
+ if (unlikely(lex->case_stmt_action_when($3, true)))
+ MYSQL_YYABORT;
+ /* 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 */
+ }
+ expr
+ {
+ LEX *lex= Lex;
+ if (unlikely(lex->case_stmt_action_when($3, false)))
+ MYSQL_YYABORT;
+ /* 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 sp_for_loop_bounds
+ {
+ if (unlikely(Lex->sp_for_loop_declarations(thd, &$$, &$1, $2)))
+ 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;
+ if (unlikely(lex->sphead->restore_lex(thd)))
+ 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); }
+ 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;
+ if (unlikely(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);
+ }
+ 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);
+ }
+ 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 ')'
+ { Lex->create_info.option_list= NULL; }
+ opt_create_table_options opt_create_partitioning opt_create_select {}
+ | opt_create_table_options opt_create_partitioning opt_create_select {}
+ /*
+ the following rule is redundant, but there's a shift/reduce
+ conflict that prevents the rule above from parsing a syntax like
+ CREATE TABLE t1 (SELECT 1);
+ */
+ | '(' create_select_query_specification ')'
+ | '(' create_select_query_specification ')'
+ { Select->set_braces(1);} union_list {}
+ | '(' create_select_query_specification ')'
+ { Select->set_braces(1);} union_order_or_limit {}
+ | create_like
+ {
+
+ Lex->create_info.add(DDL_options_st::OPT_LIKE);
+ TABLE_LIST *src_table= Lex->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; }
+ | '(' LIKE table_ident ')' { $$= $3; }
+ ;
+
+opt_create_select:
+ /* empty */ {}
+ | opt_duplicate opt_as create_select_query_expression opt_versioning_option
+ ;
+
+create_select_query_expression:
+ opt_with_clause SELECT_SYM create_select_part2 opt_table_expression
+ create_select_part4
+ {
+ Select->set_braces(0);
+ Select->set_with_clause($1);
+ }
+ union_clause
+ | opt_with_clause SELECT_SYM create_select_part2
+ create_select_part3_union_not_ready create_select_part4
+ {
+ Select->set_with_clause($1);
+ }
+ | '(' create_select_query_specification ')'
+ | '(' create_select_query_specification ')'
+ { Select->set_braces(1);} union_list {}
+ | '(' create_select_query_specification ')'
+ { Select->set_braces(1);} union_order_or_limit {}
+ ;
+
+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.
+ */
+ }
+ partition {}
+ ;
+
+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
+*/
+
+create_select_query_specification:
+ opt_with_clause SELECT_SYM create_select_part2 create_select_part3
+ create_select_part4
+ {
+ Select->set_with_clause($1);
+ }
+ ;
+
+create_select_part2:
+ {
+ LEX *lex=Lex;
+ if (lex->sql_command == SQLCOM_INSERT)
+ lex->sql_command= SQLCOM_INSERT_SELECT;
+ else if (lex->sql_command == SQLCOM_REPLACE)
+ lex->sql_command= SQLCOM_REPLACE_SELECT;
+ /*
+ The following work only with the local list, the global list
+ is created correctly in this case
+ */
+ lex->current_select->table_list.save_and_clear(&lex->save_list);
+ mysql_init_select(lex);
+ lex->current_select->parsing_place= SELECT_LIST;
+ }
+ select_options select_item_list
+ {
+ Select->parsing_place= NO_MATTER;
+ }
+ ;
+
+create_select_part3:
+ opt_table_expression
+ | create_select_part3_union_not_ready
+ ;
+
+create_select_part3_union_not_ready:
+ table_expression order_or_limit
+ | order_or_limit
+ ;
+
+create_select_part4:
+ opt_select_lock_type
+ {
+ /*
+ The following work only with the local list, the global list
+ is created correctly in this case
+ */
+ Lex->current_select->table_list.push_front(&Lex->save_list);
+ }
+ ;
+
+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->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->select_lex.table_list.first;
+ lex->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_TEMPORARY, MYF(0));
+ 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();
+ }
+ ;
+
+field_list:
+ field_list_item
+ | field_list ',' field_list_item
+ ;
+
+field_list_item:
+ column_def { }
+ | key_def
+ | constraint_def
+ | period_for_system_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_system_time($4, $6);
+ }
+ ;
+
+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);
+ }
+ expr
+ {
+ Virtual_column_info *v= add_virtual_expression(thd, $3);
+ if (unlikely(!v))
+ MYSQL_YYABORT;
+ Lex->last_field->vcol_info= v;
+ }
+ ;
+
+parenthesized_expr:
+ subselect
+ {
+ $$= new (thd->mem_root) Item_singlerow_subselect(thd, $1);
+ if (unlikely($$ == NULL))
+ MYSQL_YYABORT;
+ }
+ | 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;
+ }
+ ;
+
+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_long_blob);
+ }
+ ;
+
+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->select_lex.init_order();
+ 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;
+ DBUG_ASSERT(!Lex->m_sql_cmd);
+ }
+ alter_options TABLE_SYM table_ident opt_lock_wait_timeout
+ {
+ if (unlikely(!Lex->select_lex.add_table_to_list(thd, $5, NULL,
+ TL_OPTION_UPDATING,
+ TL_READ_NO_INSERT,
+ MDL_SHARED_UPGRADABLE)))
+ MYSQL_YYABORT;
+ Lex->select_lex.db= (Lex->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;
+ }
+ }
+ | ALTER DATABASE ident_or_empty
+ {
+ Lex->create_info.default_table_charset= NULL;
+ Lex->create_info.used_fields= 0;
+ }
+ 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;
+ }
+ | 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"));
+ lex->sp_chistics.init();
+ }
+ sp_a_chistics
+ {
+ LEX *lex=Lex;
+
+ lex->sql_command= SQLCOM_ALTER_PROCEDURE;
+ lex->spname= $3;
+ }
+ | ALTER FUNCTION_SYM sp_name
+ {
+ LEX *lex= Lex;
+
+ if (unlikely(lex->sphead))
+ my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "FUNCTION"));
+ lex->sp_chistics.init();
+ }
+ sp_a_chistics
+ {
+ LEX *lex=Lex;
+
+ lex->sql_command= SQLCOM_ALTER_FUNCTION;
+ lex->spname= $3;
+ }
+ | ALTER view_algorithm definer_opt opt_view_suid VIEW_SYM table_ident
+ {
+ if (unlikely(Lex->add_alter_view(thd, $2, $4, $6)))
+ MYSQL_YYABORT;
+ }
+ view_list_opt AS view_select
+ {}
+ | 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 (unlikely(Lex->add_alter_view(thd, VIEW_ALGORITHM_INHERIT, $3, $5)))
+ MYSQL_YYABORT;
+ }
+ view_list_opt AS view_select
+ {}
+ | ALTER definer_opt remember_name EVENT_SYM sp_name
+ {
+ /*
+ 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();
+ }
+ | 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
+ {
+ 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);
+ }
+ table_ident
+ {
+ LEX *lex= Lex;
+ if (unlikely(!(lex->create_info.seq_create_info=
+ new (thd->mem_root) sequence_definition())) ||
+ unlikely(!lex->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;
+ }
+ ;
+
+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
+ {
+ LEX *lex= thd->lex;
+ lex->select_lex.db= $6->db;
+ if (lex->select_lex.db.str == NULL &&
+ unlikely(lex->copy_db_to(&lex->select_lex.db)))
+ MYSQL_YYABORT;
+ lex->name= $6->table;
+ lex->alter_info.partition_flags|= ALTER_PARTITION_EXCHANGE;
+ if (unlikely(!lex->select_lex.add_table_to_list(thd, $6, NULL,
+ TL_OPTION_UPDATING,
+ TL_READ_NO_INSERT,
+ MDL_SHARED_NO_WRITE)))
+ MYSQL_YYABORT;
+ DBUG_ASSERT(!lex->m_sql_cmd);
+ lex->m_sql_cmd= new (thd->mem_root)
+ Sql_cmd_alter_table_exchange_partition();
+ if (unlikely(lex->m_sql_cmd == NULL))
+ 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_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 (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->select_lex.db= $3->db;
+ if (lex->select_lex.db.str == NULL &&
+ unlikely(lex->copy_db_to(&lex->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_OPTIONS;
+ }
+ | 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;
+ }
+ ;
+
+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;
+ }
+ table_to_table_list
+ {}
+ | 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();
+ }
+ preload_list_or_parts
+ {}
+ ;
+
+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->select_lex.alloc_index_hints(thd);
+ Select->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:
+ opt_with_clause select_init
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_SELECT;
+ lex->current_select->set_with_clause($1);
+ }
+ ;
+
+select_init:
+ SELECT_SYM select_options_and_item_list select_init3
+ | table_value_constructor
+ | table_value_constructor union_list
+ | table_value_constructor union_order_or_limit
+ | '(' select_paren ')'
+ | '(' select_paren ')' union_list
+ | '(' select_paren ')' union_order_or_limit
+ ;
+
+union_list_part2:
+ SELECT_SYM select_options_and_item_list select_init3_union_query_term
+ | table_value_constructor
+ | table_value_constructor union_list
+ | table_value_constructor union_order_or_limit
+ | '(' select_paren_union_query_term ')'
+ | '(' select_paren_union_query_term ')' union_list
+ | '(' select_paren_union_query_term ')' union_order_or_limit
+ ;
+
+select_paren:
+ {
+ Lex->current_select->set_braces(true);
+ }
+ table_value_constructor select_part3
+ {
+ DBUG_ASSERT(Lex->current_select->braces);
+ }
+ |
+ {
+ /*
+ In order to correctly parse UNION's global ORDER BY we need to
+ set braces before parsing the clause.
+ */
+ Lex->current_select->set_braces(true);
+ }
+ SELECT_SYM select_options_and_item_list select_part3
+ opt_select_lock_type
+ {
+ DBUG_ASSERT(Lex->current_select->braces);
+ }
+ | '(' select_paren ')'
+ ;
+
+select_parent_union_query_term_proper:
+ SELECT_SYM select_options_and_item_list select_part3_union_query_term
+ opt_select_lock_type
+ | table_value_constructor select_part3_union_query_term
+ ;
+
+select_paren_union_query_term:
+ {
+ /*
+ In order to correctly parse UNION's global ORDER BY we need to
+ set braces before parsing the clause.
+ */
+ Lex->current_select->set_braces(true);
+ }
+ select_parent_union_query_term_proper
+ {
+ DBUG_ASSERT(Lex->current_select->braces);
+ }
+ | '(' select_paren_union_query_term ')'
+ ;
+
+select_parent_view_proper:
+ SELECT_SYM select_options_and_item_list select_part3_view
+ opt_select_lock_type
+ | table_value_constructor select_part3_view
+ ;
+
+select_paren_view:
+ {
+ /*
+ In order to correctly parse UNION's global ORDER BY we need to
+ set braces before parsing the clause.
+ */
+ Lex->current_select->set_braces(true);
+ }
+ select_parent_view_proper
+ {
+ DBUG_ASSERT(Lex->current_select->braces);
+ }
+ | '(' select_paren_view ')'
+ ;
+
+/* The equivalent of select_paren for nested queries. */
+select_paren_derived:
+ {
+ Lex->current_select->set_braces(true);
+ }
+ table_value_constructor
+ {
+ DBUG_ASSERT(Lex->current_select->braces);
+ $$= Lex->current_select->master_unit()->first_select();
+ }
+ |
+ {
+ Lex->current_select->set_braces(true);
+ }
+ SELECT_SYM select_part2_derived
+ opt_table_expression
+ opt_order_clause
+ opt_limit_clause
+ opt_select_lock_type
+ {
+ DBUG_ASSERT(Lex->current_select->braces);
+ $$= Lex->current_select->master_unit()->first_select();
+ }
+ | '(' select_paren_derived ')' { $$= $2; }
+ ;
+
+select_init3:
+ opt_table_expression
+ opt_select_lock_type
+ {
+ /* Parentheses carry no meaning here */
+ Lex->current_select->set_braces(false);
+ }
+ union_clause
+ | select_part3_union_not_ready
+ opt_select_lock_type
+ {
+ /* Parentheses carry no meaning here */
+ Lex->current_select->set_braces(false);
+ }
+ ;
+
+
+select_init3_union_query_term:
+ opt_table_expression
+ opt_select_lock_type
+ {
+ /* Parentheses carry no meaning here */
+ Lex->current_select->set_braces(false);
+ }
+ union_clause
+ | select_part3_union_not_ready_noproc
+ opt_select_lock_type
+ {
+ /* Parentheses carry no meaning here */
+ Lex->current_select->set_braces(false);
+ }
+ ;
+
+
+select_init3_view:
+ opt_table_expression opt_select_lock_type
+ {
+ Lex->current_select->set_braces(false);
+ }
+ | opt_table_expression opt_select_lock_type
+ {
+ Lex->current_select->set_braces(false);
+ }
+ union_list_view
+ | order_or_limit opt_select_lock_type
+ {
+ Lex->current_select->set_braces(false);
+ }
+ | table_expression order_or_limit opt_select_lock_type
+ {
+ Lex->current_select->set_braces(false);
+ }
+ ;
+
+/*
+ The SELECT parts after select_item_list that cannot be followed by UNION.
+*/
+
+select_part3:
+ opt_table_expression
+ | select_part3_union_not_ready
+ ;
+
+select_part3_union_query_term:
+ opt_table_expression
+ | select_part3_union_not_ready_noproc
+ ;
+
+select_part3_view:
+ opt_table_expression
+ | order_or_limit
+ | table_expression order_or_limit
+ ;
+
+select_part3_union_not_ready:
+ select_part3_union_not_ready_noproc
+ | table_expression procedure_clause
+ | table_expression order_or_limit procedure_clause
+ ;
+
+select_part3_union_not_ready_noproc:
+ order_or_limit
+ | into opt_table_expression opt_order_clause opt_limit_clause
+ | table_expression into
+ | table_expression order_or_limit
+ | table_expression order_or_limit into
+ ;
+
+select_options_and_item_list:
+ {
+ LEX *lex= Lex;
+ SELECT_LEX *sel= lex->current_select;
+ if (sel->linkage != UNION_TYPE)
+ mysql_init_select(lex);
+ lex->current_select->parsing_place= SELECT_LIST;
+ }
+ select_options select_item_list
+ {
+ Select->parsing_place= NO_MATTER;
+ }
+ ;
+
+
+/**
+ <table expression>, as in the SQL standard.
+*/
+table_expression:
+ from_clause
+ opt_where_clause
+ opt_group_clause
+ opt_having_clause
+ opt_window_clause
+ ;
+
+opt_table_expression:
+ /* Empty */
+ | table_expression
+ ;
+
+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= create_temporal_literal(thd, $2.str, $2.length, YYCSCL,
+ MYSQL_TYPE_DATETIME, 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);
+ }
+ ;
+
+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 only on the first top-level SELECT statement, if
+ SQL_CACHE wasn't specified, and only once per query.
+ */
+ if (unlikely(Lex->current_select != &Lex->select_lex))
+ my_yyabort_error((ER_CANT_USE_OPTION_HERE, MYF(0), "SQL_NO_CACHE"));
+ if (unlikely(Lex->select_lex.sql_cache == SELECT_LEX::SQL_CACHE))
+ my_yyabort_error((ER_WRONG_USAGE, MYF(0), "SQL_CACHE", "SQL_NO_CACHE"));
+ if (unlikely(Lex->select_lex.sql_cache == SELECT_LEX::SQL_NO_CACHE))
+ my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "SQL_NO_CACHE"));
+
+ Lex->safe_to_cache_query=0;
+ Lex->select_lex.options&= ~OPTION_TO_QUERY_CACHE;
+ Lex->select_lex.sql_cache= SELECT_LEX::SQL_NO_CACHE;
+ }
+ | SQL_CACHE_SYM
+ {
+ /*
+ Allow this flag only on the first top-level SELECT statement, if
+ SQL_NO_CACHE wasn't specified, and only once per query.
+ */
+ if (unlikely(Lex->current_select != &Lex->select_lex))
+ my_yyabort_error((ER_CANT_USE_OPTION_HERE, MYF(0), "SQL_CACHE"));
+ if (unlikely(Lex->select_lex.sql_cache == SELECT_LEX::SQL_NO_CACHE))
+ my_yyabort_error((ER_WRONG_USAGE, MYF(0), "SQL_NO_CACHE", "SQL_CACHE"));
+ if (unlikely(Lex->select_lex.sql_cache == SELECT_LEX::SQL_CACHE))
+ my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "SQL_CACHE"));
+
+ Lex->safe_to_cache_query=1;
+ Lex->select_lex.options|= OPTION_TO_QUERY_CACHE;
+ Lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE;
+ }
+ ;
+
+opt_select_lock_type:
+ /* empty */
+ | FOR_SYM UPDATE_SYM opt_lock_wait_timeout
+ {
+ LEX *lex=Lex;
+ lex->current_select->lock_type= TL_WRITE;
+ lex->current_select->set_lock_for_tables(TL_WRITE, false);
+ lex->safe_to_cache_query=0;
+ }
+ | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM opt_lock_wait_timeout
+ {
+ LEX *lex=Lex;
+ lex->current_select->lock_type= TL_READ_WITH_SHARED_LOCKS;
+ lex->current_select->
+ set_lock_for_tables(TL_READ_WITH_SHARED_LOCKS, false);
+ lex->safe_to_cache_query=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_tok_end:
+ {
+ $$= (char*) YYLIP->get_tok_end();
+ }
+ ;
+
+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;
+ }
+ | bool_pri IS TRUE_SYM %prec IS
+ {
+ $$= new (thd->mem_root) Item_func_istrue(thd, $1);
+ if (unlikely($$ == NULL))
+ MYSQL_YYABORT;
+ }
+ | bool_pri IS not TRUE_SYM %prec IS
+ {
+ $$= new (thd->mem_root) Item_func_isnottrue(thd, $1);
+ if (unlikely($$ == NULL))
+ MYSQL_YYABORT;
+ }
+ | bool_pri IS FALSE_SYM %prec IS
+ {
+ $$= new (thd->mem_root) Item_func_isfalse(thd, $1);
+ if (unlikely($$ == NULL))
+ MYSQL_YYABORT;
+ }
+ | bool_pri IS not FALSE_SYM %prec IS
+ {
+ $$= new (thd->mem_root) Item_func_isnotfalse(thd, $1);
+ if (unlikely($$ == NULL))
+ MYSQL_YYABORT;
+ }
+ | bool_pri IS UNKNOWN_SYM %prec IS
+ {
+ $$= new (thd->mem_root) Item_func_isnull(thd, $1);
+ if (unlikely($$ == NULL))
+ MYSQL_YYABORT;
+ }
+ | bool_pri IS not UNKNOWN_SYM %prec IS
+ {
+ $$= new (thd->mem_root) Item_func_isnotnull(thd, $1);
+ if (unlikely($$ == NULL))
+ MYSQL_YYABORT;
+ }
+ | bool_pri %prec PREC_BELOW_NOT
+ ;
+
+bool_pri:
+ bool_pri IS NULL_SYM %prec IS
+ {
+ $$= new (thd->mem_root) Item_func_isnull(thd, $1);
+ if (unlikely($$ == NULL))
+ MYSQL_YYABORT;
+ }
+ | bool_pri IS not NULL_SYM %prec IS
+ {
+ $$= new (thd->mem_root) Item_func_isnotnull(thd, $1);
+ if (unlikely($$ == NULL))
+ MYSQL_YYABORT;
+ }
+ | 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:
+ bit_expr IN_SYM '(' subselect ')'
+ {
+ $$= new (thd->mem_root) Item_in_subselect(thd, $1, $4);
+ if (unlikely($$ == NULL))
+ MYSQL_YYABORT;
+ }
+ | bit_expr not IN_SYM '(' subselect ')'
+ {
+ Item *item= new (thd->mem_root) Item_in_subselect(thd, $1, $5);
+ if (unlikely(item == NULL))
+ MYSQL_YYABORT;
+ $$= negate_expression(thd, item);
+ if (unlikely($$ == NULL))
+ MYSQL_YYABORT;
+ }
+ | bit_expr IN_SYM '(' expr ')'
+ {
+ $$= handle_sql2003_note184_exception(thd, $1, true, $4);
+ if (unlikely($$ == NULL))
+ MYSQL_YYABORT;
+ }
+ | bit_expr 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;
+ }
+ | bit_expr not IN_SYM '(' expr ')'
+ {
+ $$= handle_sql2003_note184_exception(thd, $1, false, $5);
+ if (unlikely($$ == NULL))
+ MYSQL_YYABORT;
+ }
+ | bit_expr 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);
+ }
+ | bit_expr BETWEEN_SYM bit_expr AND_SYM predicate
+ {
+ $$= new (thd->mem_root) Item_func_between(thd, $1, $3, $5);
+ if (unlikely($$ == NULL))
+ MYSQL_YYABORT;
+ }
+ | bit_expr not BETWEEN_SYM bit_expr AND_SYM predicate
+ {
+ 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);
+ }
+ | bit_expr SOUNDS_SYM LIKE bit_expr
+ {
+ 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;
+ }
+ | bit_expr LIKE bit_expr opt_escape
+ {
+ $$= new (thd->mem_root) Item_func_like(thd, $1, $3, $4,
+ Lex->escape_used);
+ if (unlikely($$ == NULL))
+ MYSQL_YYABORT;
+ }
+ | bit_expr not LIKE bit_expr opt_escape
+ {
+ Item *item= new (thd->mem_root) Item_func_like(thd, $1, $4, $5,
+ Lex->escape_used);
+ if (unlikely(item == NULL))
+ MYSQL_YYABORT;
+ $$= item->neg_transformer(thd);
+ }
+ | bit_expr REGEXP bit_expr
+ {
+ $$= new (thd->mem_root) Item_func_regex(thd, $1, $3);
+ if (unlikely($$ == NULL))
+ MYSQL_YYABORT;
+ }
+ | bit_expr not REGEXP bit_expr
+ {
+ 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
+ | window_func_expr
+ | 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; }
+ ;
+
+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_add_time(thd, $3, $5, 1, 0);
+ 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();
+ }
+ ;
+
+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); }
+ ;
+
+opt_expr_list:
+ /* empty */ { $$= NULL; }
+ | expr_list { $$= $1;}
+ ;
+
+expr_list:
+ expr
+ {
+ $$= new (thd->mem_root) List<Item>;
+ if (unlikely($$ == NULL) ||
+ unlikely($$->push_back($1, thd->mem_root)))
+ 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; }
+ | derived_table_list ',' esc_table_ref
+ {
+ MYSQL_YYABORT_UNLESS($1 && ($$=$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 */
+ /*
+ Use %prec to evaluate production 'table_ref' before 'normal_join'
+ so that [INNER | CROSS] JOIN is properly nested as other
+ left-associative joins.
+ */
+ 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);
+ /* 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);
+ }
+ '(' 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));
+ $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);
+ /* 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);
+ }
+ 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);
+ 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);
+ /* 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);
+ }
+ 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);
+ 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;
+ }
+ ;
+
+/*
+ This is a flattening of the rules <table factor> and <table primary>
+ in the SQL:2003 standard, since we don't have <sample clause>
+
+ I.e.
+ <table factor> ::= <table primary> [ <sample clause> ]
+*/
+/* Warning - may return NULL in case of incomplete SELECT */
+table_factor:
+ table_primary_ident
+ | table_primary_derived
+ ;
+
+table_primary_ident:
+ {
+ DBUG_ASSERT(Select);
+ SELECT_LEX *sel= Select;
+ sel->table_join_options= 0;
+ }
+ table_ident opt_use_partition opt_for_system_time_clause opt_table_alias opt_key_definition
+ {
+ if (unlikely(!($$= Select->add_table_to_list(thd, $2, $5,
+ Select->get_table_join_options(),
+ YYPS->m_lock_type,
+ YYPS->m_mdl_type,
+ Select->
+ pop_index_hints(),
+ $3))))
+ MYSQL_YYABORT;
+ Select->add_joined_table($$);
+ if ($4)
+ $$->vers_conditions= Lex->vers_conditions;
+ }
+ ;
+
+
+
+/*
+ Represents a flattening of the following rules from the SQL:2003
+ standard. This sub-rule corresponds to the sub-rule
+ <table primary> ::= ... | <derived table> [ AS ] <correlation name>
+
+ <derived table> ::= <table subquery>
+ <table subquery> ::= <subquery>
+ <subquery> ::= <left paren> <query expression> <right paren>
+ <query expression> ::= [ <with clause> ] <query expression body>
+
+ For the time being we use the non-standard rule
+ select_derived_union which is a compromise between the standard
+ and our parser. Possibly this rule could be replaced by our
+ query_expression_body.
+*/
+
+table_primary_derived:
+ '(' get_select_lex select_derived_union ')' opt_for_system_time_clause opt_table_alias
+ {
+ /* Use $2 instead of Lex->current_select as derived table will
+ alter value of Lex->current_select. */
+ if (!($3 || $6) && $2->embedding &&
+ !$2->embedding->nested_join->join_list.elements)
+ {
+ /* we have a derived table ($3 == NULL) but no alias,
+ Since we are nested in further parentheses so we
+ can pass NULL to the outer level parentheses
+ Permits parsing of "((((select ...))) as xyz)" */
+ $$= 0;
+ }
+ else if (!$3)
+ {
+ /* Handle case of derived table, alias may be NULL if there
+ are no outer parentheses, add_table_to_list() will throw
+ error in this case */
+ LEX *lex=Lex;
+ lex->check_automatic_up(UNSPECIFIED_TYPE);
+ SELECT_LEX *sel= lex->current_select;
+ SELECT_LEX_UNIT *unit= sel->master_unit();
+ lex->current_select= sel= unit->outer_select();
+ Table_ident *ti= new (thd->mem_root) Table_ident(unit);
+ if (unlikely(ti == NULL))
+ MYSQL_YYABORT;
+ if (unlikely(!($$= sel->add_table_to_list(thd,
+ ti, $6, 0,
+ TL_READ,
+ MDL_SHARED_READ))))
+ MYSQL_YYABORT;
+ sel->add_joined_table($$);
+ lex->pop_context();
+ lex->nest_level--;
+ }
+ else if (unlikely($6 != NULL))
+ {
+ /*
+ Tables with or without joins within parentheses cannot
+ have aliases, and we ruled out derived tables above.
+ */
+ thd->parse_error();
+ MYSQL_YYABORT;
+ }
+ else
+ {
+ /* nested join: FROM (t1 JOIN t2 ...),
+ nest_level is the same as in the outer query */
+ $$= $3;
+ }
+ /*
+ Fields in derived table can be used in upper select in
+ case of merge. We do not add HAVING fields because we do
+ not merge such derived. We do not add union because
+ also do not merge them
+ */
+ if ($$ && $$->derived &&
+ !$$->derived->first_select()->next_select())
+ $$->select_lex->add_where_field($$->derived->first_select());
+ if ($5)
+ {
+ MYSQL_YYABORT_UNLESS(!$3);
+ $$->vers_conditions= Lex->vers_conditions;
+ }
+ }
+ /* Represents derived table with WITH clause */
+ | '(' get_select_lex subselect_start
+ with_clause query_expression_body
+ subselect_end ')' opt_for_system_time_clause opt_table_alias
+ {
+ LEX *lex=Lex;
+ SELECT_LEX *sel= $2;
+ SELECT_LEX_UNIT *unit= $5->master_unit();
+ Table_ident *ti= new (thd->mem_root) Table_ident(unit);
+ if (unlikely(ti == NULL))
+ MYSQL_YYABORT;
+ $5->set_with_clause($4);
+ lex->current_select= sel;
+ if (unlikely(!($$= sel->add_table_to_list(lex->thd,
+ ti, $9, 0,
+ TL_READ,
+ MDL_SHARED_READ))))
+ MYSQL_YYABORT;
+ sel->add_joined_table($$);
+ if ($8)
+ $$->vers_conditions= Lex->vers_conditions;
+ }
+ ;
+
+/*
+ This rule accepts just about anything. The reason is that we have
+ empty-producing rules in the beginning of rules, in this case
+ subselect_start. This forces bison to take a decision which rules to
+ reduce by long before it has seen any tokens. This approach ties us
+ to a very limited class of parseable languages, and unfortunately
+ SQL is not one of them. The chosen 'solution' was this rule, which
+ produces just about anything, even complete bogus statements, for
+ instance ( table UNION SELECT 1 ).
+ Fortunately, we know that the semantic value returned by
+ select_derived is NULL if it contained a derived table, and a pointer to
+ the base table's TABLE_LIST if it was a base table. So in the rule
+ regarding union's, we throw a parse error manually and pretend it
+ was bison that did it.
+
+ Also worth noting is that this rule concerns query expressions in
+ the from clause only. Top level select statements and other types of
+ subqueries have their own union rules.
+*/
+select_derived_union:
+ select_derived
+ | select_derived union_order_or_limit
+ {
+ if (unlikely($1))
+ {
+ thd->parse_error();
+ MYSQL_YYABORT;
+ }
+ }
+ | select_derived union_head_non_top
+ {
+ if (unlikely($1))
+ {
+ thd->parse_error();
+ MYSQL_YYABORT;
+ }
+ }
+ union_list_derived_part2
+ | derived_simple_table opt_select_lock_type
+ | derived_simple_table order_or_limit opt_select_lock_type
+ | derived_simple_table opt_select_lock_type union_list_derived
+ ;
+
+union_list_derived_part2:
+ query_term_union_not_ready { Lex->pop_context(); }
+ | query_term_union_ready { Lex->pop_context(); }
+ | query_term_union_ready { Lex->pop_context(); } union_list_derived
+ ;
+
+union_list_derived:
+ union_head_non_top union_list_derived_part2
+ ;
+
+
+/* The equivalent of select_init2 for nested queries. */
+select_init2_derived:
+ select_part2_derived
+ {
+ Select->set_braces(0);
+ }
+ ;
+
+/* The equivalent of select_part2 for nested queries. */
+select_part2_derived:
+ {
+ LEX *lex= Lex;
+ SELECT_LEX *sel= lex->current_select;
+ if (sel->linkage != UNION_TYPE)
+ mysql_init_select(lex);
+ lex->current_select->parsing_place= SELECT_LIST;
+ }
+ opt_query_expression_options select_item_list
+ {
+ Select->parsing_place= NO_MATTER;
+ }
+ ;
+
+/* handle contents of parentheses in join expression */
+select_derived:
+ get_select_lex_derived derived_table_list
+ {
+ LEX *lex= Lex;
+ /* for normal joins, $2 != NULL and end_nested_join() != NULL,
+ for derived tables, both must equal NULL */
+
+ if (unlikely(!($$= $1->end_nested_join(lex->thd)) && $2))
+ MYSQL_YYABORT;
+ if (unlikely(!$2 && $$))
+ {
+ thd->parse_error();
+ MYSQL_YYABORT;
+ }
+ }
+ ;
+
+derived_simple_table:
+ derived_query_specification { $$= $1; }
+ | derived_table_value_constructor { $$= $1; }
+ ;
+/*
+ Similar to query_specification, but for derived tables.
+ Example: the inner parenthesized SELECT in this query:
+ SELECT * FROM (SELECT * FROM t1);
+*/
+derived_query_specification:
+ SELECT_SYM select_derived_init select_derived2
+ {
+ if ($2)
+ Select->set_braces(1);
+ $$= NULL;
+ }
+ ;
+
+derived_table_value_constructor:
+ VALUES
+ {
+ Lex->tvc_start();
+ }
+ values_list
+ {
+ if (Lex->tvc_finalize_derived())
+ MYSQL_YYABORT;
+ $$= NULL;
+ }
+ ;
+
+
+select_derived2:
+ {
+ LEX *lex= Lex;
+ lex->derived_tables|= DERIVED_SUBQUERY;
+ if (unlikely(!lex->expr_allows_subselect ||
+ lex->sql_command == (int)SQLCOM_PURGE))
+ {
+ thd->parse_error();
+ MYSQL_YYABORT;
+ }
+ if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE ||
+ unlikely(mysql_new_select(lex, 1, NULL)))
+ MYSQL_YYABORT;
+ mysql_init_select(lex);
+ lex->current_select->linkage= DERIVED_TABLE_TYPE;
+ lex->current_select->parsing_place= SELECT_LIST;
+ }
+ select_options select_item_list
+ {
+ Select->parsing_place= NO_MATTER;
+ }
+ opt_table_expression
+ ;
+
+get_select_lex:
+ /* Empty */ { $$= Select; }
+ ;
+
+get_select_lex_derived:
+ get_select_lex
+ {
+ LEX *lex= Lex;
+ if (unlikely($1->init_nested_join(lex->thd)))
+ MYSQL_YYABORT;
+ }
+ ;
+
+select_derived_init:
+ {
+ LEX *lex= Lex;
+
+ TABLE_LIST *embedding= lex->current_select->embedding;
+ $$= embedding &&
+ !embedding->nested_join->join_list.elements;
+ /* return true if we are deeply nested */
+ }
+ ;
+
+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:
+ /* empty */ { $$=0; }
+ | 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();
+ }
+ ;
+
+opt_escape:
+ ESCAPE_SYM simple_expr
+ {
+ Lex->escape_used= TRUE;
+ $$= $2;
+ }
+ | /* empty */ %prec PREC_BELOW_ESCAPE
+ {
+ Lex->escape_used= FALSE;
+ $$= ((thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES) ?
+ new (thd->mem_root) Item_string_ascii(thd, "", 0) :
+ new (thd->mem_root) Item_string_ascii(thd, "\\", 1));
+ if (unlikely($$ == NULL))
+ MYSQL_YYABORT;
+ }
+ ;
+
+/*
+ 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->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->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
+ ;
+
+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 */
+ | order_clause
+ ;
+
+order_clause:
+ ORDER_SYM BY
+ {
+ LEX *lex=Lex;
+ SELECT_LEX *sel= lex->current_select;
+ SELECT_LEX_UNIT *unit= sel-> master_unit();
+ if (unlikely(sel->linkage != GLOBAL_OPTIONS_TYPE &&
+ sel->olap != UNSPECIFIED_OLAP_TYPE &&
+ (sel->linkage != UNION_TYPE || sel->braces)))
+ {
+ my_error(ER_WRONG_USAGE, MYF(0),
+ "CUBE/ROLLUP", "ORDER BY");
+ MYSQL_YYABORT;
+ }
+ if (lex->sql_command != SQLCOM_ALTER_TABLE &&
+ !unit->fake_select_lex)
+ {
+ /*
+ A query of the of the form (SELECT ...) ORDER BY order_list is
+ executed in the same way as the query
+ SELECT ... ORDER BY order_list
+ unless the SELECT construct contains ORDER BY or LIMIT clauses.
+ Otherwise we create a fake SELECT_LEX if it has not been
+ created yet.
+ */
+ SELECT_LEX *first_sl= unit->first_select();
+ if (unlikely(!unit->is_unit_op() &&
+ (first_sl->order_list.elements ||
+ first_sl->select_limit) &&
+ unit->add_fake_select_lex(thd)))
+ MYSQL_YYABORT;
+ }
+ if (sel->master_unit()->is_unit_op() && !sel->braces)
+ {
+ /*
+ At this point we don't know yet whether this is the last
+ select in union or not, but we move ORDER BY to
+ fake_select_lex anyway. If there would be one more select
+ in union mysql_new_select will correctly throw error.
+ */
+ DBUG_ASSERT(sel->master_unit()->fake_select_lex);
+ lex->current_select= sel->master_unit()->fake_select_lex;
+ }
+ }
+ order_list
+ {
+
+ }
+ ;
+
+order_list:
+ order_list ',' order_ident order_dir
+ {
+ if (unlikely(add_order_to_list(thd, $3,(bool) $4)))
+ MYSQL_YYABORT;
+ }
+ | order_ident order_dir
+ {
+ if (unlikely(add_order_to_list(thd, $1,(bool) $2)))
+ MYSQL_YYABORT;
+ }
+ ;
+
+order_dir:
+ /* empty */ { $$ = 1; }
+ | ASC { $$ =1; }
+ | DESC { $$ =0; }
+ ;
+
+opt_limit_clause:
+ /* empty */ {}
+ | limit_clause {}
+ ;
+
+limit_clause_init:
+ LIMIT
+ {
+ SELECT_LEX *sel= Select;
+ if (sel->master_unit()->is_unit_op() && !sel->braces)
+ {
+ /* Move LIMIT that belongs to UNION to fake_select_lex */
+ Lex->current_select= sel->master_unit()->fake_select_lex;
+ DBUG_ASSERT(Select);
+ }
+ }
+ ;
+
+limit_clause:
+ limit_clause_init limit_options
+ {
+ SELECT_LEX *sel= Select;
+ if (!sel->select_limit->basic_const_item() ||
+ sel->select_limit->val_int() > 0)
+ Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
+ }
+ | limit_clause_init limit_options
+ ROWS_SYM EXAMINED_SYM limit_rows_option
+ {
+ Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
+ }
+ | limit_clause_init ROWS_SYM EXAMINED_SYM limit_rows_option
+ {
+ Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
+ }
+ ;
+
+limit_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;
+ }
+ ;
+
+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; }
+ ;
+
+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;
+
+ DBUG_ASSERT(&lex->select_lex == lex->current_select);
+
+ 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 set expr_allows_subselect as false to disallow any subqueries
+ further. Reset expr_allows_subselect back to true once the
+ parameters are reduced.
+ */
+ Lex->expr_allows_subselect= false;
+ }
+ '(' procedure_list ')'
+ {
+ /* Subqueries are allowed from now.*/
+ Lex->expr_allows_subselect= true;
+ }
+ ;
+
+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;
+ mysql_init_select(lex);
+ }
+ expr_list
+ {
+ Lex->insert_list= $3;
+ }
+ ;
+
+/*
+ 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 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, $4.str, $3));
+ 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, $6, NULL, TL_OPTION_UPDATING,
+ TL_READ_NO_INSERT,
+ MDL_SHARED_UPGRADABLE)))
+ MYSQL_YYABORT;
+ }
+ | 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 (unlikely(!Select->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;
+ mysql_init_select(lex);
+ }
+ insert_lock_option
+ opt_ignore insert2
+ {
+ Select->set_lock_for_tables($3, true);
+ Lex->current_select= &Lex->select_lex;
+ }
+ insert_field_spec opt_insert_update
+ {
+ Lex->mark_first_table_as_inserting();
+ }
+ ;
+
+replace:
+ REPLACE
+ {
+ LEX *lex=Lex;
+ lex->sql_command = SQLCOM_REPLACE;
+ lex->duplicates= DUP_REPLACE;
+ mysql_init_select(lex);
+ }
+ replace_lock_option insert2
+ {
+ Select->set_lock_for_tables($3, true);
+ Lex->current_select= &Lex->select_lex;
+ }
+ insert_field_spec
+ {
+ 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:
+ 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_values {}
+ | '(' fields ')' 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;
+ }
+ ident_eq_list
+ ;
+
+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:
+ VALUES values_list {}
+ | VALUE_SYM values_list {}
+ | 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 rows in a table */
+
+update:
+ UPDATE_SYM
+ {
+ LEX *lex= Lex;
+ mysql_init_select(lex);
+ lex->sql_command= SQLCOM_UPDATE;
+ lex->duplicates= DUP_ERROR;
+ }
+ opt_low_priority opt_ignore join_table_list
+ SET update_list
+ {
+ SELECT_LEX *slex= &Lex->select_lex;
+ if (slex->table_list.elements > 1)
+ Lex->sql_command= SQLCOM_UPDATE_MULTI;
+ else if (unlikely(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 {}
+ ;
+
+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;
+ mysql_init_select(lex);
+ YYPS->m_lock_type= TL_WRITE_DEFAULT;
+ YYPS->m_mdl_type= MDL_SHARED_WRITE;
+
+ lex->ignore= 0;
+ lex->select_lex.init_order();
+ }
+ 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;
+ }
+ ;
+
+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;
+ }
+ ;
+
+single_multi:
+ delete_single_table
+ opt_where_clause
+ opt_order_clause
+ delete_limit_clause
+ opt_select_expressions {}
+ | 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;
+ }
+ | 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;
+ }
+ ;
+
+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->select_lex.options= 0;
+ lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE_UNSPECIFIED;
+ lex->select_lex.init_order();
+ 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;
+ mysql_init_select(lex);
+ lex->current_select->parsing_place= SELECT_LIST;
+ lex->create_info.init();
+ }
+ show_param
+ {
+ Select->parsing_place= NO_MATTER;
+ }
+ ;
+
+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->select_lex.db= $3;
+ if (unlikely(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->select_lex.db= $3;
+ if (unlikely(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->select_lex.db= $2;
+ if (unlikely(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->select_lex.db= $3;
+ if (unlikely(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->select_lex.db= $3;
+ if (unlikely(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_limit_clause
+ | RELAYLOG_SYM optional_connection_name EVENTS_SYM binlog_in binlog_from
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_SHOW_RELAYLOG_EVENTS;
+ } opt_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_limit_clause
+ { Lex->sql_command = SQLCOM_SHOW_WARNS;}
+ | ERRORS opt_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_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 (unlikely(!lex->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 (unlikely(!lex->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 (unlikely(!lex->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;
+ mysql_init_select(lex);
+ lex->current_select->parsing_place= SELECT_LIST;
+ lex->sql_command= SQLCOM_SHOW_FIELDS;
+ lex->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;
+ }
+ | describe_command opt_extended_describe
+ { Lex->describe|= DESCRIBE_NORMAL; }
+ explainable_command
+ {
+ LEX *lex=Lex;
+ lex->select_lex.options|= SELECT_DESCRIBE;
+ }
+ ;
+
+explainable_command:
+ select
+ | 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; }
+ | 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; }
+ | 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 {}
+ ;
+
+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
+ {
+ LEX *lex=Lex;
+ lex->type=0;
+ lex->sql_command = SQLCOM_PURGE;
+ }
+ purge_options
+ {}
+ ;
+
+purge_options:
+ master_or_binary LOGS_SYM purge_option
+ ;
+
+purge_option:
+ TO_SYM TEXT_STRING_sys
+ {
+ Lex->to_log = $2.str;
+ }
+ | BEFORE_SYM expr
+ {
+ LEX *lex= Lex;
+ lex->value_list.empty();
+ lex->value_list.push_front($2, thd->mem_root);
+ lex->sql_command= SQLCOM_PURGE_BEFORE;
+ }
+ ;
+
+/* 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; }
+ ;
+
+/* change database */
+
+use:
+ USE_SYM ident
+ {
+ LEX *lex=Lex;
+ lex->sql_command=SQLCOM_CHANGE_DB;
+ lex->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;
+ }
+ }
+ 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->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;
+ /*
+ it is OK only emulate fix_fields, because we need only
+ value of constant
+ */
+ tmp->quick_fix_field();
+ $$= 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->quick_fix_field();
+ $$= 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->quick_fix_field();
+ $$= 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(!($$= create_temporal_literal(thd, $2.str, $2.length,
+ YYCSCL,
+ MYSQL_TYPE_DATE,
+ true))))
+ MYSQL_YYABORT;
+ }
+ | TIME_SYM TEXT_STRING
+ {
+ if (unlikely(!($$= create_temporal_literal(thd, $2.str, $2.length,
+ YYCSCL,
+ MYSQL_TYPE_TIME,
+ true))))
+ MYSQL_YYABORT;
+ }
+ | TIMESTAMP TEXT_STRING
+ {
+ if (unlikely(!($$= create_temporal_literal(thd, $2.str, $2.length,
+ YYCSCL,
+ MYSQL_TYPE_DATETIME,
+ true))))
+ MYSQL_YYABORT;
+ }
+ ;
+
+
+opt_with_clause:
+ /*empty */ { $$= 0; }
+ | with_clause
+ {
+ $$= $1;
+ }
+ ;
+
+
+with_clause:
+ WITH opt_recursive
+ {
+ 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);
+ }
+ 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 '(' remember_tok_start subselect remember_tok_end ')'
+ {
+ LEX *lex= thd->lex;
+ const char *query_start= lex->sphead ? lex->sphead->m_tmp_query
+ : thd->query();
+ char *spec_start= $6 + 1;
+ With_element *elem= new With_element($1, *$2, $7->master_unit());
+ if (unlikely(elem == NULL) ||
+ unlikely(Lex->curr_with_clause->add_with_element(elem)))
+ MYSQL_YYABORT;
+ if (elem->set_unparsed_spec(thd, spec_start, $8,
+ 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->alloc(sizeof(LEX_USER)))))
+ MYSQL_YYABORT;
+ $$->user = $1;
+ $$->host= null_clex_str; // User or Role, see get_current_user()
+ $$->reset_auth();
+
+ 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->alloc(sizeof(LEX_USER)))))
+ MYSQL_YYABORT;
+ $$->user = $1; $$->host=$3;
+ $$->reset_auth();
+
+ 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;
+ $$->plugin= empty_clex_str;
+ $$->auth= empty_clex_str;
+ }
+ ;
+
+user_or_role: user_maybe_role | current_role;
+
+user: user_maybe_role
+ {
+ if ($1->user.str != current_user.str && $1->host.str == 0)
+ $1->host= host_not_specified;
+ $$= $1;
+ }
+ ;
+
+/* 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
+ | 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
+ | 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
+ | 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
+ | 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;
+ lex->set_stmt_init();
+ lex->var_list.empty();
+ sp_create_assignment_lex(thd, yychar == YYEMPTY);
+ }
+ start_option_value_list
+ {}
+ | SET STATEMENT_SYM
+ {
+ 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();
+ }
+ FOR_SYM verb_clause
+ {}
+ ;
+
+set_assign:
+ ident_directly_assignable SET_VAR
+ {
+ LEX *lex=Lex;
+ lex->set_stmt_init();
+ lex->var_list.empty();
+ sp_create_assignment_lex(thd, yychar == YYEMPTY);
+ }
+ 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();
+ sp_create_assignment_lex(thd, yychar == YYEMPTY);
+ }
+ 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();
+ sp_create_assignment_lex(thd, yychar == YYEMPTY);
+ }
+ 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:
+ {
+ sp_create_assignment_lex(thd, yychar == YYEMPTY);
+ }
+ option_value
+ {
+ if (unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
+ MYSQL_YYABORT;
+ }
+ | option_value_list ','
+ {
+ sp_create_assignment_lex(thd, yychar == YYEMPTY);
+ }
+ 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->plugin= empty_clex_str;
+ lex->definer->auth= empty_clex_str;
+ }
+ | FOR_SYM user equal { Lex->definer= $2; }
+ ;
+
+text_or_password:
+ TEXT_STRING { Lex->definer->pwhash= $1;}
+ | PASSWORD_SYM '(' TEXT_STRING ')' { Lex->definer->pwtext= $3; }
+ | OLD_PASSWORD_SYM '(' TEXT_STRING ')'
+ {
+ Lex->definer->pwtext= $3;
+ Lex->definer->pwhash.str= Item_func_password::alloc(thd,
+ $3.str, $3.length, Item_func_password::OLD);
+ Lex->definer->pwhash.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 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(!Select->
+ 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 table_ident OPEN_SYM opt_table_alias
+ {
+ LEX *lex= Lex;
+ if (unlikely(lex->sphead))
+ my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "HANDLER"));
+ lex->sql_command = SQLCOM_HA_OPEN;
+ if (unlikely(!lex->current_select->add_table_to_list(thd, $2, $4,
+ 0)))
+ MYSQL_YYABORT;
+ }
+ | HANDLER_SYM 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 (unlikely(!lex->current_select->add_table_to_list(thd, $2, 0,
+ 0)))
+ MYSQL_YYABORT;
+ }
+ | HANDLER_SYM table_ident_nodb READ_SYM
+ {
+ LEX *lex=Lex;
+ if (unlikely(lex->sphead))
+ my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "HANDLER"));
+ lex->expr_allows_subselect= FALSE;
+ 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 (unlikely(!lex->current_select->add_table_to_list(thd, $2, 0,
+ 0)))
+ MYSQL_YYABORT;
+ }
+ handler_read_or_scan opt_where_clause opt_limit_clause
+ {
+ Lex->expr_allows_subselect= TRUE;
+ /* Stored functions are not supported for HANDLER READ. */
+ if (unlikely(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;
+ $$->reset_auth();
+ }
+ ;
+
+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;
+ $$->reset_auth();
+
+ 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 (unlikely(lex->x509_subject))
+ my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "SUBJECT"));
+ lex->x509_subject=$2.str;
+ }
+ | ISSUER_SYM TEXT_STRING
+ {
+ LEX *lex=Lex;
+ if (unlikely(lex->x509_issuer))
+ my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "ISSUER"));
+ lex->x509_issuer=$2.str;
+ }
+ | CIPHER_SYM TEXT_STRING
+ {
+ LEX *lex=Lex;
+ if (unlikely(lex->ssl_cipher))
+ my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "CIPHER"));
+ lex->ssl_cipher=$2.str;
+ }
+ ;
+
+grant_ident:
+ '*'
+ {
+ LEX *lex= Lex;
+ if (unlikely(lex->copy_db_to(&lex->current_select->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->current_select->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->current_select->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->current_select->
+ 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->pwtext= $4;
+ if (unlikely(Lex->sql_command == SQLCOM_REVOKE))
+ MYSQL_YYABORT;
+ }
+ | user IDENTIFIED_SYM BY PASSWORD_SYM TEXT_STRING
+ {
+ $$= $1;
+ $1->pwhash= $5;
+ }
+ | user IDENTIFIED_SYM via_or_with ident_or_text
+ {
+ $$= $1;
+ $1->plugin= $4;
+ $1->auth= empty_clex_str;
+ }
+ | user IDENTIFIED_SYM via_or_with ident_or_text using_or_as TEXT_STRING_sys
+ {
+ $$= $1;
+ $1->plugin= $4;
+ $1->auth= $6;
+ }
+ | user_or_role
+ { $$= $1; }
+ ;
+
+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->ssl_type=SSL_TYPE_SPECIFIED;
+ }
+ | REQUIRE_SYM SSL_SYM
+ {
+ Lex->ssl_type=SSL_TYPE_ANY;
+ }
+ | REQUIRE_SYM X509_SYM
+ {
+ Lex->ssl_type=SSL_TYPE_X509;
+ }
+ | REQUIRE_SYM NONE_SYM
+ {
+ Lex->ssl_type=SSL_TYPE_NONE;
+ }
+ ;
+
+resource_option:
+ MAX_QUERIES_PER_HOUR ulong_num
+ {
+ LEX *lex=Lex;
+ lex->mqh.questions=$2;
+ lex->mqh.specified_limits|= USER_RESOURCES::QUERIES_PER_HOUR;
+ }
+ | MAX_UPDATES_PER_HOUR ulong_num
+ {
+ LEX *lex=Lex;
+ lex->mqh.updates=$2;
+ lex->mqh.specified_limits|= USER_RESOURCES::UPDATES_PER_HOUR;
+ }
+ | MAX_CONNECTIONS_PER_HOUR ulong_num
+ {
+ LEX *lex=Lex;
+ lex->mqh.conn_per_hour= $2;
+ lex->mqh.specified_limits|= USER_RESOURCES::CONNECTIONS_PER_HOUR;
+ }
+ | MAX_USER_CONNECTIONS_SYM int_num
+ {
+ LEX *lex=Lex;
+ lex->mqh.user_conn= $2;
+ lex->mqh.specified_limits|= USER_RESOURCES::USER_CONNECTIONS;
+ }
+ | MAX_STATEMENT_TIME_SYM NUM_literal
+ {
+ LEX *lex=Lex;
+ lex->mqh.max_statement_time= $2->val_real();
+ lex->mqh.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;
+ Lex->sphead->set_stmt_end(thd);
+ Lex->sphead->restore_thd_mem_root(thd);
+ }
+ ;
+
+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_TYPE; }
+ | INTERSECT_SYM
+ { $$= INTERSECT_TYPE; }
+ | EXCEPT_SYM
+ { $$= EXCEPT_TYPE; }
+ ;
+
+union_clause:
+ /* empty */ {}
+ | union_list
+ ;
+
+union_list:
+ unit_type_decl union_option
+ {
+ if (unlikely(Lex->add_select_to_union_list((bool)$2, $1, TRUE)))
+ MYSQL_YYABORT;
+ }
+ union_list_part2
+ {
+ /*
+ Remove from the name resolution context stack the context of the
+ last select in the union.
+ */
+ Lex->pop_context();
+ }
+ ;
+
+union_list_view:
+ unit_type_decl union_option
+ {
+ if (unlikely(Lex->add_select_to_union_list((bool)$2, $1, TRUE)))
+ MYSQL_YYABORT;
+ }
+ query_expression_body_view
+ {
+ Lex->pop_context();
+ }
+ ;
+
+union_order_or_limit:
+ {
+ LEX *lex= thd->lex;
+ DBUG_ASSERT(lex->current_select->linkage != GLOBAL_OPTIONS_TYPE);
+ SELECT_LEX *sel= lex->current_select;
+ SELECT_LEX_UNIT *unit= sel->master_unit();
+ SELECT_LEX *fake= unit->fake_select_lex;
+ if (fake)
+ {
+ fake->no_table_names_allowed= 1;
+ lex->current_select= fake;
+ }
+ thd->where= "global ORDER clause";
+ }
+ order_or_limit
+ {
+ thd->lex->current_select->no_table_names_allowed= 0;
+ thd->where= "";
+ }
+ ;
+
+order_or_limit:
+ order_clause opt_limit_clause
+ | limit_clause
+ ;
+
+/*
+ Start a UNION, for non-top level query expressions.
+*/
+union_head_non_top:
+ unit_type_decl union_option
+ {
+ if (unlikely(Lex->add_select_to_union_list((bool)$2, $1, FALSE)))
+ MYSQL_YYABORT;
+ }
+ ;
+
+union_option:
+ /* empty */ { $$=1; }
+ | DISTINCT { $$=1; }
+ | ALL { $$=0; }
+ ;
+
+simple_table:
+ query_specification { $$= $1; }
+ | table_value_constructor { $$= $1; }
+ ;
+
+table_value_constructor:
+ VALUES
+ {
+ Lex->tvc_start();
+ }
+ values_list
+ {
+ $$= Lex->current_select;
+ if (Lex->tvc_finalize())
+ MYSQL_YYABORT;
+ }
+ ;
+
+/*
+ Corresponds to the SQL Standard
+ <query specification> ::=
+ SELECT [ <set quantifier> ] <select list> <table expression>
+
+ Notes:
+ - We allow more options in addition to <set quantifier>
+ - <table expression> is optional in MariaDB
+*/
+query_specification:
+ SELECT_SYM select_init2_derived opt_table_expression
+ {
+ $$= Lex->current_select->master_unit()->first_select();
+ }
+ ;
+
+query_term_union_not_ready:
+ simple_table order_or_limit opt_select_lock_type { $$= $1; }
+ | '(' select_paren_derived ')' union_order_or_limit { $$= $2; }
+ ;
+
+query_term_union_ready:
+ simple_table opt_select_lock_type { $$= $1; }
+ | '(' select_paren_derived ')' { $$= $2; }
+ ;
+
+query_expression_body:
+ query_term_union_not_ready { $$= $1; }
+ | query_term_union_ready { $$= $1; }
+ | query_term_union_ready union_list_derived { $$= $1; }
+ ;
+
+/* Corresponds to <query expression> in the SQL:2003 standard. */
+subselect:
+ subselect_start opt_with_clause query_expression_body subselect_end
+ {
+ $3->set_with_clause($2);
+ $$= $3;
+ }
+ ;
+
+subselect_start:
+ {
+ LEX *lex=Lex;
+ if (unlikely(!lex->expr_allows_subselect ||
+ lex->sql_command == (int)SQLCOM_PURGE))
+ {
+ thd->parse_error();
+ MYSQL_YYABORT;
+ }
+ /*
+ we are making a "derived table" for the parenthesis
+ as we need to have a lex level to fit the union
+ after the parenthesis, e.g.
+ (SELECT .. ) UNION ... becomes
+ SELECT * FROM ((SELECT ...) UNION ...)
+ */
+ if (unlikely(mysql_new_select(Lex, 1, NULL)))
+ MYSQL_YYABORT;
+ }
+ ;
+
+subselect_end:
+ {
+ LEX *lex=Lex;
+
+ lex->check_automatic_up(UNSPECIFIED_TYPE);
+ lex->pop_context();
+ SELECT_LEX *child= lex->current_select;
+ lex->current_select = lex->current_select->return_after_parsing();
+ lex->nest_level--;
+ lex->current_select->n_child_sum_items += child->n_sum_items;
+
+ /*
+ A subquery (and all the subsequent query blocks in a UNION) can
+ add columns to an outer query block. Reserve space for them.
+ Aggregate functions in having clause can also add fields to an
+ outer select.
+ */
+ for (SELECT_LEX *temp= child->master_unit()->first_select();
+ temp != NULL; temp= temp->next_select())
+ {
+ lex->current_select->select_n_where_fields+=
+ temp->select_n_where_fields;
+ lex->current_select->select_n_having_items+=
+ temp->select_n_having_items;
+ }
+ }
+ ;
+
+opt_query_expression_options:
+ /* empty */
+ | query_expression_option_list
+ ;
+
+query_expression_option_list:
+ query_expression_option_list query_expression_option
+ | query_expression_option
+ ;
+
+query_expression_option:
+ STRAIGHT_JOIN { Select->options|= SELECT_STRAIGHT_JOIN; }
+ | HIGH_PRIORITY
+ {
+ if (unlikely(Lex->check_simple_select(&$1)))
+ MYSQL_YYABORT;
+ 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
+ {
+ if (unlikely(Lex->check_simple_select(&$1)))
+ MYSQL_YYABORT;
+ Select->options|= OPTION_BUFFER_RESULT;
+ }
+ | SQL_CALC_FOUND_ROWS
+ {
+ if (unlikely(Lex->check_simple_select(&$1)))
+ MYSQL_YYABORT;
+ 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->ssl_type= SSL_TYPE_NOT_SPECIFIED;
+ Lex->ssl_cipher= Lex->x509_subject= Lex->x509_issuer= 0;
+ bzero(&(Lex->mqh), sizeof(Lex->mqh));
+ }
+ ;
+
+/**************************************************************************
+
+ 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();
+ }
+ opt_with_clause query_expression_body_view view_check_option
+ {
+ LEX *lex= Lex;
+ size_t len= YYLIP->get_cpp_ptr() - lex->create_view->select.str;
+ void *create_view_select= thd->memdup(lex->create_view->select.str, len);
+ lex->create_view->select.length= len;
+ lex->create_view->select.str= (char *) create_view_select;
+ trim_whitespace(thd->charset(),
+ &lex->create_view->select);
+ lex->create_view->check= $4;
+ lex->parsing_options.allows_variable= TRUE;
+ lex->current_select->set_with_clause($2);
+ }
+ ;
+
+/*
+ SQL Standard <query expression body> for VIEWs.
+ Does not include INTO and PROCEDURE clauses.
+*/
+query_expression_body_view:
+ SELECT_SYM select_options_and_item_list select_init3_view
+ | table_value_constructor
+ | table_value_constructor union_order_or_limit
+ | table_value_constructor union_list_view
+ | '(' select_paren_view ')'
+ | '(' select_paren_view ')' union_order_or_limit
+ | '(' select_paren_view ')' union_list_view
+ ;
+
+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)))
+ MYSQL_YYABORT;
+
+ lex->sphead->set_body_start(thd, lip->get_cpp_tok_start());
+ }
+ sp_proc_stmt /* $19 */
+ { /* $20 */
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ if (unlikely(sp->check_unresolved_goto()))
+ MYSQL_YYABORT;
+
+ lex->sql_command= SQLCOM_CREATE_TRIGGER;
+ sp->set_stmt_end(thd);
+ sp->restore_thd_mem_root(thd);
+
+ if (unlikely(sp->is_not_allowed_in_function("trigger")))
+ 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 (unlikely(!lex->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.
+
+**************************************************************************/
+
+udf_tail:
+ opt_if_not_exists ident
+ RETURNS_SYM udf_type SONAME_SYM TEXT_STRING_sys
+ {
+ LEX *lex= thd->lex;
+ if (unlikely(lex->add_create_options_with_check($1)))
+ MYSQL_YYABORT;
+ if (unlikely(is_native_function(thd, & $2)))
+ my_yyabort_error((ER_NATIVE_FCT_NAME_COLLISION, MYF(0), $2.str));
+ lex->sql_command= SQLCOM_CREATE_FUNCTION;
+ lex->udf.name= $2;
+ lex->udf.returns= (Item_result) $4;
+ lex->udf.dl= $6.str;
+ }
+ ;
+
+
+sf_return_type:
+ RETURN_ORACLE_SYM
+ {
+ 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_tail:
+ opt_if_not_exists
+ sp_name
+ {
+ Lex->sql_command= SQLCOM_CREATE_SPFUNCTION;
+ if (unlikely(!Lex->make_sp_head_no_recursive(thd, $1, $2,
+ &sp_handler_function)))
+ MYSQL_YYABORT;
+ }
+ opt_sp_parenthesized_fdparam_list
+ sf_return_type
+ sp_c_chistics
+ {
+ LEX *lex= thd->lex;
+ Lex_input_stream *lip= YYLIP;
+
+ lex->sphead->set_chistics(lex->sp_chistics);
+ lex->sphead->set_body_start(thd, lip->get_cpp_tok_start());
+ }
+ sp_tail_is
+ sp_body
+ {
+ if (unlikely(Lex->sp_body_finalize_function(thd)))
+ MYSQL_YYABORT;
+ if (unlikely(Lex->sphead->m_flags & sp_head::HAS_AGGREGATE_INSTR))
+ {
+ my_yyabort_error((ER_NOT_AGGREGATE_FUNCTION, MYF(0)));
+ }
+ Lex->sphead->set_chistics_agg_type(NOT_AGGREGATE);
+ }
+ ;
+
+sp_tail:
+ opt_if_not_exists sp_name
+ {
+ Lex->sql_command= SQLCOM_CREATE_PROCEDURE;
+ if (unlikely(!Lex->make_sp_head_no_recursive(thd, $1, $2,
+ &sp_handler_procedure)))
+ MYSQL_YYABORT;
+ }
+ opt_sp_parenthesized_pdparam_list
+ sp_c_chistics
+ {
+ Lex->sphead->set_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_procedure(thd)))
+ MYSQL_YYABORT;
+ }
+ ;
+
+sf_tail_standalone:
+ sf_tail opt_sp_name
+ {
+ if (unlikely($2 && !$2->eq(Lex->sphead)))
+ my_yyabort_error((ER_END_IDENTIFIER_DOES_NOT_MATCH, MYF(0),
+ ErrConvDQName($2).ptr(),
+ ErrConvDQName(Lex->sphead).ptr()));
+ }
+ ;
+
+sp_tail_standalone:
+ sp_tail opt_sp_name
+ {
+ if (unlikely($2 && !$2->eq(Lex->sphead)))
+ my_yyabort_error((ER_END_IDENTIFIER_DOES_NOT_MATCH, MYF(0),
+ ErrConvDQName($2).ptr(),
+ ErrConvDQName(Lex->sphead).ptr()));
+ }
+ ;
+
+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 ident SONAME_SYM TEXT_STRING_sys
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_INSTALL_PLUGIN;
+ lex->comment= $3;
+ lex->ident= $5;
+ }
+ | INSTALL_SYM SONAME_SYM TEXT_STRING_sys
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_INSTALL_PLUGIN;
+ lex->comment= null_clex_str;
+ lex->ident= $3;
+ }
+ ;
+
+uninstall:
+ UNINSTALL_SYM PLUGIN_SYM ident
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_UNINSTALL_PLUGIN;
+ lex->comment= $3;
+ }
+ | UNINSTALL_SYM SONAME_SYM TEXT_STRING_sys
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_UNINSTALL_PLUGIN;
+ lex->comment= null_clex_str;
+ lex->ident= $3;
+ }
+ ;
+
+/* 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 b6c650bbf14..99ff9c50588 100644
--- a/sql/strfunc.cc
+++ b/sql/strfunc.cc
@@ -15,7 +15,7 @@
/* Some useful string utility functions used by the MySQL server */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#include "strfunc.h"
@@ -45,7 +45,7 @@
static const char field_separator=',';
-ulonglong find_set(TYPELIB *lib, const char *str, uint length, CHARSET_INFO *cs,
+ulonglong find_set(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;
@@ -79,8 +79,9 @@ ulonglong find_set(TYPELIB *lib, const char *str, uint length, CHARSET_INFO *cs,
var_len= (uint) (pos - start);
uint find= cs ? find_type2(lib, start, var_len, cs) :
find_type(lib, start, var_len, (bool) 0);
- if (!find && *err_len == 0) // report the first error with length > 0
+ if (unlikely(!find && *err_len == 0))
{
+ // report the first error with length > 0
*err_pos= (char*) start;
*err_len= var_len;
*set_warning= 1;
@@ -111,7 +112,7 @@ ulonglong find_set(TYPELIB *lib, const char *str, uint length, CHARSET_INFO *cs,
> 0 position in TYPELIB->type_names +1
*/
-uint find_type(const TYPELIB *lib, const char *find, uint length,
+uint find_type(const TYPELIB *lib, const char *find, size_t length,
bool part_match)
{
uint found_count=0, found_pos=0;
@@ -152,13 +153,13 @@ uint find_type(const TYPELIB *lib, const char *find, uint length,
>0 Offset+1 in typelib for matched string
*/
-uint find_type2(const TYPELIB *typelib, const char *x, uint length,
+uint find_type2(const TYPELIB *typelib, const char *x, size_t length,
CHARSET_INFO *cs)
{
int pos;
const char *j;
DBUG_ENTER("find_type2");
- DBUG_PRINT("enter",("x: '%.*s' lib: %p", length, x, typelib));
+ DBUG_PRINT("enter",("x: '%.*s' lib: %p", (int)length, x, typelib));
if (!typelib->count)
{
@@ -265,8 +266,8 @@ uint check_word(TYPELIB *lib, const char *val, const char *end,
*/
-uint strconvert(CHARSET_INFO *from_cs, const char *from, uint from_length,
- CHARSET_INFO *to_cs, char *to, uint to_length, uint *errors)
+uint strconvert(CHARSET_INFO *from_cs, const char *from, size_t from_length,
+ CHARSET_INFO *to_cs, char *to, size_t to_length, uint *errors)
{
int cnvres;
my_wc_t wc;
@@ -331,10 +332,10 @@ outp:
>=0 Ordinal position
*/
-int find_string_in_array(LEX_STRING * const haystack, LEX_STRING * const needle,
+int find_string_in_array(LEX_CSTRING * const haystack, LEX_CSTRING * const needle,
CHARSET_INFO * const cs)
{
- const LEX_STRING *pos;
+ 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))
@@ -345,12 +346,12 @@ int find_string_in_array(LEX_STRING * const haystack, LEX_STRING * const needle,
}
-char *set_to_string(THD *thd, LEX_STRING *result, ulonglong set,
- const char *lib[])
+const char *set_to_string(THD *thd, LEX_CSTRING *result, ulonglong set,
+ const char *lib[])
{
char buff[STRING_BUFFER_USUAL_SIZE*8];
String tmp(buff, sizeof(buff), &my_charset_latin1);
- LEX_STRING unused;
+ LEX_CSTRING unused;
if (!result)
result= &unused;
@@ -376,12 +377,12 @@ char *set_to_string(THD *thd, LEX_STRING *result, ulonglong set,
return result->str;
}
-char *flagset_to_string(THD *thd, LEX_STRING *result, ulonglong set,
- const char *lib[])
+const char *flagset_to_string(THD *thd, LEX_CSTRING *result, ulonglong set,
+ const char *lib[])
{
char buff[STRING_BUFFER_USUAL_SIZE*8];
String tmp(buff, sizeof(buff), &my_charset_latin1);
- LEX_STRING unused;
+ LEX_CSTRING unused;
if (!result) result= &unused;
diff --git a/sql/strfunc.h b/sql/strfunc.h
index 989eb4aa24c..d66d4c63444 100644
--- a/sql/strfunc.h
+++ b/sql/strfunc.h
@@ -16,34 +16,33 @@
#ifndef STRFUNC_INCLUDED
#define STRFUNC_INCLUDED
-#include "my_global.h" /* ulonglong, uint */
-
typedef struct st_typelib TYPELIB;
-ulonglong find_set(TYPELIB *lib, const char *x, uint length, CHARSET_INFO *cs,
+ulonglong find_set(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,
const char *str, uint length, CHARSET_INFO *cs,
char **err_pos, uint *err_len, bool *set_warning);
-uint find_type(const TYPELIB *lib, const char *find, uint length,
+uint find_type(const TYPELIB *lib, const char *find, size_t length,
bool part_match);
-uint find_type2(const TYPELIB *lib, const char *find, uint length,
+uint find_type2(const TYPELIB *lib, const char *find, size_t length,
CHARSET_INFO *cs);
void unhex_type2(TYPELIB *lib);
uint check_word(TYPELIB *lib, const char *val, const char *end,
const char **end_of_word);
-int find_string_in_array(LEX_STRING * const haystack, LEX_STRING * const needle,
+int find_string_in_array(LEX_CSTRING * const haystack,
+ LEX_CSTRING * const needle,
CHARSET_INFO * const cs);
-char *flagset_to_string(THD *thd, LEX_STRING *result, ulonglong set,
+const char *flagset_to_string(THD *thd, LEX_CSTRING *result, ulonglong set,
const char *lib[]);
-char *set_to_string(THD *thd, LEX_STRING *result, ulonglong set,
- const char *lib[]);
+const char *set_to_string(THD *thd, LEX_CSTRING *result, ulonglong set,
+ const char *lib[]);
/*
These functions were protected by INNODB_COMPATIBILITY_HOOKS
*/
-uint strconvert(CHARSET_INFO *from_cs, const char *from, uint from_length,
- CHARSET_INFO *to_cs, char *to, uint to_length, uint *errors);
+uint strconvert(CHARSET_INFO *from_cs, const char *from, size_t from_length,
+ CHARSET_INFO *to_cs, char *to, size_t to_length, uint *errors);
#endif /* STRFUNC_INCLUDED */
diff --git a/sql/structs.h b/sql/structs.h
index 67fb0d5dd66..351847d00ec 100644
--- a/sql/structs.h
+++ b/sql/structs.h
@@ -29,6 +29,7 @@
#include <mysql_com.h> /* USERNAME_LENGTH */
struct TABLE;
+class Type_handler;
class Field;
class Index_statistics;
@@ -38,7 +39,7 @@ typedef struct st_date_time_format {
uchar positions[8];
char time_separator; /* Separator between hour and minute */
uint flag; /* For future */
- LEX_STRING format;
+ LEX_CSTRING format;
} DATE_TIME_FORMAT;
@@ -109,8 +110,8 @@ typedef struct st_key {
ext_key_part_map.is_set(1) == false
*/
key_part_map ext_key_part_map;
+ LEX_CSTRING name;
uint block_size;
- uint name_length;
enum ha_key_alg algorithm;
/*
The flag is on if statistical data for the index prefixes
@@ -124,10 +125,9 @@ typedef struct st_key {
union
{
plugin_ref parser; /* Fulltext [pre]parser */
- LEX_STRING *parser_name; /* Fulltext [pre]parser name */
+ LEX_CSTRING *parser_name; /* Fulltext [pre]parser name */
};
KEY_PART_INFO *key_part;
- char *name; /* Name of key */
/* Unique name for cache; db + \0 + table_name + \0 + key_name + \0 */
uchar *cache_name;
/*
@@ -148,11 +148,8 @@ typedef struct st_key {
*/
Index_statistics *collected_stats;
- union {
- int bdb_return_if_eq;
- } handler;
TABLE *table;
- LEX_STRING comment;
+ LEX_CSTRING comment;
/** reference to the list of options or NULL */
engine_option_value *option_list;
ha_index_option_struct *option_struct; /* structure with parsed options */
@@ -203,24 +200,39 @@ extern const char *show_comp_option_name[];
typedef int *(*update_var)(THD *, struct st_mysql_show_var *);
-typedef struct st_lex_user {
- LEX_STRING user, host, plugin, auth;
- LEX_STRING pwtext, pwhash;
+
+struct AUTHID
+{
+ LEX_CSTRING user, host;
+ void init() { memset(this, 0, sizeof(*this)); }
+ void copy(MEM_ROOT *root, const LEX_CSTRING *usr, const LEX_CSTRING *host);
bool is_role() const { return user.str[0] && !host.str[0]; }
- void set_lex_string(LEX_STRING *l, char *buf)
+ void set_lex_string(LEX_CSTRING *l, char *buf)
{
if (is_role())
*l= user;
else
- l->length= strxmov(l->str= buf, user.str, "@", host.str, NullS) - buf;
+ {
+ l->str= buf;
+ l->length= strxmov(buf, user.str, "@", host.str, NullS) - buf;
+ }
}
+ void parse(const char *str, size_t length);
+ bool read_from_mysql_proc_row(THD *thd, TABLE *table);
+};
+
+
+struct LEX_USER: public AUTHID
+{
+ LEX_CSTRING plugin, auth;
+ LEX_CSTRING pwtext, pwhash;
void reset_auth()
{
pwtext.length= pwhash.length= plugin.length= auth.length= 0;
pwtext.str= pwhash.str= 0;
- plugin.str= auth.str= const_cast<char*>("");
+ plugin.str= auth.str= "";
}
-} LEX_USER;
+};
/*
This structure specifies the maximum amount of resources which
@@ -315,7 +327,7 @@ typedef struct st_user_stats
typedef struct st_table_stats
{
char table[NAME_LEN * 2 + 2]; // [db] + '\0' + [table] + '\0'
- uint table_name_length;
+ size_t table_name_length;
ulonglong rows_read, rows_changed;
ulonglong rows_changed_x_indexes;
/* Stores enum db_type, but forward declarations cannot be done */
@@ -326,7 +338,7 @@ typedef struct st_index_stats
{
// [db] + '\0' + [table] + '\0' + [index] + '\0'
char index[NAME_LEN * 3 + 3];
- uint index_name_length; /* Length of 'index' */
+ size_t index_name_length; /* Length of 'index' */
ulonglong rows_read;
} INDEX_STATS;
@@ -578,27 +590,31 @@ public:
struct Lex_field_type_st: public Lex_length_and_dec_st
{
private:
- enum_field_types m_type;
- void set(enum_field_types type, const char *length, const char *dec)
+ const Type_handler *m_handler;
+ void set(const Type_handler *handler, const char *length, const char *dec)
{
- m_type= type;
+ m_handler= handler;
Lex_length_and_dec_st::set(length, dec);
}
public:
- void set(enum_field_types type, Lex_length_and_dec_st length_and_dec)
+ void set(const Type_handler *handler, Lex_length_and_dec_st length_and_dec)
{
- m_type= type;
+ m_handler= handler;
Lex_length_and_dec_st::operator=(length_and_dec);
}
- void set(enum_field_types type, const char *length)
+ void set(const Type_handler *handler, const char *length)
{
- set(type, length, 0);
+ set(handler, length, 0);
}
- void set(enum_field_types type)
+ void set(const Type_handler *handler)
{
- set(type, 0, 0);
+ set(handler, 0, 0);
+ }
+ void set_handler(const Type_handler *handler)
+ {
+ m_handler= handler;
}
- enum_field_types field_type() const { return m_type; }
+ const Type_handler *type_handler() const { return m_handler; }
};
@@ -629,6 +645,135 @@ public:
};
+struct Lex_spblock_handlers_st
+{
+public:
+ int hndlrs;
+ void init(int count) { hndlrs= count; }
+};
+
+
+struct Lex_spblock_st: public Lex_spblock_handlers_st
+{
+public:
+ int vars;
+ int conds;
+ int curs;
+ void init()
+ {
+ vars= conds= hndlrs= curs= 0;
+ }
+ void init_using_vars(uint nvars)
+ {
+ vars= nvars;
+ conds= hndlrs= curs= 0;
+ }
+ void join(const Lex_spblock_st &b1, const Lex_spblock_st &b2)
+ {
+ vars= b1.vars + b2.vars;
+ conds= b1.conds + b2.conds;
+ hndlrs= b1.hndlrs + b2.hndlrs;
+ curs= b1.curs + b2.curs;
+ }
+};
+
+
+class Lex_spblock: public Lex_spblock_st
+{
+public:
+ Lex_spblock() { init(); }
+ Lex_spblock(const Lex_spblock_handlers_st &other)
+ {
+ vars= conds= curs= 0;
+ hndlrs= other.hndlrs;
+ }
+};
+
+
+struct Lex_for_loop_bounds_st
+{
+public:
+ class sp_assignment_lex *m_index; // The first iteration value (or cursor)
+ class sp_assignment_lex *m_target_bound; // The last iteration value
+ int8 m_direction;
+ bool m_implicit_cursor;
+ bool is_for_loop_cursor() const { return m_target_bound == NULL; }
+};
+
+
+class Lex_for_loop_bounds_intrange: public Lex_for_loop_bounds_st
+{
+public:
+ Lex_for_loop_bounds_intrange(int8 direction,
+ class sp_assignment_lex *left_expr,
+ class sp_assignment_lex *right_expr)
+ {
+ m_direction= direction;
+ m_index= direction > 0 ? left_expr : right_expr;
+ m_target_bound= direction > 0 ? right_expr : left_expr;
+ m_implicit_cursor= false;
+ }
+};
+
+
+struct Lex_for_loop_st
+{
+public:
+ class sp_variable *m_index; // The first iteration value (or cursor)
+ class sp_variable *m_target_bound; // The last iteration value
+ int m_cursor_offset;
+ int8 m_direction;
+ bool m_implicit_cursor;
+ void init()
+ {
+ m_index= 0;
+ m_target_bound= 0;
+ m_direction= 0;
+ m_implicit_cursor= false;
+ }
+ void init(const Lex_for_loop_st &other)
+ {
+ *this= other;
+ }
+ bool is_for_loop_cursor() const { return m_target_bound == NULL; }
+ bool is_for_loop_explicit_cursor() const
+ {
+ return is_for_loop_cursor() && !m_implicit_cursor;
+ }
+};
+
+
+enum trim_spec { TRIM_LEADING, TRIM_TRAILING, TRIM_BOTH };
+
+struct Lex_trim_st
+{
+ Item *m_remove;
+ Item *m_source;
+ trim_spec m_spec;
+public:
+ void set(trim_spec spec, Item *remove, Item *source)
+ {
+ m_spec= spec;
+ m_remove= remove;
+ m_source= source;
+ }
+ void set(trim_spec spec, Item *source)
+ {
+ set(spec, NULL, source);
+ }
+ Item *make_item_func_trim_std(THD *thd) const;
+ Item *make_item_func_trim_oracle(THD *thd) const;
+ Item *make_item_func_trim(THD *thd) const;
+};
+
+
+class Lex_trim: public Lex_trim_st
+{
+public:
+ Lex_trim(trim_spec spec, Item *source) { set(spec, source); }
+};
+
+
class Load_data_param
{
protected:
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index af261299496..b096c8c5a12 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -1,7 +1,7 @@
/* Copyright (c) 2002, 2015, Oracle and/or its affiliates.
Copyright (c) 2012, 2018, MariaDB Corporation.
- This program is free software; you can redistribute it and/or modify
+ 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.
@@ -31,10 +31,11 @@
(for example in storage/myisam/ha_myisam.cc) !
*/
-#include "sql_plugin.h" // Includes my_global.h
+#include "sql_plugin.h"
#include "sql_priv.h"
#include "sql_class.h" // set_var.h: THD
#include "sys_vars.ic"
+#include "my_sys.h"
#include "events.h"
#include <thr_alarm.h>
@@ -50,7 +51,6 @@
#include "sql_base.h" // close_cached_tables
#include "hostname.h" // host_cache_size
#include <myisam.h>
-#include "log_slow.h"
#include "debug_sync.h" // DEBUG_SYNC
#include "sql_show.h"
@@ -62,6 +62,8 @@
#include "sql_repl.h"
#include "opt_range.h"
#include "rpl_parallel.h"
+#include "semisync_master.h"
+#include "semisync_slave.h"
#include <ssl_compat.h>
/*
@@ -431,6 +433,25 @@ static Sys_var_charptr Sys_basedir(
READ_ONLY GLOBAL_VAR(mysql_home_ptr), CMD_LINE(REQUIRED_ARG, 'b'),
IN_FS_CHARSET, DEFAULT(0));
+static Sys_var_charptr 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));
+
+const char *Sys_var_vers_asof::asof_keywords[]= {"DEFAULT", NULL};
+static Sys_var_vers_asof Sys_vers_asof_timestamp(
+ "system_versioning_asof", "Default value for the FOR SYSTEM_TIME AS OF clause",
+ SESSION_VAR(vers_asof_timestamp.type), NO_CMD_LINE,
+ Sys_var_vers_asof::asof_keywords, DEFAULT(SYSTEM_TIME_UNSPECIFIED));
+
+static const char *vers_alter_history_keywords[]= {"ERROR", "KEEP", NullS};
+static Sys_var_enum Sys_vers_alter_history(
+ "system_versioning_alter_history", "Versioning ALTER TABLE mode. "
+ "ERROR: Fail ALTER with error; " /* TODO: fail only when history non-empty */
+ "KEEP: Keep historical system rows and subject them to ALTER",
+ 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(
"binlog_cache_size", "The size of the transactional cache for "
"updates to transactional engines for the binary log. "
@@ -440,6 +461,13 @@ 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(
+ "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(
"binlog_stmt_cache_size", "The size of the statement cache for "
"updates to non-transactional engines for the binary log. "
@@ -463,13 +491,13 @@ static bool
error_if_in_trans_or_substatement(THD *thd, int in_substatement_error,
int in_transaction_error)
{
- if (thd->in_sub_stmt)
+ if (unlikely(thd->in_sub_stmt))
{
my_error(in_substatement_error, MYF(0));
return true;
}
- if (thd->in_active_multi_stmt_transaction())
+ if (unlikely(thd->in_active_multi_stmt_transaction()))
{
my_error(in_transaction_error, MYF(0));
return true;
@@ -555,9 +583,9 @@ static bool binlog_format_check(sys_var *self, THD *thd, set_var *var)
return true;
}
- if (error_if_in_trans_or_substatement(thd,
- ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_FORMAT,
- ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_FORMAT))
+ if (unlikely(error_if_in_trans_or_substatement(thd,
+ ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_FORMAT,
+ ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_FORMAT)))
return true;
return false;
@@ -592,9 +620,9 @@ static bool binlog_direct_check(sys_var *self, THD *thd, set_var *var)
if (var->type == OPT_GLOBAL)
return false;
- if (error_if_in_trans_or_substatement(thd,
- ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_DIRECT,
- ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_DIRECT))
+ if (unlikely(error_if_in_trans_or_substatement(thd,
+ ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_DIRECT,
+ ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_DIRECT)))
return true;
return false;
@@ -616,8 +644,7 @@ static Sys_var_mybool Sys_explicit_defaults_for_timestamp(
"explicit_defaults_for_timestamp",
"This option causes CREATE TABLE to create all TIMESTAMP columns "
"as NULL with DEFAULT NULL attribute, Without this option, "
- "TIMESTAMP columns are NOT NULL and have implicit DEFAULT clauses. "
- "The old behavior is deprecated.",
+ "TIMESTAMP columns are NOT NULL and have implicit DEFAULT clauses.",
READ_ONLY GLOBAL_VAR(opt_explicit_defaults_for_timestamp),
CMD_LINE(OPT_ARG), DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG);
@@ -824,6 +851,53 @@ static Sys_var_struct Sys_collation_server(
offsetof(CHARSET_INFO, name), DEFAULT(&default_charset_info),
NO_MUTEX_GUARD, IN_BINLOG, ON_CHECK(check_collation_not_null));
+static Sys_var_uint Sys_column_compression_threshold(
+ "column_compression_threshold",
+ "Minimum column data length eligible for compression",
+ SESSION_VAR(column_compression_threshold), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, UINT_MAX), DEFAULT(100), BLOCK_SIZE(1));
+
+static Sys_var_uint Sys_column_compression_zlib_level(
+ "column_compression_zlib_level",
+ "zlib compression level (1 gives best speed, 9 gives best compression)",
+ SESSION_VAR(column_compression_zlib_level), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, 9), DEFAULT(6), BLOCK_SIZE(1));
+
+/*
+ Note that names must correspond to zlib strategy definition. So that we can
+ pass column_compression_zlib_strategy directly to deflateInit2().
+*/
+static const char *column_compression_zlib_strategy_names[]=
+{ "DEFAULT_STRATEGY", "FILTERED", "HUFFMAN_ONLY", "RLE", "FIXED", 0 };
+
+static Sys_var_enum Sys_column_compression_zlib_strategy(
+ "column_compression_zlib_strategy",
+ "The strategy parameter is used to tune the compression algorithm. Use "
+ "the value DEFAULT_STRATEGY for normal data, FILTERED for data produced "
+ "by a filter (or predictor), HUFFMAN_ONLY to force Huffman encoding "
+ "only (no string match), or RLE to limit match distances to one "
+ "(run-length encoding). Filtered data consists mostly of small values "
+ "with a somewhat random distribution. In this case, the compression "
+ "algorithm is tuned to compress them better. The effect of FILTERED is "
+ "to force more Huffman coding and less string matching; it is somewhat "
+ "intermediate between DEFAULT_STRATEGY and HUFFMAN_ONLY. RLE is "
+ "designed to be almost as fast as HUFFMAN_ONLY, but give better "
+ "compression for PNG image data. The strategy parameter only affects "
+ "the compression ratio but not the correctness of the compressed output "
+ "even if it is not set appropriately. FIXED prevents the use of dynamic "
+ "Huffman codes, allowing for a simpler decoder for special "
+ "applications.",
+ SESSION_VAR(column_compression_zlib_strategy), CMD_LINE(REQUIRED_ARG),
+ column_compression_zlib_strategy_names, DEFAULT(0));
+
+static Sys_var_mybool Sys_column_compression_zlib_wrap(
+ "column_compression_zlib_wrap",
+ "Generate zlib header and trailer and compute adler32 check value. "
+ "It can be used with storage engines that don't provide data integrity "
+ "verification to detect data corruption.",
+ SESSION_VAR(column_compression_zlib_wrap), CMD_LINE(OPT_ARG),
+ DEFAULT(FALSE));
+
static const char *concurrent_insert_names[]= {"NEVER", "AUTO", "ALWAYS", 0};
static Sys_var_enum Sys_concurrent_insert(
"concurrent_insert", "Use concurrent insert with MyISAM",
@@ -1095,7 +1169,7 @@ static Sys_var_lexstring Sys_init_connect(
#ifdef HAVE_REPLICATION
static bool check_master_connection(sys_var *self, THD *thd, set_var *var)
{
- LEX_STRING tmp;
+ LEX_CSTRING tmp;
tmp.str= var->save_result.string_value.str;
tmp.length= var->save_result.string_value.length;
if (!tmp.str || check_master_connection_name(&tmp))
@@ -1225,7 +1299,7 @@ static Sys_var_ulong Sys_lock_wait_timeout(
"lock_wait_timeout",
"Timeout in seconds to wait for a lock before returning an error.",
SESSION_VAR(lock_wait_timeout), CMD_LINE(REQUIRED_ARG),
- VALID_RANGE(1, LONG_TIMEOUT), DEFAULT(24 * 60 * 60), BLOCK_SIZE(1));
+ VALID_RANGE(0, LONG_TIMEOUT), DEFAULT(24 * 60 * 60), BLOCK_SIZE(1));
#ifdef HAVE_MLOCKALL
static Sys_var_mybool Sys_locked_in_memory(
@@ -1272,25 +1346,28 @@ static Sys_var_charptr Sys_log_error(
CMD_LINE(OPT_ARG, OPT_LOG_ERROR),
IN_FS_CHARSET, DEFAULT(disabled_my_option));
-static Sys_var_mybool Sys_log_queries_not_using_indexes(
+static Sys_var_bit Sys_log_queries_not_using_indexes(
"log_queries_not_using_indexes",
"Log queries that are executed without benefit of any index to the "
- "slow log if it is open",
- GLOBAL_VAR(opt_log_queries_not_using_indexes),
- CMD_LINE(OPT_ARG), DEFAULT(FALSE));
+ "slow log if it is open. Same as log_slow_filter='not_using_index'",
+ SESSION_VAR(log_slow_filter), CMD_LINE(OPT_ARG), QPLAN_NOT_USING_INDEX,
+ DEFAULT(FALSE));
-static Sys_var_mybool Sys_log_slow_admin_statements(
+static Sys_var_bit Sys_log_slow_admin_statements(
"log_slow_admin_statements",
- "Log slow OPTIMIZE, ANALYZE, ALTER and other administrative statements to "
- "the slow log if it is open.",
- GLOBAL_VAR(opt_log_slow_admin_statements),
- CMD_LINE(OPT_ARG), DEFAULT(TRUE));
+ "Log slow OPTIMIZE, ANALYZE, ALTER and other administrative statements "
+ "to the slow log if it is open. Resets or sets the option 'admin' in "
+ "log_slow_disabled_statements",
+ SESSION_VAR(log_slow_disabled_statements),
+ CMD_LINE(OPT_ARG), REVERSE(LOG_SLOW_DISABLE_ADMIN), DEFAULT(TRUE));
-static Sys_var_mybool Sys_log_slow_slave_statements(
+static Sys_var_bit Sys_log_slow_slave_statements(
"log_slow_slave_statements",
- "Log slow statements executed by slave thread to the slow log if it is open.",
- GLOBAL_VAR(opt_log_slow_slave_statements),
- CMD_LINE(OPT_ARG), DEFAULT(TRUE));
+ "Log slow statements executed by slave thread to the slow log if it is "
+ "open. Resets or sets the option 'slave' in "
+ "log_slow_disabled_statements",
+ SESSION_VAR(log_slow_disabled_statements),
+ CMD_LINE(OPT_ARG), REVERSE(LOG_SLOW_DISABLE_SLAVE), DEFAULT(TRUE));
static Sys_var_ulong Sys_log_warnings(
"log_warnings",
@@ -1589,9 +1666,9 @@ static bool check_gtid_seq_no(sys_var *self, THD *thd, set_var *var)
if (check_has_super(self, thd, var))
return true;
- if (error_if_in_trans_or_substatement(thd,
- ER_STORED_FUNCTION_PREVENTS_SWITCH_GTID_DOMAIN_ID_SEQ_NO,
- ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_GTID_DOMAIN_ID_SEQ_NO))
+ 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)))
return true;
domain_id= thd->variables.gtid_domain_id;
@@ -1627,7 +1704,7 @@ static Sys_var_gtid_binlog_pos Sys_gtid_binlog_pos(
uchar *
-Sys_var_gtid_binlog_pos::global_value_ptr(THD *thd, const LEX_STRING *base)
+Sys_var_gtid_binlog_pos::global_value_ptr(THD *thd, const LEX_CSTRING *base)
{
char buf[128];
String str(buf, sizeof(buf), system_charset_info);
@@ -1655,7 +1732,7 @@ static Sys_var_gtid_current_pos Sys_gtid_current_pos(
uchar *
-Sys_var_gtid_current_pos::global_value_ptr(THD *thd, const LEX_STRING *base)
+Sys_var_gtid_current_pos::global_value_ptr(THD *thd, const LEX_CSTRING *base)
{
String str;
char *p;
@@ -1736,7 +1813,7 @@ Sys_var_gtid_slave_pos::global_update(THD *thd, set_var *var)
uchar *
-Sys_var_gtid_slave_pos::global_value_ptr(THD *thd, const LEX_STRING *base)
+Sys_var_gtid_slave_pos::global_value_ptr(THD *thd, const LEX_CSTRING *base)
{
String str;
char *p;
@@ -1857,7 +1934,7 @@ Sys_var_gtid_binlog_state::global_update(THD *thd, set_var *var)
uchar *
-Sys_var_gtid_binlog_state::global_value_ptr(THD *thd, const LEX_STRING *base)
+Sys_var_gtid_binlog_state::global_value_ptr(THD *thd, const LEX_CSTRING *base)
{
char buf[512];
String str(buf, sizeof(buf), system_charset_info);
@@ -1892,7 +1969,7 @@ export sys_var *Sys_last_gtid_ptr= &Sys_last_gtid; // for check changing
uchar *
-Sys_var_last_gtid::session_value_ptr(THD *thd, const LEX_STRING *base)
+Sys_var_last_gtid::session_value_ptr(THD *thd, const LEX_CSTRING *base)
{
char buf[10+1+10+1+20+1];
String str(buf, sizeof(buf), system_charset_info);
@@ -2002,7 +2079,7 @@ Sys_var_slave_parallel_mode::global_update(THD *thd, set_var *var)
{
enum_slave_parallel_mode new_value=
(enum_slave_parallel_mode)var->save_result.ulonglong_value;
- LEX_STRING *base_name= &var->base;
+ LEX_CSTRING *base_name= &var->base;
Master_info *mi;
bool res= false;
@@ -2045,7 +2122,7 @@ Sys_var_slave_parallel_mode::global_update(THD *thd, set_var *var)
uchar *
Sys_var_slave_parallel_mode::global_value_ptr(THD *thd,
- const LEX_STRING *base_name)
+ const LEX_CSTRING *base_name)
{
Master_info *mi;
enum_slave_parallel_mode val=
@@ -2355,9 +2432,19 @@ static Sys_var_mybool Sys_old_mode(
"old", "Use compatible behavior from previous MariaDB version. See also --old-mode",
SESSION_VAR(old_mode), CMD_LINE(OPT_ARG), DEFAULT(FALSE));
-static Sys_var_mybool Sys_old_alter_table(
- "old_alter_table", "Use old, non-optimized alter table",
- SESSION_VAR(old_alter_table), CMD_LINE(OPT_ARG), DEFAULT(FALSE));
+static const char *alter_algorithm_modes[]= {"DEFAULT", "COPY", "INPLACE",
+"NOCOPY", "INSTANT", NULL};
+
+static Sys_var_enum Sys_alter_algorithm(
+ "alter_algorithm", "Specify the alter table algorithm",
+ SESSION_VAR(alter_algorithm), CMD_LINE(OPT_ARG),
+ alter_algorithm_modes, DEFAULT(0));
+
+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));
static bool check_old_passwords(sys_var *self, THD *thd, set_var *var)
{
@@ -2428,8 +2515,7 @@ static Sys_var_ulong Sys_optimizer_search_depth(
"optimization, but may produce very bad query plans. If set to 0, "
"the system will automatically pick a reasonable value.",
SESSION_VAR(optimizer_search_depth), CMD_LINE(REQUIRED_ARG),
- VALID_RANGE(0, MAX_TABLES+1), DEFAULT(MAX_TABLES+1), BLOCK_SIZE(1),
- NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(0));
+ VALID_RANGE(0, MAX_TABLES+1), DEFAULT(MAX_TABLES+1), BLOCK_SIZE(1));
/* this is used in the sigsegv handler */
export const char *optimizer_switch_names[]=
@@ -2457,6 +2543,7 @@ export const char *optimizer_switch_names[]=
"exists_to_in",
"orderby_uses_equalities",
"condition_pushdown_for_derived",
+ "split_materialized",
"default",
NullS
};
@@ -2817,7 +2904,7 @@ static Sys_var_enum Sys_thread_handling(
#ifdef HAVE_QUERY_CACHE
static bool fix_query_cache_size(sys_var *self, THD *thd, enum_var_type type)
{
- ulong new_cache_size= query_cache.resize((ulong)query_cache_size);
+ size_t new_cache_size= query_cache.resize((size_t)query_cache_size);
/*
Note: query_cache_size is a global variable reflecting the
requested cache size. See also query_cache_size_arg
@@ -2825,7 +2912,7 @@ static bool fix_query_cache_size(sys_var *self, THD *thd, enum_var_type type)
if (query_cache_size != new_cache_size)
push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_QC_RESIZE, ER_THD(thd, ER_WARN_QC_RESIZE),
- query_cache_size, new_cache_size);
+ query_cache_size, (ulong)new_cache_size);
query_cache_size= new_cache_size;
@@ -2856,7 +2943,7 @@ static Sys_var_ulong Sys_query_cache_limit(
static bool fix_qcache_min_res_unit(sys_var *self, THD *thd, enum_var_type type)
{
query_cache_min_res_unit=
- query_cache.set_min_res_unit(query_cache_min_res_unit);
+ (ulong)query_cache.set_min_res_unit(query_cache_min_res_unit);
return false;
}
static Sys_var_ulong Sys_query_cache_min_res_unit(
@@ -3062,8 +3149,176 @@ static Sys_var_replicate_events_marked_for_skip Replicate_events_marked_for_skip
"the slave).",
GLOBAL_VAR(opt_replicate_events_marked_for_skip), CMD_LINE(REQUIRED_ARG),
replicate_events_marked_for_skip_names, DEFAULT(RPL_SKIP_REPLICATE));
-#endif
+/* new options for semisync */
+
+static bool fix_rpl_semi_sync_master_enabled(sys_var *self, THD *thd,
+ enum_var_type type)
+{
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ mysql_mutex_lock(&repl_semisync_master.LOCK_rpl_semi_sync_master_enabled);
+ if (rpl_semi_sync_master_enabled)
+ {
+ if (repl_semisync_master.enable_master() != 0)
+ rpl_semi_sync_master_enabled= false;
+ else if (ack_receiver.start())
+ {
+ repl_semisync_master.disable_master();
+ rpl_semi_sync_master_enabled= false;
+ }
+ }
+ else
+ {
+ repl_semisync_master.disable_master();
+ ack_receiver.stop();
+ }
+ mysql_mutex_unlock(&repl_semisync_master.LOCK_rpl_semi_sync_master_enabled);
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ return false;
+}
+
+static bool fix_rpl_semi_sync_master_timeout(sys_var *self, THD *thd,
+ enum_var_type type)
+{
+ repl_semisync_master.set_wait_timeout(rpl_semi_sync_master_timeout);
+ return false;
+}
+
+static bool fix_rpl_semi_sync_master_trace_level(sys_var *self, THD *thd,
+ enum_var_type type)
+{
+ repl_semisync_master.set_trace_level(rpl_semi_sync_master_trace_level);
+ ack_receiver.set_trace_level(rpl_semi_sync_master_trace_level);
+ return false;
+}
+
+static bool fix_rpl_semi_sync_master_wait_point(sys_var *self, THD *thd,
+ enum_var_type type)
+{
+ repl_semisync_master.set_wait_point(rpl_semi_sync_master_wait_point);
+ return false;
+}
+
+static bool fix_rpl_semi_sync_master_wait_no_slave(sys_var *self, THD *thd,
+ enum_var_type type)
+{
+ repl_semisync_master.check_and_switch();
+ return false;
+}
+
+static Sys_var_mybool Sys_semisync_master_enabled(
+ "rpl_semi_sync_master_enabled",
+ "Enable semi-synchronous replication master (disabled by default).",
+ GLOBAL_VAR(rpl_semi_sync_master_enabled),
+ CMD_LINE(OPT_ARG), DEFAULT(FALSE),
+ 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(
+ "rpl_semi_sync_master_timeout",
+ "The timeout value (in ms) for semi-synchronous replication in the "
+ "master",
+ GLOBAL_VAR(rpl_semi_sync_master_timeout),
+ CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0,~0L),DEFAULT(10000),BLOCK_SIZE(1),
+ 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(
+ "rpl_semi_sync_master_wait_no_slave",
+ "Wait until timeout when no semi-synchronous replication slave "
+ "available (enabled by default).",
+ GLOBAL_VAR(rpl_semi_sync_master_wait_no_slave),
+ CMD_LINE(OPT_ARG), DEFAULT(TRUE),
+ 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(
+ "rpl_semi_sync_master_trace_level",
+ "The tracing level for semi-sync replication.",
+ GLOBAL_VAR(rpl_semi_sync_master_trace_level),
+ CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0,~0L),DEFAULT(32),BLOCK_SIZE(1),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(fix_rpl_semi_sync_master_trace_level));
+
+static const char *repl_semisync_wait_point[]=
+{"AFTER_SYNC", "AFTER_COMMIT", NullS};
+
+static Sys_var_enum 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.",
+ GLOBAL_VAR(rpl_semi_sync_master_wait_point), CMD_LINE(REQUIRED_ARG),
+ repl_semisync_wait_point, DEFAULT(1),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG,ON_CHECK(0),
+ ON_UPDATE(fix_rpl_semi_sync_master_wait_point));
+
+static bool fix_rpl_semi_sync_slave_enabled(sys_var *self, THD *thd,
+ enum_var_type type)
+{
+ repl_semisync_slave.set_slave_enabled(rpl_semi_sync_slave_enabled != 0);
+ return false;
+}
+
+static bool fix_rpl_semi_sync_slave_trace_level(sys_var *self, THD *thd,
+ enum_var_type type)
+{
+ repl_semisync_slave.set_trace_level(rpl_semi_sync_slave_trace_level);
+ return false;
+}
+
+static bool fix_rpl_semi_sync_slave_delay_master(sys_var *self, THD *thd,
+ enum_var_type type)
+{
+ repl_semisync_slave.set_delay_master(rpl_semi_sync_slave_delay_master);
+ return false;
+}
+
+static bool fix_rpl_semi_sync_slave_kill_conn_timeout(sys_var *self, THD *thd,
+ enum_var_type type)
+{
+ repl_semisync_slave.
+ set_kill_conn_timeout(rpl_semi_sync_slave_kill_conn_timeout);
+ return false;
+}
+
+static Sys_var_mybool Sys_semisync_slave_enabled(
+ "rpl_semi_sync_slave_enabled",
+ "Enable semi-synchronous replication slave (disabled by default).",
+ GLOBAL_VAR(rpl_semi_sync_slave_enabled),
+ CMD_LINE(OPT_ARG), DEFAULT(FALSE),
+ 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(
+ "rpl_semi_sync_slave_trace_level",
+ "The tracing level for semi-sync replication.",
+ GLOBAL_VAR(rpl_semi_sync_slave_trace_level),
+ CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0,~0L),DEFAULT(32),BLOCK_SIZE(1),
+ 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(
+ "rpl_semi_sync_slave_delay_master",
+ "Only write master info file when ack is needed.",
+ GLOBAL_VAR(rpl_semi_sync_slave_delay_master),
+ CMD_LINE(OPT_ARG), DEFAULT(FALSE),
+ 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(
+ "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 "
+ "is executed.",
+ GLOBAL_VAR(rpl_semi_sync_slave_kill_conn_timeout),
+ CMD_LINE(OPT_ARG),
+ VALID_RANGE(0, UINT_MAX), DEFAULT(5), BLOCK_SIZE(1),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(fix_rpl_semi_sync_slave_kill_conn_timeout));
+#endif /* HAVE_REPLICATION */
static Sys_var_ulong Sys_slow_launch_time(
"slow_launch_time",
@@ -3098,7 +3353,8 @@ export sql_mode_t expand_sql_mode(sql_mode_t sql_mode)
sql_mode|= (MODE_PIPES_AS_CONCAT | MODE_ANSI_QUOTES |
MODE_IGNORE_SPACE |
MODE_NO_KEY_OPTIONS | MODE_NO_TABLE_OPTIONS |
- MODE_NO_FIELD_OPTIONS | MODE_NO_AUTO_CREATE_USER);
+ MODE_NO_FIELD_OPTIONS | MODE_NO_AUTO_CREATE_USER |
+ MODE_SIMULTANEOUS_ASSIGNMENT);
if (sql_mode & MODE_MSSQL)
sql_mode|= (MODE_PIPES_AS_CONCAT | MODE_ANSI_QUOTES |
MODE_IGNORE_SPACE |
@@ -3167,7 +3423,7 @@ static const char *sql_mode_names[]=
"STRICT_ALL_TABLES", "NO_ZERO_IN_DATE", "NO_ZERO_DATE",
"ALLOW_INVALID_DATES", "ERROR_FOR_DIVISION_BY_ZERO", "TRADITIONAL",
"NO_AUTO_CREATE_USER", "HIGH_NOT_PRECEDENCE", "NO_ENGINE_SUBSTITUTION",
- "PAD_CHAR_TO_FULL_LENGTH",
+ "PAD_CHAR_TO_FULL_LENGTH", "EMPTY_STRING_IS_NULL", "SIMULTANEOUS_ASSIGNMENT",
0
};
@@ -3180,7 +3436,7 @@ const char *sql_mode_string_representation(uint bit_number)
export bool sql_mode_string_representation(THD *thd, sql_mode_t sql_mode,
- LEX_STRING *ls)
+ LEX_CSTRING *ls)
{
set_to_string(thd, ls, sql_mode, sql_mode_names);
return ls->str == 0;
@@ -3353,6 +3609,12 @@ static bool fix_tp_min_threads(sys_var *, THD *, enum_var_type)
static bool check_threadpool_size(sys_var *self, THD *thd, set_var *var)
{
+
+#ifdef _WIN32
+ if (threadpool_mode != TP_MODE_GENERIC)
+ return false;
+#endif
+
ulonglong v= var->save_result.ulonglong_value;
if (v > threadpool_max_size)
{
@@ -3508,14 +3770,12 @@ bool Sys_var_tx_read_only::session_update(THD *thd, set_var *var)
#ifndef EMBEDDED_LIBRARY
if (thd->variables.session_track_transaction_info > TX_TRACK_NONE)
{
- Transaction_state_tracker *tst= (Transaction_state_tracker *)
- thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER);
-
if (var->type == OPT_DEFAULT)
- tst->set_read_flags(thd,
+ thd->session_tracker.transaction_info.set_read_flags(thd,
thd->tx_read_only ? TX_READ_ONLY : TX_READ_WRITE);
else
- tst->set_read_flags(thd, TX_READ_INHERIT);
+ thd->session_tracker.transaction_info.set_read_flags(thd,
+ TX_READ_INHERIT);
}
#endif //EMBEDDED_LIBRARY
}
@@ -3598,27 +3858,14 @@ static Sys_var_charptr Sys_version_compile_os(
CMD_LINE_HELP_ONLY,
IN_SYSTEM_CHARSET, DEFAULT(SYSTEM_TYPE));
-static char *guess_malloc_library()
-{
- if (strcmp(MALLOC_LIBRARY, "system") == 0)
- {
-#ifdef HAVE_DLOPEN
- typedef int (*mallctl_type)(const char*, void*, size_t*, void*, size_t);
- mallctl_type mallctl_func;
- mallctl_func= (mallctl_type)dlsym(RTLD_DEFAULT, "mallctl");
- if (mallctl_func)
- {
- static char buf[128];
- char *ver;
- size_t len = sizeof(ver);
- mallctl_func("version", &ver, &len, NULL, 0);
- strxnmov(buf, sizeof(buf)-1, "jemalloc ", ver, NULL);
- return buf;
- }
-#endif
- }
- return const_cast<char*>(MALLOC_LIBRARY);
-}
+#include <source_revision.h>
+static char *server_version_source_revision;
+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));
+
static char *malloc_library;
static Sys_var_charptr Sys_malloc_library(
"version_malloc_library", "Version of the used malloc library",
@@ -3639,6 +3886,27 @@ static Sys_var_ulong Sys_net_wait_timeout(
VALID_RANGE(1, IF_WIN(INT_MAX32/1000, LONG_TIMEOUT)),
DEFAULT(NET_WAIT_TIMEOUT), BLOCK_SIZE(1));
+static Sys_var_uint Sys_idle_transaction_timeout(
+ "idle_transaction_timeout",
+ "The number of seconds the server waits for idle transaction",
+ SESSION_VAR(idle_transaction_timeout), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, IF_WIN(INT_MAX32/1000, LONG_TIMEOUT)),
+ DEFAULT(0), BLOCK_SIZE(1));
+
+static Sys_var_uint Sys_idle_readonly_transaction_timeout(
+ "idle_readonly_transaction_timeout",
+ "The number of seconds the server waits for read-only idle transaction",
+ SESSION_VAR(idle_readonly_transaction_timeout), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, IF_WIN(INT_MAX32/1000, LONG_TIMEOUT)),
+ DEFAULT(0), BLOCK_SIZE(1));
+
+static Sys_var_uint Sys_idle_write_transaction_timeout(
+ "idle_write_transaction_timeout",
+ "The number of seconds the server waits for write idle transaction",
+ SESSION_VAR(idle_write_transaction_timeout), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, IF_WIN(INT_MAX32/1000, LONG_TIMEOUT)),
+ DEFAULT(0), BLOCK_SIZE(1));
+
static Sys_var_plugin Sys_default_storage_engine(
"default_storage_engine", "The default storage engine for new tables",
SESSION_VAR(table_plugin), NO_CMD_LINE,
@@ -3663,6 +3931,45 @@ static Sys_var_plugin Sys_enforce_storage_engine(
NO_CMD_LINE, MYSQL_STORAGE_ENGINE_PLUGIN,
DEFAULT(&enforced_storage_engine), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_has_super));
+
+#ifdef HAVE_REPLICATION
+/*
+ Check
+ 1. Value for gtid_pos_auto_engines is not NULL.
+ 2. No slave SQL thread is running.
+*/
+static bool
+check_gtid_pos_auto_engines(sys_var *self, THD *thd, set_var *var)
+{
+ bool running;
+ bool err= false;
+
+ DBUG_ASSERT(var->type == OPT_GLOBAL);
+ if (var->value && var->value->is_null())
+ err= true;
+ else
+ {
+ running= give_error_if_slave_running(false);
+ if (running)
+ err= true;
+ }
+ return err;
+}
+
+
+static Sys_var_pluginlist 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 "
+ "is replicated. This can be used to avoid introducing cross-engine "
+ "transactions, if engines are used different from that used by table "
+ "mysql.gtid_slave_pos",
+ GLOBAL_VAR(opt_gtid_pos_auto_plugins), NO_CMD_LINE,
+ DEFAULT(&gtid_pos_auto_engines),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_gtid_pos_auto_engines));
+#endif
+
+
#if defined(ENABLED_DEBUG_SYNC)
/*
Variable can be set for the session only.
@@ -3839,15 +4146,15 @@ static bool check_sql_log_bin(sys_var *self, THD *thd, set_var *var)
if (check_has_super(self, thd, var))
return TRUE;
- if (var->type == OPT_GLOBAL)
+ if (unlikely(var->type == OPT_GLOBAL))
{
my_error(ER_INCORRECT_GLOBAL_LOCAL_VAR, MYF(0), self->name.str, "SESSION");
return TRUE;
}
- if (error_if_in_trans_or_substatement(thd,
- ER_STORED_FUNCTION_PREVENTS_SWITCH_SQL_LOG_BIN,
- ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_SQL_LOG_BIN))
+ if (unlikely(error_if_in_trans_or_substatement(thd,
+ ER_STORED_FUNCTION_PREVENTS_SWITCH_SQL_LOG_BIN,
+ ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_SQL_LOG_BIN)))
return TRUE;
return FALSE;
@@ -3889,7 +4196,7 @@ 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 "
"the common mistake of accidentally deleting or updating every row in a table.",
- SESSION_VAR(option_bits), NO_CMD_LINE, OPTION_SAFE_UPDATES,
+ SESSION_VAR(option_bits), CMD_LINE(OPT_ARG), OPTION_SAFE_UPDATES,
DEFAULT(FALSE));
static Sys_var_bit Sys_buffer_results(
@@ -3980,9 +4287,9 @@ static bool check_skip_replication(sys_var *self, THD *thd, set_var *var)
Rows_log_event without Table_map_log_event or transactional updates without
the COMMIT).
*/
- if (error_if_in_trans_or_substatement(thd,
- ER_STORED_FUNCTION_PREVENTS_SWITCH_SKIP_REPLICATION,
- ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_SKIP_REPLICATION))
+ if (unlikely(error_if_in_trans_or_substatement(thd,
+ ER_STORED_FUNCTION_PREVENTS_SWITCH_SKIP_REPLICATION,
+ ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_SKIP_REPLICATION)))
return 1;
return 0;
@@ -4005,11 +4312,24 @@ static Sys_var_harows Sys_select_limit(
SESSION_VAR(select_limit), NO_CMD_LINE,
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)
+{
+ if (opt_secure_timestamp == SECTIME_NO)
+ return false;
+ if (opt_secure_timestamp == SECTIME_SUPER)
+ return check_has_super(self, thd, var);
+ char buf[1024];
+ strxnmov(buf, sizeof(buf), "--secure-timestamp=",
+ secure_timestamp_levels[opt_secure_timestamp], NULL);
+ my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), buf);
+ return true;
+}
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);
+ NO_MUTEX_GUARD, IN_BINLOG, ON_CHECK(check_timestamp));
static bool update_last_insert_id(THD *thd, set_var *var)
{
@@ -4218,6 +4538,34 @@ static Sys_var_charptr Sys_license(
READ_ONLY GLOBAL_VAR(license), NO_CMD_LINE, IN_SYSTEM_CHARSET,
DEFAULT(STRINGIFY_ARG(LICENSE)));
+#include <proxy_protocol.h>
+char *my_proxy_protocol_networks;
+static bool check_proxy_protocol_networks(sys_var *, THD *, set_var *var)
+{
+ if (!var->value)
+ return false;
+ return !proxy_protocol_networks_valid(var->save_result.string_value.str);
+}
+
+
+static bool fix_proxy_protocol_networks(sys_var *, THD *, enum_var_type)
+{
+ return (bool)set_proxy_protocol_networks(my_proxy_protocol_networks);
+}
+
+
+static Sys_var_charptr 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 "
+ "a single host. \"*\" represents all networks and must the only "
+ "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,
+ ON_CHECK(check_proxy_protocol_networks), ON_UPDATE(fix_proxy_protocol_networks));
+
+
static bool check_log_path(sys_var *self, THD *thd, set_var *var)
{
if (!var->value)
@@ -4427,14 +4775,13 @@ static bool fix_log_state(sys_var *self, THD *thd, enum_var_type type)
oldval= logger.get_log_file_handler()->is_open();
log_type= QUERY_LOG_GENERAL;
}
- else if (self == &Sys_slow_query_log)
+ else
{
+ DBUG_ASSERT(self == &Sys_slow_query_log);
newvalptr= &global_system_variables.sql_log_slow;
oldval= logger.get_slow_log_file_handler()->is_open();
log_type= QUERY_LOG_SLOW;
}
- else
- DBUG_ASSERT(FALSE);
newval= *newvalptr;
if (oldval == newval)
@@ -4454,6 +4801,7 @@ static bool fix_log_state(sys_var *self, THD *thd, enum_var_type type)
return res;
}
+
static bool check_not_empty_set(sys_var *self, THD *thd, set_var *var)
{
return var->save_result.ulonglong_value == 0;
@@ -4543,7 +4891,7 @@ static Sys_var_mybool Sys_relay_log_recovery(
bool Sys_var_rpl_filter::global_update(THD *thd, set_var *var)
{
bool result= true; // Assume error
- LEX_STRING *base_name= &var->base;
+ LEX_CSTRING *base_name= &var->base;
if (!base_name->length)
base_name= &thd->variables.default_master_connection;
@@ -4604,7 +4952,7 @@ bool Sys_var_rpl_filter::set_filter_value(const char *value, Master_info *mi)
}
uchar *Sys_var_rpl_filter::global_value_ptr(THD *thd,
- const LEX_STRING *base_name)
+ const LEX_CSTRING *base_name)
{
char buf[256];
String tmp(buf, sizeof(buf), &my_charset_bin);
@@ -4827,6 +5175,14 @@ static Sys_var_ulonglong Sys_read_binlog_speed_limit(
GLOBAL_VAR(opt_read_binlog_speed_limit), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(0, ULONG_MAX), DEFAULT(0), BLOCK_SIZE(1));
+static Sys_var_charptr Sys_slave_transaction_retry_errors(
+ "slave_transaction_retry_errors", "Tells the slave thread to retry "
+ "transaction for replication when a query event returns an error from "
+ "the provided list. Deadlock and elapsed lock wait timeout errors are "
+ "automatically added to this list",
+ READ_ONLY GLOBAL_VAR(opt_slave_transaction_retry_errors), CMD_LINE(REQUIRED_ARG),
+ IN_SYSTEM_CHARSET, 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),
@@ -4861,10 +5217,19 @@ static Sys_var_uint Sys_sync_masterinfo_period(
#ifdef HAVE_REPLICATION
static Sys_var_ulong Sys_slave_trans_retries(
"slave_transaction_retries", "Number of times the slave SQL "
- "thread will retry a transaction in case it failed with a deadlock "
- "or elapsed lock wait timeout, before giving up and stopping",
+ "thread will retry a transaction in case it failed with a deadlock, "
+ "elapsed lock wait timeout or listed in "
+ "slave_transaction_retry_errors, before giving up and stopping",
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(
+ "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 "
+ "slave_transaction_retry_errors",
+ GLOBAL_VAR(slave_trans_retry_interval), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, 3600), DEFAULT(0), BLOCK_SIZE(1));
#endif
static bool check_locale(sys_var *self, THD *thd, set_var *var)
@@ -5107,8 +5472,7 @@ 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,
- ON_CHECK(wsrep_sst_method_check),
- ON_UPDATE(wsrep_sst_method_update));
+ 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 "
@@ -5216,9 +5580,7 @@ static const char *wsrep_OSU_method_names[]= { "TOI", "RSU", NullS };
static Sys_var_enum Sys_wsrep_OSU_method(
"wsrep_OSU_method", "Method for Online Schema Upgrade",
SESSION_VAR(wsrep_OSU_method), CMD_LINE(OPT_ARG),
- wsrep_OSU_method_names, DEFAULT(WSREP_OSU_TOI),
- NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
- ON_UPDATE(0));
+ wsrep_OSU_method_names, DEFAULT(WSREP_OSU_TOI));
static PolyLock_mutex PLock_wsrep_desync(&LOCK_wsrep_desync);
static Sys_var_mybool Sys_wsrep_desync (
@@ -5330,6 +5692,38 @@ static Sys_var_ulong Sys_host_cache_size(
NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL),
ON_UPDATE(fix_host_cache_size));
+vio_keepalive_opts opt_vio_keepalive;
+
+static Sys_var_int Sys_keepalive_time(
+ "tcp_keepalive_time",
+ "Timeout, in seconds, with no activity until the first TCP keep-alive packet is sent."
+ "If set to 0, system dependent default is used.",
+ AUTO_SET GLOBAL_VAR(opt_vio_keepalive.idle),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, INT_MAX32/1000),
+ DEFAULT(0),
+ BLOCK_SIZE(1),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL));
+
+static Sys_var_int Sys_keepalive_interval(
+ "tcp_keepalive_interval",
+ "The interval, in seconds, between when successive keep-alive packets are sent if no acknowledgement is received."
+ "If set to 0, system dependent default is used.",
+ AUTO_SET GLOBAL_VAR(opt_vio_keepalive.interval),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, INT_MAX32/1000),
+ DEFAULT(0),
+ BLOCK_SIZE(1),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL));
+
+static Sys_var_int Sys_keepalive_probes(
+ "tcp_keepalive_probes",
+ "The number of unacknowledged probes to send before considering the connection dead and notifying the application layer."
+ "If set to 0, system dependent default is used.",
+ AUTO_SET GLOBAL_VAR(opt_vio_keepalive.probes),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, INT_MAX32/1000),
+ DEFAULT(0),
+ BLOCK_SIZE(1),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL));
+
static Sys_var_charptr Sys_ignore_db_dirs(
"ignore_db_dirs",
"Specifies a directory to add to the ignore list when collecting "
@@ -5353,7 +5747,9 @@ static Sys_var_enum Sys_plugin_maturity(
"The lowest desirable plugin maturity. "
"Plugins less mature than that will not be installed or loaded",
READ_ONLY GLOBAL_VAR(plugin_maturity), CMD_LINE(REQUIRED_ARG),
- plugin_maturity_names, DEFAULT(MariaDB_PLUGIN_MATURITY_UNKNOWN));
+ plugin_maturity_names,
+ DEFAULT(SERVER_MATURITY_LEVEL > 0 ?
+ SERVER_MATURITY_LEVEL - 1 : SERVER_MATURITY_LEVEL));
static Sys_var_ulong Sys_deadlock_search_depth_short(
"deadlock_search_depth_short",
@@ -5409,15 +5805,42 @@ static Sys_var_keycache Sys_key_cache_segments(
ON_UPDATE(repartition_keycache));
static const char *log_slow_filter_names[]=
-{ "admin", "filesort", "filesort_on_disk", "full_join", "full_scan",
- "query_cache", "query_cache_miss", "tmp_table", "tmp_table_on_disk", 0
+{
+ "admin", "filesort", "filesort_on_disk", "filesort_priority_queue",
+ "full_join", "full_scan", "not_using_index", "query_cache",
+ "query_cache_miss", "tmp_table", "tmp_table_on_disk", 0
};
+
+
static Sys_var_set Sys_log_slow_filter(
"log_slow_filter",
- "Log only certain types of queries",
+ "Log only certain types of queries to the slow log. If variable empty alll kind of queries are logged. All types are bound by slow_query_time, except 'not_using_index' which is always logged if enabled",
SESSION_VAR(log_slow_filter), CMD_LINE(REQUIRED_ARG),
log_slow_filter_names,
- DEFAULT(my_set_bits(array_elements(log_slow_filter_names)-1)));
+ /* by default we log all queries except 'not_using_index' */
+ DEFAULT(my_set_bits(array_elements(log_slow_filter_names)-1) &
+ ~QPLAN_NOT_USING_INDEX));
+
+static const char *log_slow_disabled_statements_names[]=
+{ "admin", "call", "slave", "sp", 0 };
+
+static const char *log_disabled_statements_names[]=
+{ "slave", "sp", 0 };
+
+static Sys_var_set Sys_log_slow_disabled_statements(
+ "log_slow_disabled_statements",
+ "Don't log certain types of statements to slow log",
+ SESSION_VAR(log_slow_disabled_statements), CMD_LINE(REQUIRED_ARG),
+ log_slow_disabled_statements_names,
+ DEFAULT(LOG_SLOW_DISABLE_SP));
+
+static Sys_var_set Sys_log_disabled_statements(
+ "log_disabled_statements",
+ "Don't log certain types of statements to general log",
+ SESSION_VAR(log_disabled_statements), CMD_LINE(REQUIRED_ARG),
+ log_disabled_statements_names,
+ DEFAULT(LOG_DISABLE_SP),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_has_super));
static const char *default_regex_flags_names[]=
{
@@ -5625,9 +6048,7 @@ static Sys_var_enum Sys_binlog_row_image(
"before image, and only changed columns are logged in the after image. "
"(Default: FULL).",
SESSION_VAR(binlog_row_image), CMD_LINE(REQUIRED_ARG),
- binlog_row_image_names, DEFAULT(BINLOG_ROW_IMAGE_FULL),
- NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL),
- ON_UPDATE(NULL));
+ binlog_row_image_names, DEFAULT(BINLOG_ROW_IMAGE_FULL));
static bool check_pseudo_slave_mode(sys_var *self, THD *thd, set_var *var)
{
@@ -5722,20 +6143,17 @@ 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. "
- "For compatibility with MySQL defaults this variable should be set to "
- "\"autocommit, character_set_client, character_set_connection, "
- "character_set_results, time_zone\"",
+ "Track changes in registered system variables. ",
CMD_LINE(REQUIRED_ARG), IN_SYSTEM_CHARSET,
- DEFAULT(""),
+ DEFAULT("autocommit,character_set_client,character_set_connection,"
+ "character_set_results,time_zone"),
NO_MUTEX_GUARD);
static bool update_session_track_schema(sys_var *self, THD *thd,
enum_var_type type)
{
DBUG_ENTER("update_session_track_schema");
- DBUG_RETURN(thd->session_tracker.get_tracker(CURRENT_SCHEMA_TRACKER)->
- update(thd, NULL));
+ DBUG_RETURN(thd->session_tracker.current_schema.update(thd, NULL));
}
static Sys_var_mybool Sys_session_track_schema(
@@ -5752,8 +6170,7 @@ static bool update_session_track_tx_info(sys_var *self, THD *thd,
enum_var_type type)
{
DBUG_ENTER("update_session_track_tx_info");
- DBUG_RETURN(thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER)->
- update(thd, NULL));
+ DBUG_RETURN(thd->session_tracker.transaction_info.update(thd, NULL));
}
static const char *session_track_transaction_info_names[]=
@@ -5764,7 +6181,7 @@ static Sys_var_enum Sys_session_track_transaction_info(
"Track changes to the transaction attributes. OFF to disable; "
"STATE to track just transaction state (Is there an active transaction? "
"Does it have any data? etc.); CHARACTERISTICS to track transaction "
- "state and report all statements needed to start a transaction with"
+ "state and report all statements needed to start a transaction with "
"the same characteristics (isolation level, read only/read write,"
"snapshot - but not any work done / data modified within the "
"transaction).",
@@ -5778,8 +6195,7 @@ static bool update_session_track_state_change(sys_var *self, THD *thd,
enum_var_type type)
{
DBUG_ENTER("update_session_track_state_change");
- DBUG_RETURN(thd->session_tracker.get_tracker(SESSION_STATE_CHANGE_TRACKER)->
- update(thd, NULL));
+ DBUG_RETURN(thd->session_tracker.state_change.update(thd, NULL));
}
static Sys_var_mybool Sys_session_track_state_change(
@@ -5792,3 +6208,21 @@ static Sys_var_mybool Sys_session_track_state_change(
ON_UPDATE(update_session_track_state_change));
#endif //EMBEDDED_LIBRARY
+
+static Sys_var_uint Sys_in_subquery_conversion_threshold(
+ "in_predicate_conversion_threshold",
+ "The minimum number of scalar elements in the value list of "
+ "IN predicate that triggers its conversion to IN subquery. Set to "
+ "0 to disable the conversion.",
+ SESSION_VAR(in_subquery_conversion_threshold), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, UINT_MAX), DEFAULT(IN_SUBQUERY_CONVERSION_THRESHOLD), BLOCK_SIZE(1));
+
+static Sys_var_enum Sys_secure_timestamp(
+ "secure_timestamp", "Restricts direct setting of a session "
+ "timestamp. Possible levels are: YES - timestamp cannot deviate from "
+ "the system clock, REPLICATION - replication thread can adjust "
+ "timestamp to match the master's, SUPER - a user with this "
+ "privilege and a replication thread can adjust timestamp, NO - "
+ "historical behavior, anyone can modify session timestamp",
+ READ_ONLY GLOBAL_VAR(opt_secure_timestamp), CMD_LINE(REQUIRED_ARG),
+ secure_timestamp_levels, DEFAULT(SECTIME_NO));
diff --git a/sql/sys_vars.ic b/sql/sys_vars.ic
index a18440e550d..860502c40e3 100644
--- a/sql/sys_vars.ic
+++ b/sql/sys_vars.ic
@@ -77,6 +77,11 @@
#define GET_HA_ROWS GET_ULONG
#endif
+// Disable warning caused by SESSION_VAR() macro
+#ifdef __clang__
+#pragma clang diagnostic ignored "-Winvalid-offsetof"
+#endif
+
/*
special assert for sysvars. Tells the name of the variable,
and fails even in non-debug builds.
@@ -88,7 +93,7 @@
while(!(X)) \
{ \
fprintf(stderr, "Sysvar '%s' failed '%s'\n", name_arg, #X); \
- DBUG_ABORT(); \
+ DBUG_ASSERT(0); \
exit(255); \
}
@@ -317,7 +322,7 @@ public:
Class specific constructor arguments:
char* values[] - 0-terminated list of strings of valid values
- Backing store: uint
+ Backing store: ulong
@note
Do *not* use "enum FOO" variables as a backing store, there is no
@@ -379,9 +384,9 @@ public:
{ var->save_result.ulonglong_value= option.def_value; }
uchar *valptr(THD *thd, ulong val)
{ return (uchar*)typelib.type_names[val]; }
- uchar *session_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base)
{ return valptr(thd, session_var(thd, ulong)); }
- uchar *global_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base)
{ return valptr(thd, global_var(ulong)); }
uchar *default_value_ptr(THD *thd)
{ return valptr(thd, (ulong)option.def_value); }
@@ -496,7 +501,10 @@ public:
String str2(buff2, sizeof(buff2), charset), *res;
if (!(res=var->value->val_str(&str)))
+ {
var->save_result.string_value.str= 0;
+ var->save_result.string_value.length= 0; // safety
+ }
else
{
uint32 unused;
@@ -610,7 +618,7 @@ public:
char *new_val= global_update_prepare(thd, var);
if (new_val)
{
- if (sysvartrack_reprint_value(thd, new_val,
+ if (sysvartrack_global_update(thd, new_val,
var->save_result.string_value.length))
new_val= 0;
}
@@ -618,9 +626,7 @@ public:
return (new_val == 0 && var->save_result.string_value.str != 0);
}
bool session_update(THD *thd, set_var *var)
- {
- return sysvartrack_update(thd, var);
- }
+ { return thd->session_tracker.sysvars.update(thd, var); }
void session_save_default(THD *thd, set_var *var)
{
var->save_result.string_value.str= global_var(char*);
@@ -629,26 +635,15 @@ public:
/* parse and feel list with default values */
if (thd)
{
- IF_DBUG(bool res=,)
+#ifdef DBUG_ASSERT_EXISTS
+ bool res=
+#endif
sysvartrack_validate_value(thd,
var->save_result.string_value.str,
var->save_result.string_value.length);
DBUG_ASSERT(res == 0);
}
}
- uchar *session_value_ptr(THD *thd, const LEX_STRING *base)
- {
- DBUG_ASSERT(thd != NULL);
- size_t len= sysvartrack_value_len(thd);
- char *res= (char *)thd->alloc(len + sizeof(char *));
- if (res)
- {
- char *buf= res + sizeof(char *);
- *((char**) res)= buf;
- sysvartrack_value_construct(thd, buf, len);
- }
- return (uchar *)res;
- }
};
#endif //EMBEDDED_LIBRARY
@@ -686,7 +681,7 @@ public:
void global_save_default(THD *thd, set_var *var)
{ DBUG_ASSERT(FALSE); }
protected:
- uchar *session_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base)
{
return thd->security_ctx->proxy_user[0] ?
(uchar *) &(thd->security_ctx->proxy_user[0]) : NULL;
@@ -702,7 +697,7 @@ public:
{}
protected:
- uchar *session_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base)
{
return (uchar*)thd->security_ctx->external_user;
}
@@ -742,7 +737,7 @@ public:
bool global_update(THD *thd, set_var *var);
protected:
- uchar *global_value_ptr(THD *thd, const LEX_STRING *base);
+ uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base);
bool set_filter_value(const char *value, Master_info *mi);
};
@@ -753,7 +748,7 @@ protected:
Class specific constructor arguments:
enum charset_enum is_os_charset_arg
- Backing store: LEX_STRING
+ Backing store: LEX_CSTRING
@note
Behaves exactly as Sys_var_charptr, only the backing store is different.
@@ -774,22 +769,22 @@ public:
getopt, is_os_charset_arg, def_val, lock, binlog_status_arg,
on_check_func, on_update_func, substitute)
{
- global_var(LEX_STRING).length= strlen(def_val);
- SYSVAR_ASSERT(size == sizeof(LEX_STRING));
+ global_var(LEX_CSTRING).length= strlen(def_val);
+ SYSVAR_ASSERT(size == sizeof(LEX_CSTRING));
*const_cast<SHOW_TYPE*>(&show_val_type)= SHOW_LEX_STRING;
}
bool global_update(THD *thd, set_var *var)
{
if (Sys_var_charptr::global_update(thd, var))
return true;
- global_var(LEX_STRING).length= var->save_result.string_value.length;
+ global_var(LEX_CSTRING).length= var->save_result.string_value.length;
return false;
}
};
/*
- A LEX_STRING stored only in thd->variables
+ A LEX_CSTRING stored only in thd->variables
Only to be used for small buffers
*/
@@ -839,10 +834,10 @@ public:
}
bool session_update(THD *thd, set_var *var)
{
- LEX_STRING *tmp= &session_var(thd, LEX_STRING);
+ LEX_CSTRING *tmp= &session_var(thd, LEX_CSTRING);
tmp->length= var->save_result.string_value.length;
/* Store as \0 terminated string (just to be safe) */
- strmake(tmp->str, var->save_result.string_value.str, tmp->length);
+ strmake((char*) tmp->str, var->save_result.string_value.str, tmp->length);
return false;
}
bool global_update(THD *thd, set_var *var)
@@ -860,7 +855,7 @@ public:
{
DBUG_ASSERT(FALSE);
}
- uchar *global_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base)
{
DBUG_ASSERT(FALSE);
return NULL;
@@ -903,9 +898,16 @@ public:
String str(buff, sizeof(buff), system_charset_info), *res;
if (!(res=var->value->val_str(&str)))
+ {
var->save_result.string_value.str= const_cast<char*>("");
+ var->save_result.string_value.length= 0;
+ }
else
- var->save_result.string_value.str= thd->strmake(res->ptr(), res->length());
+ {
+ size_t len= res->length();
+ var->save_result.string_value.str= thd->strmake(res->ptr(), len);
+ var->save_result.string_value.length= len;
+ }
return false;
}
bool session_update(THD *thd, set_var *var)
@@ -929,14 +931,15 @@ public:
{
char *ptr= (char*)(intptr)option.def_value;
var->save_result.string_value.str= ptr;
+ var->save_result.string_value.length= safe_strlen(ptr);
}
- uchar *session_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base)
{
char buf[256];
DBUG_EXPLAIN(buf, sizeof(buf));
return (uchar*) thd->strdup(buf);
}
- uchar *global_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base)
{
char buf[256];
DBUG_EXPLAIN_INITIAL(buf, sizeof(buf));
@@ -991,7 +994,7 @@ public:
bool global_update(THD *thd, set_var *var)
{
ulonglong new_value= var->save_result.ulonglong_value;
- LEX_STRING *base_name= &var->base;
+ LEX_CSTRING *base_name= &var->base;
KEY_CACHE *key_cache;
/* If no basename, assume it's for the key cache named 'default' */
@@ -1018,7 +1021,7 @@ public:
return keycache_update(thd, key_cache, offset, new_value);
}
- uchar *global_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base)
{
KEY_CACHE *key_cache= get_key_cache(base);
if (!key_cache)
@@ -1203,7 +1206,7 @@ public:
lock, binlog_status_arg, on_check_func, on_update_func,
substitute)
{ }
- uchar *session_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base)
{
if (thd->user_connect && thd->user_connect->user_resources.user_conn)
return (uchar*) &(thd->user_connect->user_resources.user_conn);
@@ -1245,7 +1248,7 @@ public:
global_var(ulonglong)= def_val;
SYSVAR_ASSERT(typelib.count > 1);
SYSVAR_ASSERT(typelib.count <= 65);
- SYSVAR_ASSERT(def_val < my_set_bits(typelib.count));
+ SYSVAR_ASSERT(def_val <= my_set_bits(typelib.count-1));
SYSVAR_ASSERT(strcmp(values[typelib.count-1], "default") == 0);
SYSVAR_ASSERT(size == sizeof(ulonglong));
}
@@ -1281,7 +1284,7 @@ public:
default_value,
res->ptr(), res->length(),
&error, &error_len);
- if (error)
+ if (unlikely(error))
{
ErrConvString err(error, error_len, res->charset());
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name.str, err.ptr());
@@ -1317,9 +1320,9 @@ public:
{ var->save_result.ulonglong_value= option.def_value; }
uchar *valptr(THD *thd, ulonglong val)
{ return (uchar*)flagset_to_string(thd, 0, val, typelib.type_names); }
- uchar *session_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base)
{ return valptr(thd, session_var(thd, ulonglong)); }
- uchar *global_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base)
{ return valptr(thd, global_var(ulonglong)); }
uchar *default_value_ptr(THD *thd)
{ return valptr(thd, option.def_value); }
@@ -1386,7 +1389,8 @@ public:
uint error_len;
bool not_used;
- if (!(res=var->value->val_str_ascii(&str)))
+ if (!(res= var->value->val_str_ascii_revert_empty_string_is_null(thd,
+ &str)))
return true;
var->save_result.ulonglong_value=
@@ -1430,9 +1434,9 @@ public:
{ var->save_result.ulonglong_value= option.def_value; }
uchar *valptr(THD *thd, ulonglong val)
{ return (uchar*)set_to_string(thd, 0, val, typelib.type_names); }
- uchar *session_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base)
{ return valptr(thd, session_var(thd, ulonglong)); }
- uchar *global_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base)
{ return valptr(thd, global_var(ulonglong)); }
uchar *default_value_ptr(THD *thd)
{ return valptr(thd, option.def_value); }
@@ -1483,7 +1487,7 @@ public:
var->save_result.plugin= NULL;
else
{
- const LEX_STRING pname= { const_cast<char*>(res->ptr()), res->length() };
+ const LEX_CSTRING pname= { const_cast<char*>(res->ptr()), res->length() };
plugin_ref plugin;
// special code for storage engines (e.g. to handle historical aliases)
@@ -1491,7 +1495,7 @@ public:
plugin= ha_resolve_by_name(thd, &pname, false);
else
plugin= my_plugin_lock_by_name(thd, &pname, plugin_type);
- if (!plugin)
+ if (unlikely(!plugin))
{
// historically different error code
if (plugin_type == MYSQL_STORAGE_ENGINE_PLUGIN)
@@ -1537,7 +1541,7 @@ public:
if (!default_value)
return 0;
- LEX_STRING pname= { default_value, strlen(pname.str) };
+ LEX_CSTRING pname= { default_value, strlen(default_value) };
plugin_ref plugin;
if (plugin_type == MYSQL_STORAGE_ENGINE_PLUGIN)
@@ -1558,14 +1562,123 @@ public:
return (uchar*)(plugin ? thd->strmake(plugin_name(plugin)->str,
plugin_name(plugin)->length) : 0);
}
- uchar *session_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base)
{ return valptr(thd, session_var(thd, plugin_ref)); }
- uchar *global_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base)
{ return valptr(thd, global_var(plugin_ref)); }
uchar *default_value_ptr(THD *thd)
{ return valptr(thd, get_default(thd)); }
};
+/**
+ Class for variables that containg a list of plugins.
+ Currently this is used only for @@gtid_pos_auto_create_engines
+
+ Backing store: plugin_ref
+
+ @note
+ Currently this is only used for storage engine type plugins, and thus only
+ storage engine type plugin is implemented. It could be extended to other
+ plugin types later if needed, similar to Sys_var_plugin.
+
+ These variables don't support command-line equivalents, any such
+ command-line options should be added manually to my_long_options in mysqld.cc
+
+ Note on lifetimes of resources allocated: We allocate a zero-terminated array
+ of plugin_ref*, and lock the contained plugins. The list in the global
+ variable must be freed (with free_engine_list()). However, the way Sys_var
+ works, there is no place to explicitly free other lists, like the one
+ returned from get_default().
+
+ Therefore, the code needs to work with temporary lists, which are
+ registered in the THD to be automatically freed (and plugins similarly
+ automatically unlocked). This is why do_check() allocates a temporary
+ list, from which do_update() then makes a permanent copy.
+*/
+class Sys_var_pluginlist: public sys_var
+{
+public:
+ Sys_var_pluginlist(const char *name_arg,
+ const char *comment, int flag_args, ptrdiff_t off, size_t size,
+ CMD_LINE getopt,
+ 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(&all_sys_vars, name_arg, comment, flag_args, off, getopt.id,
+ getopt.arg_type, SHOW_CHAR, (intptr)def_val,
+ lock, binlog_status_arg, on_check_func, on_update_func,
+ substitute)
+ {
+ option.var_type|= GET_STR;
+ SYSVAR_ASSERT(size == sizeof(plugin_ref));
+ SYSVAR_ASSERT(getopt.id < 0); // force NO_CMD_LINE
+ }
+ bool do_check(THD *thd, set_var *var)
+ {
+ char buff[STRING_BUFFER_USUAL_SIZE];
+ String str(buff,sizeof(buff), system_charset_info), *res;
+ plugin_ref *plugins;
+
+ if (!(res=var->value->val_str(&str)))
+ plugins= resolve_engine_list(thd, "", 0, true, true);
+ else
+ plugins= resolve_engine_list(thd, res->ptr(), res->length(), true, true);
+ if (!plugins)
+ return true;
+ var->save_result.plugins= plugins;
+ return false;
+ }
+ void do_update(plugin_ref **valptr, plugin_ref* newval)
+ {
+ plugin_ref *oldval= *valptr;
+ *valptr= copy_engine_list(newval);
+ free_engine_list(oldval);
+ }
+ bool session_update(THD *thd, set_var *var)
+ {
+ do_update((plugin_ref**)session_var_ptr(thd),
+ var->save_result.plugins);
+ return false;
+ }
+ bool global_update(THD *thd, set_var *var)
+ {
+ do_update((plugin_ref**)global_var_ptr(),
+ var->save_result.plugins);
+ return false;
+ }
+ void session_save_default(THD *thd, set_var *var)
+ {
+ plugin_ref* plugins= global_var(plugin_ref *);
+ var->save_result.plugins= plugins ? temp_copy_engine_list(thd, plugins) : 0;
+ }
+ plugin_ref *get_default(THD *thd)
+ {
+ char *default_value= *reinterpret_cast<char**>(option.def_value);
+ if (!default_value)
+ return 0;
+ return resolve_engine_list(thd, default_value, strlen(default_value),
+ false, true);
+ }
+
+ void global_save_default(THD *thd, set_var *var)
+ {
+ var->save_result.plugins= get_default(thd);
+ }
+
+ uchar *valptr(THD *thd, plugin_ref *plugins)
+ {
+ return (uchar*)pretty_print_engine_list(thd, plugins);
+ }
+ uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base)
+ { return valptr(thd, session_var(thd, plugin_ref*)); }
+ uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base)
+ { return valptr(thd, global_var(plugin_ref*)); }
+ uchar *default_value_ptr(THD *thd)
+ { return valptr(thd, get_default(thd)); }
+};
+
#if defined(ENABLED_DEBUG_SYNC)
#include "debug_sync.h"
@@ -1626,11 +1739,11 @@ public:
{
DBUG_ASSERT(FALSE);
}
- uchar *session_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base)
{
return debug_sync_value_ptr(thd);
}
- uchar *global_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base)
{
DBUG_ASSERT(FALSE);
return 0;
@@ -1685,12 +1798,12 @@ public:
binlog_status_arg, on_check_func, on_update_func,
substitute)
{
- option.var_type|= GET_BOOL;
+ option.var_type|= GET_BIT;
reverse_semantics= my_count_bits(bitmask_arg) > 1;
bitmask= reverse_semantics ? ~bitmask_arg : bitmask_arg;
+ option.block_size= reverse_semantics ? -(long) bitmask : (long)bitmask;
set(global_var_ptr(), def_val);
SYSVAR_ASSERT(def_val < 2);
- SYSVAR_ASSERT(getopt.id < 0); // force NO_CMD_LINE
SYSVAR_ASSERT(size == sizeof(ulonglong));
}
bool session_update(THD *thd, set_var *var)
@@ -1713,9 +1826,9 @@ public:
thd->sys_var_tmp.my_bool_value= reverse_semantics ^ ((val & bitmask) != 0);
return (uchar*) &thd->sys_var_tmp.my_bool_value;
}
- uchar *session_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base)
{ return valptr(thd, session_var(thd, ulonglong)); }
- uchar *global_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base)
{ return valptr(thd, global_var(ulonglong)); }
uchar *default_value_ptr(THD *thd)
{
@@ -1776,12 +1889,12 @@ public:
{ var->value= 0; }
void global_save_default(THD *thd, set_var *var)
{ DBUG_ASSERT(FALSE); }
- uchar *session_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base)
{
thd->sys_var_tmp.ulonglong_value= read_func(thd);
return (uchar*) &thd->sys_var_tmp.ulonglong_value;
}
- uchar *global_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base)
{
DBUG_ASSERT(FALSE);
return 0;
@@ -1809,10 +1922,11 @@ public:
const char *comment, int flag_args,
CMD_LINE getopt,
double min_val, double max_val,
- PolyLock *lock, enum binlog_status_enum binlog_status_arg)
+ PolyLock *lock, enum binlog_status_enum binlog_status_arg,
+ on_check_function on_check_func=0)
: Sys_var_double(name_arg, comment, flag_args, 0,
sizeof(double), getopt, min_val,
- max_val, 0, lock, binlog_status_arg)
+ max_val, 0, lock, binlog_status_arg, on_check_func)
{
SYSVAR_ASSERT(scope() == ONLY_SESSION);
SYSVAR_ASSERT(getopt.id < 0); // NO_CMD_LINE, because the offset is fake
@@ -1841,13 +1955,13 @@ public:
{ var->value= 0; }
void global_save_default(THD *thd, set_var *var)
{ DBUG_ASSERT(FALSE); }
- uchar *session_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base)
{
thd->sys_var_tmp.double_value= thd->start_time +
thd->start_time_sec_part/(double)TIME_SECOND_PART_FACTOR;
return (uchar*) &thd->sys_var_tmp.double_value;
}
- uchar *global_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base)
{
DBUG_ASSERT(FALSE);
return 0;
@@ -1911,12 +2025,12 @@ public:
}
void session_save_default(THD *thd, set_var *var) { }
void global_save_default(THD *thd, set_var *var) { }
- uchar *session_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base)
{
DBUG_ASSERT(FALSE);
return 0;
}
- uchar *global_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base)
{
return (uchar*)show_comp_option_name[global_var(enum SHOW_COMP_OPTION)];
}
@@ -1988,9 +2102,9 @@ public:
}
uchar *valptr(THD *thd, uchar *val)
{ return val ? *(uchar**)(val+name_offset) : 0; }
- uchar *session_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base)
{ return valptr(thd, session_var(thd, uchar*)); }
- uchar *global_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base)
{ return valptr(thd, global_var(uchar*)); }
uchar *default_value_ptr(THD *thd)
{ return valptr(thd, *(uchar**)option.def_value); }
@@ -2064,7 +2178,7 @@ public:
}
uchar *valptr(THD *thd, Time_zone *val)
{ return (uchar *)(val->get_name()->ptr()); }
- uchar *session_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base)
{
/*
This is an ugly fix for replication: we don't replicate properly queries
@@ -2077,7 +2191,7 @@ public:
thd->time_zone_used= 1;
return valptr(thd, session_var(thd, Time_zone *));
}
- uchar *global_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base)
{ return valptr(thd, global_var(Time_zone*)); }
uchar *default_value_ptr(THD *thd)
{ return valptr(thd, *(Time_zone**)option.def_value); }
@@ -2111,14 +2225,6 @@ public:
return TRUE;
if (var->type == OPT_DEFAULT || !thd->in_active_multi_stmt_transaction())
{
-#ifndef EMBEDDED_LIBRARY
- Transaction_state_tracker *tst= NULL;
-
- if (thd->variables.session_track_transaction_info > TX_TRACK_NONE)
- tst= (Transaction_state_tracker *)
- thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER);
-#endif //EMBEDDED_LIBRARY
-
thd->tx_isolation= (enum_tx_isolation) var->save_result.ulonglong_value;
#ifndef EMBEDDED_LIBRARY
@@ -2142,13 +2248,11 @@ public:
DBUG_ASSERT(0);
return TRUE;
}
- if (tst)
- tst->set_isol_level(thd, l);
- }
- else if (tst)
- {
- tst->set_isol_level(thd, TX_ISOL_INHERIT);
+ if (thd->variables.session_track_transaction_info > TX_TRACK_NONE)
+ thd->session_tracker.transaction_info.set_isol_level(thd, l);
}
+ else if (thd->variables.session_track_transaction_info > TX_TRACK_NONE)
+ thd->session_tracker.transaction_info.set_isol_level(thd, TX_ISOL_INHERIT);
#endif //EMBEDDED_LIBRARY
}
return FALSE;
@@ -2248,7 +2352,7 @@ public:
/* Use value given in variable declaration */
global_save_default(thd, var);
}
- uchar *session_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base)
{
ulonglong *tmp, res;
tmp= (ulonglong*) (((uchar*)&(thd->variables)) + offset);
@@ -2256,7 +2360,7 @@ public:
*tmp= res;
return (uchar*) tmp;
}
- uchar *global_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base)
{
return session_value_ptr(thd, base);
}
@@ -2308,12 +2412,12 @@ public:
{
DBUG_ASSERT(false);
}
- uchar *session_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base)
{
DBUG_ASSERT(false);
return NULL;
}
- uchar *global_value_ptr(THD *thd, const LEX_STRING *base);
+ uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base);
};
@@ -2357,12 +2461,12 @@ public:
{
DBUG_ASSERT(false);
}
- uchar *session_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base)
{
DBUG_ASSERT(false);
return NULL;
}
- uchar *global_value_ptr(THD *thd, const LEX_STRING *base);
+ uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base);
};
@@ -2397,12 +2501,12 @@ public:
/* Record the attempt to use default so we can error. */
var->value= 0;
}
- uchar *session_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base)
{
DBUG_ASSERT(false);
return NULL;
}
- uchar *global_value_ptr(THD *thd, const LEX_STRING *base);
+ uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base);
uchar *default_value_ptr(THD *thd)
{ return 0; }
};
@@ -2439,12 +2543,12 @@ public:
/* Record the attempt to use default so we can error. */
var->value= 0;
}
- uchar *session_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base)
{
DBUG_ASSERT(false);
return NULL;
}
- uchar *global_value_ptr(THD *thd, const LEX_STRING *base);
+ uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base);
uchar *default_value_ptr(THD *thd)
{ return 0; }
};
@@ -2489,8 +2593,8 @@ public:
{
DBUG_ASSERT(false);
}
- uchar *session_value_ptr(THD *thd, const LEX_STRING *base);
- uchar *global_value_ptr(THD *thd, const LEX_STRING *base)
+ uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base);
+ uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base)
{
DBUG_ASSERT(false);
return NULL;
@@ -2516,5 +2620,98 @@ public:
SYSVAR_ASSERT(scope() == GLOBAL);
}
bool global_update(THD *thd, set_var *var);
- uchar *global_value_ptr(THD *thd, const LEX_STRING *base);
+ uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base);
+};
+
+
+class Sys_var_vers_asof: public Sys_var_enum
+{
+public:
+ static const char *asof_keywords[];
+
+public:
+ Sys_var_vers_asof(const char *name_arg,
+ const char *comment, int flag_args, ptrdiff_t off, size_t size,
+ CMD_LINE getopt, const char *values[],
+ uint def_val)
+ : Sys_var_enum(name_arg, comment, flag_args, off, size,
+ getopt, values, def_val)
+ {
+ // setval() accepts string rather enum
+ option.var_type= GET_STR;
+ }
+ virtual bool do_check(THD *thd, set_var *var)
+ {
+ if (!Sys_var_enum::do_check(thd, var))
+ return false;
+ MYSQL_TIME ltime;
+ bool res= var->value->get_date(&ltime, TIME_NO_ZERO_IN_DATE|TIME_NO_ZERO_DATE);
+ if (!res)
+ {
+ var->save_result.ulonglong_value= SYSTEM_TIME_AS_OF;
+ }
+ return res;
+ }
+
+private:
+ bool update(set_var *var, vers_asof_timestamp_t &out)
+ {
+ bool res= false;
+ out.type= static_cast<enum_var_type>(var->save_result.ulonglong_value);
+ if (out.type == SYSTEM_TIME_AS_OF)
+ {
+ if (var->value)
+ {
+ res= var->value->get_date(&out.ltime, TIME_NO_ZERO_IN_DATE|TIME_NO_ZERO_DATE);
+ }
+ else // set DEFAULT from global var
+ {
+ out= global_var(vers_asof_timestamp_t);
+ res= false;
+ }
+ }
+ return res;
+ }
+
+public:
+ virtual bool global_update(THD *thd, set_var *var)
+ {
+ return update(var, global_var(vers_asof_timestamp_t));
+ }
+ virtual bool session_update(THD *thd, set_var *var)
+ {
+ return update(var, session_var(thd, vers_asof_timestamp_t));
+ }
+
+private:
+ uchar *value_ptr(THD *thd, vers_asof_timestamp_t &val)
+ {
+ switch (val.type)
+ {
+ case SYSTEM_TIME_UNSPECIFIED:
+ case SYSTEM_TIME_ALL:
+ return (uchar*) thd->strdup(asof_keywords[val.type]);
+ case SYSTEM_TIME_AS_OF:
+ {
+ uchar *buf= (uchar*) thd->alloc(MAX_DATE_STRING_REP_LENGTH);
+ if (buf &&!my_datetime_to_str(&val.ltime, (char*) buf, 6))
+ {
+ // TODO: figure out variable name
+ my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "system_versioning_asof_timestamp", "NULL (wrong datetime)");
+ return (uchar*) thd->strdup("Error: wrong datetime");
+ }
+ return buf;
+ }
+ default:
+ break;
+ }
+ my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "system_versioning_asof_timestamp", "NULL (wrong range type)");
+ return (uchar*) thd->strdup("Error: wrong range type");
+ }
+
+public:
+ virtual uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base)
+ { return value_ptr(thd, session_var(thd, vers_asof_timestamp_t)); }
+ virtual uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base)
+ { return value_ptr(thd, global_var(vers_asof_timestamp_t)); }
};
diff --git a/sql/sys_vars_shared.h b/sql/sys_vars_shared.h
index 92a16d2e8d6..bc48d1f7fff 100644
--- a/sql/sys_vars_shared.h
+++ b/sql/sys_vars_shared.h
@@ -33,7 +33,7 @@ extern bool throw_bounds_warning(THD *thd, const char *name,
bool fixed, bool is_unsigned, longlong v);
extern bool throw_bounds_warning(THD *thd, const char *name, bool fixed,
double v);
-extern sys_var *intern_find_sys_var(const char *str, uint length);
+extern sys_var *intern_find_sys_var(const char *str, size_t length);
extern sys_var_chain all_sys_vars;
diff --git a/sql/table.cc b/sql/table.cc
index e4492f21a30..403b68551a0 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2017, Oracle and/or its affiliates.
- Copyright (c) 2008, 2018, MariaDB
+ Copyright (c) 2008, 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,7 +17,7 @@
/* Some general useful functions */
-#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
#include "table.h"
#include "key.h" // find_ref_key
@@ -42,6 +42,8 @@
#include "sql_view.h"
#include "rpl_filter.h"
#include "sql_cte.h"
+#include "ha_sequence.h"
+#include "sql_show.h"
/* For MySQL 5.7 virtual fields */
#define MYSQL57_GENERATED_FIELD 128
@@ -60,28 +62,34 @@ public:
static Virtual_column_info * unpack_vcol_info_from_frm(THD *, MEM_ROOT *,
TABLE *, String *, Virtual_column_info **, bool *);
-static bool check_vcol_forward_refs(Field *, Virtual_column_info *);
+static bool check_vcol_forward_refs(Field *, Virtual_column_info *,
+ bool check_constraint);
/* INFORMATION_SCHEMA name */
-LEX_STRING INFORMATION_SCHEMA_NAME= {C_STRING_WITH_LEN("information_schema")};
+LEX_CSTRING INFORMATION_SCHEMA_NAME= {STRING_WITH_LEN("information_schema")};
/* PERFORMANCE_SCHEMA name */
-LEX_STRING PERFORMANCE_SCHEMA_DB_NAME= {C_STRING_WITH_LEN("performance_schema")};
+LEX_CSTRING PERFORMANCE_SCHEMA_DB_NAME= {STRING_WITH_LEN("performance_schema")};
/* MYSQL_SCHEMA name */
-LEX_STRING MYSQL_SCHEMA_NAME= {C_STRING_WITH_LEN("mysql")};
+LEX_CSTRING MYSQL_SCHEMA_NAME= {STRING_WITH_LEN("mysql")};
/* GENERAL_LOG name */
-LEX_STRING GENERAL_LOG_NAME= {C_STRING_WITH_LEN("general_log")};
+LEX_CSTRING GENERAL_LOG_NAME= {STRING_WITH_LEN("general_log")};
/* SLOW_LOG name */
-LEX_STRING SLOW_LOG_NAME= {C_STRING_WITH_LEN("slow_log")};
+LEX_CSTRING SLOW_LOG_NAME= {STRING_WITH_LEN("slow_log")};
+
+LEX_CSTRING TRANSACTION_REG_NAME= {STRING_WITH_LEN("transaction_registry")};
+LEX_CSTRING MYSQL_USER_NAME= {STRING_WITH_LEN("user")};
+LEX_CSTRING MYSQL_DB_NAME= {STRING_WITH_LEN("db")};
+LEX_CSTRING MYSQL_PROC_NAME= {STRING_WITH_LEN("proc")};
/*
Keyword added as a prefix when parsing the defining expression for a
virtual column read from the column definition saved in the frm file
*/
-static LEX_STRING parse_vcol_keyword= { C_STRING_WITH_LEN("PARSE_VCOL_EXPR ") };
+static LEX_CSTRING parse_vcol_keyword= { STRING_WITH_LEN("PARSE_VCOL_EXPR ") };
static int64 last_table_id;
@@ -94,7 +102,7 @@ static bool fix_type_pointers(const char ***typelib_value_names,
static uint find_field(Field **fields, uchar *record, uint start, uint length);
-inline bool is_system_table_name(const char *name, uint length);
+inline bool is_system_table_name(const char *name, size_t length);
/**************************************************************************
Object_creation_ctx implementation.
@@ -173,8 +181,8 @@ View_creation_ctx * View_creation_ctx::create(THD *thd,
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_VIEW_NO_CREATION_CTX,
ER_THD(thd, ER_VIEW_NO_CREATION_CTX),
- (const char *) view->db,
- (const char *) view->table_name);
+ view->db.str,
+ view->table_name.str);
ctx->m_client_cs= system_charset_info;
ctx->m_connection_cl= system_charset_info;
@@ -199,16 +207,16 @@ View_creation_ctx * View_creation_ctx::create(THD *thd,
{
sql_print_warning("View '%s'.'%s': there is unknown charset/collation "
"names (client: '%s'; connection: '%s').",
- (const char *) view->db,
- (const char *) view->table_name,
+ view->db.str,
+ view->table_name.str,
(const char *) view->view_client_cs_name.str,
(const char *) view->view_connection_cl_name.str);
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_VIEW_INVALID_CREATION_CTX,
ER_THD(thd, ER_VIEW_INVALID_CREATION_CTX),
- (const char *) view->db,
- (const char *) view->table_name);
+ view->db.str,
+ view->table_name.str);
}
return ctx;
@@ -221,8 +229,8 @@ View_creation_ctx * View_creation_ctx::create(THD *thd,
static uchar *get_field_name(Field **buff, size_t *length,
my_bool not_used __attribute__((unused)))
{
- *length= (uint) strlen((*buff)->field_name);
- return (uchar*) (*buff)->field_name;
+ *length= (uint) (*buff)->field_name.length;
+ return (uchar*) (*buff)->field_name.str;
}
@@ -230,63 +238,50 @@ static uchar *get_field_name(Field **buff, size_t *length,
Returns pointer to '.frm' extension of the file name.
SYNOPSIS
- fn_rext()
+ fn_frm_ext()
name file name
DESCRIPTION
Checks file name part starting with the rightmost '.' character,
and returns it if it is equal to '.frm'.
- TODO
- It is a good idea to get rid of this function modifying the code
- to garantee that the functions presently calling fn_rext() always
- get arguments in the same format: either with '.frm' or without '.frm'.
-
RETURN VALUES
- Pointer to the '.frm' extension. If there is no extension,
- or extension is not '.frm', pointer at the end of file name.
+ Pointer to the '.frm' extension or NULL if not a .frm file
*/
-char *fn_rext(char *name)
+const char *fn_frm_ext(const char *name)
{
- char *res= strrchr(name, '.');
+ const char *res= strrchr(name, '.');
if (res && !strcmp(res, reg_ext))
return res;
- return name + strlen(name);
+ return 0;
}
-TABLE_CATEGORY get_table_category(const LEX_STRING *db, const LEX_STRING *name)
+
+TABLE_CATEGORY get_table_category(const LEX_CSTRING *db,
+ const LEX_CSTRING *name)
{
DBUG_ASSERT(db != NULL);
DBUG_ASSERT(name != NULL);
- if (is_infoschema_db(db->str, db->length))
+ if (is_infoschema_db(db))
return TABLE_CATEGORY_INFORMATION;
- if ((db->length == PERFORMANCE_SCHEMA_DB_NAME.length) &&
- (my_strcasecmp(system_charset_info,
- PERFORMANCE_SCHEMA_DB_NAME.str,
- db->str) == 0))
+ if (lex_string_eq(&PERFORMANCE_SCHEMA_DB_NAME, db))
return TABLE_CATEGORY_PERFORMANCE;
- if ((db->length == MYSQL_SCHEMA_NAME.length) &&
- (my_strcasecmp(system_charset_info,
- MYSQL_SCHEMA_NAME.str,
- db->str) == 0))
+ if (lex_string_eq(&MYSQL_SCHEMA_NAME, db))
{
if (is_system_table_name(name->str, name->length))
return TABLE_CATEGORY_SYSTEM;
- if ((name->length == GENERAL_LOG_NAME.length) &&
- (my_strcasecmp(system_charset_info,
- GENERAL_LOG_NAME.str,
- name->str) == 0))
+ if (lex_string_eq(&GENERAL_LOG_NAME, name))
return TABLE_CATEGORY_LOG;
- if ((name->length == SLOW_LOG_NAME.length) &&
- (my_strcasecmp(system_charset_info,
- SLOW_LOG_NAME.str,
- name->str) == 0))
+ if (lex_string_eq(&SLOW_LOG_NAME, name))
+ return TABLE_CATEGORY_LOG;
+
+ if (lex_string_eq(&TRANSACTION_REG_NAME, name))
return TABLE_CATEGORY_LOG;
}
@@ -322,7 +317,7 @@ 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_ALLOC_BLOCK_SIZE, 0, MYF(0));
+ init_sql_alloc(&mem_root, "table_share", TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0));
if (multi_alloc_root(&mem_root,
&share, sizeof(*share),
&key_buff, key_length,
@@ -335,15 +330,22 @@ TABLE_SHARE *alloc_table_share(const char *db, const char *table_name,
share->path.str= path_buff;
share->path.length= path_length;
- strmov(share->path.str, path);
+ strmov(path_buff, path);
share->normalized_path.str= share->path.str;
share->normalized_path.length= path_length;
share->table_category= get_table_category(& share->db, & share->table_name);
share->open_errno= ENOENT;
- /* The following will be fixed in open_table_from_share */
- share->cached_row_logging_check= 1;
-
- init_sql_alloc(&share->stats_cb.mem_root, TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0));
+ /* The following will be updated in open_table_from_share */
+ share->can_do_row_logging= 1;
+ 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)
+ share->not_usable_by_query_cache= 1;
+
+ init_sql_alloc(&share->stats_cb.mem_root, "share_stats",
+ TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0));
memcpy((char*) &share->mem_root, (char*) &mem_root, sizeof(mem_root));
mysql_mutex_init(key_TABLE_SHARE_LOCK_share,
@@ -404,8 +406,8 @@ 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, TABLE_ALLOC_BLOCK_SIZE, 0,
- MYF(thd->slave_thread ? 0 : MY_THREAD_SPECIFIC));
+ init_sql_alloc(&share->mem_root, "tmp_table_share", 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;
@@ -418,8 +420,8 @@ void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key,
share->normalized_path.str= (char*) path;
share->path.length= share->normalized_path.length= strlen(path);
share->frm_version= FRM_VER_CURRENT;
-
- share->cached_row_logging_check= 0; // No row logging
+ share->not_usable_by_query_cache= 1;
+ share->can_do_row_logging= 0; // No row logging
/*
table_map_id is also used for MERGE tables to suppress repeated
@@ -450,6 +452,7 @@ void TABLE_SHARE::destroy()
}
delete_stat_values_for_table_share(this);
+ delete sequence;
free_root(&stats_cb.mem_root, MYF(0));
/* The mutexes are initialized only for shares that are part of the TDC */
@@ -520,7 +523,7 @@ void free_table_share(TABLE_SHARE *share)
and should not contain user tables.
*/
-inline bool is_system_table_name(const char *name, uint length)
+inline bool is_system_table_name(const char *name, size_t length)
{
CHARSET_INFO *ci= system_charset_info;
@@ -634,7 +637,7 @@ enum open_frm_error open_table_def(THD *thd, TABLE_SHARE *share, uint flags)
share->is_view= 1;
if (flags & GTS_VIEW)
{
- LEX_STRING pathstr= { path, length };
+ LEX_CSTRING pathstr= { path, length };
/*
Create view file parser and hold it in TABLE_SHARE member
view_def.
@@ -682,7 +685,11 @@ enum open_frm_error open_table_def(THD *thd, TABLE_SHARE *share, uint flags)
frmlen= read_length + sizeof(head);
share->init_from_binary_frm_image(thd, false, buf, frmlen);
- error_given= true; // init_from_binary_frm_image has already called my_error()
+ /*
+ Don't give any additional errors. If there would be a problem,
+ init_from_binary_frm_image would call my_error() itself.
+ */
+ error_given= true;
my_free(buf);
goto err_not_open;
@@ -691,7 +698,10 @@ err:
mysql_file_close(file, MYF(MY_WME));
err_not_open:
- if (share->error && !error_given)
+ /* Mark that table was created earlier and thus should have been logged */
+ share->table_creation_was_logged= 1;
+
+ if (unlikely(share->error && !error_given))
{
share->open_errno= my_errno;
open_table_error(share, share->error, share->open_errno);
@@ -1154,9 +1164,9 @@ bool parse_vcol_defs(THD *thd, MEM_ROOT *mem_root, TABLE *table,
for (field_ptr= table->field; *field_ptr; field_ptr++)
{
Field *field= *field_ptr;
- if (check_vcol_forward_refs(field, field->vcol_info) ||
- check_vcol_forward_refs(field, field->check_constraint) ||
- check_vcol_forward_refs(field, field->default_value))
+ if (check_vcol_forward_refs(field, field->vcol_info, 0) ||
+ check_vcol_forward_refs(field, field->check_constraint, 1) ||
+ check_vcol_forward_refs(field, field->default_value, 0))
goto end;
}
@@ -1204,7 +1214,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
const uchar *frm_image_end = frm_image + frm_length;
uchar *record, *null_flags, *null_pos, *UNINIT_VAR(mysql57_vcol_null_pos);
const uchar *disk_buff, *strpos;
- ulong pos, record_offset;
+ ulong pos, record_offset;
ulong rec_buff_length;
handler *handler_file= 0;
KEY *keyinfo;
@@ -1215,15 +1225,21 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
enum legacy_db_type legacy_db_type;
my_bitmap_map *bitmaps;
bool null_bits_are_used;
- uint vcol_screen_length, UNINIT_VAR(options_len);
+ uint vcol_screen_length;
+ size_t UNINIT_VAR(options_len);
uchar *vcol_screen_pos;
const uchar *options= 0;
- uint UNINIT_VAR(gis_options_len);
+ size_t UNINIT_VAR(gis_options_len);
const uchar *gis_options= 0;
KEY first_keyinfo;
uint len;
uint ext_key_parts= 0;
plugin_ref se_plugin= 0;
+ const uchar *system_period= 0;
+ bool vers_can_native= false;
+ const uchar *extra2_field_flags= 0;
+ size_t extra2_field_flags_length= 0;
+
MEM_ROOT *old_root= thd->mem_root;
Virtual_column_info **table_check_constraints;
DBUG_ENTER("TABLE_SHARE::init_from_binary_frm_image");
@@ -1259,7 +1275,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
if (*extra2 != '/') // old frm had '/' there
{
const uchar *e2end= extra2 + len;
- while (extra2 + 3 < e2end)
+ while (extra2 + 3 <= e2end)
{
uchar type= *extra2++;
size_t length= *extra2++;
@@ -1300,7 +1316,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
case EXTRA2_DEFAULT_PART_ENGINE:
#ifdef WITH_PARTITION_STORAGE_ENGINE
{
- LEX_STRING name= { (char*)extra2, length };
+ LEX_CSTRING name= { (char*)extra2, length };
share->default_part_plugin= ha_resolve_by_name(NULL, &name, false);
if (!share->default_part_plugin)
goto err;
@@ -1317,6 +1333,17 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
}
#endif /*HAVE_SPATIAL*/
break;
+ case EXTRA2_PERIOD_FOR_SYSTEM_TIME:
+ if (system_period || length != 2 * sizeof(uint16))
+ goto err;
+ system_period = extra2;
+ break;
+ case EXTRA2_FIELD_FLAGS:
+ if (extra2_field_flags)
+ goto err;
+ extra2_field_flags= extra2;
+ extra2_field_flags_length= length;
+ break;
default:
/* abort frm parsing if it's an unknown but important extra2 value */
if (type >= EXTRA2_ENGINE_IMPORTANT)
@@ -1356,6 +1383,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
share->db_create_options= db_create_options= uint2korr(frm_image+30);
share->db_options_in_use= share->db_create_options;
share->mysql_version= uint4korr(frm_image+51);
+ share->table_type= TABLE_TYPE_NORMAL;
share->null_field_first= 0;
if (!frm_image[32]) // New frm file in 3.23
{
@@ -1369,6 +1397,14 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
enum_value_with_check(thd, share, "transactional", frm_image[39] & 3, HA_CHOICE_MAX);
share->page_checksum= (ha_choice)
enum_value_with_check(thd, share, "page_checksum", (frm_image[39] >> 2) & 3, HA_CHOICE_MAX);
+ if (((ha_choice) enum_value_with_check(thd, share, "sequence",
+ (frm_image[39] >> 4) & 3,
+ HA_CHOICE_MAX)) == HA_CHOICE_YES)
+ {
+ share->table_type= TABLE_TYPE_SEQUENCE;
+ share->sequence= new (&share->mem_root) SEQUENCE();
+ share->non_determinstic_insert= true;
+ }
share->row_type= (enum row_type)
enum_value_with_check(thd, share, "row_format", frm_image[40], ROW_TYPE_MAX);
@@ -1455,7 +1491,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
if (next_chunk + 2 < buff_end)
{
uint str_db_type_length= uint2korr(next_chunk);
- LEX_STRING name;
+ LEX_CSTRING name;
name.str= (char*) next_chunk + 2;
name.length= str_db_type_length;
@@ -1502,7 +1538,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
else if (!tmp_plugin)
{
/* purecov: begin inspected */
- name.str[name.length]=0;
+ ((char*) name.str)[name.length]=0;
my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), name.str);
goto err;
/* purecov: end */
@@ -1553,7 +1589,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
{
if (keyinfo->flags & HA_USES_PARSER)
{
- LEX_STRING parser_name;
+ LEX_CSTRING parser_name;
if (next_chunk >= buff_end)
{
DBUG_PRINT("error",
@@ -1620,14 +1656,15 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
share->rec_buff_length= rec_buff_length;
if (!(record= (uchar *) alloc_root(&share->mem_root, rec_buff_length)))
goto err; /* purecov: inspected */
- MEM_NOACCESS(record, rec_buff_length);
- MEM_UNDEFINED(record, share->reclength);
+ /* Mark bytes after record as not accessable to catch overrun bugs */
+ MEM_NOACCESS(record + share->reclength, rec_buff_length - share->reclength);
share->default_values= record;
memcpy(record, frm_image + record_offset, share->reclength);
disk_buff= frm_image + pos + FRM_FORMINFO_SIZE;
-
share->fields= uint2korr(forminfo+258);
+ if (extra2_field_flags && extra2_field_flags_length != share->fields)
+ goto err;
pos= uint2korr(forminfo+260); /* Length of all screens */
n_length= uint2korr(forminfo+268);
interval_count= uint2korr(forminfo+270);
@@ -1638,6 +1675,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
vcol_screen_length= uint2korr(forminfo+286);
share->virtual_fields= share->default_expressions=
share->field_check_constraints= share->default_fields= 0;
+ share->visible_fields= 0;
share->stored_fields= share->fields;
if (forminfo[46] != (uchar)255)
{
@@ -1770,6 +1808,28 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
strpos, vcol_screen_pos);
}
+ /* Set system versioning information. */
+ if (system_period == NULL)
+ {
+ versioned= VERS_UNDEFINED;
+ row_start_field= 0;
+ row_end_field= 0;
+ }
+ else
+ {
+ DBUG_PRINT("info", ("Setting system versioning informations"));
+ uint16 row_start= uint2korr(system_period);
+ uint16 row_end= uint2korr(system_period + sizeof(uint16));
+ if (row_start >= share->fields || row_end >= share->fields)
+ goto err;
+ DBUG_PRINT("info", ("Columns with system versioning: [%d, %d]", row_start, row_end));
+ versioned= VERS_TIMESTAMP;
+ vers_can_native= handler_file->vers_can_native(thd);
+ row_start_field= row_start;
+ row_end_field= row_end;
+ status_var_increment(thd->status_var.feature_system_versioning);
+ } // if (system_period == NULL)
+
for (i=0 ; i < share->fields; i++, strpos+=field_pack_length, field_ptr++)
{
uint pack_flag, interval_nr, unireg_type, recpos, field_length;
@@ -1778,10 +1838,13 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
enum_field_types field_type;
CHARSET_INFO *charset=NULL;
Field::geometry_type geom_type= Field::GEOM_GEOMETRY;
- LEX_STRING comment;
+ LEX_CSTRING comment;
+ LEX_CSTRING name;
Virtual_column_info *vcol_info= 0;
uint gis_length, gis_decimals, srid= 0;
Field::utype unireg_check;
+ const Type_handler *handler;
+ uint32 flags= 0;
if (new_frm_ver >= 3)
{
@@ -1824,7 +1887,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
char tmp[10];
if (!csname || csname[0] =='?')
{
- my_snprintf(tmp, sizeof(tmp), "#%d", cs_new);
+ my_snprintf(tmp, sizeof(tmp), "#%u", cs_new);
csname= tmp;
}
my_printf_error(ER_UNKNOWN_COLLATION,
@@ -1993,14 +2056,47 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
swap_variables(uint, null_bit_pos, mysql57_vcol_null_bit_pos);
}
+ if (versioned)
+ {
+ if (i == row_start_field)
+ flags|= VERS_SYS_START_FLAG;
+ else if (i == row_end_field)
+ flags|= VERS_SYS_END_FLAG;
+
+ if (flags & VERS_SYSTEM_FIELD)
+ {
+ switch (field_type)
+ {
+ case MYSQL_TYPE_TIMESTAMP2:
+ break;
+ case MYSQL_TYPE_LONGLONG:
+ if (vers_can_native)
+ {
+ versioned= VERS_TRX_ID;
+ break;
+ }
+ /* Fallthrough */
+ default:
+ my_error(ER_VERS_FIELD_WRONG_TYPE, MYF(0), fieldnames.type_names[i],
+ versioned == VERS_TIMESTAMP ? "TIMESTAMP(6)" : "BIGINT(20) UNSIGNED",
+ table_name.str);
+ goto err;
+ }
+ }
+ }
+
/* Convert pre-10.2.2 timestamps to use Field::default_value */
unireg_check= (Field::utype) MTYP_TYPENR(unireg_type);
+ name.str= fieldnames.type_names[i];
+ name.length= strlen(name.str);
+ if (!(handler= Type_handler::get_handler_by_real_type(field_type)))
+ goto err; // Not supported field type
*field_ptr= reg_field=
make_field(share, &share->mem_root, record+recpos, (uint32) field_length,
- null_pos, null_bit_pos, pack_flag, field_type, charset,
+ null_pos, null_bit_pos, pack_flag, handler, charset,
geom_type, srid, unireg_check,
(interval_nr ? share->intervals+interval_nr-1 : NULL),
- share->fieldnames.type_names[i]);
+ &name, flags);
if (!reg_field) // Not supported field type
goto err;
@@ -2016,6 +2112,19 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
reg_field->field_index= i;
reg_field->comment=comment;
reg_field->vcol_info= vcol_info;
+ reg_field->flags|= flags;
+ if (extra2_field_flags)
+ {
+ uchar flags= *extra2_field_flags++;
+ if (flags & VERS_OPTIMIZED_UPDATE)
+ reg_field->flags|= VERS_UPDATE_UNVERSIONED_FLAG;
+
+ reg_field->invisible= f_visibility(flags);
+ }
+ if (reg_field->invisible == INVISIBLE_USER)
+ status_var_increment(thd->status_var.feature_invisible_columns);
+ if (!reg_field->invisible)
+ share->visible_fields++;
if (field_type == MYSQL_TYPE_BIT && !f_bit_as_char(pack_flag))
{
null_bits_are_used= 1;
@@ -2033,8 +2142,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
if (vcol_info)
{
- vcol_info->name.str= const_cast<char*>(reg_field->field_name);
- vcol_info->name.length = strlen(reg_field->field_name);
+ vcol_info->name= reg_field->field_name;
if (mysql57_null_bits && !vcol_info->stored_in_db)
{
/* MySQL 5.7 has null bits last */
@@ -2152,18 +2260,18 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
for (uint key=0 ; key < keys ; key++,keyinfo++)
{
uint usable_parts= 0;
- keyinfo->name=(char*) share->keynames.type_names[key];
- keyinfo->name_length= strlen(keyinfo->name);
+ keyinfo->name.str= share->keynames.type_names[key];
+ keyinfo->name.length= strlen(keyinfo->name.str);
keyinfo->cache_name=
(uchar*) alloc_root(&share->mem_root,
share->table_cache_key.length+
- keyinfo->name_length + 1);
+ keyinfo->name.length + 1);
if (keyinfo->cache_name) // If not out of memory
{
uchar *pos= keyinfo->cache_name;
memcpy(pos, share->table_cache_key.str, share->table_cache_key.length);
- memcpy(pos + share->table_cache_key.length, keyinfo->name,
- keyinfo->name_length+1);
+ memcpy(pos + share->table_cache_key.length, keyinfo->name.str,
+ keyinfo->name.length+1);
}
if (ext_key_parts > share->key_parts && key)
@@ -2266,6 +2374,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
field= key_part->field= share->field[key_part->fieldnr-1];
key_part->type= field->key_type();
+ if (field->invisible > INVISIBLE_USER && !field->vers_sys_field())
+ keyinfo->flags |= HA_INVISIBLE_KEY;
if (field->null_ptr)
{
key_part->null_offset=(uint) ((uchar*) field->null_ptr -
@@ -2371,7 +2481,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
if (!(key_part->key_part_flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART |
HA_BIT_PART)) &&
key_part->type != HA_KEYTYPE_FLOAT &&
- key_part->type == HA_KEYTYPE_DOUBLE)
+ key_part->type != HA_KEYTYPE_DOUBLE)
key_part->key_part_flag|= HA_CAN_MEMCMP;
}
keyinfo->usable_key_parts= usable_parts; // Filesort
@@ -2459,15 +2569,14 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
vcol_screen_pos+= FRM_VCOL_NEW_HEADER_SIZE;
vcol_info->set_vcol_type((enum_vcol_info_type) type);
- vcol_info->name.length= name_length;
if (name_length)
+ {
vcol_info->name.str= strmake_root(&share->mem_root,
(char*)vcol_screen_pos, name_length);
- else
- {
- vcol_info->name.str= const_cast<char*>(reg_field->field_name);
- vcol_info->name.length = strlen(reg_field->field_name);
+ vcol_info->name.length= name_length;
}
+ else
+ vcol_info->name= reg_field->field_name;
vcol_screen_pos+= name_length + expr_length;
switch (type) {
@@ -2591,19 +2700,21 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
bitmap_clear_all(share->check_set);
}
- delete handler_file;
#ifndef DBUG_OFF
if (use_hash)
(void) my_hash_check(&share->name_hash);
#endif
share->db_plugin= se_plugin;
+ delete handler_file;
+
share->error= OPEN_FRM_OK;
thd->status_var.opened_shares++;
thd->mem_root= old_root;
DBUG_RETURN(0);
- err:
+err:
+ share->db_plugin= NULL;
share->error= OPEN_FRM_CORRUPTED;
share->open_errno= my_errno;
delete handler_file;
@@ -2625,7 +2736,8 @@ static bool sql_unusable_for_discovery(THD *thd, handlerton *engine,
HA_CREATE_INFO *create_info= &lex->create_info;
// ... not CREATE TABLE
- if (lex->sql_command != SQLCOM_CREATE_TABLE)
+ if (lex->sql_command != SQLCOM_CREATE_TABLE &&
+ lex->sql_command != SQLCOM_CREATE_SEQUENCE)
return 1;
// ... create like
if (lex->create_info.like())
@@ -2665,6 +2777,9 @@ static bool sql_unusable_for_discovery(THD *thd, handlerton *engine,
(create_info->db_type && create_info->db_type != engine))
return 1;
}
+ // ... WITH SYSTEM VERSIONING
+ if (create_info->versioned())
+ return 1;
return 0;
}
@@ -2685,7 +2800,7 @@ int TABLE_SHARE::init_from_sql_statement_string(THD *thd, bool write,
uint unused2;
handlerton *hton= plugin_hton(db_plugin);
LEX_CUSTRING frm= {0,0};
- LEX_STRING db_backup= { thd->db, thd->db_length };
+ LEX_CSTRING db_backup= thd->db;
DBUG_ENTER("TABLE_SHARE::init_from_sql_statement_string");
/*
@@ -2712,11 +2827,11 @@ int TABLE_SHARE::init_from_sql_statement_string(THD *thd, bool write,
else
thd->set_n_backup_active_arena(arena, &backup);
- thd->reset_db(db.str, db.length);
+ thd->reset_db(&db);
lex_start(thd);
- if ((error= parse_sql(thd, & parser_state, NULL) ||
- sql_unusable_for_discovery(thd, hton, sql_copy)))
+ if (unlikely((error= parse_sql(thd, & parser_state, NULL) ||
+ sql_unusable_for_discovery(thd, hton, sql_copy))))
goto ret;
thd->lex->create_info.db_type= hton;
@@ -2728,7 +2843,7 @@ int TABLE_SHARE::init_from_sql_statement_string(THD *thd, bool write,
thd->lex->create_info.tabledef_version= tabledef_version;
promote_first_timestamp_column(&thd->lex->alter_info.create_list);
- file= mysql_create_frm_image(thd, db.str, table_name.str,
+ file= mysql_create_frm_image(thd, &db, &table_name,
&thd->lex->create_info, &thd->lex->alter_info,
C_ORDINARY_CREATE, &unused1, &unused2, &frm);
error|= file == 0;
@@ -2744,14 +2859,14 @@ int TABLE_SHARE::init_from_sql_statement_string(THD *thd, bool write,
ret:
my_free(const_cast<uchar*>(frm.str));
lex_end(thd->lex);
- thd->reset_db(db_backup.str, db_backup.length);
+ thd->reset_db(&db_backup);
thd->lex= old_lex;
if (arena)
thd->restore_active_arena(arena, &backup);
reenable_binlog(thd);
thd->variables.sql_mode= saved_mode;
thd->variables.character_set_client= old_cs;
- if (thd->is_error() || error)
+ if (unlikely(thd->is_error() || error))
{
thd->clear_error();
my_error(ER_SQL_DISCOVER_ERROR, MYF(0),
@@ -2759,6 +2874,8 @@ ret:
sql_copy);
DBUG_RETURN(HA_ERR_GENERIC);
}
+ /* Treat the table as normal table from binary logging point of view */
+ table_creation_was_logged= 1;
DBUG_RETURN(0);
}
@@ -2799,12 +2916,12 @@ static bool fix_vcol_expr(THD *thd, Virtual_column_info *vcol)
{
DBUG_ENTER("fix_vcol_expr");
- const enum enum_mark_columns save_mark_used_columns= thd->mark_used_columns;
- thd->mark_used_columns= MARK_COLUMNS_NONE;
+ const enum enum_column_usage saved_column_usage= thd->column_usage;
+ thd->column_usage= COLUMNS_WRITE;
int error= vcol->expr->fix_fields(thd, &vcol->expr);
- thd->mark_used_columns= save_mark_used_columns;
+ thd->column_usage= saved_column_usage;
if (unlikely(error))
{
@@ -2911,14 +3028,14 @@ static bool fix_and_check_vcol_expr(THD *thd, TABLE *table,
res.errors= 0;
int error= func_expr->walk(&Item::check_vcol_func_processor, 0, &res);
- if (error || (res.errors & VCOL_IMPOSSIBLE))
+ if (unlikely(error || (res.errors & VCOL_IMPOSSIBLE)))
{
// this can only happen if the frm was corrupted
my_error(ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(0), res.name,
vcol->get_vcol_type_name(), vcol->name.str);
DBUG_RETURN(1);
}
- else if (res.errors & VCOL_AUTO_INC)
+ else if (unlikely(res.errors & VCOL_AUTO_INC))
{
/*
An auto_increment field may not be used in an expression for
@@ -2999,9 +3116,17 @@ unpack_vcol_info_from_frm(THD *thd, MEM_ROOT *mem_root, TABLE *table,
lex.last_field= &vcol_storage;
error= parse_sql(thd, &parser_state, NULL);
- if (error)
+ if (unlikely(error))
goto end;
+ if (lex.current_select->table_list.first[0].next_global)
+ {
+ /* We are using NEXT VALUE FOR sequence. Remember table name for open */
+ TABLE_LIST *sequence= lex.current_select->table_list.first[0].next_global;
+ sequence->next_global= table->internal_tables;
+ table->internal_tables= sequence;
+ }
+
vcol_storage.vcol_info->set_vcol_type(vcol->get_vcol_type());
vcol_storage.vcol_info->stored_in_db= vcol->stored_in_db;
vcol_storage.vcol_info->name= vcol->name;
@@ -3020,11 +3145,19 @@ end:
DBUG_RETURN(vcol_info);
}
-static bool check_vcol_forward_refs(Field *field, Virtual_column_info *vcol)
+static bool check_vcol_forward_refs(Field *field, Virtual_column_info *vcol,
+ bool check_constraint)
{
- bool res= vcol &&
- vcol->expr->walk(&Item::check_field_expression_processor, 0,
- field);
+ bool res;
+ uint32 flags= field->flags;
+ if (check_constraint)
+ {
+ /* Check constraints can refer it itself */
+ field->flags|= NO_DEFAULT_VALUE_FLAG;
+ }
+ res= (vcol &&
+ vcol->expr->walk(&Item::check_field_expression_processor, 0, field));
+ field->flags= flags;
return res;
}
@@ -3042,6 +3175,7 @@ static bool check_vcol_forward_refs(Field *field, Virtual_column_info *vcol)
prgflag READ_ALL etc..
ha_open_flags HA_OPEN_ABORT_IF_LOCKED etc..
outparam result table
+ partitions_to_open open only these partitions.
RETURN VALUES
0 ok
@@ -3054,13 +3188,12 @@ static bool check_vcol_forward_refs(Field *field, Virtual_column_info *vcol)
*/
enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
- const char *alias, uint db_stat, uint prgflag,
+ const LEX_CSTRING *alias, uint db_stat, uint prgflag,
uint ha_open_flags, TABLE *outparam,
- bool is_create_table)
+ bool is_create_table, List<String> *partitions_to_open)
{
enum open_frm_error error;
uint records, i, bitmap_size, bitmap_count;
- size_t tmp_length;
const char *tmp_alias;
bool error_reported= FALSE;
uchar *record, *bitmaps;
@@ -3087,17 +3220,17 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
error= OPEN_FRM_NEEDS_REBUILD;
goto err;
}
- init_sql_alloc(&outparam->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0));
+ init_sql_alloc(&outparam->mem_root, "table", TABLE_ALLOC_BLOCK_SIZE, 0,
+ MYF(0));
/*
We have to store the original alias in mem_root as constraints and virtual
functions may store pointers to it
*/
- tmp_length= strlen(alias);
- if (!(tmp_alias= strmake_root(&outparam->mem_root, alias, tmp_length)))
+ if (!(tmp_alias= strmake_root(&outparam->mem_root, alias->str, alias->length)))
goto err;
- outparam->alias.set(tmp_alias, tmp_length, table_alias_charset);
+ outparam->alias.set(tmp_alias, alias->length, table_alias_charset);
outparam->quick_keys.init();
outparam->covering_keys.init();
outparam->intersect_keys.init();
@@ -3119,34 +3252,51 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
DBUG_ASSERT(!db_stat);
}
+ if (share->sequence && outparam->file)
+ {
+ ha_sequence *file;
+ /* SEQUENCE table. Create a sequence handler over the original handler */
+ if (!(file= (ha_sequence*) sql_sequence_hton->create(sql_sequence_hton, share,
+ &outparam->mem_root)))
+ goto err;
+ file->register_original_handler(outparam->file);
+ outparam->file= file;
+ }
+
outparam->reginfo.lock_type= TL_UNLOCK;
outparam->current_lock= F_UNLCK;
records=0;
if ((db_stat & HA_OPEN_KEYFILE) || (prgflag & DELAYED_OPEN))
records=1;
- if (prgflag & (READ_ALL+EXTRA_RECORD))
+ if (prgflag & (READ_ALL + EXTRA_RECORD))
+ {
records++;
-
- if (!(record= (uchar*) alloc_root(&outparam->mem_root,
- share->rec_buff_length * records)))
- goto err; /* purecov: inspected */
- MEM_NOACCESS(record, share->rec_buff_length * records);
+ if (share->versioned)
+ records++;
+ }
if (records == 0)
{
/* We are probably in hard repair, and the buffers should not be used */
- outparam->record[0]= outparam->record[1]= share->default_values;
+ record= share->default_values;
}
else
{
- outparam->record[0]= record;
- if (records > 1)
- outparam->record[1]= record+ share->rec_buff_length;
- else
- outparam->record[1]= outparam->record[0]; // Safety
+ if (!(record= (uchar*) alloc_root(&outparam->mem_root,
+ share->rec_buff_length * records)))
+ goto err; /* purecov: inspected */
}
- MEM_UNDEFINED(outparam->record[0], share->reclength);
- MEM_UNDEFINED(outparam->record[1], share->reclength);
+
+ for (i= 0; i < 3;)
+ {
+ outparam->record[i]= record;
+ if (++i < records)
+ record+= share->rec_buff_length;
+ }
+ /* Mark bytes between records as not accessable to catch overrun bugs */
+ for (i= 0; i < records; i++)
+ MEM_NOACCESS(outparam->record[i] + share->reclength,
+ share->rec_buff_length - share->reclength);
if (!(field_ptr = (Field **) alloc_root(&outparam->mem_root,
(uint) ((share->fields+1)*
@@ -3172,6 +3322,8 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
DEBUG_SYNC(thd, "TABLE_after_field_clone");
+ outparam->vers_write= share->versioned;
+
if (share->found_next_number_field)
outparam->found_next_number_field=
outparam->field[(uint) (share->found_next_number_field - share->field)];
@@ -3269,6 +3421,7 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
break;
}
#endif
+
if (parse_vcol_defs(thd, &outparam->mem_root, outparam,
&error_reported, mode))
{
@@ -3281,6 +3434,7 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
}
#ifdef WITH_PARTITION_STORAGE_ENGINE
+ bool work_part_info_used;
if (share->partition_info_str_len && outparam->file)
{
/*
@@ -3301,7 +3455,6 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
thd->set_n_backup_active_arena(&part_func_arena, &backup_arena);
thd->stmt_arena= &part_func_arena;
bool tmp;
- bool work_part_info_used;
tmp= mysql_unpack_partition(thd, share->partition_info_str,
share->partition_info_str_len,
@@ -3415,14 +3568,15 @@ partititon_err:
int ha_err= outparam->file->ha_open(outparam, share->normalized_path.str,
(db_stat & HA_READ_ONLY ? O_RDONLY : O_RDWR),
- ha_open_flags);
+ ha_open_flags, 0, partitions_to_open);
if (ha_err)
{
share->open_errno= ha_err;
/* Set a flag if the table is crashed and it can be auto. repaired */
share->crashed= (outparam->file->auto_repair(ha_err) &&
!(ha_open_flags & HA_OPEN_FOR_REPAIR));
- outparam->file->print_error(ha_err, MYF(0));
+ if (!thd->is_error())
+ outparam->file->print_error(ha_err, MYF(0));
error_reported= TRUE;
if (ha_err == HA_ERR_TABLE_DEF_CHANGED)
@@ -3456,24 +3610,20 @@ partititon_err:
}
}
- if (share->table_category == TABLE_CATEGORY_LOG)
- {
- outparam->no_replicate= TRUE;
- }
- else if (outparam->file)
+ if (db_stat)
{
+ /* Set some flags in share on first open of the table */
handler::Table_flags flags= outparam->file->ha_table_flags();
- outparam->no_replicate= ! MY_TEST(flags & (HA_BINLOG_STMT_CAPABLE
- | HA_BINLOG_ROW_CAPABLE))
- || MY_TEST(flags & HA_HAS_OWN_BINLOGGING);
- }
- else
- {
- outparam->no_replicate= FALSE;
+ if (! MY_TEST(flags & (HA_BINLOG_STMT_CAPABLE |
+ HA_BINLOG_ROW_CAPABLE)) ||
+ MY_TEST(flags & HA_HAS_OWN_BINLOGGING))
+ share->no_replicate= TRUE;
+ if (outparam->file->table_cache_type() & HA_CACHE_TBL_NOCACHE)
+ share->not_usable_by_query_cache= TRUE;
}
- if (outparam->no_replicate || !binlog_filter->db_ok(outparam->s->db.str))
- outparam->s->cached_row_logging_check= 0; // No row based replication
+ if (share->no_replicate || !binlog_filter->db_ok(share->db.str))
+ share->can_do_row_logging= 0; // No row based replication
/* Increment the opened_tables counter, only when open flags set. */
if (db_stat)
@@ -3729,30 +3879,6 @@ fix_type_pointers(const char ***typelib_value_names,
} /* fix_type_pointers */
-TYPELIB *typelib(MEM_ROOT *mem_root, List<String> &strings)
-{
- TYPELIB *result= (TYPELIB*) alloc_root(mem_root, sizeof(TYPELIB));
- if (!result)
- return 0;
- result->count=strings.elements;
- result->name="";
- uint nbytes= (sizeof(char*) + sizeof(uint)) * (result->count + 1);
- if (!(result->type_names= (const char**) alloc_root(mem_root, nbytes)))
- return 0;
- result->type_lengths= (uint*) (result->type_names + result->count + 1);
- List_iterator<String> it(strings);
- String *tmp;
- for (uint i=0; (tmp=it++) ; i++)
- {
- result->type_names[i]= tmp->ptr();
- result->type_lengths[i]= tmp->length();
- }
- result->type_names[result->count]= 0; // End marker
- result->type_lengths[result->count]= 0;
- return result;
-}
-
-
/*
Search after a field with given start & length
If an exact field isn't found, return longest field with starts
@@ -3801,24 +3927,13 @@ static uint find_field(Field **fields, uchar *record, uint start, uint length)
May fail with some multibyte charsets though.
*/
-void append_unescaped(String *res, const char *pos, uint length)
+void append_unescaped(String *res, const char *pos, size_t length)
{
const char *end= pos+length;
res->append('\'');
for (; pos != end ; pos++)
{
-#if defined(USE_MB) && MYSQL_VERSION_ID < 40100
- uint mblen;
- if (use_mb(default_charset_info) &&
- (mblen= my_ismbchar(default_charset_info, pos, end)))
- {
- res->append(pos, mblen);
- pos+= mblen;
- continue;
- }
-#endif
-
switch (*pos) {
case 0: /* Must be escaped for 'mysql' */
res->append('\\');
@@ -3852,7 +3967,7 @@ void append_unescaped(String *res, const char *pos, uint length)
void prepare_frm_header(THD *thd, uint reclength, uchar *fileinfo,
HA_CREATE_INFO *create_info, uint keys, KEY *key_info)
{
- ulong key_comment_total_bytes= 0;
+ size_t key_comment_total_bytes= 0;
uint i;
DBUG_ENTER("prepare_frm_header");
@@ -3862,7 +3977,7 @@ void prepare_frm_header(THD *thd, uint reclength, uchar *fileinfo,
if (create_info->min_rows > UINT_MAX32)
create_info->min_rows= UINT_MAX32;
- uint key_length, tmp_key_length, tmp, csid;
+ size_t key_length, tmp_key_length, tmp, csid;
bzero((char*) fileinfo, FRM_HEADER_SIZE);
/* header */
fileinfo[0]=(uchar) 254;
@@ -3914,7 +4029,8 @@ void prepare_frm_header(THD *thd, uint reclength, uchar *fileinfo,
create_info->default_table_charset->number : 0);
fileinfo[38]= (uchar) csid;
fileinfo[39]= (uchar) ((uint) create_info->transactional |
- ((uint) create_info->page_checksum << 2));
+ ((uint) create_info->page_checksum << 2) |
+ ((create_info->sequence ? HA_CHOICE_YES : 0) << 4));
fileinfo[40]= (uchar) create_info->row_type;
/* Bytes 41-46 were for RAID support; now reused for other purposes */
fileinfo[41]= (uchar) (csid >> 8);
@@ -3952,6 +4068,7 @@ void update_create_info_from_table(HA_CREATE_INFO *create_info, TABLE *table)
create_info->transactional= share->transactional;
create_info->page_checksum= share->page_checksum;
create_info->option_list= share->option_list;
+ create_info->sequence= MY_TEST(share->sequence);
DBUG_VOID_RETURN;
}
@@ -4087,7 +4204,7 @@ bool ok_for_lower_case_names(const char *name)
bool check_db_name(LEX_STRING *org_name)
{
char *name= org_name->str;
- uint name_length= org_name->length;
+ size_t name_length= org_name->length;
bool check_for_path_chars;
if ((check_for_path_chars= check_mysql50_prefix(name)))
@@ -4124,7 +4241,6 @@ bool check_table_name(const char *name, size_t length, bool check_for_path_chars
size_t name_length= 0;
const char *end= name+length;
-
if (!check_for_path_chars &&
(check_for_path_chars= check_mysql50_prefix(name)))
{
@@ -4276,7 +4392,7 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
{
Field *field= table->field[i];
- if (strncmp(field->field_name, field_def->name.str,
+ if (strncmp(field->field_name.str, field_def->name.str,
field_def->name.length))
{
/*
@@ -4288,7 +4404,7 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
"expected column '%s' at position %d, found '%s'.",
table->s->db.str, table->alias.c_ptr(),
field_def->name.str, i,
- field->field_name);
+ field->field_name.str);
}
field->sql_type(sql_type);
/*
@@ -4394,7 +4510,7 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
}
}
- if (! error)
+ if (likely(! error))
table->s->table_field_def_cache= table_def;
end:
@@ -4622,10 +4738,10 @@ void TABLE::init(THD *thd, TABLE_LIST *tl)
if (thd->lex->need_correct_ident())
alias_name_used= my_strcasecmp(table_alias_charset,
s->table_name.str,
- tl->alias);
+ tl->alias.str);
/* Fix alias if table name changes. */
- if (!alias.alloced_length() || strcmp(alias.c_ptr(), tl->alias))
- alias.copy(tl->alias, strlen(tl->alias), alias.charset());
+ if (!alias.alloced_length() || strcmp(alias.c_ptr(), tl->alias.str))
+ alias.copy(tl->alias.str, tl->alias.length, alias.charset());
tablenr= thd->current_tablenr++;
used_fields= 0;
@@ -4643,7 +4759,9 @@ void TABLE::init(THD *thd, TABLE_LIST *tl)
created= TRUE;
cond_selectivity= 1.0;
cond_selectivity_sampling_explain= NULL;
+ vers_write= s->versioned;
quick_condition_rows=0;
+ no_cache= false;
initialize_quick_structures();
#ifdef HAVE_REPLICATION
/* used in RBR Triggers */
@@ -4719,10 +4837,13 @@ bool TABLE::fill_item_list(List<Item> *item_list) const
is the same as the number of columns in the table.
*/
-void TABLE::reset_item_list(List<Item> *item_list) const
+void TABLE::reset_item_list(List<Item> *item_list, uint skip) const
{
List_iterator_fast<Item> it(*item_list);
- for (Field **ptr= field; *ptr; ptr++)
+ Field **ptr= field;
+ for ( ; skip && *ptr; skip--)
+ ptr++;
+ for (; *ptr; ptr++)
{
Item_field *item_field= (Item_field*) it++;
DBUG_ASSERT(item_field != 0);
@@ -4738,7 +4859,7 @@ void TABLE::reset_item_list(List<Item> *item_list) const
buffer buffer for md5 writing
*/
-void TABLE_LIST::calc_md5(char *buffer)
+void TABLE_LIST::calc_md5(const char *buffer)
{
uchar digest[16];
compute_md5_hash(digest, select_stmt.str,
@@ -4776,7 +4897,7 @@ bool TABLE_LIST::create_field_translation(THD *thd)
bool res= FALSE;
DBUG_ENTER("TABLE_LIST::create_field_translation");
DBUG_PRINT("enter", ("Alias: '%s' Unit: %p",
- (alias ? alias : "<NULL>"),
+ (alias.str ? alias.str : "<NULL>"),
get_unit()));
if (thd->stmt_arena->is_conventional() ||
@@ -4804,16 +4925,20 @@ bool TABLE_LIST::create_field_translation(THD *thd)
*/
if (is_view() && get_unit()->prepared && !field_translation_updated)
{
+ field_translation_updated= TRUE;
+ if (static_cast<uint>(field_translation_end - field_translation) <
+ select->item_list.elements)
+ goto allocate;
while ((item= it++))
{
field_translation[field_count++].item= item;
}
- field_translation_updated= TRUE;
}
DBUG_RETURN(FALSE);
}
+allocate:
arena= thd->activate_stmt_arena_if_needed(&backup);
/* Create view fields translation table */
@@ -4829,8 +4954,9 @@ bool TABLE_LIST::create_field_translation(THD *thd)
while ((item= it++))
{
- DBUG_ASSERT(item->name && item->name[0]);
- transl[field_count].name= thd->strdup(item->name);
+ DBUG_ASSERT(item->name.str && item->name.str[0]);
+ transl[field_count].name.str= thd->strmake(item->name.str, item->name.length);
+ transl[field_count].name.length= item->name.length;
transl[field_count++].item= item;
}
field_translation= transl;
@@ -4922,10 +5048,8 @@ bool TABLE_LIST::prep_where(THD *thd, Item **conds,
{
if (where->fixed)
where->update_used_tables();
- if (!where->fixed && where->fix_fields(thd, &where))
- {
+ else if (where->fix_fields(thd, &where))
DBUG_RETURN(TRUE);
- }
/*
check that it is not VIEW in which we insert with INSERT SELECT
@@ -4955,12 +5079,12 @@ bool TABLE_LIST::prep_where(THD *thd, Item **conds,
}
if (tbl == 0)
{
- if (*conds && !(*conds)->fixed)
- res= (*conds)->fix_fields(thd, conds);
+ if (*conds)
+ res= (*conds)->fix_fields_if_needed_for_bool(thd, conds);
if (!res)
*conds= and_conds(thd, *conds, where->copy_andor_structure(thd));
- if (*conds && !(*conds)->fixed && !res)
- res= (*conds)->fix_fields(thd, conds);
+ if (*conds && !res)
+ res= (*conds)->fix_fields_if_needed_for_bool(thd, conds);
}
if (arena)
thd->restore_active_arena(arena, &backup);
@@ -5020,7 +5144,7 @@ merge_on_conds(THD *thd, TABLE_LIST *table, bool is_cascaded)
DBUG_ENTER("merge_on_conds");
Item *cond= NULL;
- DBUG_PRINT("info", ("alias: %s", table->alias));
+ DBUG_PRINT("info", ("alias: %s", table->alias.str));
if (table->on_expr)
cond= table->on_expr->copy_andor_structure(thd);
if (!table->view)
@@ -5107,12 +5231,8 @@ bool TABLE_LIST::prep_check_option(THD *thd, uint8 check_opt_type)
{
const char *save_where= thd->where;
thd->where= "check option";
- if ((!check_option->fixed &&
- check_option->fix_fields(thd, &check_option)) ||
- check_option->check_cols(1))
- {
+ if (check_option->fix_fields_if_needed_for_bool(thd, &check_option))
DBUG_RETURN(TRUE);
- }
thd->where= save_where;
}
DBUG_RETURN(FALSE);
@@ -5248,9 +5368,9 @@ int TABLE_LIST::view_check_option(THD *thd, bool ignore_failure)
{
TABLE_LIST *main_view= top_table();
const char *name_db= (main_view->view ? main_view->view_db.str :
- main_view->db);
+ main_view->db.str);
const char *name_table= (main_view->view ? main_view->view_name.str :
- main_view->table_name);
+ main_view->table_name.str);
my_error(ER_VIEW_CHECK_FAILED, MYF(ignore_failure ? ME_JUST_WARNING : 0),
name_db, name_table);
return ignore_failure ? VIEW_CHECK_SKIP : VIEW_CHECK_ERROR;
@@ -5273,6 +5393,8 @@ int TABLE::verify_constraints(bool ignore_failure)
if (check_constraints &&
!(in_use->variables.option_bits & OPTION_NO_CHECK_CONSTRAINT_CHECKS))
{
+ if (versioned() && !vers_end_field()->is_max())
+ return VIEW_CHECK_OK;
for (Virtual_column_info **chk= check_constraints ; *chk ; chk++)
{
/*
@@ -5306,7 +5428,6 @@ int TABLE::verify_constraints(bool ignore_failure)
return(!in_use->is_error() ? VIEW_CHECK_OK : VIEW_CHECK_ERROR);
}
-
/*
Find table in underlying tables by mask and check that only this
table belong to given mask
@@ -5576,7 +5697,7 @@ void TABLE_LIST::register_want_access(ulong want_access)
bool TABLE_LIST::prepare_view_security_context(THD *thd)
{
DBUG_ENTER("TABLE_LIST::prepare_view_security_context");
- DBUG_PRINT("enter", ("table: %s", alias));
+ DBUG_PRINT("enter", ("table: %s", alias.str));
DBUG_ASSERT(!prelocking_placeholder && view);
if (view_suid)
@@ -5584,7 +5705,7 @@ bool TABLE_LIST::prepare_view_security_context(THD *thd)
DBUG_PRINT("info", ("This table is suid view => load contest"));
DBUG_ASSERT(view && view_sctx);
if (acl_getroot(view_sctx, definer.user.str, definer.host.str,
- definer.host.str, thd->db))
+ definer.host.str, thd->db.str))
{
if ((thd->lex->sql_command == SQLCOM_SHOW_CREATE) ||
(thd->lex->sql_command == SQLCOM_SHOW_FIELDS))
@@ -5649,7 +5770,7 @@ Security_context *TABLE_LIST::find_view_security_context(THD *thd)
if (upper_view)
{
DBUG_PRINT("info", ("Securety context of view %s will be used",
- upper_view->alias));
+ upper_view->alias.str));
sctx= upper_view->view_sctx;
DBUG_ASSERT(sctx);
}
@@ -5690,7 +5811,7 @@ bool TABLE_LIST::prepare_security(THD *thd)
while ((tbl= tb++))
{
DBUG_ASSERT(tbl->referencing_view);
- char *local_db, *local_table_name;
+ const char *local_db, *local_table_name;
if (tbl->view)
{
local_db= tbl->view_db.str;
@@ -5698,8 +5819,8 @@ bool TABLE_LIST::prepare_security(THD *thd)
}
else
{
- local_db= tbl->db;
- local_table_name= tbl->table_name;
+ local_db= tbl->db.str;
+ local_table_name= tbl->table_name.str;
}
fill_effective_table_privileges(thd, &tbl->grant, local_db,
local_table_name);
@@ -5815,15 +5936,15 @@ Natural_join_column::Natural_join_column(Item_field *field_param,
}
-const char *Natural_join_column::name()
+LEX_CSTRING *Natural_join_column::name()
{
if (view_field)
{
DBUG_ASSERT(table_field == NULL);
- return view_field->name;
+ return &view_field->name;
}
- return table_field->field_name;
+ return &table_field->field_name;
}
@@ -5833,7 +5954,7 @@ Item *Natural_join_column::create_item(THD *thd)
{
DBUG_ASSERT(table_field == NULL);
return create_view_field(thd, table_ref, &view_field->item,
- view_field->name);
+ &view_field->name);
}
return table_field;
}
@@ -5850,30 +5971,29 @@ Field *Natural_join_column::field()
}
-const char *Natural_join_column::table_name()
+const char *Natural_join_column::safe_table_name()
{
DBUG_ASSERT(table_ref);
- return table_ref->alias;
+ return table_ref->alias.str ? table_ref->alias.str : "";
}
-const char *Natural_join_column::db_name()
+const char *Natural_join_column::safe_db_name()
{
if (view_field)
- return table_ref->view_db.str;
+ return table_ref->view_db.str ? table_ref->view_db.str : "";
/*
Test that TABLE_LIST::db is the same as TABLE_SHARE::db to
ensure consistency. An exception are I_S schema tables, which
are inconsistent in this respect.
*/
- DBUG_ASSERT(!strcmp(table_ref->db,
- table_ref->table->s->db.str) ||
+ DBUG_ASSERT(!cmp(&table_ref->db,
+ &table_ref->table->s->db) ||
(table_ref->schema_table &&
- is_infoschema_db(table_ref->table->s->db.str,
- table_ref->table->s->db.length)) ||
- table_ref->is_materialized_derived());
- return table_ref->db;
+ is_infoschema_db(&table_ref->table->s->db)) ||
+ table_ref->is_materialized_derived());
+ return table_ref->db.str ? table_ref->db.str : "";
}
@@ -5902,9 +6022,9 @@ void Field_iterator_view::set(TABLE_LIST *table)
}
-const char *Field_iterator_table::name()
+LEX_CSTRING *Field_iterator_table::name()
{
- return (*ptr)->field_name;
+ return &(*ptr)->field_name;
}
@@ -5913,6 +6033,7 @@ Item *Field_iterator_table::create_item(THD *thd)
SELECT_LEX *select= thd->lex->current_select;
Item_field *item= new (thd->mem_root) Item_field(thd, &select->context, *ptr);
+ DBUG_ASSERT(strlen(item->name.str) == item->name.length);
if (item && thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY &&
!thd->lex->in_sum_func && select->cur_pos_in_select_list != UNDEF_POS &&
select->join)
@@ -5925,19 +6046,19 @@ Item *Field_iterator_table::create_item(THD *thd)
}
-const char *Field_iterator_view::name()
+LEX_CSTRING *Field_iterator_view::name()
{
- return ptr->name;
+ return &ptr->name;
}
Item *Field_iterator_view::create_item(THD *thd)
{
- return create_view_field(thd, view, &ptr->item, ptr->name);
+ return create_view_field(thd, view, &ptr->item, &ptr->name);
}
Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref,
- const char *name)
+ LEX_CSTRING *name)
{
bool save_wrapper= thd->lex->select_lex.no_wrap_view_item;
Item *field= *field_ref;
@@ -5973,7 +6094,7 @@ Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref,
Name_resolution_context *context= view->view ? &view->view->select_lex.context :
&thd->lex->select_lex.context;
Item *item= (new (thd->mem_root)
- Item_direct_view_ref(thd, context, field_ref, view->alias,
+ Item_direct_view_ref(thd, context, field_ref, view->alias.str,
name, view));
if (!item)
return NULL;
@@ -6039,7 +6160,7 @@ void Field_iterator_table_ref::set_field_iterator()
table_ref->table->s->fields))));
field_it= &natural_join_it;
DBUG_PRINT("info",("field_it for '%s' is Field_iterator_natural_join",
- table_ref->alias));
+ table_ref->alias.str));
}
/* This is a merge view, so use field_translation. */
else if (table_ref->field_translation)
@@ -6047,7 +6168,7 @@ void Field_iterator_table_ref::set_field_iterator()
DBUG_ASSERT(table_ref->is_merged_derived());
field_it= &view_field_it;
DBUG_PRINT("info", ("field_it for '%s' is Field_iterator_view",
- table_ref->alias));
+ table_ref->alias.str));
}
/* This is a base table or stored view. */
else
@@ -6055,7 +6176,7 @@ void Field_iterator_table_ref::set_field_iterator()
DBUG_ASSERT(table_ref->table || table_ref->view);
field_it= &table_field_it;
DBUG_PRINT("info", ("field_it for '%s' is Field_iterator_table",
- table_ref->alias));
+ table_ref->alias.str));
}
field_it->set(table_ref);
DBUG_VOID_RETURN;
@@ -6097,12 +6218,12 @@ const char *Field_iterator_table_ref::get_table_name()
if (table_ref->is_derived())
return table_ref->table->s->table_name.str;
else if (table_ref->is_natural_join)
- return natural_join_it.column_ref()->table_name();
+ return natural_join_it.column_ref()->safe_table_name();
- DBUG_ASSERT(!strcmp(table_ref->table_name,
+ DBUG_ASSERT(!strcmp(table_ref->table_name.str,
table_ref->table->s->table_name.str) ||
table_ref->schema_table);
- return table_ref->table_name;
+ return table_ref->table_name.str;
}
@@ -6111,19 +6232,18 @@ const char *Field_iterator_table_ref::get_db_name()
if (table_ref->view)
return table_ref->view_db.str;
else if (table_ref->is_natural_join)
- return natural_join_it.column_ref()->db_name();
+ return natural_join_it.column_ref()->safe_db_name();
/*
Test that TABLE_LIST::db is the same as TABLE_SHARE::db to
ensure consistency. An exception are I_S schema tables, which
are inconsistent in this respect.
*/
- DBUG_ASSERT(!strcmp(table_ref->db, table_ref->table->s->db.str) ||
+ DBUG_ASSERT(!cmp(&table_ref->db, &table_ref->table->s->db) ||
(table_ref->schema_table &&
- is_infoschema_db(table_ref->table->s->db.str,
- table_ref->table->s->db.length)));
+ is_infoschema_db(&table_ref->table->s->db)));
- return table_ref->db;
+ return table_ref->db.str;
}
@@ -6474,9 +6594,12 @@ void TABLE::mark_columns_needed_for_delete()
need_signal= true;
}
}
- if (check_constraints)
+
+ if (s->versioned)
{
- mark_check_constraint_columns_for_read();
+ bitmap_set_bit(read_set, s->vers_start_field()->field_index);
+ bitmap_set_bit(read_set, s->vers_end_field()->field_index);
+ bitmap_set_bit(write_set, s->vers_end_field()->field_index);
need_signal= true;
}
@@ -6494,7 +6617,7 @@ void TABLE::mark_columns_needed_for_delete()
updated columns to be read.
If this is no the case, we do like in the delete case and mark
- if neeed, either the primary key column or all columns to be read.
+ if needed, either the primary key column or all columns to be read.
(see mark_columns_needed_for_delete() for details)
If the engine has HA_REQUIRES_KEY_COLUMNS_FOR_DELETE, we will
@@ -6522,7 +6645,7 @@ void TABLE::mark_columns_needed_for_update()
for (KEY *k= key_info; k < end; k++)
{
KEY_PART_INFO *kpend= k->key_part + k->ext_key_parts;
- bool any_written= false, all_read= true;
+ int any_written= 0, all_read= 1;
for (KEY_PART_INFO *kp= k->key_part; kp < kpend; kp++)
{
int idx= kp->fieldnr - 1;
@@ -6564,6 +6687,15 @@ void TABLE::mark_columns_needed_for_update()
need_signal= true;
}
}
+ if (s->versioned)
+ {
+ /*
+ For System Versioning we have to read all columns since we store
+ a copy of previous row with modified row_end back to a table.
+ */
+ bitmap_union(read_set, &s->all_set);
+ need_signal= true;
+ }
if (check_constraints)
{
mark_check_constraint_columns_for_read();
@@ -6724,8 +6856,16 @@ void TABLE::mark_columns_per_binlog_row_image()
binary log will include all columns read anyway.
*/
mark_columns_used_by_index_no_reset(s->primary_key, read_set);
- /* Only write columns that have changed */
- rpl_write_set= write_set;
+ if (versioned())
+ {
+ // TODO: After MDEV-18432 we don't pass history rows, so remove this:
+ rpl_write_set= &s->all_set;
+ }
+ else
+ {
+ /* Only write columns that have changed */
+ rpl_write_set= write_set;
+ }
break;
default:
@@ -6831,6 +6971,58 @@ bool TABLE::mark_virtual_columns_for_write(bool insert_fl
DBUG_RETURN(bitmap_updated);
}
+
+/**
+ Check if a virtual not stored column field is in read set
+
+ @retval FALSE No virtual not stored column is used
+ @retval TRUE At least one virtual not stored column is used
+*/
+
+bool TABLE::check_virtual_columns_marked_for_read()
+{
+ if (vfield)
+ {
+ Field **vfield_ptr;
+ for (vfield_ptr= vfield; *vfield_ptr; vfield_ptr++)
+ {
+ Field *tmp_vfield= *vfield_ptr;
+ if (bitmap_is_set(read_set, tmp_vfield->field_index) &&
+ !tmp_vfield->vcol_info->stored_in_db)
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+/**
+ Check if a stored virtual column field is marked for write
+
+ This can be used to check if any column that is part of a virtual
+ stored column is changed
+
+ @retval FALSE No stored virtual column is used
+ @retval TRUE At least one stored virtual column is used
+*/
+
+bool TABLE::check_virtual_columns_marked_for_write()
+{
+ if (vfield)
+ {
+ Field **vfield_ptr;
+ for (vfield_ptr= vfield; *vfield_ptr; vfield_ptr++)
+ {
+ Field *tmp_vfield= *vfield_ptr;
+ if (bitmap_is_set(write_set, tmp_vfield->field_index) &&
+ tmp_vfield->vcol_info->stored_in_db)
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
/*
Mark fields used by check constraints into s->check_set.
Mark all fields used in an expression that is part of an index
@@ -6929,6 +7121,7 @@ void TABLE::mark_default_fields_for_write(bool is_insert)
DBUG_VOID_RETURN;
}
+
void TABLE::move_fields(Field **ptr, const uchar *to, const uchar *from)
{
my_ptrdiff_t diff= to - from;
@@ -7190,7 +7383,8 @@ bool TABLE::add_tmp_key(uint key, uint key_parts,
if (unique)
keyinfo->flags|= HA_NOSAME;
sprintf(buf, "key%i", key);
- if (!(keyinfo->name= strdup_root(&mem_root, buf)))
+ keyinfo->name.length= strlen(buf);
+ if (!(keyinfo->name.str= strmake_root(&mem_root, buf, keyinfo->name.length)))
return TRUE;
keyinfo->rec_per_key= (ulong*) alloc_root(&mem_root,
sizeof(ulong)*key_parts);
@@ -7214,6 +7408,26 @@ bool TABLE::add_tmp_key(uint key, uint key_parts,
key_part_info++;
}
+ /*
+ For the case when there is a derived table that would give distinct rows,
+ the index statistics are passed to the join optimizer to tell that a ref
+ access to all the fields of the derived table will produce only one row.
+ */
+
+ st_select_lex_unit* derived= pos_in_table_list ?
+ pos_in_table_list->derived: NULL;
+ if (derived)
+ {
+ st_select_lex* first= derived->first_select();
+ uint select_list_items= first->get_item_list()->elements;
+ if (key_parts == select_list_items)
+ {
+ if ((!first->is_part_of_union() && (first->options & SELECT_DISTINCT)) ||
+ derived->check_distinct_in_union())
+ keyinfo->rec_per_key[key_parts - 1]= 1;
+ }
+ }
+
set_if_bigger(s->max_key_length, keyinfo->key_length);
s->keys++;
return FALSE;
@@ -7469,7 +7683,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);
+ my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), hint->key_name.str, alias.str);
return 1;
}
@@ -7575,7 +7789,7 @@ 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, table_list->table_name,
+ 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);
@@ -7759,7 +7973,7 @@ int TABLE::update_virtual_fields(handler *h, enum_vcol_update_mode update_mode)
if (vcol_info->expr->save_in_field(vf, 0))
field_error= error= 1;
DBUG_PRINT("info", ("field '%s' - updated error: %d",
- vf->field_name, field_error));
+ vf->field_name.str, field_error));
if (swap_values && (vf->flags & BLOB_FLAG))
{
/*
@@ -7773,7 +7987,7 @@ int TABLE::update_virtual_fields(handler *h, enum_vcol_update_mode update_mode)
}
else
{
- DBUG_PRINT("info", ("field '%s' - skipped", vf->field_name));
+ DBUG_PRINT("info", ("field '%s' - skipped", vf->field_name.str));
}
}
if (handler_pushed)
@@ -7845,7 +8059,7 @@ int TABLE::update_default_fields(bool ignore_errors)
res|= (field->default_value->expr->save_in_field(field, 0) < 0);
if (!ignore_errors && res)
{
- my_error(ER_CALCULATING_DEFAULT_VALUE, MYF(0), field->field_name);
+ my_error(ER_CALCULATING_DEFAULT_VALUE, MYF(0), field->field_name.str);
break;
}
res= 0;
@@ -7855,6 +8069,7 @@ int TABLE::update_default_fields(bool ignore_errors)
DBUG_RETURN(res);
}
+
void TABLE::evaluate_update_default_function()
{
DBUG_ENTER("TABLE::evaluate_update_default_function");
@@ -7870,6 +8085,39 @@ void TABLE::evaluate_update_default_function()
}
+void TABLE::vers_update_fields()
+{
+ bitmap_set_bit(write_set, vers_start_field()->field_index);
+ bitmap_set_bit(write_set, vers_end_field()->field_index);
+
+ if (!vers_write)
+ {
+ file->column_bitmaps_signal();
+ return;
+ }
+
+ if (versioned(VERS_TIMESTAMP) &&
+ vers_start_field()->store_timestamp(in_use->query_start(),
+ in_use->query_start_sec_part()))
+ {
+ DBUG_ASSERT(0);
+ }
+
+ vers_end_field()->set_max();
+ bitmap_set_bit(read_set, vers_end_field()->field_index);
+ file->column_bitmaps_signal();
+ if (vfield)
+ update_virtual_fields(file, VCOL_UPDATE_FOR_READ);
+}
+
+
+void TABLE::vers_update_end()
+{
+ if (vers_end_field()->store_timestamp(in_use->query_start(),
+ in_use->query_start_sec_part()))
+ DBUG_ASSERT(0);
+}
+
/**
Reset markers that fields are being updated
*/
@@ -7978,7 +8226,7 @@ bool TABLE::validate_default_values_of_unset_fields(THD *thd) const
for (Field **fld= field; *fld; fld++)
{
if (!bitmap_is_set(write_set, (*fld)->field_index) &&
- !((*fld)->flags & NO_DEFAULT_VALUE_FLAG))
+ !((*fld)->flags & (NO_DEFAULT_VALUE_FLAG | VERS_SYSTEM_FIELD)))
{
if (!(*fld)->is_null_in_record(s->default_values) &&
(*fld)->validate_value_in_record_with_warn(thd, s->default_values) &&
@@ -8017,7 +8265,7 @@ bool TABLE::insert_all_rows_into_tmp_table(THD *thd,
tmp_table->file->ha_disable_indexes(HA_KEY_SWITCH_ALL);
file->ha_index_or_rnd_end();
- if (file->ha_rnd_init_with_error(1))
+ if (unlikely(file->ha_rnd_init_with_error(1)))
DBUG_RETURN(1);
if (tmp_table->no_rows)
@@ -8029,10 +8277,10 @@ bool TABLE::insert_all_rows_into_tmp_table(THD *thd,
tmp_table->file->ha_start_bulk_insert(file->stats.records);
}
- while (!file->ha_rnd_next(tmp_table->record[0]))
+ while (likely(!file->ha_rnd_next(tmp_table->record[0])))
{
write_err= tmp_table->file->ha_write_tmp_row(tmp_table->record[0]);
- if (write_err)
+ if (unlikely(write_err))
{
bool is_duplicate;
if (tmp_table->file->is_fatal_error(write_err, HA_CHECK_DUP) &&
@@ -8043,11 +8291,8 @@ bool TABLE::insert_all_rows_into_tmp_table(THD *thd,
DBUG_RETURN(1);
}
- if (thd->check_killed())
- {
- thd->send_kill_message();
+ if (unlikely(thd->check_killed()))
goto err_killed;
- }
}
if (!tmp_table->no_rows && tmp_table->file->ha_end_bulk_insert())
goto err;
@@ -8214,7 +8459,21 @@ bool TABLE_LIST::init_derived(THD *thd, bool init_view)
(first_table && first_table->is_multitable()))
set_multitable();
- unit->derived= this;
+ if (!unit->derived)
+ unit->derived= this;
+ else if (!is_with_table_recursive_reference() && unit->derived != this)
+ {
+ if (unit->derived->is_with_table_recursive_reference())
+ unit->derived= this;
+ else if (vers_conditions.eq(unit->derived->vers_conditions))
+ vers_conditions.empty();
+ else
+ {
+ my_error(ER_CONFLICTING_FOR_SYSTEM_TIME, MYF(0));
+ return TRUE;
+ }
+ }
+
if (init_view && !view)
{
/* This is all what we can do for a derived table for now. */
@@ -8288,9 +8547,8 @@ int TABLE_LIST::fetch_number_of_rows()
return 0;
}
if (is_materialized_derived() && !fill_me)
-
{
- table->file->stats.records= ((select_union*)derived->result)->records;
+ table->file->stats.records= get_unit()->result->est_records;
set_if_bigger(table->file->stats.records, 2);
table->used_stat_records= table->file->stats.records;
}
@@ -8350,7 +8608,10 @@ bool TABLE_LIST::change_refs_to_fields()
if (!used_items.elements)
return FALSE;
- materialized_items= (Item**)thd->calloc(sizeof(void*) * table->s->fields);
+ Item **materialized_items=
+ (Item **)thd->calloc(sizeof(void *) * table->s->fields);
+ if (!materialized_items)
+ return TRUE;
while ((ref= (Item_direct_ref*)li++))
{
@@ -8407,7 +8668,6 @@ bool TABLE_LIST::is_with_table()
return derived && derived->with_element;
}
-
uint TABLE_SHARE::actual_n_key_parts(THD *thd)
{
return use_ext_keys &&
@@ -8584,8 +8844,8 @@ Item* TABLE_LIST::build_pushable_cond_for_table(THD *thd, Item *cond)
if (!(item->used_tables() == tab_map))
continue;
Item_func_eq *eq= 0;
- Item *left_item_clone= left_item->build_clone(thd, thd->mem_root);
- Item *right_item_clone= item->build_clone(thd, thd->mem_root);
+ Item *left_item_clone= left_item->build_clone(thd);
+ Item *right_item_clone= item->build_clone(thd);
if (left_item_clone && right_item_clone)
{
left_item_clone->set_item_equal(NULL);
@@ -8615,7 +8875,7 @@ Item* TABLE_LIST::build_pushable_cond_for_table(THD *thd, Item *cond)
return new_cond;
}
else if (cond->get_extraction_flag() != NO_EXTRACTION_FL)
- return cond->build_clone(thd, thd->mem_root);
+ return cond->build_clone(thd);
return 0;
}
@@ -8639,6 +8899,460 @@ bool fk_modifies_child(enum_fk_option opt)
return can_write[opt];
}
+enum TR_table::enabled TR_table::use_transaction_registry= TR_table::MAYBE;
+
+TR_table::TR_table(THD* _thd, bool rw) :
+ thd(_thd), open_tables_backup(NULL)
+{
+ init_one_table(&MYSQL_SCHEMA_NAME, &TRANSACTION_REG_NAME,
+ NULL, rw ? TL_WRITE : TL_READ);
+}
+
+bool TR_table::open()
+{
+ DBUG_ASSERT(!table);
+ open_tables_backup= new Open_tables_backup;
+ if (!open_tables_backup)
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ return true;
+ }
+
+ All_tmp_tables_list *temporary_tables= thd->temporary_tables;
+ bool error= !open_log_table(thd, this, open_tables_backup);
+ thd->temporary_tables= temporary_tables;
+
+ if (use_transaction_registry == MAYBE)
+ error= check(error);
+
+ use_transaction_registry= error ? NO : YES;
+
+ return error;
+}
+
+TR_table::~TR_table()
+{
+ if (table)
+ {
+ thd->temporary_tables= NULL;
+ close_log_table(thd, open_tables_backup);
+ }
+ delete open_tables_backup;
+}
+
+void TR_table::store(uint field_id, ulonglong val)
+{
+ table->field[field_id]->store(val, true);
+ table->field[field_id]->set_notnull();
+}
+
+void TR_table::store(uint field_id, timeval ts)
+{
+ table->field[field_id]->store_timestamp(ts.tv_sec, ts.tv_usec);
+ table->field[field_id]->set_notnull();
+}
+
+enum_tx_isolation TR_table::iso_level() const
+{
+ enum_tx_isolation res= (enum_tx_isolation) ((*this)[FLD_ISO_LEVEL]->val_int() - 1);
+ DBUG_ASSERT(res <= ISO_SERIALIZABLE);
+ return res;
+}
+
+bool TR_table::update(ulonglong start_id, ulonglong end_id)
+{
+ if (!table && open())
+ return true;
+
+ store(FLD_BEGIN_TS, thd->transaction_time());
+ thd->set_time();
+ timeval end_time= {thd->query_start(), int(thd->query_start_sec_part())};
+ store(FLD_TRX_ID, start_id);
+ store(FLD_COMMIT_ID, end_id);
+ store(FLD_COMMIT_TS, end_time);
+ store_iso_level(thd->tx_isolation);
+
+ int error= table->file->ha_write_row(table->record[0]);
+ if (unlikely(error))
+ table->file->print_error(error, MYF(0));
+ return error;
+}
+
+#define newx new (thd->mem_root)
+bool TR_table::query(ulonglong trx_id)
+{
+ if (!table && open())
+ return false;
+ SQL_SELECT_auto select;
+ READ_RECORD info;
+ int error;
+ List<TABLE_LIST> dummy;
+ SELECT_LEX &slex= thd->lex->select_lex;
+ Name_resolution_context_backup backup(slex.context, *this);
+ Item *field= newx Item_field(thd, &slex.context, (*this)[FLD_TRX_ID]);
+ Item *value= newx Item_int(thd, trx_id);
+ COND *conds= newx Item_func_eq(thd, field, value);
+ if (unlikely((error= setup_conds(thd, this, dummy, &conds))))
+ return false;
+ select= make_select(table, 0, 0, conds, NULL, 0, &error);
+ if (unlikely(error || !select))
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ return false;
+ }
+ // FIXME: (performance) force index 'transaction_id'
+ error= init_read_record(&info, thd, table, select, NULL,
+ 1 /* use_record_cache */, true /* print_error */,
+ false /* disable_rr_cache */);
+ while (!(error= info.read_record()) && !thd->killed && !thd->is_error())
+ {
+ if (select->skip_record(thd) > 0)
+ return true;
+ }
+ my_error(ER_VERS_NO_TRX_ID, MYF(0), (longlong) trx_id);
+ return false;
+}
+
+bool TR_table::query(MYSQL_TIME &commit_time, bool backwards)
+{
+ if (!table && open())
+ return false;
+ SQL_SELECT_auto select;
+ READ_RECORD info;
+ int error;
+ List<TABLE_LIST> dummy;
+ SELECT_LEX &slex= thd->lex->select_lex;
+ Name_resolution_context_backup backup(slex.context, *this);
+ Item *field= newx Item_field(thd, &slex.context, (*this)[FLD_COMMIT_TS]);
+ Item *value= newx Item_datetime_literal(thd, &commit_time, 6);
+ COND *conds;
+ if (backwards)
+ conds= newx Item_func_ge(thd, field, value);
+ else
+ conds= newx Item_func_le(thd, field, value);
+ if (unlikely((error= setup_conds(thd, this, dummy, &conds))))
+ return false;
+ // FIXME: (performance) force index 'commit_timestamp'
+ select= make_select(table, 0, 0, conds, NULL, 0, &error);
+ if (unlikely(error || !select))
+ return false;
+ error= init_read_record(&info, thd, table, select, NULL,
+ 1 /* use_record_cache */, true /* print_error */,
+ false /* disable_rr_cache */);
+
+ // With PK by transaction_id the records are ordered by PK, so we have to
+ // scan TRT fully and collect min (backwards == true)
+ // or max (backwards == false) stats.
+ bool found= false;
+ MYSQL_TIME found_ts;
+ while (!(error= info.read_record()) && !thd->killed && !thd->is_error())
+ {
+ int res= select->skip_record(thd);
+ if (res > 0)
+ {
+ MYSQL_TIME commit_ts;
+ if ((*this)[FLD_COMMIT_TS]->get_date(&commit_ts, 0))
+ {
+ found= false;
+ break;
+ }
+ int c;
+ if (!found || ((c= my_time_compare(&commit_ts, &found_ts)) &&
+ (backwards ? c < 0 : c > 0)))
+ {
+ found_ts= commit_ts;
+ found= true;
+ // TODO: (performance) make ORDER DESC and break after first found.
+ // Otherwise it is O(n) scan (+copy)!
+ store_record(table, record[1]);
+ }
+ }
+ else if (res < 0)
+ {
+ found= false;
+ break;
+ }
+ }
+ if (found)
+ restore_record(table, record[1]);
+ return found;
+}
+#undef newx
+
+bool TR_table::query_sees(bool &result, ulonglong trx_id1, ulonglong trx_id0,
+ ulonglong commit_id1, enum_tx_isolation iso_level1,
+ ulonglong commit_id0)
+{
+ if (trx_id1 == trx_id0)
+ {
+ return false;
+ }
+
+ if (trx_id1 == ULONGLONG_MAX || trx_id0 == 0)
+ {
+ result= true;
+ return false;
+ }
+
+ if (trx_id0 == ULONGLONG_MAX || trx_id1 == 0)
+ {
+ result= false;
+ return false;
+ }
+
+ if (!commit_id1)
+ {
+ if (!query(trx_id1))
+ return true;
+
+ commit_id1= (*this)[FLD_COMMIT_ID]->val_int();
+ iso_level1= iso_level();
+ }
+
+ if (!commit_id0)
+ {
+ if (!query(trx_id0))
+ return true;
+
+ commit_id0= (*this)[FLD_COMMIT_ID]->val_int();
+ }
+
+ // Trivial case: TX1 started after TX0 committed
+ if (trx_id1 > commit_id0
+ // Concurrent transactions: TX1 committed after TX0 and TX1 is read (un)committed
+ || (commit_id1 > commit_id0 && iso_level1 < ISO_REPEATABLE_READ))
+ {
+ result= true;
+ }
+ else // All other cases: TX1 does not see TX0
+ {
+ result= false;
+ }
+
+ return false;
+}
+
+void TR_table::warn_schema_incorrect(const char *reason)
+{
+ if (MYSQL_VERSION_ID == table->s->mysql_version)
+ {
+ sql_print_error("%`s.%`s schema is incorrect: %s.",
+ db.str, table_name.str, reason);
+ }
+ else
+ {
+ sql_print_error("%`s.%`s schema is incorrect: %s. Created with MariaDB %d, "
+ "now running %d.",
+ db.str, table_name.str, reason, MYSQL_VERSION_ID,
+ static_cast<int>(table->s->mysql_version));
+ }
+}
+
+bool TR_table::check(bool error)
+{
+ if (error)
+ {
+ sql_print_warning("%`s.%`s does not exist (open failed).", db.str,
+ table_name.str);
+ return true;
+ }
+
+ if (table->file->ht->db_type != DB_TYPE_INNODB)
+ {
+ warn_schema_incorrect("Wrong table engine (expected InnoDB)");
+ return true;
+ }
+
+#define WARN_SCHEMA(...) \
+ char reason[128]; \
+ snprintf(reason, 128, __VA_ARGS__); \
+ warn_schema_incorrect(reason);
+
+ if (table->s->fields != FIELD_COUNT)
+ {
+ WARN_SCHEMA("Wrong field count (expected %d)", FIELD_COUNT);
+ return true;
+ }
+
+ if (table->field[FLD_TRX_ID]->type() != MYSQL_TYPE_LONGLONG)
+ {
+ WARN_SCHEMA("Wrong field %d type (expected BIGINT UNSIGNED)", FLD_TRX_ID);
+ return true;
+ }
+
+ if (table->field[FLD_COMMIT_ID]->type() != MYSQL_TYPE_LONGLONG)
+ {
+ WARN_SCHEMA("Wrong field %d type (expected BIGINT UNSIGNED)", FLD_COMMIT_ID);
+ return true;
+ }
+
+ if (table->field[FLD_BEGIN_TS]->type() != MYSQL_TYPE_TIMESTAMP)
+ {
+ WARN_SCHEMA("Wrong field %d type (expected TIMESTAMP(6))", FLD_BEGIN_TS);
+ return true;
+ }
+
+ if (table->field[FLD_COMMIT_TS]->type() != MYSQL_TYPE_TIMESTAMP)
+ {
+ WARN_SCHEMA("Wrong field %d type (expected TIMESTAMP(6))", FLD_COMMIT_TS);
+ return true;
+ }
+
+ if (table->field[FLD_ISO_LEVEL]->type() != MYSQL_TYPE_STRING ||
+ !(table->field[FLD_ISO_LEVEL]->flags & ENUM_FLAG))
+ {
+ wrong_enum:
+ WARN_SCHEMA("Wrong field %d type (expected ENUM('READ-UNCOMMITTED', "
+ "'READ-COMMITTED', 'REPEATABLE-READ', 'SERIALIZABLE'))",
+ FLD_ISO_LEVEL);
+ return true;
+ }
+
+ Field_enum *iso_level= static_cast<Field_enum *>(table->field[FLD_ISO_LEVEL]);
+ st_typelib *typelib= iso_level->typelib;
+
+ if (typelib->count != 4)
+ goto wrong_enum;
+
+ if (strcmp(typelib->type_names[0], "READ-UNCOMMITTED") ||
+ strcmp(typelib->type_names[1], "READ-COMMITTED") ||
+ strcmp(typelib->type_names[2], "REPEATABLE-READ") ||
+ strcmp(typelib->type_names[3], "SERIALIZABLE"))
+ {
+ goto wrong_enum;
+ }
+
+ if (!table->key_info || !table->key_info->key_part)
+ goto wrong_pk;
+
+ if (strcmp(table->key_info->key_part->field->field_name.str, "transaction_id"))
+ {
+ wrong_pk:
+ WARN_SCHEMA("Wrong PRIMARY KEY (expected `transaction_id`)");
+ return true;
+ }
+
+ return false;
+}
+
+bool vers_select_conds_t::resolve_units(THD *thd)
+{
+ DBUG_ASSERT(type != SYSTEM_TIME_UNSPECIFIED);
+ DBUG_ASSERT(start.item);
+ return start.resolve_unit(thd) ||
+ end.resolve_unit(thd);
+}
+
+bool vers_select_conds_t::eq(const vers_select_conds_t &conds) const
+{
+ if (type != conds.type)
+ return false;
+ switch (type) {
+ case SYSTEM_TIME_UNSPECIFIED:
+ case SYSTEM_TIME_ALL:
+ return true;
+ case SYSTEM_TIME_BEFORE:
+ break;
+ case SYSTEM_TIME_HISTORY:
+ break;
+ case SYSTEM_TIME_AS_OF:
+ return start.eq(conds.start);
+ case SYSTEM_TIME_FROM_TO:
+ case SYSTEM_TIME_BETWEEN:
+ return start.eq(conds.start) && end.eq(conds.end);
+ }
+ DBUG_ASSERT(0);
+ return false;
+}
+
+
+bool Vers_history_point::resolve_unit(THD *thd)
+{
+ if (!item)
+ return false;
+ if (item->fix_fields_if_needed(thd, &item))
+ return true;
+ return item->this_item()->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");
+}
+
+
+void Vers_history_point::fix_item()
+{
+ if (item && item->decimals == 0 && item->type() == Item::FUNC_ITEM &&
+ ((Item_func*)item)->functype() == Item_func::NOW_FUNC)
+ item->decimals= 6;
+}
+
+
+bool Vers_history_point::eq(const vers_history_point_t &point) const
+{
+ return unit == point.unit && item->eq(point.item, false);
+}
+
+void Vers_history_point::print(String *str, enum_query_type query_type,
+ const char *prefix, size_t plen) const
+{
+ const static LEX_CSTRING unit_type[]=
+ {
+ { STRING_WITH_LEN("") },
+ { STRING_WITH_LEN("TIMESTAMP ") },
+ { STRING_WITH_LEN("TRANSACTION ") }
+ };
+ str->append(prefix, plen);
+ str->append(unit_type + unit);
+ item->print(str, query_type);
+}
+
+Field *TABLE::find_field_by_name(LEX_CSTRING *str) const
+{
+ Field **tmp;
+ size_t length= str->length;
+ if (s->name_hash.records)
+ {
+ tmp= (Field**) my_hash_search(&s->name_hash, (uchar*) str->str, length);
+ return tmp ? field[tmp - s->field] : NULL;
+ }
+ else
+ {
+ for (tmp= field; *tmp; tmp++)
+ {
+ if ((*tmp)->field_name.length == length &&
+ !lex_string_cmp(system_charset_info, &(*tmp)->field_name, str))
+ return *tmp;
+ }
+ }
+ return NULL;
+}
+
+
+bool TABLE::export_structure(THD *thd, Row_definition_list *defs)
+{
+ for (Field **src= field; *src; src++)
+ {
+ uint offs;
+ if (defs->find_row_field_by_name(&src[0]->field_name, &offs))
+ {
+ my_error(ER_DUP_FIELDNAME, MYF(0), src[0]->field_name.str);
+ return true;
+ }
+ Spvar_definition *def= new (thd->mem_root) Spvar_definition(thd, *src);
+ if (!def)
+ return true;
+ def->flags&= (uint) ~NOT_NULL_FLAG;
+ if ((def->sp_prepare_create_field(thd, thd->mem_root)) ||
+ (defs->push_back(def, thd->mem_root)))
+ return true;
+ }
+ return false;
+}
/*
@brief
@@ -8652,10 +9366,10 @@ bool fk_modifies_child(enum_fk_option opt)
void TABLE::initialize_quick_structures()
{
- bzero(quick_rows, sizeof(quick_rows));
- bzero(quick_key_parts, sizeof(quick_key_parts));
- bzero(quick_costs, sizeof(quick_costs));
- bzero(quick_n_ranges, sizeof(quick_n_ranges));
+ 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));
}
/*
diff --git a/sql/table.h b/sql/table.h
index 14ab0027a79..a42feba68c4 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -1,7 +1,7 @@
#ifndef TABLE_INCLUDED
#define TABLE_INCLUDED
/* Copyright (c) 2000, 2017, 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
@@ -16,12 +16,12 @@
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" /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_plist.h"
#include "sql_list.h" /* Sql_alloc */
#include "mdl.h"
#include "datadict.h"
#include "sql_string.h" /* String */
+#include "lex_string.h"
#ifndef MYSQL_CLIENT
@@ -54,6 +54,7 @@ struct TDC_element;
class Virtual_column_info;
class Table_triggers_list;
class TMP_TABLE_PARAM;
+class SEQUENCE;
struct Name_resolution_context;
/*
@@ -320,7 +321,7 @@ typedef struct st_grant_info
enum tmp_table_type
{
- NO_TMP_TABLE, NON_TRANSACTIONAL_TMP_TABLE, TRANSACTIONAL_TMP_TABLE,
+ NO_TMP_TABLE= 0, NON_TRANSACTIONAL_TMP_TABLE, TRANSACTIONAL_TMP_TABLE,
INTERNAL_TMP_TABLE, SYSTEM_TMP_TABLE
};
enum release_type { RELEASE_NORMAL, RELEASE_WAIT_FOR_DROP };
@@ -349,6 +350,19 @@ enum enum_vcol_update_mode
VCOL_UPDATE_FOR_REPLACE
};
+/* Field visibility enums */
+
+enum field_visibility_t {
+ VISIBLE= 0,
+ INVISIBLE_USER,
+ /* automatically added by the server. Can be queried explicitly
+ in SELECT, otherwise invisible from anything" */
+ INVISIBLE_SYSTEM,
+ INVISIBLE_FULL
+};
+
+#define INVISIBLE_MAX_BITS 3
+
/**
Category of table found in the table share.
@@ -463,15 +477,15 @@ enum enum_table_category
};
typedef enum enum_table_category TABLE_CATEGORY;
-TABLE_CATEGORY get_table_category(const LEX_STRING *db,
- const LEX_STRING *name);
+TABLE_CATEGORY get_table_category(const LEX_CSTRING *db,
+ const LEX_CSTRING *name);
typedef struct st_table_field_type
{
- LEX_STRING name;
- LEX_STRING type;
- LEX_STRING cset;
+ LEX_CSTRING name;
+ LEX_CSTRING type;
+ LEX_CSTRING cset;
} TABLE_FIELD_TYPE;
@@ -610,7 +624,7 @@ class TABLE_STATISTICS_CB
return true;
if (expected == READY)
return false;
- (void) LF_BACKOFF;
+ (void) LF_BACKOFF();
}
}
@@ -657,7 +671,6 @@ public:
void abort_stats_load() { stats_state.abort_load(); }
};
-
/**
This structure is shared between different table objects. There is one
instance of table share per one table in the database.
@@ -697,7 +710,7 @@ struct TABLE_SHARE
TABLE_STATISTICS_CB stats_cb;
uchar *default_values; /* row with default values */
- LEX_STRING comment; /* Comment about table */
+ LEX_CSTRING comment; /* Comment about table */
CHARSET_INFO *table_charset; /* Default charset of string fields */
MY_BITMAP *check_set; /* Fields used by check constrant */
@@ -712,12 +725,12 @@ struct TABLE_SHARE
should correspond to each other.
To ensure this one can use set_table_cache() methods.
*/
- LEX_STRING table_cache_key;
- LEX_STRING db; /* Pointer to db */
- LEX_STRING table_name; /* Table name (for open) */
- LEX_STRING path; /* Path to .frm file (from datadir) */
- LEX_STRING normalized_path; /* unpack_filename(path) */
- LEX_STRING connect_string;
+ LEX_CSTRING table_cache_key;
+ LEX_CSTRING db; /* Pointer to db */
+ LEX_CSTRING table_name; /* Table name (for open) */
+ LEX_CSTRING path; /* Path to .frm file (from datadir) */
+ LEX_CSTRING normalized_path; /* unpack_filename(path) */
+ LEX_CSTRING connect_string;
/*
Set of keys in use, implemented as a Bitmap.
@@ -739,6 +752,7 @@ struct TABLE_SHARE
db_plugin ? plugin_hton(db_plugin) : NULL;
}
enum row_type row_type; /* How rows are stored */
+ enum Table_type table_type;
enum tmp_table_type tmp_table;
/** Transactional or not. */
@@ -766,6 +780,7 @@ struct TABLE_SHARE
uint blob_fields; /* number of blob fields */
uint varchar_fields; /* number of varchar fields */
uint default_fields; /* number of default fields */
+ uint visible_fields; /* number of visible fields */
uint default_expressions;
uint table_check_constraints, field_check_constraints;
@@ -795,14 +810,21 @@ struct TABLE_SHARE
bool use_ext_keys; /* Extended keys can be used */
bool null_field_first;
bool system; /* Set if system table (one record) */
- bool crypted; /* If .frm file is crypted */
+ bool not_usable_by_query_cache;
+ /*
+ This is used by log tables, for tables that have their own internal
+ binary logging or for tables that doesn't support statement or row logging
+ */
+ bool no_replicate;
bool crashed;
bool is_view;
bool can_cmp_whole_record;
+ /* This is set for temporary tables where CREATE was binary logged */
bool table_creation_was_logged;
bool non_determinstic_insert;
bool vcols_need_refixing;
bool has_update_default_function;
+ bool can_do_row_logging; /* 1 if table supports RBR */
ulong table_map_id; /* for row-based replication */
/*
@@ -818,16 +840,8 @@ struct TABLE_SHARE
*/
const File_parser *view_def;
- /*
- Cache for row-based replication table share checks that does not
- need to be repeated. Possible values are: -1 when cache value is
- not calculated yet, 0 when table *shall not* be replicated, 1 when
- table *may* be replicated.
- */
- int cached_row_logging_check;
-
- /* Name of the tablespace used for this table */
- char *tablespace;
+ /* For sequence tables, the current sequence state */
+ SEQUENCE *sequence;
#ifdef WITH_PARTITION_STORAGE_ENGINE
/* filled in when reading from frm */
@@ -839,6 +853,24 @@ struct TABLE_SHARE
#endif
/**
+ System versioning support.
+ */
+
+ vers_sys_type_t versioned;
+ uint16 row_start_field;
+ uint16 row_end_field;
+
+ Field *vers_start_field()
+ {
+ return field[row_start_field];
+ }
+
+ Field *vers_end_field()
+ {
+ return field[row_end_field];
+ }
+
+ /**
Cache the checked structure of this table.
The pointer data is used to describe the structure that
@@ -910,12 +942,6 @@ struct TABLE_SHARE
set_table_cache_key(key_buff, key_length);
}
- inline bool honor_global_locks()
- {
- return ((table_category == TABLE_CATEGORY_USER)
- || (table_category == TABLE_CATEGORY_SYSTEM));
- }
-
inline bool require_write_privileges()
{
return (table_category == TABLE_CATEGORY_LOG);
@@ -1077,7 +1103,8 @@ private:
public:
Blob_mem_storage() :truncated_value(false)
{
- init_alloc_root(&storage, MAX_FIELD_VARCHARLENGTH, 0, MYF(0));
+ init_alloc_root(&storage, "Blob_mem_storage", MAX_FIELD_VARCHARLENGTH, 0,
+ MYF(0));
}
~ Blob_mem_storage()
{
@@ -1098,7 +1125,7 @@ public:
@retval Pointer to the copied string.
@retval 0 if an error occurred.
*/
- char *store(const char *from, uint length)
+ char *store(const char *from, size_t length)
{
return (char*) memdup_root(&storage, from, length);
}
@@ -1126,6 +1153,8 @@ struct st_cond_statistic;
/* Bitmap of table's fields */
typedef Bitmap<MAX_FIELDS> Field_map;
+class SplM_opt_info;
+
struct TABLE
{
TABLE() {} /* Remove gcc warning */
@@ -1150,7 +1179,7 @@ public:
uint32 instance; /** Table cache instance this TABLE is belonging to */
THD *in_use; /* Which thread uses this */
- uchar *record[2]; /* Pointer to records */
+ uchar *record[3]; /* Pointer to records */
uchar *write_row_record; /* Used as optimisation in
THD::write_row */
uchar *insert_values; /* used by INSERT ... UPDATE */
@@ -1190,6 +1219,8 @@ public:
TABLE_LIST *pos_in_table_list;/* Element referring to this table */
/* Position in thd->locked_table_list under LOCK TABLES */
TABLE_LIST *pos_in_locked_tables;
+ /* Tables used in DEFAULT and CHECK CONSTRAINT (normally sequence tables) */
+ TABLE_LIST *internal_tables;
/*
Not-null for temporary tables only. Non-null values means this table is
@@ -1361,7 +1392,6 @@ public:
If set, indicate that the table is not replicated by the server.
*/
bool locked_by_logger;
- bool no_replicate;
bool locked_by_name;
bool fulltext_searched;
bool no_cache;
@@ -1412,10 +1442,18 @@ public:
bool histograms_are_read;
MDL_ticket *mdl_ticket;
+ /*
+ This is used only for potentially splittable materialized tables and it
+ points to the info used by the optimizer to apply splitting optimization
+ */
+ SplM_opt_info *spl_opt_info;
+ key_map keys_usable_for_splitting;
+
+
inline void reset() { bzero((void*)this, sizeof(*this)); }
void init(THD *thd, TABLE_LIST *tl);
bool fill_item_list(List<Item> *item_list) const;
- void reset_item_list(List<Item> *item_list) const;
+ void reset_item_list(List<Item> *item_list, uint skip) const;
void clear_column_bitmaps(void);
void prepare_for_position(void);
MY_BITMAP *prepare_for_keyread(uint index, MY_BITMAP *map);
@@ -1431,6 +1469,8 @@ public:
void mark_columns_per_binlog_row_image(void);
bool mark_virtual_col(Field *field);
bool mark_virtual_columns_for_write(bool insert_fl);
+ bool check_virtual_columns_marked_for_read();
+ bool check_virtual_columns_marked_for_write();
void mark_default_fields_for_write(bool insert_fl);
void mark_columns_used_by_virtual_fields(void);
void mark_check_constraint_columns_for_read(void);
@@ -1559,7 +1599,7 @@ public:
bool prepare_triggers_for_delete_stmt_or_event();
bool prepare_triggers_for_update_stmt_or_event();
- inline Field **field_to_fill();
+ Field **field_to_fill();
bool validate_default_values_of_unset_fields(THD *thd) const;
bool insert_all_rows_into_tmp_table(THD *thd,
@@ -1567,6 +1607,68 @@ public:
TMP_TABLE_PARAM *tmp_table_param,
bool with_cleanup);
int fix_vcol_exprs(THD *thd);
+ Field *find_field_by_name(LEX_CSTRING *str) const;
+ bool export_structure(THD *thd, class Row_definition_list *defs);
+ bool is_splittable() { return spl_opt_info != NULL; }
+ void set_spl_opt_info(SplM_opt_info *spl_info);
+ void deny_splitting();
+ double get_materialization_cost(); // Now used only if is_splittable()==true
+ void add_splitting_info_for_key_field(struct KEY_FIELD *key_field);
+
+ /**
+ System Versioning support
+ */
+ bool vers_write;
+
+ bool versioned() const
+ {
+ DBUG_ASSERT(s);
+ return s->versioned;
+ }
+
+ bool versioned(vers_sys_type_t type) const
+ {
+ DBUG_ASSERT(s);
+ DBUG_ASSERT(type);
+ return s->versioned == type;
+ }
+
+ bool versioned_write() const
+ {
+ DBUG_ASSERT(versioned() || !vers_write);
+ return versioned() ? vers_write : false;
+ }
+
+ bool versioned_write(vers_sys_type_t type) const
+ {
+ DBUG_ASSERT(type);
+ DBUG_ASSERT(versioned() || !vers_write);
+ return versioned(type) ? vers_write : false;
+ }
+
+ Field *vers_start_field() const
+ {
+ DBUG_ASSERT(s && s->versioned);
+ return field[s->row_start_field];
+ }
+
+ Field *vers_end_field() const
+ {
+ DBUG_ASSERT(s && s->versioned);
+ return field[s->row_end_field];
+ }
+
+ ulonglong vers_start_id() const;
+ ulonglong vers_end_id() const;
+
+ bool vers_check_update(List<Item> &items);
+
+ int delete_row();
+ void vers_update_fields();
+ void vers_update_end();
+
+/** Number of additional fields used in versioned tables */
+#define VERSIONING_FIELDS 2
};
@@ -1613,16 +1715,16 @@ enum enum_fk_option { FK_OPTION_UNDEF, FK_OPTION_RESTRICT, FK_OPTION_CASCADE,
typedef struct st_foreign_key_info
{
- LEX_STRING *foreign_id;
- LEX_STRING *foreign_db;
- LEX_STRING *foreign_table;
- LEX_STRING *referenced_db;
- LEX_STRING *referenced_table;
+ LEX_CSTRING *foreign_id;
+ LEX_CSTRING *foreign_db;
+ LEX_CSTRING *foreign_table;
+ LEX_CSTRING *referenced_db;
+ LEX_CSTRING *referenced_table;
enum_fk_option update_method;
enum_fk_option delete_method;
- LEX_STRING *referenced_key_name;
- List<LEX_STRING> foreign_fields;
- List<LEX_STRING> referenced_fields;
+ LEX_CSTRING *referenced_key_name;
+ List<LEX_CSTRING> foreign_fields;
+ List<LEX_CSTRING> referenced_fields;
} FOREIGN_KEY_INFO;
LEX_CSTRING *fk_option_name(enum_fk_option opt);
@@ -1677,7 +1779,7 @@ typedef class Item COND;
typedef struct st_schema_table
{
- const char* table_name;
+ const char *table_name;
ST_FIELD_INFO *fields_info;
/* for FLUSH table_name */
int (*reset_table) ();
@@ -1686,7 +1788,8 @@ typedef struct st_schema_table
/* 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, LEX_STRING *db_name, LEX_STRING *table_name);
+ 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) */
@@ -1746,10 +1849,6 @@ class IS_table_read_plan;
#define JOIN_TYPE_RIGHT 2U
#define JOIN_TYPE_OUTER 4U /* Marker that this is an outer join */
-#define VIEW_SUID_INVOKER 0
-#define VIEW_SUID_DEFINER 1
-#define VIEW_SUID_DEFAULT 2
-
/* view WITH CHECK OPTION parameter options */
#define VIEW_CHECK_NONE 0
#define VIEW_CHECK_LOCAL 1
@@ -1763,16 +1862,16 @@ class IS_table_read_plan;
/** The threshold size a blob field buffer before it is freed */
#define MAX_TDC_BLOB_SIZE 65536
-class select_union;
+class select_unit;
class TMP_TABLE_PARAM;
Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref,
- const char *name);
+ LEX_CSTRING *name);
struct Field_translator
{
Item *item;
- const char *name;
+ LEX_CSTRING name;
};
@@ -1799,11 +1898,11 @@ public:
public:
Natural_join_column(Field_translator *field_param, TABLE_LIST *tab);
Natural_join_column(Item_field *field_param, TABLE_LIST *tab);
- const char *name();
+ LEX_CSTRING *name();
Item *create_item(THD *thd);
Field *field();
- const char *table_name();
- const char *db_name();
+ const char *safe_table_name();
+ const char *safe_db_name();
GRANT_INFO *grant();
};
@@ -1822,6 +1921,101 @@ class SJ_MATERIALIZATION_INFO;
class Index_hint;
class Item_in_subselect;
+/* trivial class, for %union in sql_yacc.yy */
+struct vers_history_point_t
+{
+ vers_sys_type_t unit;
+ Item *item;
+};
+
+class Vers_history_point : public vers_history_point_t
+{
+ void fix_item();
+
+public:
+ Vers_history_point() { empty(); }
+ Vers_history_point(vers_sys_type_t unit_arg, Item *item_arg)
+ {
+ unit= unit_arg;
+ item= item_arg;
+ fix_item();
+ }
+ Vers_history_point(vers_history_point_t p)
+ {
+ unit= p.unit;
+ item= p.item;
+ fix_item();
+ }
+ void empty() { unit= VERS_UNDEFINED; 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;
+ }
+ void bad_expression_data_type_error(const char *type) const;
+ bool eq(const vers_history_point_t &point) const;
+};
+
+struct vers_select_conds_t
+{
+ vers_system_time_t type;
+ vers_system_time_t orig_type;
+ bool used:1;
+ bool delete_history:1;
+ Vers_history_point start;
+ Vers_history_point end;
+
+ void empty()
+ {
+ type= SYSTEM_TIME_UNSPECIFIED;
+ orig_type= SYSTEM_TIME_UNSPECIFIED;
+ used= false;
+ delete_history= false;
+ start.empty();
+ end.empty();
+ }
+
+ void init(vers_system_time_t _type,
+ Vers_history_point _start= Vers_history_point(),
+ Vers_history_point _end= Vers_history_point())
+ {
+ type= _type;
+ orig_type= _type;
+ used= false;
+ delete_history= (type == SYSTEM_TIME_HISTORY ||
+ type == SYSTEM_TIME_BEFORE);
+ start= _start;
+ end= _end;
+ }
+
+ void print(String *str, enum_query_type query_type) const;
+
+ bool init_from_sysvar(THD *thd);
+
+ bool is_set() const
+ {
+ return type != SYSTEM_TIME_UNSPECIFIED;
+ }
+ bool was_set() const
+ {
+ return orig_type != SYSTEM_TIME_UNSPECIFIED;
+ }
+ bool need_setup() const
+ {
+ return type != SYSTEM_TIME_UNSPECIFIED && type != SYSTEM_TIME_ALL;
+ }
+ bool resolve_units(THD *thd);
+ bool eq(const vers_select_conds_t &conds) const;
+};
/*
Table reference in the FROM clause.
@@ -1858,22 +2052,27 @@ class Item_in_subselect;
4) jtbm semi-join (jtbm_subselect != NULL)
*/
+/** last_leaf_for_name_resolutioning support. */
+
struct LEX;
class Index_hint;
struct TABLE_LIST
{
TABLE_LIST() {} /* Remove gcc warning */
+ enum prelocking_types
+ {
+ PRELOCK_NONE, PRELOCK_ROUTINE, PRELOCK_FK
+ };
+
/**
Prepare TABLE_LIST that consists of one table instance to use in
open_and_lock_tables
*/
inline void reset() { bzero((void*)this, sizeof(*this)); }
- inline void init_one_table(const char *db_name_arg,
- size_t db_length_arg,
- const char *table_name_arg,
- size_t table_name_length_arg,
- const char *alias_arg,
+ inline void init_one_table(const LEX_CSTRING *db_arg,
+ const LEX_CSTRING *table_name_arg,
+ const LEX_CSTRING *alias_arg,
enum thr_lock_type lock_type_arg)
{
enum enum_mdl_type mdl_type;
@@ -1884,44 +2083,54 @@ struct TABLE_LIST
else
mdl_type= MDL_SHARED_READ;
- DBUG_ASSERT(!db_name_arg || strlen(db_name_arg) == db_length_arg);
- DBUG_ASSERT(!table_name_arg || strlen(table_name_arg) == table_name_length_arg);
reset();
- db= (char*) db_name_arg;
- db_length= db_length_arg;
- table_name= (char*) table_name_arg;
- table_name_length= table_name_length_arg;
- alias= (char*) (alias_arg ? alias_arg : table_name_arg);
+ DBUG_ASSERT(!db_arg->str || strlen(db_arg->str) == db_arg->length);
+ DBUG_ASSERT(!table_name_arg->str || strlen(table_name_arg->str) == table_name_arg->length);
+ DBUG_ASSERT(!alias_arg || strlen(alias_arg->str) == alias_arg->length);
+ db= *db_arg;
+ table_name= *table_name_arg;
+ 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, table_name, mdl_type, MDL_TRANSACTION);
- }
-
- inline void init_one_table_for_prelocking(const char *db_name_arg,
- size_t db_length_arg,
- const char *table_name_arg,
- size_t table_name_length_arg,
- const char *alias_arg,
- enum thr_lock_type lock_type_arg,
- bool routine,
- TABLE_LIST *belong_to_view_arg,
- uint8 trg_event_map_arg,
- TABLE_LIST ***last_ptr)
- {
- init_one_table(db_name_arg, db_length_arg, table_name_arg,
- table_name_length_arg, alias_arg, lock_type_arg);
+ mdl_request.init(MDL_key::TABLE, db.str, table_name.str, mdl_type,
+ MDL_TRANSACTION);
+ }
+
+ TABLE_LIST(TABLE *table_arg, thr_lock_type lock_type)
+ {
+ DBUG_ASSERT(table_arg->s);
+ init_one_table(&table_arg->s->db, &table_arg->s->table_name,
+ NULL, lock_type);
+ table= table_arg;
+ }
+
+ inline void init_one_table_for_prelocking(const LEX_CSTRING *db_arg,
+ const LEX_CSTRING *table_name_arg,
+ const LEX_CSTRING *alias_arg,
+ enum thr_lock_type lock_type_arg,
+ prelocking_types prelocking_type,
+ TABLE_LIST *belong_to_view_arg,
+ uint8 trg_event_map_arg,
+ TABLE_LIST ***last_ptr,
+ my_bool insert_data)
+
+ {
+ init_one_table(db_arg, table_name_arg, alias_arg, lock_type_arg);
cacheable_table= 1;
- prelocking_placeholder= routine ? ROUTINE : FK;
- open_type= routine ? OT_TEMPORARY_OR_BASE : OT_BASE_ONLY;
+ prelocking_placeholder= prelocking_type;
+ open_type= (prelocking_type == PRELOCK_ROUTINE ?
+ OT_TEMPORARY_OR_BASE :
+ OT_BASE_ONLY);
belong_to_view= belong_to_view_arg;
trg_event_map= trg_event_map_arg;
/* MDL is enough for read-only FK checks, we don't need the table */
- if (prelocking_placeholder == FK && lock_type < TL_WRITE_ALLOW_WRITE)
+ if (prelocking_type == PRELOCK_FK && lock_type < TL_WRITE_ALLOW_WRITE)
open_strategy= OPEN_STUB;
**last_ptr= this;
prev_global= *last_ptr;
*last_ptr= &next_global;
+ for_insert_data= insert_data;
}
@@ -1933,8 +2142,11 @@ struct TABLE_LIST
TABLE_LIST *next_local;
/* link in a global list of all queries tables */
TABLE_LIST *next_global, **prev_global;
- char *db, *alias, *table_name, *schema_table_name;
- char *option; /* Used by cache index */
+ LEX_CSTRING db;
+ LEX_CSTRING table_name;
+ LEX_CSTRING schema_table_name;
+ LEX_CSTRING alias;
+ const char *option; /* Used by cache index */
Item *on_expr; /* Used with outer join */
Name_resolution_context *on_context; /* For ON expressions */
@@ -1978,6 +2190,7 @@ struct TABLE_LIST
parsing 'this' is a NATURAL/USING join iff (natural_join != NULL).
*/
TABLE_LIST *natural_join;
+ bool part_of_natural_join;
/*
True if 'this' represents a nested join that is a NATURAL JOIN.
For one of the operands of 'this', the member 'natural_join' points
@@ -2010,7 +2223,7 @@ struct TABLE_LIST
select_result for derived table to pass it from table creation to table
filling procedure
*/
- select_union *derived_result;
+ select_unit *derived_result;
/* Stub used for materialized derived tables. */
table_map map; /* ID bit of table (1,2,4,8,16...) */
table_map get_map()
@@ -2065,7 +2278,9 @@ struct TABLE_LIST
st_select_lex_unit *derived; /* SELECT_LEX_UNIT of derived table */
With_element *with; /* With element defining this table (if any) */
/* Bitmap of the defining with element */
- table_map with_internal_reference_map;
+ table_map with_internal_reference_map;
+ TABLE_LIST * next_with_rec_ref;
+ bool is_derived_with_recursive_reference;
bool block_handle_derived;
ST_SCHEMA_TABLE *schema_table; /* Information_schema table */
st_select_lex *schema_select_lex;
@@ -2126,12 +2341,12 @@ struct TABLE_LIST
Item *where; /* VIEW WHERE clause condition */
Item *check_option; /* WITH CHECK OPTION condition */
LEX_STRING select_stmt; /* text of (CREATE/SELECT) statement */
- LEX_STRING md5; /* md5 of query text */
- LEX_STRING source; /* source of CREATE VIEW */
- LEX_STRING view_db; /* saved view database */
- LEX_STRING view_name; /* saved view name */
+ LEX_CSTRING md5; /* md5 of query text */
+ LEX_CSTRING source; /* source of CREATE VIEW */
+ LEX_CSTRING view_db; /* saved view database */
+ LEX_CSTRING view_name; /* saved view name */
LEX_STRING timestamp; /* GMT time stamp of last operation */
- st_lex_user definer; /* definer of view */
+ LEX_USER definer; /* definer of view */
ulonglong file_version; /* version of file's field set */
ulonglong mariadb_version; /* version of server on creation */
ulonglong updatable_view; /* VIEW can be updated */
@@ -2168,8 +2383,6 @@ struct TABLE_LIST
thr_lock_type lock_type;
uint outer_join; /* Which join type */
uint shared; /* Used in multi-upd */
- size_t db_length;
- size_t table_name_length;
bool updatable; /* VIEW/TABLE can be updated now */
bool straight; /* optimize with prev table */
bool updating; /* for replicate-do/ignore table */
@@ -2198,15 +2411,15 @@ struct TABLE_LIST
bool where_processed;
/* TRUE <=> VIEW CHECK OPTION expression has been processed */
bool check_option_processed;
- /* FRMTYPE_ERROR if any type is acceptable */
- enum frm_type_enum required_type;
+ /* TABLE_TYPE_UNKNOWN if any type is acceptable */
+ Table_type required_type;
handlerton *db_type; /* table_type for handler */
- char timestamp_buffer[20]; /* buffer for timestamp (19+1) */
+ char timestamp_buffer[MAX_DATETIME_WIDTH + 1];
/*
This TABLE_LIST object is just placeholder for prelocking, it will be
used for implicit LOCK TABLES only and won't be used in real statement.
*/
- enum { USER, ROUTINE, FK } prelocking_placeholder;
+ prelocking_types prelocking_placeholder;
/**
Indicates that if TABLE_LIST object corresponds to the table/view
which requires special handling.
@@ -2220,9 +2433,6 @@ struct TABLE_LIST
/* Don't associate a table share. */
OPEN_STUB
} open_strategy;
- /* For transactional locking. */
- int lock_timeout; /* NOWAIT or WAIT [X] */
- bool lock_transactional; /* If transactional lock requested. */
/** TRUE if an alias for this table was specified in the SQL. */
bool is_alias;
/** TRUE if the table is referred to in the statement using a fully
@@ -2236,8 +2446,7 @@ struct TABLE_LIST
/* TODO: replace with derived_type */
bool merged;
bool merged_for_insert;
- /* TRUE <=> don't prepare this derived table/view as it should be merged.*/
- bool skip_prepare_derived;
+ bool sequence; /* Part of NEXTVAL/CURVAL/LASTVAL */
/*
Items created by create_view_field and collected to change them in case
@@ -2246,7 +2455,6 @@ struct TABLE_LIST
List<Item> used_items;
/* Sublist (tail) of persistent used_items */
List<Item> persistent_used_items;
- Item **materialized_items;
/* View creation context. */
@@ -2262,14 +2470,14 @@ struct TABLE_LIST
These attributes MUST NOT be used for any purposes but the parsing.
*/
- LEX_STRING view_client_cs_name;
- LEX_STRING view_connection_cl_name;
+ LEX_CSTRING view_client_cs_name;
+ LEX_CSTRING view_connection_cl_name;
/*
View definition (SELECT-statement) in the UTF-form.
*/
- LEX_STRING view_body_utf8;
+ LEX_CSTRING view_body_utf8;
/* End of view definition context. */
@@ -2313,7 +2521,7 @@ struct TABLE_LIST
List<String> *partition_names;
#endif /* WITH_PARTITION_STORAGE_ENGINE */
- void calc_md5(char *buffer);
+ void calc_md5(const char *buffer);
int view_check_option(THD *thd, bool ignore_failure);
bool create_field_translation(THD *thd);
bool setup_underlying(THD *thd);
@@ -2331,6 +2539,12 @@ struct TABLE_LIST
TABLE_LIST *find_underlying_table(TABLE *table);
TABLE_LIST *first_leaf_for_name_resolution();
TABLE_LIST *last_leaf_for_name_resolution();
+
+ /* System Versioning */
+ vers_select_conds_t vers_conditions;
+
+ my_bool for_insert_data;
+
/**
@brief
Find the bottom in the chain of embedded table VIEWs.
@@ -2437,6 +2651,8 @@ struct TABLE_LIST
bool is_with_table();
bool is_recursive_with_table();
bool is_with_table_recursive_reference();
+ void register_as_derived_with_rec_ref(With_element *rec_elem);
+ bool is_nonrecursive_derived_with_rec_ref();
bool fill_recursive(THD *thd);
inline void set_view()
@@ -2455,7 +2671,7 @@ struct TABLE_LIST
{
DBUG_ENTER("set_merged_derived");
DBUG_PRINT("enter", ("Alias: '%s' Unit: %p",
- (alias ? alias : "<NULL>"),
+ (alias.str ? alias.str : "<NULL>"),
get_unit()));
derived_type= ((derived_type & DTYPE_MASK) |
DTYPE_TABLE | DTYPE_MERGE);
@@ -2470,7 +2686,7 @@ struct TABLE_LIST
{
DBUG_ENTER("set_materialized_derived");
DBUG_PRINT("enter", ("Alias: '%s' Unit: %p",
- (alias ? alias : "<NULL>"),
+ (alias.str ? alias.str : "<NULL>"),
get_unit()));
derived= get_unit();
derived_type= ((derived_type & (derived ? DTYPE_MASK : DTYPE_VIEW)) |
@@ -2500,7 +2716,7 @@ struct TABLE_LIST
@brief Returns the name of the database that the referenced table belongs
to.
*/
- char *get_db_name() const { return view != NULL ? view_db.str : db; }
+ const char *get_db_name() const { return view != NULL ? view_db.str : db.str; }
/**
@brief Returns the name of the table that this TABLE_LIST represents.
@@ -2508,7 +2724,7 @@ struct TABLE_LIST
@details The unqualified table name or view name for a table or view,
respectively.
*/
- char *get_table_name() const { return view != NULL ? view_name.str : table_name; }
+ const char *get_table_name() const { return view != NULL ? view_name.str : table_name.str; }
bool is_active_sjm();
bool is_jtbm() { return MY_TEST(jtbm_subselect != NULL); }
st_select_lex_unit *get_unit();
@@ -2572,7 +2788,7 @@ public:
virtual void set(TABLE_LIST *)= 0;
virtual void next()= 0;
virtual bool end_of_fields()= 0; /* Return 1 at end of list */
- virtual const char *name()= 0;
+ virtual LEX_CSTRING *name()= 0;
virtual Item *create_item(THD *)= 0;
virtual Field *field()= 0;
};
@@ -2592,7 +2808,7 @@ public:
void set_table(TABLE *table) { ptr= table->field; }
void next() { ptr++; }
bool end_of_fields() { return *ptr == 0; }
- const char *name();
+ LEX_CSTRING *name();
Item *create_item(THD *thd);
Field *field() { return *ptr; }
};
@@ -2609,7 +2825,7 @@ public:
void set(TABLE_LIST *table);
void next() { ptr++; }
bool end_of_fields() { return ptr == array_end; }
- const char *name();
+ LEX_CSTRING *name();
Item *create_item(THD *thd);
Item **item_ptr() {return &ptr->item; }
Field *field() { return 0; }
@@ -2633,7 +2849,7 @@ public:
void set(TABLE_LIST *table);
void next();
bool end_of_fields() { return !cur_column_ref; }
- const char *name() { return cur_column_ref->name(); }
+ LEX_CSTRING *name() { return cur_column_ref->name(); }
Item *create_item(THD *thd) { return cur_column_ref->create_item(thd); }
Field *field() { return cur_column_ref->field(); }
Natural_join_column *column_ref() { return cur_column_ref; }
@@ -2670,7 +2886,7 @@ public:
void next();
bool end_of_fields()
{ return (table_ref == last_leaf && field_it->end_of_fields()); }
- const char *name() { return field_it->name(); }
+ LEX_CSTRING *name() { return field_it->name(); }
const char *get_table_name();
const char *get_db_name();
GRANT_INFO *grant();
@@ -2725,9 +2941,11 @@ typedef struct st_nested_join
Before each use the counters are zeroed by reset_nj_counters.
*/
uint counter;
+
/*
- Number of elements in join_list that were not (or contain table(s) that
- weren't) removed by table elimination.
+ Number of elements in join_list that participate in the join plan choice:
+ - Base tables that were not removed by table elimination
+ - Join nests that were not removed by mark_join_nest_as_const
*/
uint n_tables;
nested_join_map nj_map; /* Bit used to identify this nested join*/
@@ -2755,7 +2973,7 @@ typedef struct st_changed_table_list
{
struct st_changed_table_list *next;
char *key;
- uint32 key_length;
+ size_t key_length;
} CHANGED_TABLE_LIST;
@@ -2786,7 +3004,7 @@ static inline void tmp_restore_column_map(MY_BITMAP *bitmap,
static inline my_bitmap_map *dbug_tmp_use_all_columns(TABLE *table,
MY_BITMAP *bitmap)
{
-#ifndef DBUG_OFF
+#ifdef DBUG_ASSERT_EXISTS
return tmp_use_all_columns(table, bitmap);
#else
return 0;
@@ -2796,7 +3014,7 @@ static inline my_bitmap_map *dbug_tmp_use_all_columns(TABLE *table,
static inline void dbug_tmp_restore_column_map(MY_BITMAP *bitmap,
my_bitmap_map *old)
{
-#ifndef DBUG_OFF
+#ifdef DBUG_ASSERT_EXISTS
tmp_restore_column_map(bitmap, old);
#endif
}
@@ -2811,7 +3029,7 @@ static inline void dbug_tmp_use_all_columns(TABLE *table,
MY_BITMAP *read_set,
MY_BITMAP *write_set)
{
-#ifndef DBUG_OFF
+#ifdef DBUG_ASSERT_EXISTS
save[0]= read_set->bitmap;
save[1]= write_set->bitmap;
(void) tmp_use_all_columns(table, read_set);
@@ -2824,7 +3042,7 @@ static inline void dbug_tmp_restore_column_maps(MY_BITMAP *read_set,
MY_BITMAP *write_set,
my_bitmap_map **old)
{
-#ifndef DBUG_OFF
+#ifdef DBUG_ASSERT_EXISTS
tmp_restore_column_map(read_set, old[0]);
tmp_restore_column_map(write_set, old[1]);
#endif
@@ -2845,9 +3063,10 @@ size_t max_row_length(TABLE *table, MY_BITMAP const *cols, const uchar *data);
void init_mdl_requests(TABLE_LIST *table_list);
enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
- const char *alias, uint db_stat, uint prgflag,
+ const LEX_CSTRING *alias, uint db_stat, uint prgflag,
uint ha_open_flags, TABLE *outparam,
- bool is_create_table);
+ bool is_create_table,
+ List<String> *partitions_to_open= NULL);
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);
@@ -2865,7 +3084,6 @@ enum open_frm_error open_table_def(THD *thd, TABLE_SHARE *share,
void open_table_error(TABLE_SHARE *share, enum open_frm_error error,
int db_errno);
void update_create_info_from_table(HA_CREATE_INFO *info, TABLE *form);
-bool check_and_convert_db_name(LEX_STRING *db, bool preserve_lettercase);
bool check_db_name(LEX_STRING *db);
bool check_column_name(const char *name);
bool check_table_name(const char *name, size_t length, bool check_for_path_chars);
@@ -2873,17 +3091,17 @@ int rename_file_ext(const char * from,const char * to,const char * ext);
char *get_field(MEM_ROOT *mem, Field *field);
bool get_field(MEM_ROOT *mem, Field *field, class String *res);
-bool validate_comment_length(THD *thd, LEX_STRING *comment, size_t max_len,
+bool validate_comment_length(THD *thd, LEX_CSTRING *comment, size_t max_len,
uint err_code, const char *name);
int closefrm(TABLE *table);
void free_blobs(TABLE *table);
void free_field_buffers_larger_than(TABLE *table, uint32 size);
ulong get_form_pos(File file, uchar *head, TYPELIB *save_names);
-void append_unescaped(String *res, const char *pos, uint length);
+void append_unescaped(String *res, const char *pos, size_t length);
void prepare_frm_header(THD *thd, uint reclength, uchar *fileinfo,
HA_CREATE_INFO *create_info, uint keys, KEY *key_info);
-char *fn_rext(char *name);
+const char *fn_frm_ext(const char *name);
/* Check that the integer is in the internal */
static inline int set_zone(int nr,int min_zone,int max_zone)
@@ -2896,30 +3114,26 @@ static inline int set_zone(int nr,int min_zone,int max_zone)
}
/* performance schema */
-extern LEX_STRING PERFORMANCE_SCHEMA_DB_NAME;
+extern LEX_CSTRING PERFORMANCE_SCHEMA_DB_NAME;
-extern LEX_STRING GENERAL_LOG_NAME;
-extern LEX_STRING SLOW_LOG_NAME;
+extern LEX_CSTRING GENERAL_LOG_NAME;
+extern LEX_CSTRING SLOW_LOG_NAME;
+extern LEX_CSTRING TRANSACTION_REG_NAME;
/* information schema */
-extern LEX_STRING INFORMATION_SCHEMA_NAME;
-extern LEX_STRING MYSQL_SCHEMA_NAME;
+extern LEX_CSTRING INFORMATION_SCHEMA_NAME;
+extern LEX_CSTRING MYSQL_SCHEMA_NAME;
-inline bool is_infoschema_db(const char *name, size_t len)
-{
- return (INFORMATION_SCHEMA_NAME.length == len &&
- !my_strcasecmp(system_charset_info,
- INFORMATION_SCHEMA_NAME.str, name));
-}
+/* table names */
+extern LEX_CSTRING MYSQL_USER_NAME, MYSQL_DB_NAME, MYSQL_PROC_NAME;
-inline bool is_infoschema_db(const char *name)
+inline bool is_infoschema_db(const LEX_CSTRING *name)
{
- return !my_strcasecmp(system_charset_info,
- INFORMATION_SCHEMA_NAME.str, name);
+ return (INFORMATION_SCHEMA_NAME.length == name->length &&
+ !my_strcasecmp(system_charset_info,
+ INFORMATION_SCHEMA_NAME.str, name->str));
}
-TYPELIB *typelib(MEM_ROOT *mem_root, List<String> &strings);
-
inline void mark_as_null_row(TABLE *table)
{
table->null_row=1;
@@ -2929,6 +3143,150 @@ inline void mark_as_null_row(TABLE *table)
bool is_simple_order(ORDER *order);
+class Open_tables_backup;
+
+/** Transaction Registry Table (TRT)
+
+ This table holds transaction IDs, their corresponding times and other
+ transaction-related data which is used for transaction order resolution.
+ When versioned table marks its records lifetime with transaction IDs,
+ TRT is used to get their actual timestamps. */
+
+class TR_table: public TABLE_LIST
+{
+ THD *thd;
+ Open_tables_backup *open_tables_backup;
+
+public:
+ enum field_id_t {
+ FLD_TRX_ID= 0,
+ FLD_COMMIT_ID,
+ FLD_BEGIN_TS,
+ FLD_COMMIT_TS,
+ FLD_ISO_LEVEL,
+ FIELD_COUNT
+ };
+
+ enum enabled {NO, MAYBE, YES};
+ static enum enabled use_transaction_registry;
+
+ /**
+ @param[in,out] Thread handle
+ @param[in] Current transaction is read-write.
+ */
+ TR_table(THD *_thd, bool rw= false);
+ /**
+ Opens a transaction_registry table.
+
+ @retval true on error, false otherwise.
+ */
+ bool open();
+ ~TR_table();
+ /**
+ @retval current thd
+ */
+ THD *get_thd() const { return thd; }
+ /**
+ Stores value to internal transaction_registry TABLE object.
+
+ @param[in] field number in a TABLE
+ @param[in] value to store
+ */
+ void store(uint field_id, ulonglong val);
+ /**
+ Stores value to internal transaction_registry TABLE object.
+
+ @param[in] field number in a TABLE
+ @param[in] value to store
+ */
+ void store(uint field_id, timeval ts);
+ /**
+ Update the transaction_registry right before commit.
+ @param start_id transaction identifier at start
+ @param end_id transaction identifier at commit
+
+ @retval false on success
+ @retval true on error (the transaction must be rolled back)
+ */
+ bool update(ulonglong start_id, ulonglong end_id);
+ // return true if found; false if not found or error
+ bool query(ulonglong trx_id);
+ /**
+ Gets a row from transaction_registry with the closest commit_timestamp to
+ first argument. We can search for a value which a lesser or greater than
+ first argument. Also loads a row into an internal TABLE object.
+
+ @param[in] timestamp
+ @param[in] true if we search for a lesser timestamp, false if greater
+ @retval true if exists, false it not exists or an error occurred
+ */
+ bool query(MYSQL_TIME &commit_time, bool backwards);
+ /**
+ Checks whether transaction1 sees transaction0.
+
+ @param[out] true if transaction1 sees transaction0, undefined on error and
+ when transaction1=transaction0 and false otherwise
+ @param[in] transaction_id of transaction1
+ @param[in] transaction_id of transaction0
+ @param[in] commit time of transaction1 or 0 if we want it to be queried
+ @param[in] isolation level (from handler.h) of transaction1
+ @param[in] commit time of transaction0 or 0 if we want it to be queried
+ @retval true on error, false otherwise
+ */
+ bool query_sees(bool &result, ulonglong trx_id1, ulonglong trx_id0,
+ ulonglong commit_id1= 0,
+ enum_tx_isolation iso_level1= ISO_READ_UNCOMMITTED,
+ ulonglong commit_id0= 0);
+
+ /**
+ @retval transaction isolation level of a row from internal TABLE object.
+ */
+ enum_tx_isolation iso_level() const;
+ /**
+ Stores transactioin isolation level to internal TABLE object.
+ */
+ void store_iso_level(enum_tx_isolation iso_level)
+ {
+ DBUG_ASSERT(iso_level <= ISO_SERIALIZABLE);
+ store(FLD_ISO_LEVEL, iso_level + 1);
+ }
+
+ /**
+ Writes a message to MariaDB log about incorrect transaction_registry schema.
+
+ @param[in] a message explained what's incorrect in schema
+ */
+ void warn_schema_incorrect(const char *reason);
+ /**
+ Checks whether transaction_registry table has a correct schema.
+
+ @retval true if schema is incorrect and false otherwise
+ */
+ bool check(bool error);
+
+ TABLE * operator-> () const
+ {
+ return table;
+ }
+ Field * operator[] (uint field_id) const
+ {
+ DBUG_ASSERT(field_id < FIELD_COUNT);
+ return table->field[field_id];
+ }
+ operator bool () const
+ {
+ return table;
+ }
+ bool operator== (const TABLE_LIST &subj) const
+ {
+ return (!cmp(&db, &subj.db) && !cmp(&table_name, &subj.table_name));
+ }
+ bool operator!= (const TABLE_LIST &subj) const
+ {
+ return !(*this == subj);
+ }
+};
+
#endif /* MYSQL_CLIENT */
#endif /* TABLE_INCLUDED */
diff --git a/sql/table_cache.cc b/sql/table_cache.cc
index 8313fd0a9d9..c1ae89f85ed 100644
--- a/sql/table_cache.cc
+++ b/sql/table_cache.cc
@@ -46,7 +46,7 @@
TABLE_SHARE::tdc.flushed is true
*/
-#include "my_global.h"
+#include "mariadb.h"
#include "lf.h"
#include "table.h"
#include "sql_base.h"
@@ -56,7 +56,7 @@
ulong tdc_size; /**< Table definition cache threshold for LRU eviction. */
ulong tc_size; /**< Table cache threshold for LRU eviction. */
uint32 tc_instances;
-static uint32 tc_active_instances= 1;
+uint32 tc_active_instances= 1;
static uint32 tc_contention_warning_reported;
/** Data collections. */
@@ -369,18 +369,30 @@ void tc_add_table(THD *thd, TABLE *table)
mysql_mutex_unlock(&element->LOCK_table_share);
mysql_mutex_lock(&tc[i].LOCK_table_cache);
- if (tc[i].records == tc_size && (LRU_table= tc[i].free_tables.pop_front()))
+ if (tc[i].records == tc_size)
{
- LRU_table->s->tdc->free_tables[i].list.remove(LRU_table);
- /* Needed if MDL deadlock detector chimes in before tc_remove_table() */
- LRU_table->in_use= thd;
+ if ((LRU_table= tc[i].free_tables.pop_front()))
+ {
+ LRU_table->s->tdc->free_tables[i].list.remove(LRU_table);
+ /* Needed if MDL deadlock detector chimes in before tc_remove_table() */
+ LRU_table->in_use= thd;
+ mysql_mutex_unlock(&tc[i].LOCK_table_cache);
+ /* Keep out of locked LOCK_table_cache */
+ tc_remove_table(LRU_table);
+ }
+ else
+ {
+ tc[i].records++;
+ mysql_mutex_unlock(&tc[i].LOCK_table_cache);
+ }
+ /* Keep out of locked LOCK_table_cache */
+ status_var_increment(thd->status_var.table_open_cache_overflows);
}
else
+ {
tc[i].records++;
- mysql_mutex_unlock(&tc[i].LOCK_table_cache);
-
- if (LRU_table)
- tc_remove_table(LRU_table);
+ mysql_mutex_unlock(&tc[i].LOCK_table_cache);
+ }
}
@@ -448,6 +460,7 @@ static TABLE *tc_acquire_table(THD *thd, TDC_element *element)
void tc_release_table(TABLE *table)
{
uint32 i= table->instance;
+ DBUG_ENTER("tc_release_table");
DBUG_ASSERT(table->in_use);
DBUG_ASSERT(table->file);
DBUG_ASSERT(!table->pos_in_locked_tables);
@@ -467,6 +480,7 @@ void tc_release_table(TABLE *table)
tc[i].free_tables.push_back(table);
mysql_mutex_unlock(&tc[i].LOCK_table_cache);
}
+ DBUG_VOID_RETURN;
}
@@ -576,17 +590,17 @@ static void lf_alloc_destructor(uchar *arg)
}
-static void tdc_hash_initializer(LF_HASH *hash __attribute__((unused)),
+static void tdc_hash_initializer(LF_HASH *,
TDC_element *element, LEX_STRING *key)
{
memcpy(element->m_key, key->str, key->length);
- element->m_key_length= key->length;
+ element->m_key_length= (uint)key->length;
tdc_assert_clean_share(element);
}
static uchar *tdc_hash_key(const TDC_element *element, size_t *length,
- my_bool not_used __attribute__((unused)))
+ my_bool)
{
*length= element->m_key_length;
return (uchar*) element->m_key;
@@ -734,7 +748,7 @@ TDC_element *tdc_lock_share(THD *thd, const char *db, const char *table_name)
char key[MAX_DBKEY_LENGTH];
DBUG_ENTER("tdc_lock_share");
- if (fix_thd_pins(thd))
+ if (unlikely(fix_thd_pins(thd)))
DBUG_RETURN((TDC_element*) MY_ERRPTR);
element= (TDC_element *) lf_hash_search(&tdc_hash, thd->tdc_hash_pins,
@@ -743,7 +757,7 @@ TDC_element *tdc_lock_share(THD *thd, const char *db, const char *table_name)
if (element)
{
mysql_mutex_lock(&element->LOCK_table_share);
- if (!element->share || element->share->error)
+ if (unlikely(!element->share || element->share->error))
{
mysql_mutex_unlock(&element->LOCK_table_share);
element= 0;
@@ -816,7 +830,7 @@ retry:
lf_hash_search_unpin(thd->tdc_hash_pins);
DBUG_ASSERT(element);
- if (!(share= alloc_table_share(tl->db, tl->table_name, key, key_length)))
+ if (!(share= alloc_table_share(tl->db.str, tl->table_name.str, key, key_length)))
{
lf_hash_delete(&tdc_hash, thd->tdc_hash_pins, key, key_length);
DBUG_RETURN(0);
@@ -825,7 +839,7 @@ retry:
/* note that tdc_acquire_share() *always* uses discovery */
open_table_def(thd, share, flags | GTS_USE_DISCOVERY);
- if (share->error)
+ if (checked_unlikely(share->error))
{
free_table_share(share);
lf_hash_delete(&tdc_hash, thd->tdc_hash_pins, key, key_length);
@@ -842,7 +856,10 @@ retry:
tdc_purge(false);
if (out_table)
+ {
+ status_var_increment(thd->status_var.table_open_cache_misses);
*out_table= 0;
+ }
share->m_psi= PSI_CALL_get_table_share(false, share);
goto end;
}
@@ -859,8 +876,10 @@ retry:
DBUG_ASSERT(element->share);
DBUG_ASSERT(!element->share->error);
DBUG_ASSERT(!element->share->is_view);
+ status_var_increment(thd->status_var.table_open_cache_hits);
DBUG_RETURN(element->share);
}
+ status_var_increment(thd->status_var.table_open_cache_misses);
}
mysql_mutex_lock(&element->LOCK_table_share);
@@ -876,7 +895,7 @@ retry:
We found an existing table definition. Return it if we didn't get
an error when reading the table definition from file.
*/
- if (share->error)
+ if (unlikely(share->error))
{
open_table_error(share, share->error, share->open_errno);
goto err;
@@ -1295,7 +1314,8 @@ int tdc_iterate(THD *thd, my_hash_walk_action action, void *argument,
if (no_dups)
{
- init_alloc_root(&no_dups_argument.root, 4096, 4096, MYF(alloc_flags));
+ 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);
no_dups_argument.action= action;
diff --git a/sql/table_cache.h b/sql/table_cache.h
index 6643f91ca54..b2790e44b6e 100644
--- a/sql/table_cache.h
+++ b/sql/table_cache.h
@@ -71,6 +71,7 @@ enum enum_tdc_remove_table_type
extern ulong tdc_size;
extern ulong tc_size;
extern uint32 tc_instances;
+extern uint32 tc_active_instances;
extern bool tdc_init(void);
extern void tdc_start_shutdown(void);
diff --git a/sql/temporary_tables.cc b/sql/temporary_tables.cc
index 005a520ff64..e957488e3db 100644
--- a/sql/temporary_tables.cc
+++ b/sql/temporary_tables.cc
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2016 MariaDB Corporation
+ Copyright (c) 2016, 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
@@ -19,6 +19,7 @@
All methods pertaining to temporary tables.
*/
+#include "mariadb.h"
#include "sql_acl.h" /* TMP_TABLE_ACLS */
#include "sql_base.h" /* tdc_create_key */
#include "lock.h" /* mysql_lock_remove */
@@ -64,7 +65,8 @@ TABLE *THD::create_and_open_tmp_table(handlerton *hton,
const char *path,
const char *db,
const char *table_name,
- bool open_in_engine)
+ bool open_in_engine,
+ bool open_internal_tables)
{
DBUG_ENTER("THD::create_and_open_tmp_table");
@@ -73,7 +75,9 @@ TABLE *THD::create_and_open_tmp_table(handlerton *hton,
if ((share= create_temporary_table(hton, frm, path, db, table_name)))
{
+ open_options|= HA_OPEN_FOR_CREATE;
table= open_temporary_table(share, table_name, open_in_engine);
+ open_options&= ~HA_OPEN_FOR_CREATE;
/*
Failed to open a temporary table instance. As we are not passing
@@ -87,6 +91,15 @@ TABLE *THD::create_and_open_tmp_table(handlerton *hton,
/* Free the TMP_TABLE_SHARE. */
free_tmp_table_share(share, false);
+ DBUG_RETURN(0);
+ }
+
+ /* Open any related tables */
+ if (open_internal_tables && table->internal_tables &&
+ open_and_lock_internal_tables(table, open_in_engine))
+ {
+ drop_temporary_table(table, NULL, false);
+ DBUG_RETURN(0);
}
}
@@ -248,7 +261,7 @@ TMP_TABLE_SHARE *THD::find_tmp_table_share(const TABLE_LIST *tl)
@return Success A pointer to table share object
Failure NULL
*/
-TMP_TABLE_SHARE *THD::find_tmp_table_share(const char *key, uint key_length)
+TMP_TABLE_SHARE *THD::find_tmp_table_share(const char *key, size_t key_length)
{
DBUG_ENTER("THD::find_tmp_table_share");
@@ -313,7 +326,7 @@ TMP_TABLE_SHARE *THD::find_tmp_table_share(const char *key, uint key_length)
bool THD::open_temporary_table(TABLE_LIST *tl)
{
DBUG_ENTER("THD::open_temporary_table");
- DBUG_PRINT("enter", ("table: '%s'.'%s'", tl->db, tl->table_name));
+ DBUG_PRINT("enter", ("table: '%s'.'%s'", tl->db.str, tl->table_name.str));
TMP_TABLE_SHARE *share;
TABLE *table= NULL;
@@ -338,7 +351,7 @@ bool THD::open_temporary_table(TABLE_LIST *tl)
DBUG_RETURN(false);
}
- if (!tl->db)
+ if (!tl->db.str)
{
DBUG_PRINT("info",
("Table reference to a temporary table must have database set"));
@@ -399,7 +412,7 @@ bool THD::open_temporary_table(TABLE_LIST *tl)
if (tl->open_type == OT_TEMPORARY_ONLY &&
tl->open_strategy == TABLE_LIST::OPEN_NORMAL)
{
- my_error(ER_NO_SUCH_TABLE, MYF(0), tl->db, tl->table_name);
+ my_error(ER_NO_SUCH_TABLE, MYF(0), tl->db.str, tl->table_name.str);
DBUG_RETURN(true);
}
DBUG_RETURN(false);
@@ -553,25 +566,21 @@ bool THD::close_temporary_tables()
true Error
*/
bool THD::rename_temporary_table(TABLE *table,
- const char *db,
- const char *table_name)
+ const LEX_CSTRING *db,
+ const LEX_CSTRING *table_name)
{
- DBUG_ENTER("THD::rename_temporary_table");
-
char *key;
uint key_length;
-
TABLE_SHARE *share= table->s;
+ DBUG_ENTER("THD::rename_temporary_table");
if (!(key= (char *) alloc_root(&share->mem_root, MAX_DBKEY_LENGTH)))
- {
DBUG_RETURN(true);
- }
/*
Temporary tables are renamed by simply changing their table definition key.
*/
- key_length= create_tmp_table_def_key(key, db, table_name);
+ key_length= create_tmp_table_def_key(key, db->str, table_name->str);
share->set_table_cache_key(key, key_length);
DBUG_RETURN(false);
@@ -1099,22 +1108,25 @@ TABLE *THD::find_temporary_table(const char *key, uint key_length,
Failure NULL
*/
TABLE *THD::open_temporary_table(TMP_TABLE_SHARE *share,
- const char *alias,
+ const char *alias_arg,
bool open_in_engine)
{
+ TABLE *table;
+ LEX_CSTRING alias= {alias_arg, strlen(alias_arg) };
DBUG_ENTER("THD::open_temporary_table");
- TABLE *table;
if (!(table= (TABLE *) my_malloc(sizeof(TABLE), MYF(MY_WME))))
{
DBUG_RETURN(NULL); /* Out of memory */
}
- if (open_table_from_share(this, share, alias,
+ if (open_table_from_share(this, share, &alias,
open_in_engine ? (uint)HA_OPEN_KEYFILE : 0,
- EXTRA_RECORD, open_options | ha_open_options, table,
- open_in_engine ? false : true))
+ EXTRA_RECORD,
+ (ha_open_options |
+ (open_options & HA_OPEN_FOR_CREATE)),
+ table, open_in_engine ? false : true))
{
my_free(table);
DBUG_RETURN(NULL);
@@ -1122,8 +1134,9 @@ TABLE *THD::open_temporary_table(TMP_TABLE_SHARE *share,
table->reginfo.lock_type= TL_WRITE; /* Simulate locked */
table->grant.privilege= TMP_TABLE_ACLS;
- share->tmp_table= (table->file->has_transactions() ?
+ 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;
@@ -1137,7 +1150,8 @@ TABLE *THD::open_temporary_table(TMP_TABLE_SHARE *share,
thread_safe_increment32(&slave_open_temp_tables);
}
- DBUG_PRINT("tmptable", ("Opened table: '%s'.'%s'%p", table->s->db.str,
+ DBUG_PRINT("tmptable", ("Opened table: '%s'.'%s table: %p",
+ table->s->db.str,
table->s->table_name.str, table));
DBUG_RETURN(table);
}
@@ -1373,15 +1387,15 @@ bool THD::log_events_and_free_tmp_shares()
/*
We are going to add ` around the table names and possible more
due to special characters.
- */
- append_identifier(this, &s_query, share->table_name.str,
- share->table_name.length);
+ */
+ append_identifier(this, &s_query, &share->table_name);
s_query.append(',');
}
rm_temporary_table(share->db_type(), share->path.str);
free_table_share(share);
my_free(share);
}
+
if (at_least_one_create_logged)
{
clear_error();
@@ -1398,7 +1412,8 @@ bool THD::log_events_and_free_tmp_shares()
get_stmt_da()->set_overwrite_status(true);
transaction.stmt.mark_dropped_temp_table();
- if ((error= (mysql_bin_log.write(&qinfo) || error)))
+ bool error2= mysql_bin_log.write(&qinfo);
+ if (unlikely(error|= error2))
{
/*
If we're here following THD::cleanup, thence the connection
@@ -1411,7 +1426,7 @@ bool THD::log_events_and_free_tmp_shares()
that THD::close_tables failed. (Actually, the SQL
thread only calls THD::close_tables while applying
old Start_log_event_v3 events.)
- */
+ */
sql_print_error("Failed to write the DROP statement for "
"temporary tables to binary log");
}
diff --git a/sql/thr_malloc.cc b/sql/thr_malloc.cc
index 4ad99bba7b5..d43e7c4c9b5 100644
--- a/sql/thr_malloc.cc
+++ b/sql/thr_malloc.cc
@@ -17,7 +17,7 @@
/* Mallocs for used in threads */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#include "thr_malloc.h"
@@ -27,7 +27,7 @@ extern "C" {
void sql_alloc_error_handler(void)
{
THD *thd= current_thd;
- if (thd)
+ if (likely(thd))
{
if (! thd->is_error())
{
@@ -58,10 +58,11 @@ extern "C" {
}
}
-void init_sql_alloc(MEM_ROOT *mem_root, uint block_size, uint pre_alloc,
- myf my_flags)
+void init_sql_alloc(MEM_ROOT *mem_root,
+ const char *area_name __attribute__((unused)),
+ uint block_size, uint pre_alloc, myf my_flags)
{
- init_alloc_root(mem_root, block_size, pre_alloc, my_flags);
+ init_alloc_root(mem_root, area_name, 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 90f5a2ea8fb..a6ab5477d41 100644
--- a/sql/thr_malloc.h
+++ b/sql/thr_malloc.h
@@ -16,12 +16,10 @@
#ifndef THR_MALLOC_INCLUDED
#define THR_MALLOC_INCLUDED
-#include "my_global.h" // uint, size_t
-
typedef struct st_mem_root MEM_ROOT;
-void init_sql_alloc(MEM_ROOT *root, uint block_size, uint pre_alloc_size,
- myf my_flags);
+void init_sql_alloc(MEM_ROOT *root, const char *area_name, 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/threadpool.h b/sql/threadpool.h
index d41435a766c..ec320c6945c 100644
--- a/sql/threadpool.h
+++ b/sql/threadpool.h
@@ -74,6 +74,7 @@ enum TP_STATE
{
TP_STATE_IDLE,
TP_STATE_RUNNING,
+ TP_STATE_PENDING
};
/*
diff --git a/sql/threadpool_common.cc b/sql/threadpool_common.cc
index b0438770aae..1d51f0da411 100644
--- a/sql/threadpool_common.cc
+++ b/sql/threadpool_common.cc
@@ -1,4 +1,4 @@
-/* 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
@@ -13,7 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include <violite.h>
#include <sql_priv.h>
#include <sql_class.h>
@@ -83,17 +83,13 @@ struct Worker_thread_context
void save()
{
-#ifdef HAVE_PSI_THREAD_INTERFACE
- psi_thread = PSI_THREAD_CALL(get_thread)();
-#endif
+ psi_thread= PSI_CALL_get_thread();
mysys_var= my_thread_var;
}
void restore()
{
-#ifdef HAVE_PSI_THREAD_INTERFACE
- PSI_THREAD_CALL(set_thread)(psi_thread);
-#endif
+ PSI_CALL_set_thread(psi_thread);
set_mysys_var(mysys_var);
pthread_setspecific(THR_THD, 0);
}
@@ -143,9 +139,7 @@ static void thread_attach(THD* thd)
set_mysys_var(thd->mysys_var);
thd->thread_stack=(char*)&thd;
thd->store_globals();
-#ifdef HAVE_PSI_THREAD_INTERFACE
- PSI_THREAD_CALL(set_thread)(thd->event_scheduler.m_psi);
-#endif
+ PSI_CALL_set_thread(thd->event_scheduler.m_psi);
mysql_socket_set_thread_owner(thd->net.vio->mysql_socket);
}
@@ -176,7 +170,7 @@ void tp_callback(TP_connection *c)
c->state = TP_STATE_RUNNING;
- if (!thd)
+ if (unlikely(!thd))
{
/* No THD, need to login first. */
DBUG_ASSERT(c->connect);
@@ -190,7 +184,7 @@ void tp_callback(TP_connection *c)
}
else if (threadpool_process_request(thd))
{
- /* QUIT or an error occured. */
+ /* QUIT or an error occurred. */
goto error;
}
@@ -198,7 +192,7 @@ void tp_callback(TP_connection *c)
c->priority= get_priority(c);
/* Read next command from client. */
- c->set_io_timeout(thd->variables.net_wait_timeout);
+ c->set_io_timeout(thd->get_net_wait_timeout());
c->state= TP_STATE_IDLE;
if (c->start_io())
goto error;
@@ -249,14 +243,12 @@ static THD* threadpool_add_connection(CONNECT *connect, void *scheduler_data)
}
delete connect;
add_to_active_threads(thd);
- thd->mysys_var= mysys_var;
+ thd->set_mysys_var(mysys_var);
thd->event_scheduler.data= scheduler_data;
/* Create new PSI thread for use with the THD. */
-#ifdef HAVE_PSI_THREAD_INTERFACE
thd->event_scheduler.m_psi=
- PSI_THREAD_CALL(new_thread)(key_thread_one_connection, thd, thd->thread_id);
-#endif
+ PSI_CALL_new_thread(key_thread_one_connection, thd, thd->thread_id);
/* Login. */
@@ -476,12 +468,26 @@ void tp_timeout_handler(TP_connection *c)
{
if (c->state != TP_STATE_IDLE)
return;
- THD *thd=c->thd;
- mysql_mutex_lock(&thd->LOCK_thd_data);
- thd->set_killed(KILL_WAIT_TIMEOUT);
- c->priority= TP_PRIORITY_HIGH;
- post_kill_notification(thd);
- mysql_mutex_unlock(&thd->LOCK_thd_data);
+ THD *thd= c->thd;
+ mysql_mutex_lock(&thd->LOCK_thd_kill);
+ Vio *vio= thd->net.vio;
+ if (vio && (vio_pending(vio) > 0 || vio->has_data(vio)) &&
+ c->state == TP_STATE_IDLE)
+ {
+ /*
+ There is some data on that connection, i.e
+ i.e there was no inactivity timeout.
+ Don't kill.
+ */
+ c->state= TP_STATE_PENDING;
+ }
+ else if (c->state == TP_STATE_IDLE)
+ {
+ thd->set_killed_no_mutex(KILL_WAIT_TIMEOUT);
+ c->priority= TP_PRIORITY_HIGH;
+ post_kill_notification(thd);
+ }
+ mysql_mutex_unlock(&thd->LOCK_thd_kill);
}
diff --git a/sql/threadpool_generic.cc b/sql/threadpool_generic.cc
index 02eb336facb..2433369dbbb 100644
--- a/sql/threadpool_generic.cc
+++ b/sql/threadpool_generic.cc
@@ -13,7 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include <violite.h>
#include <sql_priv.h>
#include <sql_class.h>
@@ -63,9 +63,6 @@ typedef OVERLAPPED_ENTRY native_event;
#error threadpool is not available on this platform
#endif
-#ifdef _MSC_VER
-#pragma warning (disable : 4312)
-#endif
static void io_poll_close(TP_file_handle fd)
{
@@ -181,7 +178,7 @@ connection_queue_t;
const int NQUEUES=2; /* We have high and low priority queues*/
-struct thread_group_t
+struct MY_ALIGNED(CPU_LEVEL1_DCACHE_LINESIZE) thread_group_t
{
mysql_mutex_t mutex;
connection_queue_t queues[NQUEUES];
@@ -199,7 +196,7 @@ struct thread_group_t
int shutdown_pipe[2];
bool shutdown;
bool stalled;
-} MY_ALIGNED(CPU_LEVEL1_DCACHE_LINESIZE);
+};
static thread_group_t *all_groups;
static uint group_count;
@@ -594,11 +591,8 @@ static void timeout_check(pool_timer_t *timer)
THD *thd;
while ((thd=it++))
{
- if (thd->net.reading_or_writing != 1)
- continue;
-
TP_connection_generic *connection= (TP_connection_generic *)thd->event_scheduler.data;
- if (!connection)
+ if (!connection || connection->state != TP_STATE_IDLE)
{
/*
Connection does not have scheduler data. This happens for example
@@ -1502,7 +1496,7 @@ void TP_connection_generic::set_io_timeout(int timeout_sec)
}
-
+#ifndef HAVE_IOCP
/**
Handle a (rare) special case,where connection needs to
migrate to a different group because group_count has changed
@@ -1537,7 +1531,7 @@ static int change_group(TP_connection_generic *c,
mysql_mutex_unlock(&new_group->mutex);
return ret;
}
-
+#endif
int TP_connection_generic::start_io()
{
diff --git a/sql/threadpool_win.cc b/sql/threadpool_win.cc
index 1e2084b9c07..ce0ea2252c1 100644
--- a/sql/threadpool_win.cc
+++ b/sql/threadpool_win.cc
@@ -19,7 +19,7 @@
#define _WIN32_WINNT 0x0601
-#include <my_global.h>
+#include "mariadb.h"
#include <violite.h>
#include <sql_priv.h>
#include <sql_class.h>
@@ -92,9 +92,6 @@ static void CALLBACK work_callback(PTP_CALLBACK_INSTANCE instance, PVOID context
static void CALLBACK shm_read_callback(PTP_CALLBACK_INSTANCE instance,
PVOID Context, PTP_WAIT wait,TP_WAIT_RESULT wait_result);
-static void CALLBACK shm_close_callback(PTP_CALLBACK_INSTANCE instance,
- PVOID Context, PTP_WAIT wait,TP_WAIT_RESULT wait_result);
-
static void pre_callback(PVOID context, PTP_CALLBACK_INSTANCE instance);
/* Get current time as Windows time */
@@ -151,8 +148,8 @@ TP_connection_win::TP_connection_win(CONNECT *c) :
timeout(ULONGLONG_MAX),
callback_instance(0),
io(0),
- shm_read(0),
timer(0),
+ shm_read(0),
work(0)
{
}
@@ -259,7 +256,7 @@ int TP_connection_win::start_io()
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)
+ if (skip_completion_port_on_success)
{
CancelThreadpoolIo(io);
io_completion_callback(callback_instance, this, &overlapped, last_error,
@@ -268,7 +265,7 @@ int TP_connection_win::start_io()
return 0;
}
- if(last_error == ERROR_IO_PENDING)
+ if (last_error == ERROR_IO_PENDING)
{
return 0;
}
diff --git a/sql/transaction.cc b/sql/transaction.cc
index 72b7f8e6fe4..e2c120ffebb 100644
--- a/sql/transaction.cc
+++ b/sql/transaction.cc
@@ -18,13 +18,12 @@
#pragma implementation // gcc: Class implementation
#endif
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "transaction.h"
-#include "rpl_handler.h"
#include "debug_sync.h" // DEBUG_SYNC
#include "sql_acl.h"
-
+#include "semisync_master.h"
#ifndef EMBEDDED_LIBRARY
/**
@@ -33,10 +32,7 @@
static void trans_track_end_trx(THD *thd)
{
if (thd->variables.session_track_transaction_info > TX_TRACK_NONE)
- {
- ((Transaction_state_tracker *)
- thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER))->end_trx(thd);
- }
+ thd->session_tracker.transaction_info.end_trx(thd);
}
#else
#define trans_track_end_trx(A) do{}while(0)
@@ -52,11 +48,8 @@ void trans_reset_one_shot_chistics(THD *thd)
#ifndef EMBEDDED_LIBRARY
if (thd->variables.session_track_transaction_info > TX_TRACK_NONE)
{
- Transaction_state_tracker *tst= (Transaction_state_tracker *)
- thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER);
-
- tst->set_read_flags(thd, TX_READ_INHERIT);
- tst->set_isol_level(thd, TX_ISOL_INHERIT);
+ thd->session_tracker.transaction_info.set_read_flags(thd, TX_READ_INHERIT);
+ thd->session_tracker.transaction_info.set_isol_level(thd, TX_ISOL_INHERIT);
}
#endif //EMBEDDED_LIBRARY
thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
@@ -163,20 +156,11 @@ static bool xa_trans_force_rollback(THD *thd)
bool trans_begin(THD *thd, uint flags)
{
int res= FALSE;
-#ifndef EMBEDDED_LIBRARY
- Transaction_state_tracker *tst= NULL;
-#endif //EMBEDDED_LIBRARY
DBUG_ENTER("trans_begin");
if (trans_check(thd))
DBUG_RETURN(TRUE);
-#ifndef EMBEDDED_LIBRARY
- if (thd->variables.session_track_transaction_info > TX_TRACK_NONE)
- tst= (Transaction_state_tracker *)
- thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER);
-#endif //EMBEDDED_LIBRARY
-
thd->locked_tables_list.unlock_locked_tables(thd);
DBUG_ASSERT(!thd->locked_tables_mode);
@@ -204,6 +188,7 @@ bool trans_begin(THD *thd, uint flags)
thd->transaction.all.reset();
thd->has_waiter= false;
thd->waiting_on_group_commit= false;
+ thd->transaction.start_time.reset(thd);
if (res)
DBUG_RETURN(TRUE);
@@ -221,8 +206,8 @@ bool trans_begin(THD *thd, uint flags)
{
thd->tx_read_only= true;
#ifndef EMBEDDED_LIBRARY
- if (tst)
- tst->set_read_flags(thd, TX_READ_ONLY);
+ if (thd->variables.session_track_transaction_info > TX_TRACK_NONE)
+ thd->session_tracker.transaction_info.set_read_flags(thd, TX_READ_ONLY);
#endif //EMBEDDED_LIBRARY
}
else if (flags & MYSQL_START_TRANS_OPT_READ_WRITE)
@@ -246,8 +231,8 @@ bool trans_begin(THD *thd, uint flags)
just from the session's default.
*/
#ifndef EMBEDDED_LIBRARY
- if (tst)
- tst->set_read_flags(thd, TX_READ_WRITE);
+ if (thd->variables.session_track_transaction_info > TX_TRACK_NONE)
+ thd->session_tracker.transaction_info.set_read_flags(thd, TX_READ_WRITE);
#endif //EMBEDDED_LIBRARY
}
@@ -264,16 +249,16 @@ bool trans_begin(THD *thd, uint flags)
DBUG_PRINT("info", ("setting SERVER_STATUS_IN_TRANS"));
#ifndef EMBEDDED_LIBRARY
- if (tst)
- tst->add_trx_state(thd, TX_EXPLICIT);
+ if (thd->variables.session_track_transaction_info > TX_TRACK_NONE)
+ thd->session_tracker.transaction_info.add_trx_state(thd, TX_EXPLICIT);
#endif //EMBEDDED_LIBRARY
/* ha_start_consistent_snapshot() relies on OPTION_BEGIN flag set. */
if (flags & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT)
{
#ifndef EMBEDDED_LIBRARY
- if (tst)
- tst->add_trx_state(thd, TX_WITH_SNAPSHOT);
+ if (thd->variables.session_track_transaction_info > TX_TRACK_NONE)
+ thd->session_tracker.transaction_info.add_trx_state(thd, TX_WITH_SNAPSHOT);
#endif //EMBEDDED_LIBRARY
res= ha_start_consistent_snapshot(thd);
}
@@ -318,9 +303,17 @@ bool trans_commit(THD *thd)
transaction, so the hooks for rollback will be called.
*/
if (res)
- (void) RUN_HOOK(transaction, after_rollback, (thd, FALSE));
+ {
+#ifdef HAVE_REPLICATION
+ repl_semisync_master.wait_after_rollback(thd, FALSE);
+#endif
+ }
else
- (void) RUN_HOOK(transaction, after_commit, (thd, FALSE));
+ {
+#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->lex->start_transaction_opt= 0;
@@ -413,7 +406,9 @@ bool trans_rollback(THD *thd)
~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
res= ha_rollback_trans(thd, TRUE);
- (void) RUN_HOOK(transaction, after_rollback, (thd, FALSE));
+#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;
@@ -526,9 +521,17 @@ bool trans_commit_stmt(THD *thd)
transaction, so the hooks for rollback will be called.
*/
if (res)
- (void) RUN_HOOK(transaction, after_rollback, (thd, FALSE));
+ {
+#ifdef HAVE_REPLICATION
+ repl_semisync_master.wait_after_rollback(thd, FALSE);
+#endif
+ }
else
- (void) RUN_HOOK(transaction, after_commit, (thd, FALSE));
+ {
+#ifdef HAVE_REPLICATION
+ repl_semisync_master.wait_after_commit(thd, FALSE);
+#endif
+ }
thd->transaction.stmt.reset();
@@ -567,7 +570,9 @@ bool trans_rollback_stmt(THD *thd)
trans_reset_one_shot_chistics(thd);
}
- (void) RUN_HOOK(transaction, after_rollback, (thd, FALSE));
+#ifdef HAVE_REPLICATION
+ repl_semisync_master.wait_after_rollback(thd, FALSE);
+#endif
thd->transaction.stmt.reset();
@@ -576,7 +581,7 @@ bool trans_rollback_stmt(THD *thd)
/* Find a named savepoint in the current transaction. */
static SAVEPOINT **
-find_savepoint(THD *thd, LEX_STRING name)
+find_savepoint(THD *thd, LEX_CSTRING name)
{
SAVEPOINT **sv= &thd->transaction.savepoints;
@@ -602,7 +607,7 @@ find_savepoint(THD *thd, LEX_STRING name)
@retval TRUE Failure
*/
-bool trans_savepoint(THD *thd, LEX_STRING name)
+bool trans_savepoint(THD *thd, LEX_CSTRING name)
{
SAVEPOINT **sv, *newsv;
DBUG_ENTER("trans_savepoint");
@@ -633,14 +638,14 @@ bool trans_savepoint(THD *thd, LEX_STRING name)
}
newsv->name= strmake_root(&thd->transaction.mem_root, name.str, name.length);
- newsv->length= name.length;
+ newsv->length= (uint)name.length;
/*
if we'll get an error here, don't add new savepoint to the list.
we'll lose a little bit of memory in transaction mem_root, but it'll
be free'd when transaction ends anyway
*/
- if (ha_savepoint(thd, newsv))
+ if (unlikely(ha_savepoint(thd, newsv)))
DBUG_RETURN(TRUE);
newsv->prev= thd->transaction.savepoints;
@@ -678,7 +683,7 @@ bool trans_savepoint(THD *thd, LEX_STRING name)
@retval TRUE Failure
*/
-bool trans_rollback_to_savepoint(THD *thd, LEX_STRING name)
+bool trans_rollback_to_savepoint(THD *thd, LEX_CSTRING name)
{
int res= FALSE;
SAVEPOINT *sv= *find_savepoint(thd, name);
@@ -754,7 +759,7 @@ bool trans_rollback_to_savepoint(THD *thd, LEX_STRING name)
@retval TRUE Failure
*/
-bool trans_release_savepoint(THD *thd, LEX_STRING name)
+bool trans_release_savepoint(THD *thd, LEX_CSTRING name)
{
int res= FALSE;
SAVEPOINT *sv= *find_savepoint(thd, name);
diff --git a/sql/transaction.h b/sql/transaction.h
index a612b782922..56cc6f8f011 100644
--- a/sql/transaction.h
+++ b/sql/transaction.h
@@ -20,7 +20,6 @@
#pragma interface /* gcc class implementation */
#endif
-#include <my_global.h>
#include <m_string.h>
class THD;
@@ -34,9 +33,9 @@ bool trans_rollback_implicit(THD *thd);
bool trans_commit_stmt(THD *thd);
bool trans_rollback_stmt(THD *thd);
-bool trans_savepoint(THD *thd, LEX_STRING name);
-bool trans_rollback_to_savepoint(THD *thd, LEX_STRING name);
-bool trans_release_savepoint(THD *thd, LEX_STRING name);
+bool trans_savepoint(THD *thd, LEX_CSTRING name);
+bool trans_rollback_to_savepoint(THD *thd, LEX_CSTRING name);
+bool trans_release_savepoint(THD *thd, LEX_CSTRING name);
bool trans_xa_start(THD *thd);
bool trans_xa_end(THD *thd);
diff --git a/sql/tztime.cc b/sql/tztime.cc
index 35e18f47629..ca4c10975ca 100644
--- a/sql/tztime.cc
+++ b/sql/tztime.cc
@@ -29,22 +29,21 @@
#pragma implementation // gcc: Class implementation
#endif
-#include <my_global.h>
+#include "mariadb.h"
#if !defined(TZINFO2SQL) && !defined(TESTTIME)
#include "sql_priv.h"
#include "unireg.h"
-#include "tztime.h"
#include "sql_time.h" // localtime_to_TIME
#include "sql_base.h" // open_system_tables_for_read,
// close_system_tables
#else
#include <my_time.h>
-#include "tztime.h"
#include <my_sys.h>
#include <mysql_version.h>
#include <my_getopt.h>
#endif
+#include "tztime.h"
#include "tzfile.h"
#include <m_string.h>
#include <my_dir.h>
@@ -168,7 +167,7 @@ static my_bool
tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage)
{
uchar *p;
- int read_from_file;
+ ssize_t read_from_file;
uint i;
MYSQL_FILE *file;
@@ -189,7 +188,7 @@ tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage)
uint ttisgmtcnt;
char *tzinfo_buf;
- read_from_file= mysql_file_fread(file, u.buf, sizeof(u.buf), MYF(MY_WME));
+ read_from_file= (ssize_t)mysql_file_fread(file, u.buf, sizeof(u.buf), MYF(MY_WME));
if (mysql_file_fclose(file, MYF(MY_WME)) != 0)
return 1;
@@ -1335,7 +1334,7 @@ Time_zone_offset::Time_zone_offset(long tz_offset_arg):
{
uint hours= abs((int)(offset / SECS_PER_HOUR));
uint minutes= abs((int)(offset % SECS_PER_HOUR / SECS_PER_MIN));
- ulong length= my_snprintf(name_buff, sizeof(name_buff), "%s%02d:%02d",
+ size_t length= my_snprintf(name_buff, sizeof(name_buff), "%s%02d:%02d",
(offset>=0) ? "+" : "-", hours, minutes);
name.set(name_buff, length, &my_charset_latin1);
}
@@ -1478,19 +1477,14 @@ static bool time_zone_tables_exist= 1;
for dynamical loading of time zone descriptions.
*/
-static const LEX_STRING tz_tables_names[MY_TZ_TABLES_COUNT]=
+static const LEX_CSTRING tz_tables_names[MY_TZ_TABLES_COUNT]=
{
- { C_STRING_WITH_LEN("time_zone_name")},
- { C_STRING_WITH_LEN("time_zone")},
- { C_STRING_WITH_LEN("time_zone_transition_type")},
- { C_STRING_WITH_LEN("time_zone_transition")}
+ { STRING_WITH_LEN("time_zone_name")},
+ { STRING_WITH_LEN("time_zone")},
+ { STRING_WITH_LEN("time_zone_transition_type")},
+ { STRING_WITH_LEN("time_zone_transition")}
};
-/* Name of database to which those tables belong. */
-
-static const LEX_STRING tz_tables_db_name= { C_STRING_WITH_LEN("mysql")};
-
-
class Tz_names_entry: public Sql_alloc
{
public:
@@ -1540,8 +1534,7 @@ tz_init_table_list(TABLE_LIST *tz_tabs)
{
for (int i= 0; i < MY_TZ_TABLES_COUNT; i++)
{
- tz_tabs[i].init_one_table(tz_tables_db_name.str, tz_tables_db_name.length,
- tz_tables_names[i].str, tz_tables_names[i].length,
+ tz_tabs[i].init_one_table(&MYSQL_SCHEMA_NAME, tz_tables_names + i,
NULL, TL_READ);
if (i != MY_TZ_TABLES_COUNT - 1)
tz_tabs[i].next_global= tz_tabs[i].next_local= &tz_tabs[i+1];
@@ -1603,9 +1596,9 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap)
THD *thd;
TABLE_LIST tz_tables[1+MY_TZ_TABLES_COUNT];
TABLE *table;
+ const LEX_CSTRING tmp_table_name= { STRING_WITH_LEN("time_zone_leap_second") };
Tz_names_entry *tmp_tzname;
my_bool return_val= 1;
- char db[]= "mysql";
int res;
DBUG_ENTER("my_tz_init");
@@ -1635,7 +1628,7 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap)
my_hash_free(&tz_names);
goto end;
}
- init_sql_alloc(&tz_storage, 32 * 1024, 0, MYF(0));
+ init_sql_alloc(&tz_storage, "timezone_storage", 32 * 1024, 0, MYF(0));
mysql_mutex_init(key_tz_LOCK, &tz_LOCK, MY_MUTEX_INIT_FAST);
tz_inited= 1;
@@ -1666,13 +1659,10 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap)
leap seconds shared by all time zones.
*/
- thd->set_db(db, sizeof(db)-1);
+ thd->set_db(&MYSQL_SCHEMA_NAME);
bzero((char*) &tz_tables[0], sizeof(TABLE_LIST));
- tz_tables[0].alias= tz_tables[0].table_name=
- (char*)"time_zone_leap_second";
- tz_tables[0].table_name_length= 21;
- tz_tables[0].db= db;
- tz_tables[0].db_length= sizeof(db)-1;
+ tz_tables[0].alias= tz_tables[0].table_name= tmp_table_name;
+ tz_tables[0].db= MYSQL_SCHEMA_NAME;
tz_tables[0].lock_type= TL_READ;
tz_init_table_list(tz_tables+1);
@@ -1775,7 +1765,8 @@ end_with_setting_default_tz:
most of them once more, but this is OK for system tables open
for READ.
*/
- if (!(global_system_variables.time_zone= my_tz_find(thd, &tmp_tzname2)))
+ if (unlikely(!(global_system_variables.time_zone=
+ my_tz_find(thd, &tmp_tzname2))))
{
sql_print_error("Fatal error: Illegal or unknown default time zone '%s'",
default_tzname);
@@ -1790,7 +1781,7 @@ end_with_close:
end_with_cleanup:
/* if there were error free time zone describing structs */
- if (return_val)
+ if (unlikely(return_val))
my_tz_free();
end:
delete thd;
@@ -2577,7 +2568,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, 32768, 0, MYF(MY_THREAD_SPECIFIC));
+ init_alloc_root(&tz_storage, "timezone_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
@@ -2768,7 +2760,7 @@ main(int argc, char **argv)
First argument is timezonefile.
The second is timezonename if opt_leap is not given
*/
- init_alloc_root(&tz_storage, 32768, 0, MYF(0));
+ init_alloc_root(&tz_storage, "timezone_storage", 32768, 0, MYF(0));
if (tz_load(argv[0], &tz_info, &tz_storage))
{
@@ -2853,7 +2845,7 @@ main(int argc, char **argv)
MY_INIT(argv[0]);
- init_alloc_root(&tz_storage, 32768, MYF(0));
+ init_alloc_root(&tz_storage, "timezone_storage", 32768, MYF(0));
/* let us set some well known timezone */
setenv("TZ", "MET", 1);
diff --git a/sql/tztime.h b/sql/tztime.h
index dde014ffe65..d24a379e634 100644
--- a/sql/tztime.h
+++ b/sql/tztime.h
@@ -89,6 +89,5 @@ extern my_time_t sec_since_epoch_TIME(MYSQL_TIME *t);
static const int MY_TZ_TABLES_COUNT= 4;
-
#endif /* !defined(TESTTIME) && !defined(TZINFO2SQL) */
#endif /* TZTIME_INCLUDED */
diff --git a/sql/udf_example.c b/sql/udf_example.c
index bfd4c8c3a9f..26afa538e88 100644
--- a/sql/udf_example.c
+++ b/sql/udf_example.c
@@ -56,7 +56,7 @@
**
** Function 'myfunc_int' returns summary length of all its arguments.
**
-** Function 'sequence' returns an sequence starting from a certain number.
+** Function 'udf_sequence' returns an sequence starting from a certain number.
**
** Function 'myfunc_argument_name' returns name of argument.
**
@@ -80,7 +80,7 @@
** CREATE FUNCTION metaphon RETURNS STRING SONAME "udf_example.so";
** CREATE FUNCTION myfunc_double RETURNS REAL SONAME "udf_example.so";
** CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME "udf_example.so";
-** CREATE FUNCTION sequence RETURNS INTEGER SONAME "udf_example.so";
+** CREATE FUNCTION udf_sequence RETURNS INTEGER SONAME "udf_example.so";
** CREATE FUNCTION lookup RETURNS STRING SONAME "udf_example.so";
** CREATE FUNCTION reverse_lookup RETURNS STRING SONAME "udf_example.so";
** CREATE AGGREGATE FUNCTION avgcost RETURNS REAL SONAME "udf_example.so";
@@ -112,6 +112,11 @@
**
*/
+#ifdef _WIN32
+/* Silence warning about deprecated functions , gethostbyname etc*/
+#define _WINSOCK_DEPRECATED_NO_WARNINGS
+#endif
+
#ifdef STANDARD
/* STANDARD is defined, don't use any mysql functions */
#include <stdlib.h>
@@ -125,7 +130,7 @@ typedef unsigned long long ulonglong;
typedef long long longlong;
#endif /*__WIN__*/
#else
-#include <my_global.h>
+#include "mariadb.h"
#include <my_sys.h>
#if defined(MYSQL_SERVER)
#include <m_string.h> /* To get strmov() */
@@ -139,10 +144,6 @@ typedef long long longlong;
#include <mysql.h>
#include <ctype.h>
-#ifdef _WIN32
-/* inet_aton needs winsock library */
-#pragma comment(lib, "ws2_32")
-#endif
#ifdef HAVE_DLOPEN
@@ -162,9 +163,9 @@ double myfunc_double(UDF_INIT *initid, UDF_ARGS *args, char *is_null,
my_bool myfunc_int_init(UDF_INIT *initid, UDF_ARGS *args, char *message);
longlong myfunc_int(UDF_INIT *initid, UDF_ARGS *args, char *is_null,
char *error);
-my_bool sequence_init(UDF_INIT *initid, UDF_ARGS *args, char *message);
- void sequence_deinit(UDF_INIT *initid);
-longlong sequence(UDF_INIT *initid, UDF_ARGS *args, char *is_null,
+my_bool udf_sequence_init(UDF_INIT *initid, UDF_ARGS *args, char *message);
+ void udf_sequence_deinit(UDF_INIT *initid);
+longlong udf_sequence(UDF_INIT *initid, UDF_ARGS *args, char *is_null,
char *error);
my_bool avgcost_init( UDF_INIT* initid, UDF_ARGS* args, char* message );
void avgcost_deinit( UDF_INIT* initid );
@@ -642,7 +643,7 @@ my_bool myfunc_int_init(UDF_INIT *initid __attribute__((unused)),
or 1 if no arguments have been given
*/
-my_bool sequence_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+my_bool udf_sequence_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
if (args->arg_count > 1)
{
@@ -659,22 +660,22 @@ my_bool sequence_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
}
bzero(initid->ptr,sizeof(longlong));
/*
- sequence() is a non-deterministic function : it has different value
+ udf_sequence() is a non-deterministic function : it has different value
even if called with the same arguments.
*/
initid->const_item=0;
return 0;
}
-void sequence_deinit(UDF_INIT *initid)
+void udf_sequence_deinit(UDF_INIT *initid)
{
if (initid->ptr)
free(initid->ptr);
}
-longlong sequence(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args,
- char *is_null __attribute__((unused)),
- char *error __attribute__((unused)))
+longlong udf_sequence(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args,
+ char *is_null __attribute__((unused)),
+ char *error __attribute__((unused)))
{
ulonglong val=0;
if (args->arg_count)
diff --git a/sql/udf_example.def b/sql/udf_example.def
index 41150b24e8f..74230b638bf 100644
--- a/sql/udf_example.def
+++ b/sql/udf_example.def
@@ -14,9 +14,9 @@ EXPORTS
myfunc_double
myfunc_int_init
myfunc_int
- sequence_init
- sequence_deinit
- sequence
+ udf_sequence_init
+ udf_sequence_deinit
+ udf_sequence
avgcost_init
avgcost_deinit
avgcost_reset
diff --git a/sql/uniques.cc b/sql/uniques.cc
index 4509cc8edaf..1b8e91f2566 100644
--- a/sql/uniques.cc
+++ b/sql/uniques.cc
@@ -31,7 +31,7 @@
deletes in disk order.
*/
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#include "sql_sort.h"
@@ -312,7 +312,7 @@ double Unique::get_use_cost(uint *buffer, size_t nkeys, uint key_size,
{
size_t max_elements_in_tree;
size_t last_tree_elems;
- int n_full_trees; /* number of trees in unique - 1 */
+ size_t n_full_trees; /* number of trees in unique - 1 */
double result;
max_elements_in_tree= ((size_t) max_in_memory_size /
@@ -353,9 +353,9 @@ double Unique::get_use_cost(uint *buffer, size_t nkeys, uint key_size,
/* Cost of merge */
if (intersect_fl)
key_size+= sizeof(element_count);
- double merge_cost= get_merge_many_buffs_cost(buffer, n_full_trees,
- max_elements_in_tree,
- last_tree_elems, key_size,
+ double merge_cost= get_merge_many_buffs_cost(buffer, (uint)n_full_trees,
+ (uint)max_elements_in_tree,
+ (uint)last_tree_elems, key_size,
compare_factor);
result += merge_cost;
/*
@@ -370,7 +370,7 @@ double Unique::get_use_cost(uint *buffer, size_t nkeys, uint key_size,
Unique::~Unique()
{
close_cached_file(&file);
- delete_tree(&tree);
+ delete_tree(&tree, 0);
delete_dynamic(&file_ptrs);
}
@@ -390,7 +390,7 @@ bool Unique::flush()
(void*) this, left_root_right) ||
insert_dynamic(&file_ptrs, (uchar*) &file_ptr))
return 1;
- delete_tree(&tree);
+ delete_tree(&tree, 0);
return 0;
}
@@ -512,7 +512,7 @@ static bool merge_walk(uchar *merge_buffer, size_t merge_buffer_size,
key_length);
/* if piece_size is aligned reuse_freed_buffer will always hit */
uint piece_size= max_key_count_per_piece * key_length;
- uint bytes_read; /* to hold return value of read_to_buffer */
+ ulong bytes_read; /* to hold return value of read_to_buffer */
BUFFPEK *top;
int res= 1;
uint cnt_ofs= key_length - (with_counters ? sizeof(element_count) : 0);
@@ -528,7 +528,7 @@ static bool merge_walk(uchar *merge_buffer, size_t merge_buffer_size,
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);
- if (bytes_read == (uint) (-1))
+ if (unlikely(bytes_read == (ulong) -1))
goto end;
DBUG_ASSERT(bytes_read);
queue_insert(&queue, (uchar *) top);
@@ -557,9 +557,9 @@ static bool merge_walk(uchar *merge_buffer, size_t merge_buffer_size,
memcpy(save_key_buff, old_key, key_length);
old_key= save_key_buff;
bytes_read= read_to_buffer(file, top, key_length);
- if (bytes_read == (uint) (-1))
+ if (unlikely(bytes_read == (ulong) -1))
goto end;
- else if (bytes_read > 0) /* top->key, top->mem_count are reset */
+ else if (bytes_read) /* top->key, top->mem_count are reset */
queue_replace_top(&queue); /* in read_to_buffer */
else
{
@@ -605,7 +605,7 @@ static bool merge_walk(uchar *merge_buffer, size_t merge_buffer_size,
}
while (--top->mem_count);
bytes_read= read_to_buffer(file, top, key_length);
- if (bytes_read == (uint) (-1))
+ if (unlikely(bytes_read == (ulong) -1))
goto end;
}
while (bytes_read);
diff --git a/sql/unireg.cc b/sql/unireg.cc
index 083960523c1..cccb09a9fb9 100644
--- a/sql/unireg.cc
+++ b/sql/unireg.cc
@@ -25,7 +25,7 @@
str is a (long) to record position where 0 is the first position.
*/
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#include "sql_partition.h" // struct partition_info
@@ -73,7 +73,7 @@ static uchar *extra2_write_len(uchar *pos, size_t len)
}
static uchar *extra2_write(uchar *pos, enum extra2_frm_value_type type,
- LEX_STRING *str)
+ const LEX_CSTRING *str)
{
*pos++ = type;
pos= extra2_write_len(pos, str->length);
@@ -84,10 +84,72 @@ static uchar *extra2_write(uchar *pos, enum extra2_frm_value_type type,
static uchar *extra2_write(uchar *pos, enum extra2_frm_value_type type,
LEX_CUSTRING *str)
{
- return extra2_write(pos, type, reinterpret_cast<LEX_STRING *>(str));
+ return extra2_write(pos, type, reinterpret_cast<LEX_CSTRING *>(str));
}
-/*
+static uchar *extra2_write_field_properties(uchar *pos,
+ List<Create_field> &create_fields)
+{
+ List_iterator<Create_field> it(create_fields);
+ *pos++= EXTRA2_FIELD_FLAGS;
+ /*
+ always first 2 for field visibility
+ */
+ pos= extra2_write_len(pos, create_fields.elements);
+ while (Create_field *cf= it++)
+ {
+ uchar flags= cf->invisible;
+ if (cf->flags & VERS_UPDATE_UNVERSIONED_FLAG)
+ flags|= VERS_OPTIMIZED_UPDATE;
+ *pos++= flags;
+ }
+ return pos;
+}
+
+static const bool ROW_START = true;
+static const bool ROW_END = false;
+
+static inline
+uint16
+vers_get_field(HA_CREATE_INFO *create_info, List<Create_field> &create_fields, bool row_start)
+{
+ DBUG_ASSERT(create_info->versioned());
+
+ List_iterator<Create_field> it(create_fields);
+ Create_field *sql_field = NULL;
+
+ const Lex_ident row_field= row_start ? create_info->vers_info.as_row.start
+ : create_info->vers_info.as_row.end;
+ DBUG_ASSERT(row_field);
+
+ for (unsigned field_no = 0; (sql_field = it++); ++field_no)
+ {
+ if (row_field.streq(sql_field->field_name))
+ {
+ DBUG_ASSERT(field_no <= uint16(~0U));
+ return uint16(field_no);
+ }
+ }
+
+ DBUG_ASSERT(0); /* Not Reachable */
+ return 0;
+}
+
+static inline
+bool has_extra2_field_flags(List<Create_field> &create_fields)
+{
+ List_iterator<Create_field> it(create_fields);
+ while (Create_field *f= it++)
+ {
+ if (f->invisible)
+ return true;
+ if (f->flags & VERS_UPDATE_UNVERSIONED_FLAG)
+ return true;
+ }
+ return false;
+}
+
+/**
Create a frm (table definition) file
@param thd Thread handler
@@ -102,20 +164,21 @@ static uchar *extra2_write(uchar *pos, enum extra2_frm_value_type type,
or null LEX_CUSTRING (str==0) in case of an error.
*/
-LEX_CUSTRING build_frm_image(THD *thd, const char *table,
+LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING *table,
HA_CREATE_INFO *create_info,
List<Create_field> &create_fields,
uint keys, KEY *key_info, handler *db_file)
{
- LEX_STRING str_db_type;
+ LEX_CSTRING str_db_type;
uint reclength, key_info_length, i;
ulong key_buff_length;
- ulong filepos, data_offset;
+ size_t filepos;
+ ulong data_offset;
uint options_len;
uint gis_extra2_len= 0;
uchar fileinfo[FRM_HEADER_SIZE],forminfo[FRM_FORMINFO_SIZE];
const partition_info *part_info= IF_PARTITIONING(thd->work_part_info, 0);
- int error;
+ bool error;
uchar *frm_ptr, *pos;
LEX_CUSTRING frm= {0,0};
StringBuffer<MAX_FIELD_WIDTH> vcols;
@@ -131,15 +194,15 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table,
error= pack_vcols(&vcols, create_fields, create_info->check_constraint_list);
thd->variables.sql_mode= save_sql_mode;
- if (error)
+ if (unlikely(error))
DBUG_RETURN(frm);
if (vcols.length())
create_info->expression_length= vcols.length() + FRM_VCOL_NEW_BASE_SIZE;
error= pack_header(thd, forminfo, create_fields, create_info,
- data_offset, db_file);
- if (error)
+ (ulong)data_offset, db_file);
+ if (unlikely(error))
DBUG_RETURN(frm);
reclength= uint2korr(forminfo+266);
@@ -147,7 +210,7 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table,
/* Calculate extra data segment length */
str_db_type= *hton_name(create_info->db_type);
/* str_db_type */
- create_info->extra_size= (2 + str_db_type.length +
+ create_info->extra_size= (uint)(2 + str_db_type.length +
2 + create_info->connect_string.length);
/*
Partition:
@@ -158,12 +221,12 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table,
*/
create_info->extra_size+= 6;
if (part_info)
- create_info->extra_size+= part_info->part_info_len;
+ create_info->extra_size+= (uint)part_info->part_info_len;
for (i= 0; i < keys; i++)
{
if (key_info[i].parser_name)
- create_info->extra_size+= key_info[i].parser_name->length + 1;
+ create_info->extra_size+= (uint)key_info[i].parser_name->length + 1;
}
options_len= engine_table_options_frm_length(create_info->option_list,
@@ -175,8 +238,7 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table,
DBUG_PRINT("info", ("Options length: %u", options_len));
if (validate_comment_length(thd, &create_info->comment, TABLE_COMMENT_MAXLEN,
- ER_TOO_LONG_TABLE_COMMENT,
- table))
+ ER_TOO_LONG_TABLE_COMMENT, table->str))
DBUG_RETURN(frm);
/*
If table comment is longer than TABLE_COMMENT_INLINE_MAXLEN bytes,
@@ -186,7 +248,7 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table,
if (create_info->comment.length > TABLE_COMMENT_INLINE_MAXLEN)
{
forminfo[46]=255;
- create_info->extra_size+= 2 + create_info->comment.length;
+ create_info->extra_size+= 2 + (uint)create_info->comment.length;
}
else
{
@@ -210,7 +272,7 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table,
prepare_frm_header(thd, reclength, fileinfo, create_info, keys, key_info);
/* one byte for a type, one or three for a length */
- uint extra2_size= 1 + 1 + create_info->tabledef_version.length;
+ size_t extra2_size= 1 + 1 + create_info->tabledef_version.length;
if (options_len)
extra2_size+= 1 + (options_len > 255 ? 3 : 1) + options_len;
@@ -220,6 +282,17 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table,
if (gis_extra2_len)
extra2_size+= 1 + (gis_extra2_len > 255 ? 3 : 1) + gis_extra2_len;
+ if (create_info->versioned())
+ {
+ extra2_size+= 1 + 1 + 2 * sizeof(uint16);
+ }
+
+ bool has_extra2_field_flags_= has_extra2_field_flags(create_fields);
+ if (has_extra2_field_flags_)
+ {
+ extra2_size+= 1 + (create_fields.elements > 255 ? 3 : 1) +
+ create_fields.elements;
+ }
key_buff_length= uint4korr(fileinfo+47);
@@ -240,7 +313,7 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table,
if (frm.length > FRM_MAX_SIZE ||
create_info->expression_length > UINT_MAX32)
{
- my_error(ER_TABLE_DEFINITION_TOO_BIG, MYF(0), table);
+ my_error(ER_TABLE_DEFINITION_TOO_BIG, MYF(0), table->str);
DBUG_RETURN(frm);
}
@@ -276,6 +349,19 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table,
}
#endif /*HAVE_SPATIAL*/
+ if (create_info->versioned())
+ {
+ *pos++= EXTRA2_PERIOD_FOR_SYSTEM_TIME;
+ *pos++= 2 * sizeof(uint16);
+ int2store(pos, vers_get_field(create_info, create_fields, ROW_START));
+ pos+= sizeof(uint16);
+ int2store(pos, vers_get_field(create_info, create_fields, ROW_END));
+ pos+= sizeof(uint16);
+ }
+
+ if (has_extra2_field_flags_)
+ pos= extra2_write_field_properties(pos, create_fields);
+
int4store(pos, filepos); // end of the extra2 segment
pos+= 4;
@@ -286,7 +372,7 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table,
my_printf_error(ER_CANT_CREATE_TABLE,
"Cannot create table %`s: index information is too long. "
"Decrease number of indexes or use shorter index names or shorter comments.",
- MYF(0), table);
+ MYF(0), table->str);
goto err;
}
@@ -350,8 +436,8 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table,
pos+= create_info->comment.length;
}
- memcpy(frm_ptr + filepos, forminfo, 288);
- pos= frm_ptr + filepos + 288;
+ memcpy(frm_ptr + filepos, forminfo, FRM_FORMINFO_SIZE);
+ pos= frm_ptr + filepos + FRM_FORMINFO_SIZE;
if (pack_fields(&pos, create_fields, create_info, data_offset))
goto err;
@@ -488,7 +574,7 @@ static uint pack_keys(uchar *keybuff, uint key_count, KEY *keyinfo,
*pos++=(uchar) NAMES_SEP_CHAR;
for (key=keyinfo ; key != end ; key++)
{
- uchar *tmp=(uchar*) strmov((char*) pos,key->name);
+ uchar *tmp=(uchar*) strmov((char*) pos,key->name.str);
*tmp++= (uchar) NAMES_SEP_CHAR;
*tmp=0;
pos=tmp;
@@ -549,7 +635,7 @@ static bool pack_expression(String *buf, Virtual_column_info *vcol,
size_t len_off= buf->length();
buf->q_append2b(0); // to be added later
buf->q_append((char)vcol->name.length);
- buf->q_append(vcol->name.str, vcol->name.length);
+ buf->q_append(&vcol->name);
size_t expr_start= buf->length();
vcol->print(buf);
size_t expr_len= buf->length() - expr_start;
@@ -638,7 +724,8 @@ static bool pack_header(THD *thd, uchar *forminfo,
while ((field=it++))
{
if (validate_comment_length(thd, &field->comment, COLUMN_COMMENT_MAXLEN,
- ER_TOO_LONG_FIELD_COMMENT, field->field_name))
+ ER_TOO_LONG_FIELD_COMMENT,
+ field->field_name.str))
DBUG_RETURN(1);
totlength+= (size_t)field->length;
@@ -647,14 +734,14 @@ static bool pack_header(THD *thd, uchar *forminfo,
We mark first TIMESTAMP field with NOW() in DEFAULT or ON UPDATE
as auto-update field.
*/
- if (field->sql_type == MYSQL_TYPE_TIMESTAMP &&
+ if (field->real_field_type() == MYSQL_TYPE_TIMESTAMP &&
MTYP_TYPENR(field->unireg_check) != Field::NONE &&
!time_stamp_pos)
time_stamp_pos= (uint) field->offset+ (uint) data_offset + 1;
length=field->pack_length;
if ((uint) field->offset+ (uint) data_offset+ length > reclength)
reclength=(uint) (field->offset+ data_offset + length);
- n_length+= (ulong) strlen(field->field_name)+1;
+ n_length+= field->field_name.length + 1;
field->interval_id=0;
field->save_interval= 0;
if (field->interval)
@@ -685,10 +772,10 @@ static bool pack_header(THD *thd, uchar *forminfo,
{
char *dst;
const char *src= field->save_interval->type_names[pos];
- uint hex_length;
+ size_t hex_length;
length= field->save_interval->type_lengths[pos];
hex_length= length * 2;
- field->interval->type_lengths[pos]= hex_length;
+ field->interval->type_lengths[pos]= (uint)hex_length;
field->interval->type_names[pos]= dst=
(char*) thd->alloc(hex_length + 1);
octet2hex(dst, src, length);
@@ -793,7 +880,7 @@ static size_t packed_fields_length(List<Create_field> &create_fields)
}
length+= FCOMP;
- length+= strlen(field->field_name)+1;
+ length+= field->field_name.length + 1;
length+= field->comment.length;
}
length+= 2;
@@ -807,7 +894,8 @@ static bool pack_fields(uchar **buff_arg, List<Create_field> &create_fields,
ulong data_offset)
{
uchar *buff= *buff_arg;
- uint int_count, comment_length= 0;
+ uint int_count;
+ size_t comment_length= 0;
Create_field *field;
DBUG_ENTER("pack_fields");
@@ -824,8 +912,8 @@ static bool pack_fields(uchar **buff_arg, List<Create_field> &create_fields,
int2store(buff+8,field->pack_flag);
buff[10]= (uchar) field->unireg_check;
buff[12]= (uchar) field->interval_id;
- buff[13]= (uchar) field->sql_type;
- if (field->sql_type == MYSQL_TYPE_GEOMETRY)
+ buff[13]= (uchar) field->real_field_type();
+ if (field->real_field_type() == MYSQL_TYPE_GEOMETRY)
{
buff[11]= 0;
buff[14]= (uchar) field->geom_type;
@@ -854,7 +942,7 @@ static bool pack_fields(uchar **buff_arg, List<Create_field> &create_fields,
it.rewind();
while ((field=it++))
{
- buff= (uchar*)strmov((char*) buff, field->field_name);
+ buff= (uchar*)strmov((char*) buff, field->field_name.str);
*buff++=NAMES_SEP_CHAR;
}
*buff++= 0;
@@ -970,13 +1058,14 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options,
null_pos + null_count / 8,
null_count & 7,
field->pack_flag,
- field->sql_type,
+ field->type_handler(),
field->charset,
field->geom_type, field->srid,
field->unireg_check,
field->save_interval ? field->save_interval
: field->interval,
- field->field_name);
+ &field->field_name,
+ field->flags);
if (!regfield)
{
error= 1;
@@ -992,16 +1081,18 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options,
null_count++;
}
- if (field->sql_type == MYSQL_TYPE_BIT && !f_bit_as_char(field->pack_flag))
+ if (field->real_field_type() == MYSQL_TYPE_BIT &&
+ !f_bit_as_char(field->pack_flag))
null_count+= field->length & 7;
if (field->default_value && !field->default_value->flags &&
- (!(field->flags & BLOB_FLAG) || field->sql_type == MYSQL_TYPE_GEOMETRY))
+ !field->vers_sys_field() &&
+ (!(field->flags & BLOB_FLAG) ||
+ field->real_field_type() == MYSQL_TYPE_GEOMETRY))
{
Item *expr= field->default_value->expr;
-
- int res= !expr->fixed && // may be already fixed if ALTER TABLE
- expr->fix_fields(thd, &expr);
+ // may be already fixed if ALTER TABLE
+ int res= expr->fix_fields_if_needed(thd, &expr);
if (!res)
res= expr->save_in_field(regfield, 1);
if (!res && (field->flags & BLOB_FLAG))
@@ -1010,7 +1101,7 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options,
/* If not ok or warning of level 'note' */
if (res != 0 && res != 3)
{
- my_error(ER_INVALID_DEFAULT, MYF(0), regfield->field_name);
+ my_error(ER_INVALID_DEFAULT, MYF(0), regfield->field_name.str);
error= 1;
delete regfield; //To avoid memory leak
goto err;
@@ -1018,6 +1109,7 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options,
delete regfield; //To avoid memory leak
}
else if (regfield->real_type() == MYSQL_TYPE_ENUM &&
+ !field->vers_sys_field() &&
(field->flags & NOT_NULL_FLAG))
{
regfield->set_notnull();
diff --git a/sql/unireg.h b/sql/unireg.h
index 6ce638928e8..bfd9a4c393b 100644
--- a/sql/unireg.h
+++ b/sql/unireg.h
@@ -49,11 +49,14 @@
#define DEFAULT_ERRMSGS my_default_lc_messages->errmsgs->errmsgs
#define CURRENT_THD_ERRMSGS (current_thd)->variables.errmsgs
+#ifndef mysqld_error_find_printf_error_used
#define ER_DEFAULT(X) DEFAULT_ERRMSGS[((X)-ER_ERROR_FIRST) / ERRORS_PER_RANGE][(X)% ERRORS_PER_RANGE]
#define ER_THD(thd,X) ((thd)->variables.errmsgs[((X)-ER_ERROR_FIRST) / ERRORS_PER_RANGE][(X) % ERRORS_PER_RANGE])
#define ER(X) ER_THD(current_thd, (X))
+#endif
#define ER_THD_OR_DEFAULT(thd,X) ((thd) ? ER_THD(thd, (X)) : ER_DEFAULT(X))
+
#define ME_INFO (ME_HOLDTANG | ME_NOREFRESH)
#define ME_ERROR (ME_BELL | ME_NOREFRESH)
#define MYF_RW MYF(MY_WME+MY_NABP) /* Vid my_read & my_write */
@@ -172,17 +175,23 @@ enum extra2_frm_value_type {
EXTRA2_TABLEDEF_VERSION=0,
EXTRA2_DEFAULT_PART_ENGINE=1,
EXTRA2_GIS=2,
+ EXTRA2_PERIOD_FOR_SYSTEM_TIME=4,
#define EXTRA2_ENGINE_IMPORTANT 128
EXTRA2_ENGINE_TABLEOPTS=128,
+ EXTRA2_FIELD_FLAGS=129
+};
+
+enum extra2_field_flags {
+ VERS_OPTIMIZED_UPDATE= 1 << INVISIBLE_MAX_BITS
};
int rea_create_table(THD *thd, LEX_CUSTRING *frm,
const char *path, const char *db, const char *table_name,
HA_CREATE_INFO *create_info, handler *file,
bool no_ha_create_table);
-LEX_CUSTRING build_frm_image(THD *thd, const char *table,
+LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING *table,
HA_CREATE_INFO *create_info,
List<Create_field> &create_fields,
uint keys, KEY *key_info, handler *db_file);
diff --git a/sql/upgrade_conf_file.cc b/sql/upgrade_conf_file.cc
new file mode 100644
index 00000000000..4e167f0263f
--- /dev/null
+++ b/sql/upgrade_conf_file.cc
@@ -0,0 +1,177 @@
+/*
+ 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 */
+
+
+/*
+ Variables that were present in older releases, but are now removed.
+ to get the list of variables that are present in current release
+ execute
+
+ SELECT LOWER(variable_name) from INFORMATION_SCHEMA.GLOBAL_VARIABLES ORDER BY 1
+
+ Compare the list between releases to figure out which variables have gone.
+
+ Note : the list below only includes the default-compiled server and none of the
+ loadable plugins.
+*/
+#include <windows.h>
+#include <initializer_list>
+#include <stdlib.h>
+#include <stdio.h>
+#include <algorithm>
+
+static const char *removed_variables[] =
+{
+"aria_recover",
+"debug_crc_break",
+"engine_condition_pushdown",
+"have_csv",
+"have_innodb",
+"have_ndbcluster",
+"have_partitioning",
+"innodb_adaptive_flushing_method",
+"innodb_adaptive_hash_index_partitions",
+"innodb_additional_mem_pool_size",
+"innodb_api_bk_commit_interval",
+"innodb_api_disable_rowlock",
+"innodb_api_enable_binlog",
+"innodb_api_enable_mdl",
+"innodb_api_trx_level",
+"innodb_blocking_buffer_pool_restore",
+"innodb_buffer_pool_populate",
+"innodb_buffer_pool_restore_at_startup",
+"innodb_buffer_pool_shm_checksum",
+"innodb_buffer_pool_shm_key",
+"innodb_checkpoint_age_target",
+"innodb_cleaner_eviction_factor",
+"innodb_cleaner_flush_chunk_size",
+"innodb_cleaner_free_list_lwm",
+"innodb_cleaner_lru_chunk_size",
+"innodb_cleaner_lsn_age_factor",
+"innodb_cleaner_max_flush_time",
+"innodb_cleaner_max_lru_time",
+"innodb_corrupt_table_action",
+"innodb_dict_size_limit",
+"innodb_doublewrite_file",
+"innodb_empty_free_list_algorithm",
+"innodb_fake_changes",
+"innodb_fast_checksum",
+"innodb_file_format",
+"innodb_file_format_check",
+"innodb_file_format_max",
+"innodb_flush_neighbor_pages",
+"innodb_foreground_preflush",
+"innodb_ibuf_accel_rate",
+"innodb_ibuf_active_contract",
+"innodb_ibuf_max_size",
+"innodb_import_table_from_xtrabackup",
+"innodb_instrument_semaphores",
+"innodb_kill_idle_transaction",
+"innodb_large_prefix",
+"innodb_lazy_drop_table",
+"innodb_locking_fake_changes",
+"innodb_log_arch_dir",
+"innodb_log_arch_expire_sec",
+"innodb_log_archive",
+"innodb_log_block_size",
+"innodb_log_checksum_algorithm",
+"innodb_max_bitmap_file_size",
+"innodb_max_changed_pages",
+"innodb_merge_sort_block_size",
+"innodb_mirrored_log_groups",
+"innodb_mtflush_threads",
+"innodb_persistent_stats_root_page",
+"innodb_print_lock_wait_timeout_info",
+"innodb_purge_run_now",
+"innodb_purge_stop_now",
+"innodb_read_ahead",
+"innodb_recovery_stats",
+"innodb_recovery_update_relay_log",
+"innodb_show_locks_held",
+"innodb_show_verbose_locks",
+"innodb_stats_auto_update",
+"innodb_stats_update_need_lock",
+"innodb_support_xa",
+"innodb_thread_concurrency_timer_based",
+"innodb_track_changed_pages",
+"innodb_track_redo_log_now",
+"innodb_use_fallocate",
+"innodb_use_global_flush_log_at_trx_commit",
+"innodb_use_mtflush",
+"innodb_use_stacktrace",
+"innodb_use_sys_malloc",
+"innodb_use_sys_stats_table",
+"innodb_use_trim",
+"log",
+"log_slow_queries",
+"rpl_recovery_rank",
+"sql_big_tables",
+"sql_low_priority_updates",
+"sql_max_join_size"
+};
+
+
+static int cmp_strings(const void* a, const void *b)
+{
+ return strcmp((const char *)a, *(const char **)b);
+}
+
+/**
+ Convert file from a previous version, by removing
+*/
+int upgrade_config_file(const char *myini_path)
+{
+#define MY_INI_SECTION_SIZE 32*1024 +3
+ static char section_data[MY_INI_SECTION_SIZE];
+ for (const char *section_name : { "mysqld","server","mariadb" })
+ {
+ DWORD size = GetPrivateProfileSection(section_name, section_data, MY_INI_SECTION_SIZE, myini_path);
+ if (size == MY_INI_SECTION_SIZE - 2)
+ {
+ return -1;
+ }
+
+ for (char *keyval = section_data; *keyval; keyval += strlen(keyval) + 1)
+ {
+ char varname[256];
+ char *key_end = strchr(keyval, '=');
+ if (!key_end)
+ key_end = keyval+ strlen(keyval);
+
+ if (key_end - keyval > sizeof(varname))
+ continue;
+ // copy and normalize (convert dash to underscore) to variable names
+ for (char *p = keyval, *q = varname;; p++,q++)
+ {
+ if (p == key_end)
+ {
+ *q = 0;
+ break;
+ }
+ *q = (*p == '-') ? '_' : *p;
+ }
+ const char *v = (const char *)bsearch(varname, removed_variables, sizeof(removed_variables) / sizeof(removed_variables[0]),
+ sizeof(char *), cmp_strings);
+
+ if (v)
+ {
+ fprintf(stdout, "Removing variable '%s' from config file\n", varname);
+ // delete variable
+ *key_end = 0;
+ WritePrivateProfileString(section_name, keyval, 0, myini_path);
+ }
+ }
+ }
+ return 0;
+}
diff --git a/sql/vers_string.h b/sql/vers_string.h
new file mode 100644
index 00000000000..3709cdbf786
--- /dev/null
+++ b/sql/vers_string.h
@@ -0,0 +1,100 @@
+/*
+ Copyright (c) 2018, 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-1301 USA */
+
+#ifndef VERS_STRING_INCLUDED
+#define VERS_STRING_INCLUDED
+
+/*
+ LEX_CSTRING with comparison semantics.
+*/
+
+// db and table names: case sensitive (or insensitive) in table_alias_charset
+struct Compare_table_names
+{
+ int operator()(const LEX_CSTRING& a, const LEX_CSTRING& b) const
+ {
+ 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);
+ }
+};
+
+// column names and other identifiers: case insensitive in system_charset_info
+struct Compare_identifiers
+{
+ int operator()(const LEX_CSTRING& a, const LEX_CSTRING& b) const
+ {
+ DBUG_ASSERT(a.str[a.length] == 0);
+ DBUG_ASSERT(b.str[b.length] == 0);
+ return my_strcasecmp(system_charset_info, a.str, b.str);
+ }
+};
+
+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;
+ }
+ void set(const char *_str, size_t _len)
+ {
+ str= _str;
+ length= _len;
+ }
+};
+
+template <class Compare>
+struct Lex_cstring_with_compare : public Lex_cstring
+{
+public:
+ Lex_cstring_with_compare() {}
+ Lex_cstring_with_compare(const char *_str, size_t _len) :
+ Lex_cstring(_str, _len)
+ { }
+ Lex_cstring_with_compare(const LEX_STRING src) :
+ Lex_cstring(src.str, src.length)
+ { }
+ Lex_cstring_with_compare(const LEX_CSTRING src) : Lex_cstring(src.str, src.length)
+ { }
+ Lex_cstring_with_compare(const char *_str) : Lex_cstring(_str, strlen(_str))
+ { }
+ bool streq(const Lex_cstring_with_compare& b) const
+ {
+ return Lex_cstring::length == b.length && 0 == Compare()(*this, b);
+ }
+ operator const char* () const
+ {
+ return str;
+ }
+ operator bool () const
+ {
+ return str != NULL;
+ }
+};
+
+typedef Lex_cstring_with_compare<Compare_identifiers> Lex_ident;
+typedef Lex_cstring_with_compare<Compare_table_names> Lex_table_name;
+
+#endif // VERS_STRING_INCLUDED
diff --git a/sql/vers_utils.h b/sql/vers_utils.h
new file mode 100644
index 00000000000..e896f84135e
--- /dev/null
+++ b/sql/vers_utils.h
@@ -0,0 +1,47 @@
+#ifndef VERS_UTILS_INCLUDED
+#define VERS_UTILS_INCLUDED
+
+#include "table.h"
+#include "sql_class.h"
+#include "vers_string.h"
+
+class MDL_auto_lock
+{
+ THD *thd;
+ TABLE_LIST &table;
+ bool error;
+
+public:
+ MDL_auto_lock(THD *_thd, TABLE_LIST &_table) :
+ thd(_thd), table(_table)
+ {
+ DBUG_ASSERT(thd);
+ MDL_request protection_request;
+ if (thd->global_read_lock.can_acquire_protection())
+ {
+ error= true;
+ return;
+ }
+ protection_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE,
+ MDL_EXPLICIT);
+ error= thd->mdl_context.acquire_lock(&protection_request, thd->variables.lock_wait_timeout);
+ if (error)
+ return;
+
+ table.mdl_request.init(MDL_key::TABLE, table.db.str, table.table_name.str, MDL_EXCLUSIVE, MDL_EXPLICIT);
+ error= thd->mdl_context.acquire_lock(&table.mdl_request, thd->variables.lock_wait_timeout);
+ thd->mdl_context.release_lock(protection_request.ticket);
+ }
+ ~MDL_auto_lock()
+ {
+ if (!error)
+ {
+ DBUG_ASSERT(table.mdl_request.ticket);
+ thd->mdl_context.release_lock(table.mdl_request.ticket);
+ table.mdl_request.ticket= NULL;
+ }
+ }
+ bool acquire_error() const { return error; }
+};
+
+#endif // VERS_UTILS_INCLUDED
diff --git a/sql/winservice.c b/sql/winservice.c
index 371a447c51d..d7cfd2f7584 100644
--- a/sql/winservice.c
+++ b/sql/winservice.c
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2011, 2012, 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
@@ -166,9 +166,10 @@ int get_mysql_service_properties(const wchar_t *bin_path,
if(numargs == 2)
{
/*
- There are rare cases where service config does not have
- --defaults-file in the binary parth . There services were registered with
- plain mysqld --install, the data directory is next to "bin" in this case.
+ There are rare cases where service config does not have
+ --defaults-file in the binary path . There services were
+ registered with plain mysqld --install, the data directory is
+ next to "bin" in this case.
*/
have_inifile= FALSE;
}
diff --git a/sql/wsrep_applier.cc b/sql/wsrep_applier.cc
index 66335c412e2..4e19e15680e 100644
--- a/sql/wsrep_applier.cc
+++ b/sql/wsrep_applier.cc
@@ -13,6 +13,7 @@
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA. */
+#include "mariadb.h"
#include "wsrep_priv.h"
#include "wsrep_binlog.h" // wsrep_dump_rbr_buf()
#include "wsrep_xid.h"
@@ -145,6 +146,7 @@ static wsrep_cb_status_t wsrep_apply_events(THD* thd,
/* 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);
wsrep_xid_init(&thd->transaction.xid_state.xid,
thd->wsrep_trx_meta.gtid.uuid,
thd->wsrep_trx_meta.gtid.seqno);
@@ -234,11 +236,11 @@ wsrep_cb_status_t wsrep_apply_cb(void* const ctx,
#ifdef WSREP_PROC_INFO
snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
- "applying write set %lld: %p, %zu",
+ "Applying write set %lld: %p, %zu",
(long long)wsrep_thd_trx_seqno(thd), buf, buf_len);
thd_proc_info(thd, thd->wsrep_info);
#else
- thd_proc_info(thd, "applying write set");
+ thd_proc_info(thd, "Applying write set");
#endif /* WSREP_PROC_INFO */
/* tune FK and UK checking policy */
@@ -268,10 +270,10 @@ wsrep_cb_status_t wsrep_apply_cb(void* const ctx,
#ifdef WSREP_PROC_INFO
snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
- "applied write set %lld", (long long)wsrep_thd_trx_seqno(thd));
+ "Applied write set %lld", (long long)wsrep_thd_trx_seqno(thd));
thd_proc_info(thd, thd->wsrep_info);
#else
- thd_proc_info(thd, "applied write set");
+ thd_proc_info(thd, "Applied write set");
#endif /* WSREP_PROC_INFO */
if (WSREP_CB_SUCCESS != rcode)
@@ -293,10 +295,10 @@ static wsrep_cb_status_t wsrep_commit(THD* const thd)
{
#ifdef WSREP_PROC_INFO
snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
- "committing %lld", (long long)wsrep_thd_trx_seqno(thd));
+ "Committing %lld", (long long)wsrep_thd_trx_seqno(thd));
thd_proc_info(thd, thd->wsrep_info);
#else
- thd_proc_info(thd, "committing");
+ thd_proc_info(thd, "Committing");
#endif /* WSREP_PROC_INFO */
wsrep_cb_status_t const rcode(trans_commit(thd) ?
@@ -317,10 +319,10 @@ static wsrep_cb_status_t wsrep_commit(THD* const thd)
#ifdef WSREP_PROC_INFO
snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
- "committed %lld", (long long) wsrep_thd_trx_seqno(thd));
+ "Committed %lld", (long long) wsrep_thd_trx_seqno(thd));
thd_proc_info(thd, thd->wsrep_info);
#else
- thd_proc_info(thd, "committed");
+ thd_proc_info(thd, "Committed");
#endif /* WSREP_PROC_INFO */
return rcode;
@@ -330,10 +332,10 @@ static wsrep_cb_status_t wsrep_rollback(THD* const thd)
{
#ifdef WSREP_PROC_INFO
snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
- "rolling back %lld", (long long)wsrep_thd_trx_seqno(thd));
+ "Rolling back %lld", (long long)wsrep_thd_trx_seqno(thd));
thd_proc_info(thd, thd->wsrep_info);
#else
- thd_proc_info(thd, "rolling back");
+ thd_proc_info(thd, "Rolling back");
#endif /* WSREP_PROC_INFO */
wsrep_cb_status_t const rcode(trans_rollback(thd) ?
@@ -341,10 +343,10 @@ static wsrep_cb_status_t wsrep_rollback(THD* const thd)
#ifdef WSREP_PROC_INFO
snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
- "rolled back %lld", (long long)wsrep_thd_trx_seqno(thd));
+ "Rolled back %lld", (long long)wsrep_thd_trx_seqno(thd));
thd_proc_info(thd, thd->wsrep_info);
#else
- thd_proc_info(thd, "rolled back");
+ thd_proc_info(thd, "Rolled back");
#endif /* WSREP_PROC_INFO */
return rcode;
@@ -358,7 +360,7 @@ wsrep_cb_status_t wsrep_commit_cb(void* const ctx,
{
THD* const thd((THD*)ctx);
- assert(meta->gtid.seqno == wsrep_thd_trx_seqno(thd));
+ assert(meta->gtid.seqno >= wsrep_thd_trx_seqno(thd));
wsrep_cb_status_t rcode;
diff --git a/sql/wsrep_binlog.cc b/sql/wsrep_binlog.cc
index 9602dd698eb..85c1deb0d71 100644
--- a/sql/wsrep_binlog.cc
+++ b/sql/wsrep_binlog.cc
@@ -13,6 +13,7 @@
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA. */
+#include "mariadb.h"
#include "wsrep_binlog.h"
#include "wsrep_priv.h"
#include "log.h"
@@ -30,13 +31,13 @@ int wsrep_write_cache_buf(IO_CACHE *cache, uchar **buf, size_t *buf_len)
{
*buf= NULL;
*buf_len= 0;
-
my_off_t const saved_pos(my_b_tell(cache));
+ DBUG_ENTER("wsrep_write_cache_buf");
if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0))
{
WSREP_ERROR("failed to initialize io-cache");
- return ER_ERROR_ON_WRITE;
+ DBUG_RETURN(ER_ERROR_ON_WRITE);
}
uint length = my_b_bytes_in_cache(cache);
@@ -85,7 +86,7 @@ int wsrep_write_cache_buf(IO_CACHE *cache, uchar **buf, size_t *buf_len)
goto cleanup;
}
- return 0;
+ DBUG_RETURN(0);
error:
if (reinit_io_cache(cache, WRITE_CACHE, saved_pos, 0, 0))
@@ -96,7 +97,7 @@ cleanup:
my_free(*buf);
*buf= NULL;
*buf_len= 0;
- return ER_ERROR_ON_WRITE;
+ DBUG_RETURN(ER_ERROR_ON_WRITE);
}
#define STACK_SIZE 4096 /* 4K - for buffer preallocated on the stack:
@@ -120,6 +121,7 @@ wsrep_append_data(wsrep_t* const wsrep,
struct wsrep_buf const buff = { data, len };
wsrep_status_t const rc(wsrep->append_data(wsrep, ws, &buff, 1,
WSREP_DATA_ORDERED, true));
+ DBUG_DUMP("buff", (uchar*) data, len);
if (rc != WSREP_OK)
{
WSREP_WARN("append_data() returned %d", rc);
@@ -143,11 +145,12 @@ static int wsrep_write_cache_once(wsrep_t* const wsrep,
size_t* const len)
{
my_off_t const saved_pos(my_b_tell(cache));
+ DBUG_ENTER("wsrep_write_cache_once");
if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0))
{
WSREP_ERROR("failed to initialize io-cache");
- return ER_ERROR_ON_WRITE;
+ DBUG_RETURN(ER_ERROR_ON_WRITE);
}
int err(WSREP_OK);
@@ -229,7 +232,7 @@ cleanup:
}
my_free(heap_buf);
- return err;
+ DBUG_RETURN(err);
}
/*
@@ -246,11 +249,12 @@ static int wsrep_write_cache_inc(wsrep_t* const wsrep,
size_t* const len)
{
my_off_t const saved_pos(my_b_tell(cache));
+ DBUG_ENTER("wsrep_write_cache_inc");
if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0))
{
WSREP_ERROR("failed to initialize io-cache");
- return WSREP_TRX_ERROR;
+ DBUG_RETURN(WSREP_TRX_ERROR);
}
int err(WSREP_OK);
@@ -294,7 +298,7 @@ cleanup:
WSREP_ERROR("failed to reinitialize io-cache");
}
- return err;
+ DBUG_RETURN(err);
}
/*
@@ -423,7 +427,7 @@ void wsrep_dump_rbr_direct(THD* thd, IO_CACHE* cache)
break;
}
} while ((bytes_in_cache= my_b_fill(cache)));
- if(cache->error == -1)
+ if (cache->error == -1)
{
WSREP_ERROR("RBR inconsistent");
goto cleanup;
@@ -452,8 +456,8 @@ void wsrep_dump_rbr_buf_with_header(THD *thd, const void *rbr_buf,
File file;
IO_CACHE cache;
- Log_event_writer writer(&cache);
- Format_description_log_event *ev=NULL;
+ Log_event_writer writer(&cache, 0);
+ Format_description_log_event *ev= 0;
longlong thd_trx_seqno= (long long)wsrep_thd_trx_seqno(thd);
int len= snprintf(NULL, 0, "%s/GRA_%lld_%lld_v2.log",
diff --git a/sql/wsrep_check_opts.cc b/sql/wsrep_check_opts.cc
index 6a8b7abe9e1..075546d827a 100644
--- a/sql/wsrep_check_opts.cc
+++ b/sql/wsrep_check_opts.cc
@@ -14,6 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
+#include "mariadb.h"
#include "mysqld.h"
#include "sys_vars_shared.h"
#include "wsrep.h"
diff --git a/sql/wsrep_dummy.cc b/sql/wsrep_dummy.cc
index d8ab86c25f2..1af74035355 100644
--- a/sql/wsrep_dummy.cc
+++ b/sql/wsrep_dummy.cc
@@ -13,7 +13,7 @@
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA. */
-#include <my_global.h>
+#include "mariadb.h"
#include <sql_class.h>
#include <mysql/service_wsrep.h>
@@ -29,6 +29,15 @@ enum wsrep_conflict_state wsrep_thd_conflict_state(THD *, my_bool)
int wsrep_is_wsrep_xid(const XID*)
{ return 0; }
+long long wsrep_xid_seqno(const XID* x)
+{ return -1; }
+
+const unsigned char* wsrep_xid_uuid(const XID*)
+{
+ static const unsigned char uuid[16] = {0};
+ return uuid;
+}
+
bool wsrep_prepare_key(const uchar*, size_t, const uchar*, size_t, struct wsrep_buf*, size_t*)
{ return 0; }
diff --git a/sql/wsrep_hton.cc b/sql/wsrep_hton.cc
index 575c57c5d24..05be257cbcb 100644
--- a/sql/wsrep_hton.cc
+++ b/sql/wsrep_hton.cc
@@ -13,6 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
+#include "mariadb.h"
#include <mysqld.h>
#include "sql_base.h"
#include "rpl_filter.h"
@@ -96,12 +97,13 @@ void wsrep_register_hton(THD* thd, bool all)
{
trans_register_ha(thd, all, wsrep_hton);
- /* follow innodb read/write settting
+ /* follow innodb read/write setting
* but, as an exception: CTAS with empty result set will not be
* replicated unless we declare wsrep hton as read/write here
*/
if (i->is_trx_read_write() ||
- (thd->lex->sql_command == SQLCOM_CREATE_TABLE &&
+ ((thd->lex->sql_command == SQLCOM_CREATE_TABLE ||
+ thd->lex->sql_command == SQLCOM_CREATE_SEQUENCE) &&
thd->wsrep_exec_mode == LOCAL_STATE))
{
thd->ha_data[wsrep_hton->slot].ha_info[all].set_trx_read_write();
@@ -272,9 +274,8 @@ static int wsrep_rollback(handlerton *hton, THD *thd, bool all)
if (wsrep && wsrep->post_rollback(wsrep, &thd->wsrep_ws_handle))
{
DBUG_PRINT("wsrep", ("setting rollback fail"));
- WSREP_ERROR("settting rollback fail: thd: %llu, schema: %s, SQL: %s",
- (long long)thd->real_id, (thd->db ? thd->db : "(null)"),
- thd->query());
+ WSREP_ERROR("setting rollback fail: thd: %llu, schema: %s, SQL: %s",
+ (long long)thd->real_id, thd->get_db(), thd->query());
}
wsrep_cleanup_transaction(thd);
}
@@ -314,8 +315,8 @@ int wsrep_commit(handlerton *hton, THD *thd, bool all)
if (wsrep && wsrep->post_rollback(wsrep, &thd->wsrep_ws_handle))
{
DBUG_PRINT("wsrep", ("setting rollback fail"));
- WSREP_ERROR("settting rollback fail: thd: %llu, schema: %s, SQL: %s",
- (long long)thd->real_id, (thd->db ? thd->db : "(null)"),
+ WSREP_ERROR("setting rollback fail: thd: %llu, schema: %s, SQL: %s",
+ (long long)thd->real_id, thd->get_db(),
thd->query());
}
}
@@ -389,6 +390,11 @@ wsrep_run_wsrep_commit(THD *thd, bool all)
mysql_mutex_lock(&LOCK_wsrep_replaying);
+ DBUG_PRINT("info", ("wsrep_replaying: %d wsrep_conflict_state: %d killed: %d shutdown_in_progress: %d",
+ (int) wsrep_replaying, (int) thd->wsrep_conflict_state,
+ (int) thd->killed,
+ (int) shutdown_in_progress));
+
while (wsrep_replaying > 0 &&
thd->wsrep_conflict_state == NO_CONFLICT &&
thd->killed == NOT_KILLED &&
@@ -399,7 +405,7 @@ wsrep_run_wsrep_commit(THD *thd, bool all)
mysql_mutex_unlock(&thd->LOCK_thd_data);
mysql_mutex_lock(&thd->mysys_var->mutex);
- thd_proc_info(thd, "wsrep waiting on replaying");
+ thd_proc_info(thd, "WSREP waiting on replaying");
thd->mysys_var->current_mutex= &LOCK_wsrep_replaying;
thd->mysys_var->current_cond= &COND_wsrep_replaying;
mysql_mutex_unlock(&thd->mysys_var->mutex);
@@ -452,6 +458,9 @@ wsrep_run_wsrep_commit(THD *thd, bool all)
}
}
+ DBUG_PRINT("info", ("rcode: %d wsrep_conflict_state: %d",
+ rcode, thd->wsrep_conflict_state));
+
if (data_len == 0)
{
if (thd->get_stmt_da()->is_ok() &&
@@ -499,8 +508,7 @@ wsrep_run_wsrep_commit(THD *thd, bool all)
"QUERY: %s\n"
" => Skipping replication",
(ulonglong) thd->thread_id, data_len,
- (thd->db ? thd->db : "(null)"), thd->query());
-
+ thd->get_db(), thd->query());
rcode = WSREP_TRX_FAIL;
}
else if (!rcode)
@@ -514,10 +522,12 @@ wsrep_run_wsrep_commit(THD *thd, bool all)
0ULL : WSREP_FLAG_PA_UNSAFE),
&thd->wsrep_trx_meta);
+ DBUG_PRINT("info", ("rcode after pre_commit: %d", rcode));
+
if (rcode == WSREP_TRX_MISSING) {
WSREP_WARN("Transaction missing in provider, thd: %lld schema: %s SQL: %s",
(longlong) thd->thread_id,
- (thd->db ? thd->db : "(null)"), thd->query());
+ thd->get_db(), thd->query());
rcode = WSREP_TRX_FAIL;
} else if (rcode == WSREP_BF_ABORT) {
WSREP_DEBUG("thd: %lld seqno: %lld BF aborted by provider, will replay",
@@ -544,6 +554,9 @@ wsrep_run_wsrep_commit(THD *thd, bool all)
DEBUG_SYNC(thd, "wsrep_after_replication");
+ DBUG_PRINT("info", ("rcode: %d wsrep_conflict_state: %d",
+ rcode, thd->wsrep_conflict_state));
+
switch(rcode) {
case 0:
/*
diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc
index f95ef168a23..cfba0ace2cb 100644
--- a/sql/wsrep_mysqld.cc
+++ b/sql/wsrep_mysqld.cc
@@ -13,7 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <sql_plugin.h> // SHOW_MY_BOOL
+#include "sql_plugin.h" /* wsrep_plugins_pre_init() */
#include <mysqld.h>
#include <sql_class.h>
#include <sql_parse.h>
@@ -37,8 +37,6 @@
#include <cstdio>
#include <cstdlib>
#include "log_event.h"
-#include "sql_plugin.h" /* wsrep_plugins_pre_init() */
-#include <vector>
wsrep_t *wsrep = NULL;
/*
@@ -547,7 +545,7 @@ static void wsrep_synced_cb(void* app_ctx)
if (wsrep_restart_slave_activated)
{
int rcode;
- WSREP_INFO("MySQL slave restart");
+ WSREP_INFO("MariaDB slave restart");
wsrep_restart_slave_activated= FALSE;
mysql_mutex_lock(&LOCK_active_mi);
@@ -849,7 +847,7 @@ void wsrep_init_startup (bool first)
Pre-initialize global_system_variables.table_plugin with a dummy engine
(placeholder) required during the initialization of wsrep threads (THDs).
(see: plugin_thdvar_init())
- Note: This only needs to be done for rsync & xtrabackup based SST methods.
+ Note: This only needs to be done for rsync & mariabackup based SST methods.
In case of mysqldump SST method, the wsrep threads are created after the
server plugins & global system variables are initialized.
*/
@@ -1175,7 +1173,7 @@ static bool wsrep_prepare_key_for_isolation(const char* db,
}
-static bool wsrep_prepare_keys_for_alter_add_fk(char* child_table_db,
+static bool wsrep_prepare_keys_for_alter_add_fk(const char* child_table_db,
Alter_info* alter_info,
wsrep_key_arr_t* ka)
{
@@ -1220,13 +1218,13 @@ static bool wsrep_prepare_keys_for_isolation(THD* thd,
for (const TABLE_LIST* table= table_list; table; table= table->next_global)
{
- if (!wsrep_prepare_key_for_isolation(table->db, table->table_name, ka))
+ if (!wsrep_prepare_key_for_isolation(table->db.str, table->table_name.str, ka))
goto err;
}
- if (alter_info && (alter_info->flags & (Alter_info::ADD_FOREIGN_KEY)))
+ if (alter_info && (alter_info->flags & (ALTER_ADD_FOREIGN_KEY)))
{
- if (!wsrep_prepare_keys_for_alter_add_fk(table_list->db, alter_info, ka))
+ if (!wsrep_prepare_keys_for_alter_add_fk(table_list->db.str, alter_info, ka))
goto err;
}
@@ -1301,7 +1299,7 @@ int wsrep_to_buf_helper(
THD* thd, const char *query, uint query_len, uchar** buf, size_t* buf_len)
{
IO_CACHE tmp_io_cache;
- Log_event_writer writer(&tmp_io_cache);
+ Log_event_writer writer(&tmp_io_cache,0);
if (open_cached_file(&tmp_io_cache, mysql_tmpdir, TEMP_PREFIX,
65536, MYF(MY_WME)))
return 1;
@@ -1376,7 +1374,7 @@ static int wsrep_alter_event_query(THD *thd, uchar** buf, size_t* buf_len)
if (wsrep_alter_query_string(thd, &log_query))
{
WSREP_WARN("events alter string failed: schema: %s, query: %s",
- (thd->db ? thd->db : "(null)"), thd->query());
+ thd->get_db(), thd->query());
return 1;
}
return wsrep_to_buf_helper(thd, log_query.ptr(), log_query.length(), buf, buf_len);
@@ -1390,22 +1388,17 @@ create_view_query(THD *thd, uchar** buf, size_t* buf_len)
SELECT_LEX *select_lex= &lex->select_lex;
TABLE_LIST *first_table= select_lex->table_list.first;
TABLE_LIST *views = first_table;
-
+ LEX_USER *definer;
String buff;
- const LEX_STRING command[3]=
- {{ C_STRING_WITH_LEN("CREATE ") },
- { C_STRING_WITH_LEN("ALTER ") },
- { C_STRING_WITH_LEN("CREATE OR REPLACE ") }};
+ const LEX_CSTRING command[3]=
+ {{ STRING_WITH_LEN("CREATE ") },
+ { STRING_WITH_LEN("ALTER ") },
+ { STRING_WITH_LEN("CREATE OR REPLACE ") }};
- buff.append(command[thd->lex->create_view_mode].str,
- command[thd->lex->create_view_mode].length);
-
- LEX_USER *definer;
+ buff.append(&command[thd->lex->create_view->mode]);
if (lex->definer)
- {
definer= get_current_user(thd, lex->definer);
- }
else
{
/*
@@ -1426,39 +1419,37 @@ create_view_query(THD *thd, uchar** buf, size_t* buf_len)
return 1;
}
- views->algorithm = lex->create_view_algorithm;
- views->view_suid = lex->create_view_suid;
- views->with_check = lex->create_view_check;
+ views->algorithm = lex->create_view->algorithm;
+ views->view_suid = lex->create_view->suid;
+ views->with_check = lex->create_view->check;
view_store_options(thd, views, &buff);
buff.append(STRING_WITH_LEN("VIEW "));
/* Test if user supplied a db (ie: we did not use thd->db) */
- if (views->db && views->db[0] &&
- (thd->db == NULL || strcmp(views->db, thd->db)))
+ if (views->db.str && views->db.str[0] &&
+ (thd->db.str == NULL || cmp(&views->db, &thd->db)))
{
- append_identifier(thd, &buff, views->db,
- views->db_length);
+ append_identifier(thd, &buff, &views->db);
buff.append('.');
}
- append_identifier(thd, &buff, views->table_name,
- views->table_name_length);
+ append_identifier(thd, &buff, &views->table_name);
if (lex->view_list.elements)
{
- List_iterator_fast<LEX_STRING> names(lex->view_list);
- LEX_STRING *name;
+ List_iterator_fast<LEX_CSTRING> names(lex->view_list);
+ LEX_CSTRING *name;
int i;
for (i= 0; (name= names++); i++)
{
buff.append(i ? ", " : "(");
- append_identifier(thd, &buff, name->str, name->length);
+ append_identifier(thd, &buff, name);
}
buff.append(')');
}
buff.append(STRING_WITH_LEN(" AS "));
//buff.append(views->source.str, views->source.length);
- buff.append(thd->lex->create_view_select.str,
- thd->lex->create_view_select.length);
+ buff.append(thd->lex->create_view->select.str,
+ thd->lex->create_view->select.length);
//int errcode= query_error_code(thd, TRUE);
//if (thd->binlog_query(THD::STMT_QUERY_TYPE,
// buff.ptr(), buff.length(), FALSE, FALSE, FALSE, errcod
@@ -1485,7 +1476,7 @@ static int wsrep_drop_table_query(THD* thd, uchar** buf, size_t* buf_len)
bool found_temp_table= false;
for (TABLE_LIST* table= first_table; table; table= table->next_global)
{
- if (thd->find_temporary_table(table->db, table->table_name))
+ if (thd->find_temporary_table(table->db.str, table->table_name.str))
{
found_temp_table= true;
break;
@@ -1500,12 +1491,12 @@ static int wsrep_drop_table_query(THD* thd, uchar** buf, size_t* buf_len)
for (TABLE_LIST* table= first_table; table; table= table->next_global)
{
- if (!thd->find_temporary_table(table->db, table->table_name))
+ if (!thd->find_temporary_table(table->db.str, table->table_name.str))
{
- append_identifier(thd, &buff, table->db, strlen(table->db));
+ append_identifier(thd, &buff, table->db.str, table->db.length);
buff.append(".");
- append_identifier(thd, &buff, table->table_name,
- strlen(table->table_name));
+ append_identifier(thd, &buff,
+ table->table_name.str, table->table_name.length);
buff.append(",");
}
}
@@ -1638,7 +1629,7 @@ static bool wsrep_can_run_in_toi(THD *thd, const char *db, const char *table,
{
for (TABLE_LIST* table= first_table; table; table= table->next_global)
{
- if (!thd->find_temporary_table(table->db, table->table_name))
+ if (!thd->find_temporary_table(table->db.str, table->table_name.str))
{
return true;
}
@@ -1654,7 +1645,7 @@ static bool wsrep_can_run_in_toi(THD *thd, const char *db, const char *table,
1: TOI replication was skipped
-1: TOI replication failed
*/
-static int wsrep_TOI_begin(THD *thd, char *db_, char *table_,
+static int wsrep_TOI_begin(THD *thd, const char *db_, const char *table_,
const TABLE_LIST* table_list,
Alter_info* alter_info)
{
@@ -1728,7 +1719,7 @@ static int wsrep_TOI_begin(THD *thd, char *db_, char *table_,
WSREP_WARN("TO isolation failed for: %d, schema: %s, sql: %s. Check wsrep "
"connection state and retry the query.",
ret,
- (thd->db ? thd->db : "(null)"),
+ thd->get_db(),
(thd->query()) ? thd->query() : "void");
my_message(ER_LOCK_DEADLOCK, "WSREP replication failed. Check "
"your wsrep connection state and retry the query.", MYF(0));
@@ -1764,12 +1755,12 @@ static void wsrep_TOI_end(THD *thd) {
else {
WSREP_WARN("TO isolation end failed for: %d, schema: %s, sql: %s",
ret,
- (thd->db ? thd->db : "(null)"),
+ thd->get_db(),
(thd->query()) ? thd->query() : "void");
}
}
-static int wsrep_RSU_begin(THD *thd, char *db_, char *table_)
+static int wsrep_RSU_begin(THD *thd, const char *db_, const char *table_)
{
wsrep_status_t ret(WSREP_WARNING);
WSREP_DEBUG("RSU BEGIN: %lld, %d : %s", (long long)wsrep_thd_trx_seqno(thd),
@@ -1779,7 +1770,7 @@ static int wsrep_RSU_begin(THD *thd, char *db_, char *table_)
if (ret != WSREP_OK)
{
WSREP_WARN("RSU desync failed %d for schema: %s, query: %s",
- ret, (thd->db ? thd->db : "(null)"), thd->query());
+ ret, thd->get_db(), thd->query());
my_error(ER_LOCK_DEADLOCK, MYF(0));
return(ret);
}
@@ -1792,8 +1783,7 @@ static int wsrep_RSU_begin(THD *thd, char *db_, char *table_)
{
/* no can do, bail out from DDL */
WSREP_WARN("RSU failed due to pending transactions, schema: %s, query %s",
- (thd->db ? thd->db : "(null)"),
- thd->query());
+ thd->get_db(), thd->query());
mysql_mutex_lock(&LOCK_wsrep_replaying);
wsrep_replaying--;
mysql_mutex_unlock(&LOCK_wsrep_replaying);
@@ -1802,7 +1792,7 @@ static int wsrep_RSU_begin(THD *thd, char *db_, char *table_)
if (ret != WSREP_OK)
{
WSREP_WARN("resync failed %d for schema: %s, query: %s",
- ret, (thd->db ? thd->db : "(null)"), thd->query());
+ ret, thd->get_db(), thd->query());
}
my_error(ER_LOCK_DEADLOCK, MYF(0));
@@ -1813,8 +1803,7 @@ static int wsrep_RSU_begin(THD *thd, char *db_, char *table_)
if (seqno == WSREP_SEQNO_UNDEFINED)
{
WSREP_WARN("pause failed %lld for schema: %s, query: %s", (long long)seqno,
- (thd->db ? thd->db : "(null)"),
- thd->query());
+ thd->get_db(), thd->query());
return(1);
}
WSREP_DEBUG("paused at %lld", (long long)seqno);
@@ -1837,22 +1826,21 @@ static void wsrep_RSU_end(THD *thd)
if (ret != WSREP_OK)
{
WSREP_WARN("resume failed %d for schema: %s, query: %s", ret,
- (thd->db ? thd->db : "(null)"),
- thd->query());
+ thd->get_db(), thd->query());
}
ret = wsrep->resync(wsrep);
if (ret != WSREP_OK)
{
WSREP_WARN("resync failed %d for schema: %s, query: %s", ret,
- (thd->db ? thd->db : "(null)"), thd->query());
+ thd->get_db(), thd->query());
return;
}
thd->variables.wsrep_on = 1;
}
-int wsrep_to_isolation_begin(THD *thd, char *db_, char *table_,
+int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_,
const TABLE_LIST* table_list,
Alter_info* alter_info)
{
@@ -1869,9 +1857,7 @@ int wsrep_to_isolation_begin(THD *thd, char *db_, char *table_,
if (thd->wsrep_conflict_state == MUST_ABORT)
{
WSREP_INFO("thread: %lld schema: %s query: %s has been aborted due to multi-master conflict",
- (longlong) thd->thread_id,
- (thd->db ? thd->db : "(null)"),
- thd->query());
+ (longlong) thd->thread_id, thd->get_db(), thd->query());
mysql_mutex_unlock(&thd->LOCK_thd_data);
return WSREP_TRX_FAIL;
}
@@ -2042,7 +2028,8 @@ bool wsrep_grant_mdl_exception(MDL_context *requestor_ctx,
/* Print some debug information. */
if (wsrep_debug)
{
- if (request_thd->lex->sql_command == SQLCOM_DROP_TABLE)
+ if (request_thd->lex->sql_command == SQLCOM_DROP_TABLE ||
+ request_thd->lex->sql_command == SQLCOM_DROP_SEQUENCE)
{
WSREP_DEBUG("DROP caused BF abort, conf %d", granted_thd->wsrep_conflict_state);
}
@@ -2116,7 +2103,6 @@ pthread_handler_t start_wsrep_THD(void *arg)
close_connection(thd, ER_OUT_OF_RESOURCES);
statistic_increment(aborted_connects,&LOCK_status);
MYSQL_CALLBACK(thread_scheduler, end_thread, (thd, 0));
-
goto error;
}
@@ -2129,7 +2115,7 @@ pthread_handler_t start_wsrep_THD(void *arg)
need to know the start of the stack so that we could check for
stack overruns.
*/
- DBUG_PRINT("wsrep", ("handle_one_connection called by thread %lld\n",
+ DBUG_PRINT("wsrep", ("handle_one_connection called by thread %lld",
(long long)thd->thread_id));
/* now that we've called my_thread_init(), it is safe to call DBUG_* */
@@ -2139,7 +2125,6 @@ pthread_handler_t start_wsrep_THD(void *arg)
close_connection(thd, ER_OUT_OF_RESOURCES);
statistic_increment(aborted_connects,&LOCK_status);
MYSQL_CALLBACK(thread_scheduler, end_thread, (thd, 0));
- delete thd;
goto error;
}
@@ -2214,13 +2199,8 @@ pthread_handler_t start_wsrep_THD(void *arg)
// at server shutdown
}
- if (thread_handling > SCHEDULER_ONE_THREAD_PER_CONNECTION)
- {
- mysql_mutex_lock(&LOCK_thread_count);
- thd->unlink();
- mysql_mutex_unlock(&LOCK_thread_count);
- delete thd;
- }
+ unlink_not_visible_thd(thd);
+ delete thd;
my_thread_end();
return(NULL);
@@ -2547,29 +2527,27 @@ static int wsrep_create_sp(THD *thd, uchar** buf, size_t* buf_len)
sp_head *sp = thd->lex->sphead;
sql_mode_t saved_mode= thd->variables.sql_mode;
String retstr(64);
+ LEX_CSTRING returns= empty_clex_str;
retstr.set_charset(system_charset_info);
log_query.set_charset(system_charset_info);
- if (sp->m_type == TYPE_ENUM_FUNCTION)
+ if (sp->m_handler->type() == TYPE_ENUM_FUNCTION)
{
sp_returns_type(thd, retstr, sp);
- }
-
- if (!show_create_sp(thd, &log_query,
- sp->m_type,
- (sp->m_explicit_name ? sp->m_db.str : NULL),
- (sp->m_explicit_name ? sp->m_db.length : 0),
- sp->m_name.str, sp->m_name.length,
- sp->m_params.str, sp->m_params.length,
- retstr.c_ptr(), retstr.length(),
- sp->m_body.str, sp->m_body.length,
- sp->m_chistics, &(thd->lex->definer->user),
- &(thd->lex->definer->host),
+ returns= retstr.lex_cstring();
+ }
+ if (sp->m_handler->
+ show_create_sp(thd, &log_query,
+ sp->m_explicit_name ? sp->m_db : null_clex_str,
+ sp->m_name, sp->m_params, returns,
+ sp->m_body, sp->chistics(),
+ thd->lex->definer[0],
+ thd->lex->create_info,
saved_mode))
{
WSREP_WARN("SP create string failed: schema: %s, query: %s",
- (thd->db ? thd->db : "(null)"), thd->query());
+ thd->get_db(), thd->query());
return 1;
}
@@ -2762,9 +2740,7 @@ extern "C" void wsrep_thd_awake(THD *thd, my_bool signal)
{
if (signal)
{
- mysql_mutex_lock(&thd->LOCK_thd_data);
thd->awake(KILL_QUERY);
- mysql_mutex_unlock(&thd->LOCK_thd_data);
}
else
{
@@ -2847,8 +2823,8 @@ bool wsrep_create_like_table(THD* thd, TABLE_LIST* table,
}
else if (!(thd->find_temporary_table(src_table)))
{
- /* this is straight CREATE TABLE LIKE... eith no tmp tables */
- WSREP_TO_ISOLATION_BEGIN(table->db, table->table_name, NULL);
+ /* this is straight CREATE TABLE LIKE... with no tmp tables */
+ WSREP_TO_ISOLATION_BEGIN(table->db.str, table->table_name.str, NULL);
}
else
{
@@ -2871,7 +2847,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, table->table_name, NULL);
+ WSREP_TO_ISOLATION_BEGIN(table->db.str, table->table_name.str, NULL);
thd->wsrep_TOI_pre_query= NULL;
thd->wsrep_TOI_pre_query_len= 0;
@@ -2890,8 +2866,8 @@ static int wsrep_create_trigger_query(THD *thd, uchar** buf, size_t* buf_len)
LEX *lex= thd->lex;
String stmt_query;
- LEX_STRING definer_user;
- LEX_STRING definer_host;
+ LEX_CSTRING definer_user;
+ LEX_CSTRING definer_host;
if (!lex->definer)
{
@@ -2928,12 +2904,11 @@ static int wsrep_create_trigger_query(THD *thd, uchar** buf, size_t* buf_len)
append_definer(thd, &stmt_query, &definer_user, &definer_host);
- LEX_STRING stmt_definition;
- uint not_used;
+ LEX_CSTRING stmt_definition;
stmt_definition.str= (char*) thd->lex->stmt_definition_begin;
stmt_definition.length= thd->lex->stmt_definition_end
- thd->lex->stmt_definition_begin;
- trim_whitespace(thd->charset(), &stmt_definition, &not_used);
+ trim_whitespace(thd->charset(), &stmt_definition);
stmt_query.append(stmt_definition.str, stmt_definition.length);
@@ -2996,6 +2971,7 @@ void wsrep_unlock_rollback()
my_bool wsrep_aborting_thd_contains(THD *thd)
{
+ mysql_mutex_assert_owner(&LOCK_wsrep_rollback);
wsrep_aborting_thd_t abortees = wsrep_aborting_thd;
while (abortees)
{
@@ -3008,6 +2984,7 @@ my_bool wsrep_aborting_thd_contains(THD *thd)
void wsrep_aborting_thd_enqueue(THD *thd)
{
+ mysql_mutex_assert_owner(&LOCK_wsrep_rollback);
wsrep_aborting_thd_t aborting = (wsrep_aborting_thd_t)
my_malloc(sizeof(struct wsrep_aborting_thd), MYF(0));
aborting->aborting_thd = thd;
diff --git a/sql/wsrep_mysqld.h b/sql/wsrep_mysqld.h
index e28b90885b4..55ea032e835 100644
--- a/sql/wsrep_mysqld.h
+++ b/sql/wsrep_mysqld.h
@@ -50,7 +50,7 @@ struct wsrep_thd_shadow {
enum wsrep_exec_mode wsrep_exec_mode;
Vio *vio;
ulong tx_isolation;
- char *db;
+ const char *db;
size_t db_length;
my_hrtime_t user_time;
longlong row_count_func;
@@ -290,7 +290,7 @@ extern PSI_thread_key key_wsrep_applier;
struct TABLE_LIST;
class Alter_info;
-int wsrep_to_isolation_begin(THD *thd, char *db_, char *table_,
+int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_,
const TABLE_LIST* table_list,
Alter_info* alter_info = NULL);
void wsrep_to_isolation_end(THD *thd);
@@ -367,7 +367,6 @@ void wsrep_keys_free(wsrep_key_arr_t* key_arr);
#define WSREP_FORMAT(my_format) ((ulong)my_format)
#define WSREP_PROVIDER_EXISTS (0)
#define wsrep_emulate_bin_log (0)
-#define wsrep_xid_seqno(X) (0)
#define wsrep_to_isolation (0)
#define wsrep_init() (1)
#define wsrep_prepend_PATH(X)
diff --git a/sql/wsrep_notify.cc b/sql/wsrep_notify.cc
index fb04d7be7c1..4a8d2a392f9 100644
--- a/sql/wsrep_notify.cc
+++ b/sql/wsrep_notify.cc
@@ -13,6 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
+#include "mariadb.h"
#include <mysqld.h>
#include "wsrep_priv.h"
#include "wsrep_utils.h"
diff --git a/sql/wsrep_sst.cc b/sql/wsrep_sst.cc
index 9472d66f4d3..714df35de8b 100644
--- a/sql/wsrep_sst.cc
+++ b/sql/wsrep_sst.cc
@@ -13,13 +13,12 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
+#include "mariadb.h"
#include "wsrep_sst.h"
-
#include <inttypes.h>
#include <ctype.h>
#include <mysqld.h>
#include <m_ctype.h>
-#include <my_sys.h>
#include <strfunc.h>
#include <sql_class.h>
#include <set_var.h>
@@ -62,11 +61,6 @@ bool wsrep_sst_method_check (sys_var *self, THD* thd, set_var* var)
return 0;
}
-bool wsrep_sst_method_update (sys_var *self, THD* thd, enum_var_type type)
-{
- return 0;
-}
-
static const char* data_home_dir = NULL;
void wsrep_set_data_home_dir(const char *data_dir)
@@ -1130,6 +1124,7 @@ ssize_t wsrep_sst_prepare (void** msg)
{
const char* addr_in= NULL;
const char* addr_out= NULL;
+ const char* method;
if (!strcmp(wsrep_sst_method, WSREP_SST_SKIP))
{
@@ -1188,7 +1183,8 @@ ssize_t wsrep_sst_prepare (void** msg)
}
ssize_t addr_len= -ENOSYS;
- if (!strcmp(wsrep_sst_method, WSREP_SST_MYSQLDUMP))
+ method = wsrep_sst_method;
+ if (!strcmp(method, WSREP_SST_MYSQLDUMP))
{
addr_len= sst_prepare_mysqldump (addr_in, &addr_out);
if (addr_len < 0) unireg_abort(1);
@@ -1198,6 +1194,13 @@ ssize_t wsrep_sst_prepare (void** msg)
/*! A heuristic workaround until we learn how to stop and start engines */
if (SE_initialized)
{
+ if (!strcmp(method, WSREP_SST_XTRABACKUP) ||
+ !strcmp(method, WSREP_SST_XTRABACKUPV2))
+ {
+ WSREP_WARN("The %s SST method is deprecated, so it is automatically "
+ "replaced by %s", method, WSREP_SST_MARIABACKUP);
+ method = WSREP_SST_MARIABACKUP;
+ }
// we already did SST at initializaiton, now engines are running
// sql_print_information() is here because the message is too long
// for WSREP_INFO.
@@ -1207,28 +1210,28 @@ ssize_t wsrep_sst_prepare (void** msg)
"Wsrep provider won't be able to fall back to it "
"if other means of state transfer are unavailable. "
"In that case you will need to restart the server.",
- wsrep_sst_method);
+ method);
*msg = 0;
return 0;
}
- addr_len = sst_prepare_other (wsrep_sst_method, sst_auth_real,
+ addr_len = sst_prepare_other (method, sst_auth_real,
addr_in, &addr_out);
if (addr_len < 0)
{
WSREP_ERROR("Failed to prepare for '%s' SST. Unrecoverable.",
- wsrep_sst_method);
+ method);
unireg_abort(1);
}
}
- size_t const method_len(strlen(wsrep_sst_method));
+ size_t const method_len(strlen(method));
size_t const msg_len (method_len + addr_len + 2 /* + auth_len + 1*/);
*msg = malloc (msg_len);
if (NULL != *msg) {
char* const method_ptr(reinterpret_cast<char*>(*msg));
- strcpy (method_ptr, wsrep_sst_method);
+ strcpy (method_ptr, method);
char* const addr_ptr(method_ptr + method_len + 1);
strcpy (addr_ptr, addr_out);
diff --git a/sql/wsrep_sst.h b/sql/wsrep_sst.h
index 063cab5f0f1..38706fa4671 100644
--- a/sql/wsrep_sst.h
+++ b/sql/wsrep_sst.h
@@ -50,6 +50,9 @@
#define WSREP_SST_MYSQLDUMP "mysqldump"
#define WSREP_SST_RSYNC "rsync"
#define WSREP_SST_SKIP "skip"
+#define WSREP_SST_MARIABACKUP "mariabackup"
+#define WSREP_SST_XTRABACKUP "xtrabackup"
+#define WSREP_SST_XTRABACKUPV2 "xtrabackupv2"
#define WSREP_SST_DEFAULT WSREP_SST_RSYNC
#define WSREP_SST_ADDRESS_AUTO "AUTO"
#define WSREP_SST_AUTH_MASK "********"
diff --git a/sql/wsrep_thd.cc b/sql/wsrep_thd.cc
index 1e60088c5f1..9515fd550f2 100644
--- a/sql/wsrep_thd.cc
+++ b/sql/wsrep_thd.cc
@@ -13,8 +13,8 @@
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA. */
+#include "mariadb.h"
#include "wsrep_thd.h"
-
#include "transaction.h"
#include "rpl_rli.h"
#include "log_event.h"
@@ -105,7 +105,7 @@ static rpl_group_info* wsrep_relay_group_init(THD *thd, const char* log_fname)
new Format_description_log_event(4);
}
- static LEX_STRING connection_name= { C_STRING_WITH_LEN("wsrep") };
+ static LEX_CSTRING connection_name= { STRING_WITH_LEN("wsrep") };
/*
Master_info's constructor initializes rpl_filter by either an already
@@ -167,22 +167,23 @@ static void wsrep_prepare_bf_thd(THD *thd, struct wsrep_thd_shadow* shadow)
thd->variables.tx_isolation = ISO_READ_COMMITTED;
thd->tx_isolation = ISO_READ_COMMITTED;
- shadow->db = thd->db;
- shadow->db_length = thd->db_length;
+ shadow->db = thd->db.str;
+ shadow->db_length = thd->db.length;
shadow->user_time = thd->user_time;
shadow->row_count_func= thd->get_row_count_func();
- thd->reset_db(NULL, 0);
+ thd->reset_db(&null_clex_str);
}
static void wsrep_return_from_bf_mode(THD *thd, struct wsrep_thd_shadow* shadow)
{
+ LEX_CSTRING db= {shadow->db, shadow->db_length };
thd->variables.option_bits = shadow->options;
thd->server_status = shadow->server_status;
thd->wsrep_exec_mode = shadow->wsrep_exec_mode;
thd->net.vio = shadow->vio;
thd->variables.tx_isolation = shadow->tx_isolation;
thd->user_time = shadow->user_time;
- thd->reset_db(shadow->db, shadow->db_length);
+ thd->reset_db(&db);
if (!thd->slave_thread)
delete thd->system_thread_info.rpl_sql_info;
@@ -278,7 +279,7 @@ void wsrep_replay_sp_transaction(THD* thd)
default:
WSREP_ERROR("trx_replay failed for: %d, schema: %s, query: %s",
rcode,
- (thd->db ? thd->db : "(null)"),
+ (thd->db.str ? thd->db.str : "(null)"),
WSREP_QUERY(thd));
/* we're now in inconsistent state, must abort */
mysql_mutex_unlock(&thd->LOCK_thd_data);
@@ -359,7 +360,7 @@ void wsrep_replay_transaction(THD *thd)
MYSQL_END_STATEMENT(thd->m_statement_psi, thd->get_stmt_da());
thd->m_statement_psi= NULL;
thd->m_digest= NULL;
- thd_proc_info(thd, "wsrep replaying trx");
+ thd_proc_info(thd, "WSREP replaying trx");
WSREP_DEBUG("replay trx: %s %lld",
thd->query() ? thd->query() : "void",
(long long)wsrep_thd_trx_seqno(thd));
@@ -423,15 +424,13 @@ void wsrep_replay_transaction(THD *thd)
else
{
WSREP_DEBUG("replay failed, rolling back");
- //my_error(ER_LOCK_DEADLOCK, MYF(0), "wsrep aborted transaction");
}
thd->wsrep_conflict_state= ABORTED;
wsrep->post_rollback(wsrep, &thd->wsrep_ws_handle);
break;
default:
WSREP_ERROR("trx_replay failed for: %d, schema: %s, query: %s",
- rcode,
- (thd->db ? thd->db : "(null)"),
+ rcode, thd->get_db(),
thd->query() ? thd->query() : "void");
/* we're now in inconsistent state, must abort */
@@ -610,7 +609,7 @@ static void wsrep_rollback_process(THD *thd)
wsrep_aborting_thd= NULL;
while (thd->killed == NOT_KILLED) {
- thd_proc_info(thd, "wsrep aborter idle");
+ thd_proc_info(thd, "WSREP aborter idle");
thd->mysys_var->current_mutex= &LOCK_wsrep_rollback;
thd->mysys_var->current_cond= &COND_wsrep_rollback;
@@ -619,7 +618,7 @@ static void wsrep_rollback_process(THD *thd)
WSREP_DEBUG("WSREP rollback thread wakes for signal");
mysql_mutex_lock(&thd->mysys_var->mutex);
- thd_proc_info(thd, "wsrep aborter active");
+ thd_proc_info(thd, "WSREP aborter active");
thd->mysys_var->current_mutex= 0;
thd->mysys_var->current_cond= 0;
mysql_mutex_unlock(&thd->mysys_var->mutex);
diff --git a/sql/wsrep_utils.cc b/sql/wsrep_utils.cc
index 599ece4cb40..6230bcbe529 100644
--- a/sql/wsrep_utils.cc
+++ b/sql/wsrep_utils.cc
@@ -20,6 +20,7 @@
#define _GNU_SOURCE // POSIX_SPAWN_USEVFORK flag
#endif
+#include "mariadb.h"
#include "wsrep_utils.h"
#include "wsrep_mysqld.h"
diff --git a/sql/wsrep_var.cc b/sql/wsrep_var.cc
index 9777cc6ec62..f18dc565329 100644
--- a/sql/wsrep_var.cc
+++ b/sql/wsrep_var.cc
@@ -651,9 +651,8 @@ bool wsrep_desync_check (sys_var *self, THD* thd, set_var* var)
if (new_wsrep_desync) {
ret = wsrep->desync (wsrep);
if (ret != WSREP_OK) {
- WSREP_WARN ("SET desync failed %d for schema: %s, query: %s", ret,
- (thd->db ? thd->db : "(null)"),
- thd->query());
+ WSREP_WARN ("SET desync failed %d for schema: %s, query: %s",
+ ret, thd->get_db(), thd->query());
my_error (ER_CANNOT_USER, MYF(0), "'desync'", thd->query());
return true;
}
@@ -661,8 +660,7 @@ bool wsrep_desync_check (sys_var *self, THD* thd, set_var* var)
ret = wsrep->resync (wsrep);
if (ret != WSREP_OK) {
WSREP_WARN ("SET resync failed %d for schema: %s, query: %s", ret,
- (thd->db ? thd->db : "(null)"),
- thd->query());
+ thd->get_db(), thd->query());
my_error (ER_CANNOT_USER, MYF(0), "'resync'", thd->query());
return true;
}
@@ -692,7 +690,7 @@ bool wsrep_max_ws_size_update (sys_var *self, THD *thd, enum_var_type)
char max_ws_size_opt[128];
my_snprintf(max_ws_size_opt, sizeof(max_ws_size_opt),
- "repl.max_ws_size=%d", wsrep_max_ws_size);
+ "repl.max_ws_size=%lu", wsrep_max_ws_size);
wsrep_status_t ret= wsrep->options_set(wsrep, max_ws_size_opt);
if (ret != WSREP_OK)
{
@@ -771,4 +769,3 @@ int wsrep_show_status (THD *thd, SHOW_VAR *var, char *buff,
v->name= 0; // terminator
return 0;
}
-
diff --git a/sql/wsrep_xid.cc b/sql/wsrep_xid.cc
index ffb280b2032..923abe53c6c 100644
--- a/sql/wsrep_xid.cc
+++ b/sql/wsrep_xid.cc
@@ -16,6 +16,7 @@
//! @file some utility functions and classes not directly related to replication
+#include "mariadb.h"
#include "wsrep_xid.h"
#include "sql_class.h"
#include "wsrep_mysqld.h" // for logging macros
@@ -26,8 +27,11 @@
* WSREPXid
*/
-#define WSREP_XID_PREFIX "WSREPXid"
-#define WSREP_XID_PREFIX_LEN MYSQL_XID_PREFIX_LEN
+#define WSREP_XID_PREFIX "WSREPXi"
+#define WSREP_XID_PREFIX_LEN 7
+#define WSREP_XID_VERSION_OFFSET WSREP_XID_PREFIX_LEN
+#define WSREP_XID_VERSION_1 'd'
+#define WSREP_XID_VERSION_2 'e'
#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))
@@ -39,8 +43,9 @@ void wsrep_xid_init(XID* xid, const wsrep_uuid_t& uuid, wsrep_seqno_t seqno)
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;
memcpy(xid->data + WSREP_XID_UUID_OFFSET, &uuid, sizeof(wsrep_uuid_t));
- memcpy(xid->data + WSREP_XID_SEQNO_OFFSET, &seqno, sizeof(wsrep_seqno_t));
+ int8store(xid->data + WSREP_XID_SEQNO_OFFSET,seqno);
}
int wsrep_is_wsrep_xid(const XID* xid)
@@ -48,7 +53,9 @@ int wsrep_is_wsrep_xid(const XID* xid)
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));
+ !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));
}
const wsrep_uuid_t* wsrep_xid_uuid(const XID& xid)
@@ -60,18 +67,36 @@ const wsrep_uuid_t* wsrep_xid_uuid(const XID& xid)
return &WSREP_UUID_UNDEFINED;
}
+const unsigned char* wsrep_xid_uuid(const xid_t* xid)
+{
+ DBUG_ASSERT(xid);
+ return wsrep_xid_uuid(*xid)->data;
+}
+
wsrep_seqno_t wsrep_xid_seqno(const XID& xid)
{
+ wsrep_seqno_t ret= WSREP_SEQNO_UNDEFINED;
if (wsrep_is_wsrep_xid(&xid))
{
- wsrep_seqno_t seqno;
- memcpy(&seqno, xid.data + WSREP_XID_SEQNO_OFFSET, sizeof(wsrep_seqno_t));
- return seqno;
- }
- else
- {
- return WSREP_SEQNO_UNDEFINED;
+ switch (xid.data[WSREP_XID_VERSION_OFFSET])
+ {
+ case WSREP_XID_VERSION_1:
+ memcpy(&ret, xid.data + WSREP_XID_SEQNO_OFFSET, sizeof ret);
+ break;
+ case WSREP_XID_VERSION_2:
+ ret= sint8korr(xid.data + WSREP_XID_SEQNO_OFFSET);
+ break;
+ default:
+ break;
+ }
}
+ return ret;
+}
+
+long long wsrep_xid_seqno(const xid_t* xid)
+{
+ DBUG_ASSERT(xid);
+ return wsrep_xid_seqno(*xid);
}
static my_bool set_SE_checkpoint(THD* unused, plugin_ref plugin, void* arg)